Calvert's murmur

Golang 的排序和透過函數自訂排序

2021-09-16

約 5365 字 / 需 29 分鐘閱讀

原文:CalliCoderGolang Sorting and Custom Sorting by functions

排序是我們平常撰寫程式時常見的用法。在本文中,你將學到如何在 Go 中對原始類型(string、int、float64)和自訂的集合進行排序。

在 Go 中對字串、整數或浮點數的 slice 進行排序

Go 的 sort package 提供了幾種方便的方法來對原始類型的 slice 進行排序。以下範例示範了如何在 Go 中對字串、整數和浮點數的 slice 進行排序:

package main

import (
	"fmt"
	"sort"
)

func main() {
	// Sorting a slice of Strings
	strs := []string{"quick", "brown", "fox", "jumps"}
	sort.Strings(strs)
	fmt.Println("Sorted strings: ", strs)

	// Sorting a slice of Integers
	ints := []int{56, 19, 78, 67, 14, 25}
	sort.Ints(ints)
	fmt.Println("Sorted integers: ", ints)

	// Sorting a slice of Floats
	floats := []float64{176.8, 19.5, 20.8, 57.4}
	sort.Float64s(floats)
	fmt.Println("Sorted floats: ", floats)
}
$ go run sorting.go
Sorted strings:  [brown fox jumps quick]
Sorted integers:  [14 19 25 56 67 78]
Sorted floats:  [19.5 20.8 57.4 176.8]

以相反順序對 slice 進行排序

要以相反順序對 slice 進行排序,你可以在 StringSliceIntSliceFloat64Slice 類型上使用 Reverse() 函數。這些類型實作了 sort package 中定義用來比較和交換的 Interface

Reverse() 函數返回了相反順序的資料。讓我們來看一個以相反順序對字串、整數和浮點數的 slice 進行排序的範例:

package main

import (
	"fmt"
	"sort"
)

func main() {
	// Sorting a slice of Strings
	strs := []string{"quick", "brown", "fox", "jumps"}
	sort.Sort(sort.Reverse(sort.StringSlice(strs)))
	fmt.Println("Sorted strings in reverse order: ", strs)

	// Sorting a slice of Integers
	ints := []int{56, 19, 78, 67, 14, 25}
	sort.Sort(sort.Reverse(sort.IntSlice(ints)))
	fmt.Println("Sorted integers in reverse order: ", ints)

	// Sorting a slice of Floats
	floats := []float64{176.8, 19.5, 20.8, 57.4}
	sort.Sort(sort.Reverse(sort.Float64Slice(floats)))
	fmt.Println("Sorted floats in reverse order: ", floats)
}
$ go run reverse-sorting.go
Sorted strings in reverse order:  [quick jumps fox brown]
Sorted integers in reverse order:  [78 67 56 25 19 14]
Sorted floats in reverse order:  [176.8 57.4 20.8 19.5]

在 Go 中使用比較器函數對 slice 進行排序

很多時候,你可能希望按照自然順序以外的方式對集合進行排序。例如,你想按照長度對字串 slice 進行排序,或者按照年齡對使用者進行排序。

對於這些案例,你可以使用 sort package 提供的 Slice()SliceStable() 函數。這些是能接受 less 函數作為參數的高階函數:

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

less 函數是一個比較器函數,它回報索引 i 處的元素是否應該排在索引 j 處的元素前。如果 less(i, j)less(j, i) 都是返回 false,那麼索引 ij 處的元素被視為是相同的。

當使用 Slice() 函數時,排序不保證穩定:相等的元素可能會和它們原本的順序顛倒。要穩定的排序,請使用 SliceStable()

讓我們來看個範例:

package main

import (
	"fmt"
	"sort"
)

