4

我想将我的所有命令包含在一个映射中,并将命令映射到一个执行该工作的函数(只是一个标准调度表)。我从以下代码开始:

package main

import "fmt"

func hello() {
    fmt.Print("Hello World!")
}

func list() {
    for key, _ := range whatever {
        fmt.Print(key)
    }
}

var whatever = map[string](func()) {
    "hello": hello,
    "list": list,
}

但是,它无法编译,因为函数和结构之间存在递归引用。尝试前向声明函数失败,并在定义时出现关于重新定义的错误,并且映射位于顶层。您如何定义这样的结构并在顶层初始化它们而无需使用init()函数。

我在语言定义中看不到很好的解释。

  • 存在的前向引用用于“外部”函数,当我尝试前向声明该函数时它不会编译。
  • 我也找不到前向声明变量的方法。

更新:我正在寻找一种不需要您在启动程序或init()函数时显式填充变量的解决方案。不确定这是否可能,但它适用于我所知道的所有可比较的语言。

更新 2: FigmentEngine提出了一种方法,我在下面给出了答案。它可以处理递归类型,还允许对所有命令的映射进行静态初始化。

4

3 回答 3

2

正如您可能已经发现的那样,Go 规范声明(我的重点):

如果 A 的初始化器依赖于 B,则 A 将在 B 之后设置。依赖关系分析不依赖于被初始化项的实际值,只依赖于它们在源中的外观。如果 A 的值包含对 B 的提及、包含其初始化程序提及 B 的值或提及提及 B 的函数,则 A 依赖于B。如果这样的依赖形成一个循环,那就是错误的。

所以,不,不可能做你想做的事。问题 1817提到了这个问题,Russ Cox 确实说 Go 中的方法有时可能会过度限制。但它清晰且定义明确,并且有可用的解决方法。

所以,绕过它的方法仍然是使用init(). 对不起。

于 2013-10-28T11:27:36.617 回答
1

根据上面FigmentEngine的建议,实际上可以创建一个静态初始化的命令数组。但是,您必须预先声明传递给函数的类型。我在下面给出了重新编写的示例,因为它可能对其他人有用。

我们称之为新类型Context。它可以包含如下循环引用。

type Context struct {
    commands map[string]func(Context)
}

完成后,可以像这样在顶层声明数组:

var context = Context {
    commands: map[string]func(Context) {
        "hello": hello,
        "list": list,
    },
}

请注意,引用文件后面定义的函数是完全可以的,所以我们现在可以介绍这些函数:

func hello(ctx Context) {
    fmt.Print("Hello World!")
}

func list(ctx Context) {
    for key, _ := range ctx.commands {
        fmt.Print(key)
    }
}

完成后,我们可以创建一个 main 函数,它将调用声明的上下文中的每个函数:

func main() {
    for key, fn := range context.commands {
        fmt.Printf("Calling %q\n", key)
        fn(context)
    }
}
于 2013-10-30T10:17:14.730 回答
0

只需在使用之前在函数内填充地图list()。像那样

对不起,我没有看到您写了“没有init()”:这是不可能的。

于 2013-10-28T10:58:25.397 回答