When attempting to solve tree walk portion of the Equivalent Binary Trees problem in the Go Tour, the obvious solution is to use recursion. Other solutions, such as a closure, are provided in answers to the general question on how to solve the problem.
My original thought was to use a Goroutine for each step of the walk. Isn't this a better, more Go-onic (what's the Go equivalent of Pythonic?) solution? The problem is I couldn't figure out how to either A) close the channel after the tree has been walked, or B) signal in some other way that the tree walk completed. An earlier example uses 2 channels, one for data and one for a quit signal. Passing a second channel doesn't fit with the problem definition, and the root issue of when the walk is finished is still present. Is there an elegant solution with a Goroutine for each walk step, or is recursion the best solution?
func Walk(t *tree.Tree, ch chan int) {
if t != nil {
go Walk(t.Left, ch)
ch <- t.Value
go Walk(t.Right, ch)
}
//Done with this node, how do I know when all are done?
}
Using a goroutine for each step of the walk will not work. Entirely apart from not knowing when you can close the channel (which I don't know of a good way to solve), you are not guaranteed to get the ordering you want. For example, the following code:
go fmt.Println(1)
go fmt.Println(2)
go fmt.Println(3)
Could print any of 123, 132, 213, 231, 312, or 321 depending on how the scheduler chooses to run those goroutines. This means that your Walk
implementation is no longer going to give you the values in the correct order.
Goroutines are only the right answer when there is actually something you want to do concurrently; given that the output to the channel has to be strictly ordered there is no concurrency to exploit in this problem.
You can use sync.WaitGroup
func internalWalk(t *tree.Tree, wg *sync.WaitGroup, ch chan int) {
wg.Add(1)
if t != nil {
go Walk(t.Left, ch)
ch <- t.Value
go Walk(t.Right, ch)
}
wg.Done()
}
func Walk(t *tree.Tree, ch chan int) {
var wg sync.WaitGroup
internalWalk(t, &wg, ch)
wg.Wait()
//they are all done now, do something here
}