【问题标题】:How to use function as map's key如何使用函数作为地图的关键
【发布时间】:2015-01-31 17:45:22
【问题描述】:

如何使用函数作为地图的键?例如:

type Action func(int)
func test(a int) { }
func test2(a int) { }

func main() {
  x := map[Action]bool{}
  x[test] = true
  x[test2] = false
}

那些代码会显示错误:invalid map key type Action

【问题讨论】:

  • 具体用例是什么?这里的接口不是更具语义性和可扩展性吗?
  • 我需要知道我从trie 获得的函数是否是由函数X 创建的函数..

标签: function dictionary go key


【解决方案1】:

您不能将函数用作映射键。 language specification 明确表示:

必须为键类型的操作数完全定义比较运算符 == 和 !=;因此键类型不能是函数、映射或切片。

【讨论】:

    【解决方案2】:

    您不能将函数用作地图中的键:键类型必须具有可比性。

    来自Go blog

    映射键可以是任何可比较的类型。语言规范 精确地定义了这一点,但简而言之,可比较的类型是布尔值, 数字、字符串、指针、通道和接口类型,以及结构或 仅包含这些类型的数组。 名单中明显缺席的是 切片、地图和函数;这些类型无法使用 == 进行比较, 并且不能用作地图键

    根据您的具体用例,您可能使用的是接口。

    【讨论】:

      【解决方案3】:

      您可以使用reflect

          import (
             "reflect"
             "math"
          )
      
      
          func foo () {
             table := make(map[uintptr] string)
             table[reflect.ValueOf(math.Sin)] = "Sin"
             table[reflect.ValueOf(math.Cos)] = "Cos"
             println(table[reflect.ValueOf(math.Cos)])
          }
      

      【讨论】:

        【解决方案4】:

        虽然函数不能是键,但函数指针可以。

        package main
        
        import "fmt"
        
        type strFunc *func() string
        
        func main() {
        
            myFunc := func() string { return "bar" }
            m := make(map[strFunc]string)
            m[(strFunc)(&myFunc)] = "f"
        
            for f, name := range m {
                fmt.Println((*f)(), name)
            }
        }
        

        http://play.golang.org/p/9DdhYduX7E

        【讨论】:

        • 这只是因为您使用的是变量的地址,这与“函数指针”不同。这是您将相同函数分配给另一个变量的示例,结果映射有两个条目,因为变量地址不同:play.golang.org/p/8DBIR4h1jU
        【解决方案5】:

        函数不能是键:

        必须为键类型的操作数完全定义比较运算符 == 和 !=;因此键类型不能是函数、映射或切片。

        Source

        【讨论】:

          【解决方案6】:

          正如已经提到的,你不能直接这样做,但你可以有点假装你这样做:

          package main
          
          import "fmt"
          
          func a(i int) int {
              return i + 1
          }
          
          func b(i int) int {
              return i + 2
          }
          
          type Function func(int)int
          type FunctionWrapper struct {
              f *Function
          }
          
          var fnMap = make(map[string]FunctionWrapper)
          
          // MakeFunctionWrapper returns a unique FunctionWrapper per Function pointer, using fnMap to avoid having multiple values for the same function
          func MakeFunctionWrapper(f Function) FunctionWrapper {
              key := fmt.Sprintf("%#v", f)
              data, ok := fnMap[key]
              if !ok {
                  data = FunctionWrapper{&f}
                  fnMap[key] = data
              }
              return data
          }
          
          func main() {
              functions := make(map[FunctionWrapper]bool)
              fa := MakeFunctionWrapper(a)
              fb := MakeFunctionWrapper(b)
              fb2 := MakeFunctionWrapper(b)
              functions[fa] = true
              functions[fb] = true
              functions[fb2] = false              // This overwrites the previous value since fb is essentially the same as fb2
          
              fmt.Println(functions[fa])          // "true"
              fmt.Println(functions[fb])          // "false"
              fmt.Println(functions[fb2])         // "false"
          }
          

          Check it out on the Go playground

          这有点麻烦,老实说,我认为本质上使用指针的字符串版本作为地图的键是一个非常糟糕的主意。但是......如果你真的需要它,它至少是一个选择。

          【讨论】:

          • 这只有在你保留你的函数包装实例并且总是重复使用它们时才有效。围绕同一个函数创建 2 个包装器不会产生相同的结果:play.golang.org/p/DdwzCTORBf
          【解决方案7】:

          实现此目的的一种简洁方法是将函数包装在 struct 中,然后使用指向此结构的指针作为映射键:

          type FuncPack struct {
              TheFunc func(a int)
          }
          
          func Test(a int)  {}
          func Test2(a int) {}
          
          func main() {
              theMap := make(map[*FuncPack]bool)
              theMap[&FuncPack{TheFunc: Test}] = true
              theMap[&FuncPack{TheFunc: Test2}] = false
          }
          

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 2011-05-13
            • 2021-01-15
            • 1970-01-01
            • 2022-12-20
            • 2018-12-28
            • 1970-01-01
            • 2019-01-25
            相关资源
            最近更新 更多