在go中制作通用算法

时间:2011-06-06 17:26:31

标签: algorithm go

我无法找到一种干净的方法来实现适用于任何类型的算法。

以下代码在尝试将字符串或类型化切片转换为接口时会产生错误,您无法比较接口{}对象: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
}

虽然这很痛苦(我认为它应该是自动的),但我可以手动将打包和取消打包的切片放入接口{},上面的真正问题是比较。它不断变得越来越笨拙。

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

我甚至想过使用vector.Vector,但很多人都说不要使用它们。

那么我应该为int切片和字符串实现相同的算法吗? myObject的片段怎么样?我可以使用自定义比较函数创建一个界面,但是如何使它与标准类型一起使用?

3 个答案:

答案 0 :(得分:11)

您可以在Go中使用界面执行此操作。采用接口类型的函数是通用的,因为它不关心底层具体类型的数据表示。它通过方法调用完成所有操作。

要制作算法的通用版本,您必须确定算法对数据对象所需的所有功能,并且必须定义抽象这些功能的方法。抽象方法签名成为接口的方法集。

要使类型与这种通用算法兼容,可以在类型上定义方法以满足算法参数的接口。

我将采用您的示例代码并显示一种方法来执行此操作。大多数所需的功能恰好由sort.Interface覆盖,所以我选择嵌入它。只需要一个其他功能,一个用于复制数据。

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

以下是根据示例代码制作的完整工作程序。

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
}

答案 1 :(得分:8)

由于Go编程语言目前不支持泛型类型,因此这很难做到。

  

Why does Go not have generic types?

     

一些人可能会添加泛型   点。我们感到不紧急   他们,虽然我们了解一些   程序员做的。

     

泛型很方便,但它们来了   代价是复杂的类型   系统和运行时。我们还没有   找到了一种赋予价值的设计   与复杂性成比例,   虽然我们继续思考   它。同时,Go的内置地图和   切片,加上使用的能力   要构造的空接口   容器(带有显式拆箱)   在许多情况下意味着有可能   编写做泛型的代码   如果不太顺利,将会启用。

     

这仍然是一个悬而未决的问题。

通过使用Len,Less和Swap方法定义sort package类型,查看Go sort.Interface以了解它如何处理特定于类型的比较和其他操作。

答案 2 :(得分:6)

Go没有泛型类型,但您可以查看sort如何找到解决方法。他们所做的是创建一个这样的界面:

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)
}

现在,对于任何自定义类型,您都可以创建可以排序的相应自定义集合类型。排序算法只需要处理整数和布尔值,因此不会看到或关心底层数据类型是什么。