Go:有没有办法避免执行完整的sort.Interface切片结构?

时间:2011-02-09 15:50:13

标签: sorting interface go

如果我在Go中有一个数组/切片结构并希望使用sort包对它们进行排序,我觉得我需要实现包含3个方法的整个排序接口:

  • 莱恩
  • 交换

无论数组中的struct类型如何,Len和Swap似乎总是应该具有相同的实现。

有没有办法避免每次都使用Len和Swap,或者这只是Go中缺少泛型的限制?

4 个答案:

答案 0 :(得分:7)

如果要在同一切片类型上实现多个不同的比较操作,则可以使用嵌入来避免每次重新定义Len和Swap。您还可以使用此技术向排序添加参数,例如,根据某些运行时值反向排序或不排序。

e.g。

package main

import (
    "sort"
)

type T struct {
    Foo int
    Bar int
}

// TVector is our basic vector type.
type TVector []T

func (v TVector) Len() int {
    return len(v)
}

func (v TVector) Swap(i, j int) {
    v[i], v[j] = v[j], v[i]
}

// default comparison.
func (v TVector) Less(i, j int) bool {
    return v[i].Foo < v[j].Foo
}

// TVectorBarOrdered embeds TVector and overrides
// its Less method so that it is ordered by the Bar field.
type TVectorBarOrdered struct {
    TVector
}

func (v TVectorBarOrdered) Less(i, j int) bool {
    return v.TVector[i].Bar < v.TVector[j].Bar
}

// TVectorArbitraryOrdered sorts in normal or reversed
// order depending on the order of its Reversed field.
type TVectorArbitraryOrdered struct {
    Reversed bool
    TVector
}

func (v TVectorArbitraryOrdered) Less(i, j int) bool {
    if v.Reversed {
        i, j = j, i
    }
    return v.TVector[i].Foo < v.TVector[j].Foo
}

func main() {
    v := []T{{1, 3}, {0, 6}, {3, 2}, {8, 7}}
    sort.Sort(TVector(v))
    sort.Sort(TVectorBarOrdered{v})
    sort.Sort(TVectorArbitraryOrdered{true, v})
}

答案 1 :(得分:5)

你自己的答案是对的。在你的数组或切片的情况下,Len()和Swap()的实现很简单。像len()Go可以在这里提供原生swap()。但是现在使用的接口也可以用于更复杂的数据结构,如BTrees。它仍然允许Sort()函数工作(就像我的并行快速排序,它使用相同的排序接口)。

答案 2 :(得分:0)

虽然这是一个老问题,但我想指出 github.com/bradfitz/slice 包。 但仅作为一个例子或概念证明,我建议实际使用它(它用“粗略”一词记录):

  

它使用粗略的低级操作,可以轻松地使用较少的函数对任意切片进行排序,而无需使用Len和Swap操作定义新类型。

在实际的代码中,我发现它完全是微不足道的,快速的,简短的,可读的,并且不会分散注意力,只需执行以下操作:

type points []point

func (p []points) Len() int      { return len(p) }
func (p []points) Swap(i, j int) { p[i], p[j] = p[j], p[i] }
func (p []points) Less(i, j int) bool {
        // custom, often multi-line, comparison code here
}

此处gofmt坚持在typefunc行之间留空 但它没有问题 多行单行功能,无空行 它很好地排列了函数体。 我觉得这是一个很好的可读的紧凑形式。

至于你的评论:

  

似乎Len和Swap应始终具有相同的实现,无论[slice]中的struct类型

就在另一周,我需要一种将切片中的元素对保存在一起的类(对于strings.NewReplacer的输入),这需要一个微不足道的变化,如:

type pairByLen []string

func (p pairByLen) Len() int           { return len(p) / 2 }
func (p pairByLen) Less(i, j int) bool { return len(p[i*2]) > len(p[j*2]) }
func (p pairByLen) Swap(i, j int) {
        p[i*2], p[j*2] = p[j*2], p[i*2]
        p[i*2+1], p[j*2+1] = p[j*2+1], p[i*2+1]
}

github.com/bradfitz/slice中的接口不支持此功能。 我再次发现这种布局简单,紧凑,易读。 虽然(在这种情况下可能更多),但其他人可能不同意。

答案 3 :(得分:0)

如果要对切片进行排序(Len和Swap总是具有相同的实现),sort包现在有一个只需要实现Less的函数:

func Slice(slice interface{}, less func(i, j int) bool)