【发布时间】:2011-06-24 05:55:29
【问题描述】:
如果我在 Go 中有一个结构数组/切片,并且想使用 sort 包对它们进行排序,在我看来,我需要实现包含 3 个方法的整个排序接口:
- 伦
- 交换
- 少
无论数组中的结构类型如何,Len 和 Swap 似乎都应该始终具有相同的实现。
有没有办法避免每次都使用 Len 和 Swap,或者这只是 Go 中缺乏泛型的限制?
【问题讨论】:
如果我在 Go 中有一个结构数组/切片,并且想使用 sort 包对它们进行排序,在我看来,我需要实现包含 3 个方法的整个排序接口:
无论数组中的结构类型如何,Len 和 Swap 似乎都应该始终具有相同的实现。
有没有办法避免每次都使用 Len 和 Swap,或者这只是 Go 中缺乏泛型的限制?
【问题讨论】:
你自己的答案是对的。在您的数组或切片的情况下, Len() 和 Swap() 的实现很简单。就像 len() Go 可以在这里提供一个原生的 swap()。但是现在使用的接口也可以用于更复杂的数据结构,比如 BTrees。它仍然允许 Sort() 函数工作(就像我的并行快速排序,它使用相同的排序接口)。
【讨论】:
如果您在同一个切片类型上实现多个不同的比较操作,您可以使用嵌入来避免每次都重新定义 Len 和 Swap。您还可以使用此技术向排序添加参数,例如根据某些运行时值进行反向排序或不反向排序。
例如
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})
}
【讨论】:
虽然这是一个老问题,但我想指出
github.com/bradfitz/slice
包裹。
但仅作为示例或概念证明,我不建议实际使用它(它用“粗略”一词记录):
它使用粗略的低级操作,只需一个 less 函数即可轻松对任意切片进行排序,而无需使用 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 坚持在type 和func 行之间有一个空行
但它没有问题
多个没有空行的单行函数
它很好地排列了函数体。
对于此类事情,我发现这是一个很好的可读紧凑形式。
至于你的评论:
似乎 Len 和 Swap 应该始终具有相同的实现,无论 [slice] 中的结构类型如何
就在前一周,我需要一种将元素对放在一个切片中的排序(用于输入到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 中的接口不支持此功能。
同样,我发现这种布局简单、紧凑且易读。
虽然(在这种情况下可能更是如此),但其他人可能不同意。
【讨论】:
如果你想对切片进行排序(Len 和 Swap 总是有相同的实现),sort 包现在有一个只需要实现 Less 的函数:
【讨论】: