【问题标题】:How to write a function on a map with unknown type?如何在未知类型的地图上编写函数?
【发布时间】:2020-11-30 23:54:19
【问题描述】:

我感兴趣地阅读了question 关于如何在 Go 中创建映射键的切片(使用范围循环),但是您将如何编写一个函数来为任何类型的映射执行此操作? map 值类型特别麻烦,因为它甚至不会影响结果切片的类型。

例如,我可以编写一个函数,将 map[int]T(其中 T 是任何类型)作为参数并返回 map 键的 []int(整数切片)吗?如果可能的话,是否可以让函数在 map[T1]T2(其中 T1 和 T2 是任意类型)上进行操作并返回 []T1 切片?也就是说,我在下面的代码中用什么替换问号?我猜我需要一个界面,但我对这些有点初学者。

    function keys(m map[?]?) []? {
       var s []?
       k := range m {
          s = append(s, k)
       }
       return s
    }

【问题讨论】:

    标签: go types


    【解决方案1】:

    您可以为 map[int]interface{} 之类的类型执行此操作。

    func keys(m map[int]interface{}) []int {
       var s []int
       for k := range m {
          s = append(s, k)
       }
       return s
    }
    

    要了解接口,请从 Tour of Go 开始:https://tour.golang.org/methods/9


    但在 Go 获得泛型之前,这对于具体类型是不可能的。

    不过,泛型是 on the way。一旦他们登陆 Go,您就可以编写:

    func keys[T any](m map[int]T) []int {
        var s []int
        for k := range m {
            s = append(s, k)
        }
        return s
    }
    

    【讨论】:

      【解决方案2】:

      在 Go 1.18 或更高版本中使用泛型:

      // Keys returns the keys from in map m.
      func keys[M ~map[K]V, K comparable, V any](m M) []K {
          result := make([]K, 0, len(m))
          for k := range m {
              result = append(result, k)
          }
          return result
      }
      

      像这样使用它:

      r := keys(map[string]string{"Foo": "Bar", "Quux": "Baz"})
      // r is a []string
      

      在 1.18 之前的 Go 版本中,使用 reflect 包从任意映射类型中提取键:

      // Keys stores the keys from m to the slice pointed to by pkeys.
      // The slice element type must be the same as the map key type.
      func keys(m interface{}, pkeys interface{}) {
          result := reflect.ValueOf(pkeys).Elem()
          result.SetLen(0)
          result.Set(reflect.Append(result, reflect.ValueOf(m).MapKeys()...))
      }
      

      像这样使用它:

      var result []string
      keys(map[string]string{"Foo": "Bar", "Quux": "Baz"}, &result)
      fmt.Println(result) // prints [Foo Quux]
      

      以下是为特定键类型编写函数的方法。在本例中,密钥类型为int:

      // Keys returns the keys for for map m with int keys.
      func keys(m interface{}) []int {
          mv := reflect.ValueOf(m)
          result := make([]int, mv.Len())
          for i, key := range mv.MapKeys() {
              result[i] = key.Interface().(int)
          }
          return result
      }
      

      像这样使用它:

      result := keys(map[int]string{1: "Bar", 2: "Baz"})
      fmt.Println(result)  // prints [1 2]
      

      See all of the examples in action on the Go playground.

      【讨论】:

      • 很好,我看了第一句话,心里在想“反射并不能解决表达返回类型的问题!”解决方法:伪装成C,取一个指向目的地的指针。
      • @hobbs 当目标类型是任意的时,通常使用指向目标的指针。标准库中的一些示例是Rows.Scan、json.Unmarshal 和encoding/* 包中的其他解码器。
      • 是的,我只是没有想到他们这样做是出于这个原因,而不是为了避免分配(stdlib 喜欢的其他东西)。
      • 谢谢,是的,这正是我想要的。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2021-01-23
      • 2014-10-08
      • 2016-01-17
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2022-01-10
      相关资源
      最近更新 更多