在 Go 中使用 Yield 实现递归函数生成器的惯用方式
在 Go 中,不直接支持使用 Yield 关键字实现生成器通过语言。然而,惯用的方法是使用 goroutine 和通道来模拟此功能。以下是对您的问题的仔细检查:
1。实现生成器的惯用方法
实现与yield行为相同的生成器的最惯用方法是使用一个基于goroutine的函数,将值发送到通道中。当所有值生成后,这个 goroutine 应该关闭通道。这是一个示例:
package main import ( "fmt" "time" ) // Generate integers in a goroutine. func generateIntegers(numbers []int, c chan<- int) { defer close(c) for _, n := range numbers { c <- n time.Sleep(time.Second) // Simulate delay for clarity } } func main() { numbers := []int{1, 2, 3, 4, 5} c := make(chan int) go generateIntegers(numbers, c) // Consume the generated values. for v := range c { fmt.Println(v) } }
2。关闭通道的职责
按照习惯用法,生成器函数(生成值的函数)应该负责关闭通道。这可以确保在生成所有值时关闭通道,从而使消费者知道何时停止接收值。
3.修改代码
您建议的修改,让调用者负责关闭通道,无论它是否需要所有排列,也是一种惯用的方法。然而,重要的是使用 defer 来确保即使发生错误也能关闭通道。
// Modify the caller code: package main import ( "./lib" "fmt" ) var ( fruits = []string{"apple", "banana", "cherry", "durian"} banned = "durian" ) func main() { channel := lib.PermutateWithChannel(fruits) defer close(channel) for myFruits := range channel { fmt.Println(myFruits) if myFruits[0] == banned { // Close the channel when the banned fruit is encountered. // Since we're using defer, this will happen even if an error occurs. break } } }
4.调用者关闭通道后关闭通道的潜在负面影响
在您的示例中,调用者在生成器发送所有排列后关闭通道。然而,运行生成器代码的 goroutine 仍可能尝试发送到已关闭的通道。这不会导致任何可观察到的负面影响,因为 goroutine 只会发生恐慌。但是,始终建议仅在不再需要通道时关闭通道,以防止意外行为并确保有效的资源管理。
5.返回仅接收通道
可以从生成器函数返回仅接收通道。这可以防止调用者关闭通道,确保只有生成器有责任。具体做法如下:
// Generator function returns a receive-only channel. func generateIntegers(numbers []int) <-chan int { c := make(chan int) go func() { defer close(c) for _, n := range numbers { c <- n time.Sleep(time.Second) // Simulate delay for clarity } }() return c }
这可确保调用者无法关闭通道,从而防止出现问题 4 中描述的问题。
以上是如何使用 Goroutine 和 Channel 来惯用地实现 Go 生成器?的详细内容。更多信息请关注PHP中文网其他相关文章!