Go: Is there a way to avoid the implementation of the full sort.Interface for slices of structs?
如果我在Go中有一个数组的结构体/切片,并想使用sort包对它们进行排序,在我看来,我需要实现包含3个方法的整个sort接口:
- 伦
- 交换
- 减
无论数组中的struct类型如何,Len和Swap似乎始终应该具有相同的实现。
有没有一种方法可以避免每次都使用工具Len和Swap,或者这仅仅是Go语言中缺少泛型的限制?
如果您要对同一切片类型实施几种不同的比较操作,则可以使用嵌入来避免每次重新定义Len和Swap。您还可以使用此技术将参数添加到排序中,例如根据某些运行时值进行反向排序或不进行排序。
例如
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 | 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}) } |
您自己的答案是正确的。对于数组或切片,Len()和Swap()的实现很简单。像len()一样,Go可以在此处提供一个本机swap()。但是现在使用的接口也可以用于更复杂的数据结构,例如BTrees。它仍然允许Sort()函数正常工作(就像我的并行quicksort一样,它使用相同的sort接口)。
如果要对切片进行排序(Len和Swap始终具有相同的实现),那么sort包现在具有仅需要实现Less的功能:
func Slice(切片接口{},少了func(i,j int)bool)
尽管这是一个古老的问题,但我想指出
包。
但是,仅作为示例或概念证明,我不建议您实际使用它(它以"总"一词记录):
It uses gross, low-level operations to make it easy to sort arbitrary slices with only a less function, without defining a new type with Len and Swap operations.
在实际代码中,我发现执行以下操作完全无关紧要,快速,简短,易读且分心:
1 2 3 4 5 6 7 | 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 } |
在这里
但它没有问题
多个单行功能,无空行
并且很好地将功能主体排列在一起。
对于这样的事情,我发现这是一个很好的可读紧凑形式。
至于您的评论:
It seems that Len and Swap should always have the same implementation no matter the type of struct is in the [slice]
就在前一周,我需要一种将多个切片中的成对元素保持在一起(用于输入
1 2 3 4 5 6 7 8 | 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] } |
类似于
再次,我发现此布局简单,紧凑且可读。
尽管(在这种情况下可能更多),其他人可能会不同意。