func main() {
	// Sorting a slice of strings by length
	strs := []string{"United States", "India", "France", "United Kingdom", "Spain"}
	sort.Slice(strs, func(i, j int) bool {
		return len(strs[i]) < len(strs[j])
	})
	fmt.Println("Sorted strings by length: ", strs)

	// Stable sort
	sort.SliceStable(strs, func(i, j int) bool {
		return len(strs[i]) < len(strs[j])
	})
	fmt.Println("[Stable] Sorted strings by length: ", strs)

	// Sorting a slice of strings in the reverse order of length
	sort.SliceStable(strs, func(i, j int) bool {
		return len(strs[j]) < len(strs[i])
	})
	fmt.Println("[Stable] Sorted strings by reverse order of length: ", strs)
}
$ go run sorting-by-comparator.go
Sorted strings by length:  [India Spain France United States United Kingdom]
[Stable] Sorted strings by length:  [India Spain France United States United Kingdom]
[Stable] Sorted strings by reverse order of length:  [United Kingdom United States France India Spain]

在 Go 中使用比較器函數對 struce slice 進行排序

延續上一個範例,此範例將示範如何透過將自訂的 less 函數傳遞給 Slice()SliceStable() 函數,來對使用者自訂的 struct 進行排序:

package main

import (
	"fmt"
	"sort"
)

type User struct {
	Name string
	Age  int
}

func main() {
	// Sorting a slice of structs by a field
	users := []User{
		{
			Name: "Rajeev",
			Age:  28,
		},
		{
			Name: "Monica",
			Age:  31,
		},
		{
			Name: "John",
			Age:  56,
		},
		{
			Name: "Amanda",
			Age:  16,
		},
		{
			Name: "Steven",
			Age:  28,
		},
	}

    // Sort users by their age
	sort.Slice(users, func(i, j int) bool {
		return users[i].Age < users[j].Age
	})
	fmt.Println("Sorted users by age: ", users)

	// Stable sort
	sort.SliceStable(users, func(i, j int) bool {
		return users[i].Age < users[j].Age
	})
	fmt.Println("Sorted users by age: ", users)
}

透過實作 sort.Interface 來自訂排序

最後,讓我們看一下在 Go 中對原始類型的集合或使用者自訂的 struct 進行排序的最通用方式。要啟用對任何類型集合的自訂排序,你需要定義一個相對應的類型並實作 sort package 提供的通用 Interface。該 Interface 包含以下方法:

type Interface interface {
	// Len is the number of elements in the collection.
	Len() int

	// Less reports whether the element with index i must sort before the element with index j.
	// If both Less(i, j) and Less(j, i) are false, then the elements at index i and j are considered equal.
	Less(i, j int) bool

	// Swap swaps the elements with indexes i and j.
	Swap(i, j int)
}

實作上述 Interface 後,你可以使用 Sort()Stable() 函數對任何實作 sort.Interface 介面的集合進行排序。

讓我們透過一個範例來理解:

package main

import (
	"fmt"
	"sort"
)

type User struct {
	Name string
	Age  int
}

// Define a collection type that implements sort.Interface
type UsersByAge []User

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

func main() {
	users := []User{
		{
			Name: "Rajeev",
			Age:  28,
		},
		{
			Name: "Monica",
			Age:  31,
		},
		{
			Name: "John",
			Age:  56,
		},
		{
			Name: "Amanda",
			Age:  16,
		},
		{
			Name: "Steven",
			Age:  28,
		},
	}

	// Sorting a slice of users by age (Sort may place equal elements in any order in the final result)
	sort.Sort(UsersByAge(users))
	fmt.Println("Sorted users by age: ", users)

	// Stable Sorting (Stavle sort preserves the original input order of equal elements)
	sort.Stable(UsersByAge(users))
	fmt.Println("[Stable] Sorted users by age: ", users)
}
$ go run custom-sorting-struct.go
Sorted users by age:  [{Amanda 16} {Rajeev 28} {Steven 28} {Monica 31} {John 56}]
[Stable] Sorted users by age:  [{Amanda 16} {Rajeev 28} {Steven 28} {Monica 31} {John 56}]
Tags: Golang