【问题标题】:Is there a way to avoid the implementation of the full sort.Interface for slices of structs?有没有办法避免对结构切片执行完整的 sort.Interface ?
【发布时间】:2011-06-24 05:55:29
【问题描述】:

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

  • 伦
  • 交换
  • 少

无论数组中的结构类型如何,Len 和 Swap 似乎都应该始终具有相同的实现。

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

【问题讨论】:

    标签: sorting interface go


    【解决方案1】:

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

    【讨论】:

      【解决方案2】:

      如果您在同一个切片类型上实现多个不同的比较操作,您可以使用嵌入来避免每次都重新定义 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})
      }
      

      【讨论】:

      • 非常有趣。我想知道如何在同一个 Slice 类型上使用多种排序。
      【解决方案3】:

      虽然这是一个老问题,但我想指出 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 中的接口不支持此功能。 同样,我发现这种布局简单、紧凑且易读。 虽然(在这种情况下可能更是如此),但其他人可能不同意。

      【讨论】:

        【解决方案4】:

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

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

        【讨论】:

          猜你喜欢
          • 2019-11-01
          • 2021-12-15
          • 1970-01-01
          • 2019-09-07
          • 1970-01-01
          • 1970-01-01
          • 2015-07-31
          • 2014-11-17
          • 2019-05-29
          相关资源
          最近更新 更多