匿名go函数可以调用吗?

时间:2017-07-23 18:03:05

标签: go

我刚刚参加了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)
}

这是一个简单的琐事问题,但我对语言不够新,我找不到答案......

3 个答案:

答案 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.Callerreflect.Call来避免这种情况。

但这甚至不像惯用Go,所以我不认为它适用于你的实际情况,尽管它确实解决了你的问题。 :)