第七色在线视频,2021少妇久久久久久久久久,亚洲欧洲精品成人久久av18,亚洲国产精品特色大片观看完整版,孙宇晨将参加特朗普的晚宴

為了賬號安全,請及時綁定郵箱和手機(jī)立即綁定
已解決430363個問題,去搜搜看,總會有你想問的

在 Golang 中為遞歸函數(shù)實現(xiàn)生成器(yield)的慣用方法

在 Golang 中為遞歸函數(shù)實現(xiàn)生成器(yield)的慣用方法

Go
湖上湖 2021-11-29 19:22:48
在 Python / Ruby / JavaScript / ECMAScript 6 中,可以使用yield語言提供的關(guān)鍵字編寫生成器函數(shù)。在 Go 中,它可以使用一個 goroutine 和一個通道來模擬。編碼以下代碼顯示了如何實現(xiàn)置換函數(shù)(abcd、abdc、acbd、acdb、...、dcba):// $src/lib/lib.gopackage lib// private, starts with lowercase "p"func permutateWithChannel(channel chan<- []string, strings, prefix []string) {    length := len(strings)    if length == 0 {        // Base case        channel <- prefix        return    }    // Recursive case    newStrings := make([]string, 0, length-1)    for i, s := range strings {        // Remove strings[i] and assign the result to newStringI        // Append strings[i] to newPrefixI        // Call the recursive case        newStringsI := append(newStrings, strings[:i]...)        newStringsI = append(newStringsI, strings[i+1:]...)        newPrefixI := append(prefix, s)        permutateWithChannel(channel, newStringsI, newPrefixI)    }}// public, starts with uppercase "P"func PermutateWithChannel(strings []string) chan []string {    channel := make(chan []string)    prefix := make([]string, 0, len(strings))    go func() {        permutateWithChannel(channel, strings, prefix)        close(channel)    }()    return channel}以下是它的使用方法:// $src/main.gopackage mainimport (    "./lib"    "fmt")var (    fruits  = []string{"apple", "banana", "cherry", "durian"}    banned = "durian")func main() {    channel := lib.PermutateWithChannel(fruits)    for myFruits := range channel {        fmt.Println(myFruits)        if myFruits[0] == banned {            close(channel)            //break        }    }}筆記:break不需要該語句(上面注釋),因為close(channel)會導(dǎo)致在下一次迭代中range返回false,循環(huán)將終止。問題如果調(diào)用者不需要所有的排列,則需要close()顯式地對通道進(jìn)行處理,否則通道不會被關(guān)閉,直到程序終止(發(fā)生資源泄漏)。另一方面,如果調(diào)用者需要所有的排列(即range循環(huán)直到結(jié)束),調(diào)用者必須不是close()通道。這是因為close()-ing 一個已經(jīng)關(guān)閉的通道會導(dǎo)致運(yùn)行時恐慌(請參閱規(guī)范中的此處)。但是,如果判斷是否應(yīng)該停止的邏輯不像上圖那么簡單,我覺得還是使用defer close(channel).問題實現(xiàn)這樣的生成器的慣用方法是什么?習(xí)慣上,誰應(yīng)該close()對通道負(fù)責(zé)——庫函數(shù)還是調(diào)用者?像下面這樣修改我的代碼是否是個好主意,以便調(diào)用者defer close()無論如何都要對頻道負(fù)責(zé)?
查看完整描述

3 回答

?
慕斯709654

TA貢獻(xiàn)1840條經(jīng)驗 獲得超5個贊

一、替代方案

前言:我會使用一個更簡單的生成器,因為問題不關(guān)心生成器的復(fù)雜性,而是關(guān)心生成器和消費(fèi)者之間的信號,以及消費(fèi)者本身的調(diào)用。這個簡單的生成器只生成從0to的整數(shù)9。


1.帶函數(shù)值

通過傳遞一個簡單的消費(fèi)者函數(shù),生成消費(fèi)者模式更加清晰,它還有一個優(yōu)點(diǎn),即如果需要中止或任何其他操作,它可以返回一個值信號。


由于在示例中只有一個事件要發(fā)出信號(“中止”),消費(fèi)者函數(shù)將具有bool返回類型,在需要中止時發(fā)出信號。


所以看這個簡單的例子,消費(fèi)者函數(shù)值傳遞給生成器:


func generate(process func(x int) bool) {

    for i := 0; i < 10; i++ {

        if process(i) {

            break

        }

    }

}


func main() {

    process := func(x int) bool {

        fmt.Println("Processing", x)

        return x == 3 // Terminate if x == 3

    }

    generate(process)

}

輸出(在Go Playground上試試):


Processing 0

Processing 1

