简体   繁体   English

在 go 中制作通用算法

[英]making generic algorithms in go

I can't figure out a clean way to implement an algorithm that will work on any type.我想不出一种干净的方法来实现适用于任何类型的算法。

The following code will produce errors trying to convert a string or a typed slice into interfaces, and you can't compare interface{} objects: invalid operation: result[0] > result[n - 1] (operator > not defined on interface)以下代码在尝试将字符串或类型化切片转换为接口时会产生错误,并且无法比较 interface{} 对象: invalid operation: result[0] > result[n - 1] (operator > not defined on interface)

func main() {
    c := Algo("abc")
    //...
    c := Algo([3]int{1,2,3})
    //...
}

func Algo(list []interface{}) chan []interface{} {
    n := len(list)
    out := make(chan []interface{})
    go func () {
        for i := 0; i < n; i++ {
            result := make([]interface{}, n)
            copy(result, list)
            // an actually useful algorithm goes here:
            if (result[0] > result[n-1]) {
                result[0], result[n-1] = result[n-1], result[0]
            }
            out <- result
        }
        close(out)
    }()
    return out
}

Although it's a pain (I think it should be automatic), I can manually box and unbox typed slices into interface{}s, the real problem above is the comparison.虽然很痛苦(我认为它应该是自动的),但我可以手动将键入的切片装箱和拆箱到 interface{} 中,上面真正的问题是比较。 And it just keeps getting more and more kludgy.它只是变得越来越笨拙。

a := [3]int{1,2,3}
b := make([]interface{}, len(a))
for i, _ := range a {
    b[i] = a[i]
}

I've even thought of using vector.Vector, but so many people say never to use them.我什至想过使用vector.Vector,但是很多人说永远不要使用它们。

So should I just implement the same algorithm for int slices and strings?那么我应该为 int 切片和字符串实现相同的算法吗? What about slices of myObject? myObject 的切片呢? I can make an interface with a custom comparison func, but then how do I make it work with standard types?我可以使用自定义比较函数制作一个接口,但是如何使它与标准类型一起使用?

You can do this in Go using interfaces.您可以使用接口在 Go 中执行此操作。 A function that takes an interface type is generic in the sense that it doesn't care about the data representation of the underlying concrete type.采用接口类型的 function 是通用的,因为它不关心底层具体类型的数据表示。 It does everything through method calls.它通过方法调用完成所有工作。

To make a generic version of your algorithm then, you have to identify all of the capabilities that the algorithm requires of the data objects and you have to define methods that abstract these capabilities.然后,要制作算法的通用版本,您必须确定算法所需的数据对象的所有功能,并且您必须定义抽象这些功能的方法。 The abstract method signatures become method sets of interfaces.抽象方法签名成为接口的方法集。

To make a type compatible with this kind of generic algorithm, you define methods on the type to satisfy the interface of the algorithm parameter.要使类型与这种泛型算法兼容,您需要在类型上定义方法以满足算法参数的接口。

I'll take your example code and show one way to do this.我将采用您的示例代码并展示一种方法来做到这一点。 Most of the required capabilities happen to be covered by sort.Interface so I chose to embed it.大多数必需的功能恰好被 sort.Interface 覆盖,所以我选择嵌入它。 Only one other capability is needed, one to make a copy of the data.只需要一项其他功能,即制作数据副本。

type algoContainer interface {
    sort.Interface
    Copy() algoContainer
}

Below is a complete working program made from your example code.以下是由您的示例代码制作的完整工作程序。

package main

import (
    "fmt"
    "sort"
)

func main() {
    s1 := sortableString("abc")
    c1 := Algo(s1)
    fmt.Println(s1, <-c1)

    s2 := sortable3Ints([3]int{1,2,3})
    c2 := Algo(&s2)
    fmt.Println(s2, <-c2)
}

type algoContainer interface {
    sort.Interface
    Copy() algoContainer
}

type sortableString []byte
func (s sortableString) Len() int { return len(s) }
func (s sortableString) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
func (s sortableString) Less(i, j int) bool { return s[i] < s[j] }
func (s sortableString) Copy() algoContainer {
   return append(sortableString{}, s...)
}
func (s sortableString) String() string { return string(s) }

type sortable3Ints [3]int
func (sortable3Ints) Len() int { return 3 }
func (s *sortable3Ints) Swap(i, j int) {
   (*s)[i], (*s)[j] = (*s)[j], (*s)[i]
}
func (s sortable3Ints) Less(i, j int) bool { return s[i] < s[j] }
func (s sortable3Ints) Copy() algoContainer { c := s; return &c }

func Algo(list algoContainer) chan algoContainer {
    n := list.Len()
    out := make(chan algoContainer)
    go func () {
        for i := 0; i < n; i++ {
            result := list.Copy()
            // actually useful:
            if result.Less(n-1, 0) {
                result.Swap(n-1, 0)
            }
            out <- result
        }
        close(out)
    }()
    return out
}

Since the Go Programming Language doesn't currently support generic types, this is going to be hard to do.由于 Go 编程语言目前不支持泛型类型,这将很难做到。

Why does Go not have generic types?为什么 Go 没有泛型类型?

Generics may well be added at some point. Generics 可能会在某个时候添加。 We don't feel an urgency for them, although we understand some programmers do.我们不觉得他们有紧迫感,尽管我们知道一些程序员会这样做。

Generics are convenient but they come at a cost in complexity in the type system and run-time. Generics 很方便,但它们的代价是类型系统和运行时的复杂性。 We haven't yet found a design that gives value proportionate to the complexity, although we continue to think about it.尽管我们仍在继续思考,但我们还没有找到一种能够提供与复杂性成比例的价值的设计。 Meanwhile, Go's built-in maps and slices, plus the ability to use the empty interface to construct containers (with explicit unboxing) mean in many cases it is possible to write code that does what generics would enable, if less smoothly.同时,Go 的内置映射和切片,以及使用空接口构造容器(通过显式拆箱)的能力,意味着在许多情况下,即使不太顺利,也可以编写 generics 将启用的代码。

This remains an open issue.这仍然是一个悬而未决的问题。

Look at the Go sort package to see how it handles comparisons and other operations specific to a type by defining the sort.Interface type with Len, Less, and Swap methods.查看 Go排序 package以了解它如何通过使用 Len、Less 和 Swap 方法定义sort.Interface类型来处理特定于类型的比较和其他操作。

Go does not have generic types, but you can take a look at how sort works to find a workaround. Go 没有泛型类型,但您可以查看sort的工作原理以找到解决方法。 What they do is create an interface like this:他们所做的是创建一个这样的界面:

type Interface interface {
    // Len is the number of elements in the collection.
    Len() int
    // Less returns 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)
}

And now for any custom type you can make a corresponding custom collection type that can be sorted.现在,对于任何自定义类型,您都可以制作相应的可排序的自定义集合类型。 The sort algorithm only has to deal with integers and booleans, and so doesn't see or care what the underlying data types are.排序算法只需要处理整数和布尔值,因此不会看到或关心底层数据类型是什么。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM