在 Go 中制作通用算法

发布于 2024-11-14 06:42:14 字数 1136 浏览 3 评论 0原文

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

以下代码在尝试将字符串或类型化切片转换为接口时会产生错误,并且无法比较 interface{} 对象: invalid operation: result[0] > > result[n - 1] (operator > not Define 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 的切片怎么样?我可以使用自定义比较函数创建一个接口,但是如何使其与标准类型一起工作?

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)

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. 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.

So should I just implement the same algorithm for int slices and strings? What about slices of myObject? I can make an interface with a custom comparison func, but then how do I make it work with standard types?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

柠栀 2024-11-21 06:42:14

您可以在 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
}

You can do this in Go using interfaces. 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. 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. 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
}
只是偏爱你 2024-11-21 06:42:14

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

为什么 Go 没有泛型
类型?

在某些地方很可能会添加泛型
观点。我们并不感到紧迫
他们,虽然我们了解一些
程序员就是这么做的。

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

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

查看 Go sort 包,了解它如何通过定义sort.Interface 类型,包含 Len、Less 和 Swap 方法。

Since the Go Programming Language doesn't currently support generic types, this is going to be hard to do.

Why does Go not have generic
types?

Generics may well be added at some
point. 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. 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.

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.

标点 2024-11-21 06:42:14

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

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

Go does not have generic types, but you can take a look at how sort works to find a workaround. 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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文