Processing 2

Processing 3

請注意,使用者 ( process) 不需要是“本地”函數(shù),它可以在 之外聲明main(),例如,它可以是全局函數(shù)或來自另一個包的函數(shù)。


該解決方案的潛在缺點(diǎn)是它僅使用 1 個 goroutine 來生成和使用值。


2. 有渠道

如果你仍然想用頻道來做,你可以。請注意,由于通道是由生成器創(chuàng)建的,并且由于消費(fèi)者對從通道接收到的值進(jìn)行循環(huán)(最好使用for ... range構(gòu)造),因此生成器有責(zé)任關(guān)閉通道。解決此問題還允許您返回僅接收通道。


是的,關(guān)閉生成器中返回的通道最好作為延遲語句完成,因此即使生成器發(fā)生恐慌,消費(fèi)者也不會被阻塞。但請注意,這個延遲關(guān)閉不是在generate()函數(shù)中,而是在匿名函數(shù)中g(shù)enerate(),作為一個新的 goroutine啟動并執(zhí)行;否則通道將在返回之前關(guān)閉generate()- 根本沒有用......


如果您想從消費(fèi)者處向生成器發(fā)出信號(例如中止而不生成更多值),您可以使用例如另一個通道,該通道傳遞給生成器。由于生成器只會“偵聽”此通道,因此它也可以聲明為生成器的僅接收通道。如果你只需要發(fā)出一個事件信號(在我們的例子中是中止),不需要在這個通道上發(fā)送任何值,一個簡單的關(guān)閉就可以了。如果您需要向多個事件發(fā)送信號,可以通過在此通道上實際發(fā)送一個值來完成,即要執(zhí)行的事件/操作(其中中止可能是多個事件中的一個)。


并且您可以使用該select語句作為處理在返回通道上發(fā)送值并觀察傳遞給生成器的通道的慣用方式。


這是一個帶有abort頻道的解決方案:


func generate(abort <-chan struct{}) <-chan int {

    ch := make(chan int)

    go func() {

        defer close(ch)

        for i := 0; i < 10; i++ {

            select {

            case ch <- i:

                fmt.Println("Sent", i)

            case <-abort: // receive on closed channel can proceed immediately

                fmt.Println("Aborting")

                return

            }

        }

    }()

    return ch

}


func main() {

    abort := make(chan struct{})

    ch := generate(abort)

    for v := range ch {

        fmt.Println("Processing", v)

        if v == 3 { // Terminate if v == 3

            close(abort)

            break

        }

    }

    // Sleep to prevent termination so we see if other goroutine panics

    time.Sleep(time.Second)

}

輸出(在Go Playground上試試):


Sent 0

Processing 0

Processing 1

Sent 1

Sent 2

Processing 2

Processing 3

Sent 3

Aborting

這個解決方案的明顯優(yōu)勢是它已經(jīng)使用了 2 個 goroutines(1 個生成值,1 個消耗/處理它們),并且很容易擴(kuò)展它以使用任意數(shù)量的 goroutines 作為返回的通道來處理生成的值生成器可以同時從多個 goroutine 中使用 - 通道可以安全地同時接收,數(shù)據(jù)競爭不會發(fā)生,按照設(shè)計;更多閱讀:如果我正確使用通道,我是否需要使用互斥鎖?


二、未解決問題的答案

goroutine 上的“未捕獲”恐慌將結(jié)束 goroutine 的執(zhí)行,但不會導(dǎo)致資源泄漏問題。但是,如果作為單獨(dú)的 goroutine 執(zhí)行的函數(shù)會在非恐慌的情況下釋放由它分配的資源(在非延遲語句中),那么該代碼顯然不會運(yùn)行并且會導(dǎo)致例如資源泄漏。


您沒有觀察到這一點(diǎn),因為程序在主協(xié)程終止時終止(并且它不會等待其他非主協(xié)程完成 - 因此您的其他協(xié)程沒有機(jī)會恐慌)。請參閱規(guī)范:程序執(zhí)行。


