【问题标题】:Idiomatic way to write a method that operates on a generic type编写对泛型类型进行操作的方法的惯用方式
【发布时间】:2019-06-20 02:41:15
【问题描述】:

编写对“通用”数组进行操作的方法的惯用方式是什么?

我有一个类型化数组:

a := make([]int, 0)

我想写一个可以操作任意类型数组的简单方法:

func reverse(a []interface{}) []interface{} {
    for i, j := 0, len(a)-1; i < j; i, j = i+1, j-1 {
        a[i], a[j] = a[j], a[i]
    }
    return a
}

使用这种方法a = reverse(a) 给了我2个错误:

cannot use a (type []int) as type []interface {} in argument to reverse
cannot use reverse(a) (type []interface {}) as type []int in assignment

【问题讨论】:

标签: go generics slice go-2


【解决方案1】:

不是说您现在可以在生产中使用泛型(截至 2020 年 10 月 2 日),但是对于即将推出的 go 泛型功能感兴趣的人,使用最新的 go 的 design draft,您可以编写一个泛型函数 reverse如下

package main

import (
    "fmt"
)

func reverse[T any](s []T) []T {
    for i, j := 0, len(s)-1; i < j; i, j = i+1, j-1 {
        s[i], s[j] = s[j], s[i]
    }
    return s
}

func main() {
    s := []int{1, 2, 3, 4, 5}
    s = reverse(s)
    fmt.Println(s)
}

输出:

[5 4 3 2 1]

【讨论】:

  • 和我之前的回答差别不大,是吗?
  • @jub0bs 您上次编辑之前的代码使用的是去年草稿中的语法。
【解决方案2】:

在泛型出现之前(很可能称为contracts),反射和接口是实现这种泛化的唯一工具。

您可以定义reverse() 以获取interface{} 的值并使用reflect 包对其进行索引和交换元素。这通常很慢,而且更难阅读/维护。

接口提供了一种更好的方法,但需要您将方法写入不同的类型。看看sort 包,特别是sort.Sort() 函数:

func Sort(data Interface)

sort.Interface 在哪里:

type Interface interface {
        // Len is the number of elements in the collection.
        Len() int
        // Less reports whether the element with
        // index i should sort before the element with index j.
        Less(i, j int) bool
        // Swap swaps the elements with indexes i and j.
        Swap(i, j int)
}

sort.Sort() 能够对实现sort.Interface 的任何切片进行排序,任何切片具有排序算法需要完成其工作的方法。这种方法的好处是,您也可以对其他数据结构进行排序,而不仅仅是切片(例如链表或数组),而且通常使用切片。

【讨论】:

    【解决方案3】:

    耐心点!根据 latest draft proposal to add type parameters 的语言,你将能够在未来的 Go 版本中编写这样一个通用的 reverse 函数:

    func reverse[T any](s []T) []T {
        for i, j := 0, len(s)-1; i < j; i, j = i+1, j-1 {
            s[i], s[j] = s[j], s[i]
        }
        return s
    }
    
    func main() {
        s := []int{1, 2, 3, 4, 5}
        s = reverse(s)
        fmt.Println(s)
    }
    

    (playground)


    出于性能原因,您可能希望原地反转切片:

    package main
    
    import "fmt"
    
    func reverse[T any](s []T) {
        for i, j := 0, len(s)-1; i < j; i, j = i+1, j-1 {
            s[i], s[j] = s[j], s[i]
        }
    }
    
    func main() {
        s := []int{1, 2, 3, 4, 5}
        reverse(s)
        fmt.Println(s)
    }
    

    (playground)

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2020-02-23
      • 1970-01-01
      • 2021-06-07
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多