[ Note: I read Python-style generators in Go, this is not a duplicate of it. ]
In Python / Ruby / JavaScript / ECMAScript 6, generator functions could be written usi
I agree with icza's answer. To summarize, there are are two alternatives:
func myIterationFn(yieldfunc (myType)) (stopIterating bool). This has the disadvantage of ceding the control flow to myGenerator function. myIterationFn is not a Pythonic generator because it doesn't return an iterable sequence.myIterationFn into a function that returns an iterable sequence. The following code provides an example of such a transformation.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)
}
Here's a complete program as an example. mapperToIterator does the transformation from a mapping function to a generator. Go's lack of generics requires casting from interface{} to 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))
}
}