我刚刚参加了Tour of Go,并参加了树木步行练习。它明显的递归,但关闭通道是最后一次弹出调用堆栈后的特殊情况。无论如何,我最终实现了它:
// Walk walks the tree t sending all values
// from the tree to the channel ch.
func Walk(t *tree.Tree, ch chan int) {
var walker func(t *tree.Tree, ch chan int)
walker = func(t *tree.Tree, ch chan int) {
if (t.Left != nil) {
walker(t.Left, ch)
}
ch <- t.Value
if (t.Right != nil) {
walker(t.Right, ch)
}
}
walker(t, ch)
close(ch)
}
到目前为止,我对go的印象是他们更愿意避免在可能的情况下说出来,所以在定义之前var walker
的声明似乎 off 。也许我错过了一些允许函数在没有声明的情况下引用自身的细节?如果它可能会更好一点:
// Walk walks the tree t sending all values
// from the tree to the channel ch.
func Walk(t *tree.Tree, ch chan int) {
func(t *tree.Tree, ch chan int) {
if (t.Left != nil) {
__me__(t.Left, ch)
}
ch <- t.Value
if (t.Right != nil) {
__me__(t.Right, ch)
}
}(t, ch)
close(ch)
}
这是一个简单的琐事问题,但我对语言不够新,我找不到答案......
答案 0 :(得分:4)
在声明变量之前不能使用变量,并且它尚未在其初始化语句中声明。
所以是的,声明行是必需的,不,没有办法避免它。
答案 1 :(得分:3)
我同意@ milo-chirstiansen的说法,匿名func不可能在声明中引用它的实例。
如果你想尝试编写惯用的Go代码,它可能看起来更像这样,取消了匿名函数:
// Walk walks the tree t sending all values
// from the tree to the channel ch.
func Walk(t *tree.Tree, ch chan int) {
Walker(t, ch)
close(ch)
}
// Walker does the things, made to be called recursively
func Walker(t *tree.Tree, ch chan int) {
if t.Left != nil {
Walker(t.Left, ch)
}
ch <- t.Value
if t.Right != nil {
Walker(t.Right, ch)
}
}
Go允许一些其他语言无法实现的有趣内容。这有时需要对代码进行一些不同的思考。
Frances Campoy在GopherCon 2016上发表了关于他在Go中nil
概念的传奇历史的演讲。他的一个例子是nil
如何被精美地和惯用地使用,涉及一个接收二叉树总和的解决方案。以下是从他的演讲开始的链接,如果你有时间,我建议你查看它。
参考:https://youtu.be/ynoY2xz-F8s?t=16m28s
我意识到你的示例中没有对Tree结构的控制,但是如果你这样做,那么代码的外观如下:https://play.golang.com/p/iM10NQXfgw
package main
import "fmt"
// A Tree is a binary tree with integer values.
type Tree struct {
Left *Tree
Value int
Right *Tree
}
// Walk loads value into channel; caller is responsible for providing and closing chan
func (t *Tree) Walk(ch chan int) {
// Super interesting: Go supports the calling of a func on a nil instance of a struct
if t == nil {
return // return nothing
}
t.Left.Walk(ch) // recursively call Walk on left node
ch <- t.Value
t.Right.Walk(ch) // recursively call Walk on right node
}
func main() {
// Initial value for our tree; I'm not being very idiomatic with this
tree := &Tree{
Left: &Tree{Value: 2},
Value: 1,
Right: &Tree{Left: &Tree{Value: 4}, Value: 3},
}
ch := make(chan int)
// Load values into chan in separate goroutine
// to prevent blocking
go func() {
tree.Walk(ch)
close(ch)
}()
// Write each val added to chan until all values
// have been written and chan is closed
for val := range ch {
fmt.Println(val)
}
}
1
2
3
4
答案 2 :(得分:1)
应该可以通过组合runtime.Caller
和reflect.Call
来避免这种情况。
但这甚至不像惯用Go,所以我不认为它适用于你的实际情况,尽管它确实解决了你的问题。 :)