但是要知道,panic()并且recover()是針對例外情況,它們不適用于諸如try-catchJava 中的異常和塊之類的一般用例。例如,應(yīng)該通過返回錯誤(并處理它們?。﹣肀苊饪只牛⑶铱只沤^對不應(yīng)該離開包的“邊界”(例如panic(),recover()可能有理由在包實現(xiàn)中使用,但恐慌狀態(tài)應(yīng)該被“捕獲” " 放在包裹里面,不要從里面拿出來)。


查看完整回答
反對 回復(fù) 2021-11-29
?
慕絲7291255

TA貢獻(xiàn)1859條經(jīng)驗 獲得超6個贊

在我看來,生成器通常只是內(nèi)部封閉的包裝器。像這樣的東西


package main


import "fmt"


// This function `generator` returns another function, which

// we define anonymously in the body of `generator`. The

// returned function _closes over_ the variable `data` to

// form a closure.

func generator(data int, permutation func(int) int, bound int) func() (int, bool) {

    return func() (int, bool) {

        data = permutation(data)

        return data, data < bound

    }

}


// permutation function

func increment(j int) int {

    j += 1

    return j

}


func main() {

    // We call `generator`, assigning the result (a function)

    // to `next`. This function value captures its

    // own `data` value, which will be updated each time

    // we call `next`.

    next := generator(1, increment, 7)

    // See the effect of the closure by calling `next`

    // a few times.

    fmt.Println(next())

    fmt.Println(next())

    fmt.Println(next())

    // To confirm that the state is unique to that

    // particular function, create and test a new one.

    for next, generation, ok := generator(11, increment, 17), 0, true; ok; {

        generation, ok = next()

        fmt.Println(generation)

    }

}

它看起來不像“范圍”那么優(yōu)雅,但對我來說在語義和句法上非常清晰。它有效http://play.golang.org/p/fz8xs0RYz9


查看完整回答
反對 回復(fù) 2021-11-29
?
守候你守候我

TA貢獻(xiàn)1802條經(jīng)驗 獲得超10個贊

我同意icza的回答??偨Y(jié)一下,有兩種選擇:


映射函數(shù):使用回調(diào)來迭代集合。. 這樣做的缺點(diǎn)是讓控制流發(fā)揮作用。不是 Pythonic 生成器,因為它不返回可迭代序列。func myIterationFn(yieldfunc (myType)) (stopIterating bool)myGeneratormyIterationFn

通道:使用通道并警惕泄漏的 goroutine??梢赞D(zhuǎn)換myIterationFn為返回可迭代序列的函數(shù)。以下代碼提供了此類轉(zhuǎn)換的示例。

myMapper := func(yield func(int) bool) {

    for i := 0; i < 5; i++ {

        if done := yield(i); done {

            return

        }

    }

}

iter, cancel := mapperToIterator(myMapper)

defer cancel() // This line is very important - it prevents goroutine leaks.

for value, ok := iter(); ok; value, ok = iter() {

    fmt.Printf("value: %d\n", value)

}

這里以一個完整的程序為例。mapperToIterator做從映射函數(shù)到生成器的轉(zhuǎn)換。Go 缺乏泛型需要從interface{}to 進(jìn)行轉(zhuǎn)換int。


package main


import "fmt"


// yieldFn reports true if an iteration should continue. It is called on values

// of a collection.

type yieldFn func(interface{}) (stopIterating bool)


// mapperFn calls yieldFn for each member of a collection.

type mapperFn func(yieldFn)


// iteratorFn returns the next item in an iteration or the zero value. The

// second return value is true when iteration is complete.

type iteratorFn func() (value interface{}, done bool)


// cancelFn should be called to clean up the goroutine that would otherwise leak.

type cancelFn func()


// mapperToIterator returns an iteratorFn version of a mappingFn. The second

// return value must be called at the end of iteration, or the underlying

// goroutine will leak.

func mapperToIterator(m mapperFn) (iteratorFn, cancelFn) {

    generatedValues := make(chan interface{}, 1)

    stopCh := make(chan interface{}, 1)

    go func() {

        m(func(obj interface{}) bool {

            select {

            case <-stopCh:

                return false

            case generatedValues <- obj:

                return true

            }

        })

        close(generatedValues)

    }()

    iter := func() (value interface{}, notDone bool) {

        value, notDone = <-generatedValues

        return

    }

    return iter, func() {

        stopCh <- nil

    }

}


func main() {

    myMapper := func(yield yieldFn) {

        for i := 0; i < 5; i++ {

            if keepGoing := yield(i); !keepGoing {

                return

            }

        }

    }

    iter, cancel := mapperToIterator(myMapper)

    defer cancel()

    for value, notDone := iter(); notDone; value, notDone = iter() {

        fmt.Printf("value: %d\n", value.(int))

    }

}


查看完整回答
反對 回復(fù) 2021-11-29
  • 3 回答
  • 0 關(guān)注
  • 1384 瀏覽
慕課專欄
更多

添加回答

舉報

0/150
提交
取消
微信客服

購課補(bǔ)貼
聯(lián)系客服咨詢優(yōu)惠詳情

幫助反饋 APP下載

慕課網(wǎng)APP
您的移動學(xué)習(xí)伙伴

公眾號

掃描二維碼
關(guān)注慕課網(wǎng)微信公眾號