如何避免重新实现类似golang结构的sort.Interface

Lic*_*ich 8 coding-style interface go slice

Golang有一个问题困扰我.说我有两个结构:

type Dog struct {
   Name string
   Breed string
   Age int
}

type Cat struct {
    Name string
    FavoriteFood string
    Age int
}
Run Code Online (Sandbox Code Playgroud)

当我尝试进行排序[]*Dog[]*Cat通过Age,我必须定义2个不同类型的结构,如:

type SortCat []*Cat
func (c SortCat) Len() int {//..}
func (c SortCat) Swap(i, j int) {//..}
func (c SortCat) Less(i, j int) bool {//..}

type SortDog []*Dog
func (c SortDog) Len() int {//..}
func (c SortDog) Swap(i, j int) {//..}
func (c SortDog) Less(i, j int) bool {//..}
Run Code Online (Sandbox Code Playgroud)

一个自然的想法是使用接口函数实现一些SortableByAge接口并创建一个Less函数.喜欢:

type SortableByAge interface {
    AgeValue() int
}
Run Code Online (Sandbox Code Playgroud)

然后:

type SortAnimal []SortableByAge
func (c SortDog) Less(i, j int) bool {
    return c[i].AgeValue() < c[j].AgeValue() 
}
Run Code Online (Sandbox Code Playgroud)

但是,根据:http: //golang.org/doc/faq#convert_slice_of_interface

dogs := make([]*Dogs, 0 , 1)
//add dogs here
sort.Sort(SortAnimal(dogs))
Run Code Online (Sandbox Code Playgroud)

以上是不可能的.

所以我想知道这个案例的最佳做法是什么

有没有其他技术可以减少sort.Interface我一次又一次地实现类似结构的需要?

编辑:我意识到我的例子很可怕:(

在现实生活中,这两个结构是非常不同的,它们之间唯一的共同点是我希望用一个共同的数值对它们进行排序.

一个更好的例子是:

type Laptop {//...}
type Pizza {//...}
Run Code Online (Sandbox Code Playgroud)

这两个结构的唯一共同之处在于,我希望Pizza按价格对它们进行分类(agh ...不应该在示例中使用).

因此,将它们与一个通用结构相结合并不适用于很多情况.但是会考虑去生成.

icz*_*cza 11

这个具体案例

在这种特定情况下,您不应该使用两种不同的类型,因为它们是相同的,只需使用一个通用Animal类型:

type Animal struct {
    Name string
    Age  int
}

func (a Animal) String() string { return fmt.Sprintf("%s(%d)", a.Name, a.Age) }

type SortAnim []*Animal

func (c SortAnim) Len() int           { return len(c) }
func (c SortAnim) Swap(i, j int)      { c[i], c[j] = c[j], c[i] }
func (c SortAnim) Less(i, j int) bool { return c[i].Age < c[j].Age }

func main() {
    dogs := []*Animal{&Animal{"Max", 4}, &Animal{"Buddy", 3}}
    cats := []*Animal{&Animal{"Bella", 4}, &Animal{"Kitty", 3}}

    fmt.Println(dogs)
    sort.Sort(SortAnim(dogs))
    fmt.Println(dogs)

    fmt.Println(cats)
    sort.Sort(SortAnim(cats))
    fmt.Println(cats)
}
Run Code Online (Sandbox Code Playgroud)

输出(Go Playground):

[Max(4) Buddy(3)]
[Buddy(3) Max(4)]
[Bella(4) Kitty(3)]
[Kitty(3) Bella(4)]
Run Code Online (Sandbox Code Playgroud)

一般情况

通常,如果您愿意放弃具体类型并使用接口类型,则只能使用通用排序实现.

创建希望切片保留的接口类型:

type Animal interface {
    Name() string
    Age() int
}
Run Code Online (Sandbox Code Playgroud)

你可以有一个共同的实现:

type animal struct {
    name string
    age  int
}

func (a *animal) Name() string  { return a.name }
func (a *animal) Age() int      { return a.age }
func (a animal) String() string { return fmt.Sprintf("%s(%d)", a.name, a.age) }
Run Code Online (Sandbox Code Playgroud)

您的特定动物类型:

type Dog struct {
    animal  // Embed animal (its methods and fields)
}

type Cat struct {
    animal // Embed animal (its methods and fields)
}
Run Code Online (Sandbox Code Playgroud)

您可以实现sort.InterfaceSortAnim:

type SortAnim []Animal

func (c SortAnim) Len() int           { return len(c) }
func (c SortAnim) Swap(i, j int)      { c[i], c[j] = c[j], c[i] }
func (c SortAnim) Less(i, j int) bool { return c[i].Age() < c[j].Age() }
Run Code Online (Sandbox Code Playgroud)

使用它:

dogs := SortAnim{&Dog{animal{"Max", 4}}, &Dog{animal{"Buddy", 3}}}
cats := SortAnim{&Cat{animal{"Bella", 4}}, &Cat{animal{"Kitty", 3}}}

fmt.Println(dogs)
sort.Sort(SortAnim(dogs))
fmt.Println(dogs)

fmt.Println(cats)
sort.Sort(SortAnim(cats))
fmt.Println(cats)
Run Code Online (Sandbox Code Playgroud)

输出(Go Playground):

[Max(4) Buddy(3)]
[Buddy(3) Max(4)]
[Bella(4) Kitty(3)]
[Kitty(3) Bella(4)]
Run Code Online (Sandbox Code Playgroud)


Von*_*onC 7

注:如图提交ad26bb5,在Go 1.8(Q1 2017),你不会有落实Len(),并Swap()Less()发行16721得到解决.只有Less()需要,其余的都是通过反思完成的.

问题是:

  1. 绝大多数sort.Interface使用切片
  2. 必须定义一个新的顶级类型
  3. LenSwap方法总是一样的
  4. 想要使普通案例更简单,性能最低

看到新的sort.go:

// Slice sorts the provided slice given the provided less function.
//
// The sort is not guaranteed to be stable. For a stable sort, use
// SliceStable.
//
// The function panics if the provided interface is not a slice.
func Slice(slice interface{}, less func(i, j int) bool) {
    rv := reflect.ValueOf(slice)
    swap := reflect.Swapper(slice)
    length := rv.Len()
    quickSort_func(lessSwap{less, swap}, 0, length, maxDepth(length))
}
Run Code Online (Sandbox Code Playgroud)

因此,只要您有一个Less()函数比较两个关于接口的实例,您就可以对所述公共接口进行任意数量的结构排序.