↔️

【Go】cmpパッケージの3つの関数について解説!

に公開

はじめに

前回投稿した安定ソートと不安定ソートの実装時に、cmpパッケージのCompare関数を使いました。
あまり使い慣れていないパッケージだったため、調べた内容を備忘録としてまとめました。
※前回投稿した記事はこちら!
https://zenn.dev/tmyhrn/articles/db1bb1a47aa2ac

この記事でわかること

  • cmpパッケージについて
  • cmpパッケージにある3つの関数の基本的な使い方
  • 3つの関数の実際の用途

cmpパッケージとは?

cmpパッケージはGo 1.21で追加されました。
Go 1.21のリリースノートには以下のように記載されています。

The new cmp package defines the type constraint Ordered and two new generic functions Less and Compare that are useful with ordered types.

Orderedというインターフェース型と、Compare関数とLess関数が新しく追加されました。
そして、Go 1.22のリリースノートを見ると、

The new function Or returns the first in a sequence of values that is not the zero value.

ここでOr関数が新しく追加されました。
Go1.24.4時点では、Compare関数・Less関数・Or関数・Orderedインターフェス型がcmpパッケージに存在します。

Compare

Comparefunc Compare[T Ordered](x, y T) intと定義されており、

1. xがyよりも小さかったら-1を返す
2. xがyと同値なら0を返す
3. xがyよりも大きかったら1を返す

このように動作します。
例えば、以下のように使うことができます。

main.go
package main

import (
    "cmp"
    "fmt"
)

func main() {
    fmt.Println(cmp.Compare(1, 2)) // -1
    fmt.Println(cmp.Compare(2, 2)) // 0
    fmt.Println(cmp.Compare(3, 2)) // 1
}

実際の用途としては、SlicesパッケージのSortFuncSortStableFuncでの比較関数として使うことが挙げられます。

main.go
package main

import (
    "cmp"
    "fmt"
    "slices"
)

func main() {
    nums := []int{3, 7, 1}
    fmt.Println("ソート前:", nums) // ソート前: [3 7 1]
    slices.SortStableFunc(nums, func(a, b int) int {
        return cmp.Compare(a, b)
    })
    fmt.Println("ソート後:", nums) // ソート後: [1 3 7]
}

Less

Lessfunc Less[T Ordered](x, y T) boolと定義されており、

xがyよりも小さい場合はtrueを返す
xがyよりも大きい場合または同値の場合はfalseを返す

このように動作します。
例えば、以下のように使うことができます。

main.go
package main

import (
    "cmp"
    "fmt"
)

func main() {
    fmt.Println(cmp.Less(1, 2))
    fmt.Println(cmp.Less(2, 2))
    fmt.Println(cmp.Less(3, 2))
}

実際の用途としては、条件分岐の比較処理に使うことが挙げられます。
成績評価を例にします。

main.go
package main

import (
    "cmp"
    "fmt"
)

func evaluateScore(score int) string {
    switch  {
    case cmp.Less(score, 30):
        return "赤点"
    case cmp.Less(score, 60):
        return "可"
    case cmp.Less(score, 80):
        return "良"
    case cmp.Less(score, 90):
        return "優"
    }
    return "秀"
}

func main() {
    fmt.Println(evaluateScore(20)) // 赤点
    fmt.Println(evaluateScore(50)) // 可
    fmt.Println(evaluateScore(70)) // 良
    fmt.Println(evaluateScore(85)) // 優
    fmt.Println(evaluateScore(99)) // 秀
}

Or

OrOr[T comparable](vals ...T) Tと定義されており、ゼロ値でない最初の引数を返します。
例えば、以下のように使うことができます。

main.go
package main

import (
	"cmp"
	"fmt"
)

func main() {
    fmt.Println(cmp.Or("", "default")) // default
}

実際の用途として、複数キーでのソートが挙げられます。

main.go
package main

import (
	"cmp"
	"fmt"
	"slices"
)

type User struct {
    Name string
    Age int
}

func main() {
    users := []User{
        {"Bob", 30},
        {"Michael", 40},
        {"Jeff", 10},
        {"Liz", 25},
        {"Bob", 20},
    }
    fmt.Println("ソート前:", users)
    // 名前の昇順→年齢の降順で並び替えを行う
    slices.SortStableFunc(users, func(a, b User) int {
        return cmp.Or(
            cmp.Compare(a.Name, b.Name),
            -cmp.Compare(a.Age, b.Age),
        )
    })
    fmt.Println("ソート後:", users)
}
ターミナル
// 2人いるボブのうち、年齢の降順なので30歳のBobが先に並べられている
ソート前: [{Bob 30} {Michael 40} {Jeff 10} {Liz 25} {Bob 20}]
ソート後: [{Bob 30} {Bob 20} {Jeff 10} {Liz 25} {Michael 40}]

まとめ

  • cmpパッケージはGo 1.21で導入され、比較と順序付けをサポートする
  • Compare関数とLess関数は順序付け可能な型の比較に使用される
  • Or関数はゼロ値ではない最初の値を返し、複数キーでのソートに便利

参考

https://pkg.go.dev/cmp
https://go.dev/doc/go1.21#cmp
https://go.dev/doc/go1.22#minor_library_changes
https://future-architect.github.io/articles/20230815a/#cmp-package
https://speakerdeck.com/otakakot/impressed-with-cmp-or

Discussion