[Go] sort.Sliceで文字列を辞書順にソートする

# はじめに
最近、Go言語で文字列を辞書順にソートしたい場面があり、少し調べてみるとGoの標準パッケージであるsortパッケージのSlice関数というのを使えば出来そうだということが分かりました。
そこで公式ドキュメントのsort.Sliceの使い方の説明を見たところ、こんな処理の書き方初めて見た、Slice関数の中でどのように処理されているのかいまいちイメージがつかないという部分があったので詳しく調べてみました。
この記事では、初めに結論としてsort.Sliceで文字列を辞書順にソートする方法を説明し、その後sort.Sliceは中でどのように処理を行なっているのかを確かめてみます。

# 対象読者
- Go言語を使用して文字列を辞書順にソートしたい人
- sort.Sliceの中でどのように処理されているのかを知りたい人

# 文字列を辞書順にソートする方法
Goの標準パッケージである[sortパッケージのSlice関数](https://pkg.go.dev/sort#Slice)を使うと、文字列を辞書順にソートすることができます。

```go
package main

import (
	"fmt"
	"sort"
)

func main() {
	names := []string{"Gopher", "Alice", "Vera", "Bob"}
		
	sort.Slice(names, func(i, j int) bool { return names[i] < names[j] })
	fmt.Println(names)
}
```
**出力結果**
```
[Alice Bob Gopher Vera]
```

# sort.Sliceとは？
これはGoの`sort`パッケージにある関数で**任意のスライスを、指定したルールで並び替える**便利なやつです。

```go
// 第一引数にソートしたいスライス、第二引数にどんなルールでソートするかの比較関数を渡す
sort.Slice(slice, lessFunc)
// 例
sort.Slice(names, func(i, j int) bool { return names[i] < names[j] })
```
上記の例の場合だと、`names`スライスを対象に`func(i, j int) bool`という**比較関数**を渡して、`names[i]`と`names[j]`を比べて**並び順が正しいか**を判定しています。

**比較関数の説明**
- `i`と`j`は、`names`スライス中の2つのインデックス
- `names[i] < names[j]`は、この比較が**true**なら「i番目はj番目より前にあるべき」、**false**なら「i番目とj番目の値の順番を入れ替える」というのを必要に応じて行なっています


# sort.Sliceは中でどんな処理を行なっているのか
初め比較関数のコードを見た時こう思いました。
なんでこれで比較ができるのか？
iとjには具体的にどんな値が入っているのだろう？
**結論から言うと**
`i`と`j`は、`sort.Slice`の**中の仕組みで自動的に渡されている引数**です。
つまり、`i`と`j`には自動的にスライスのインデックス番号が入り、比較する必要があれば`sort.Slice`内部のソートの処理で何度も比較関数が呼ばれ、文字列をソートします。

## 処理の過程をログを出力して見てみる
それでは実際に`i`や`j`にはどんな値が入っていて、どんな風に値がソートされているのかをログを仕込んで確認してみます。

```go
package main

import (
    "fmt"
    "sort"
)

func main() {
	names := []string{"Gopher", "Alice", "Vera", "Bob"}
		
	sort.Slice(names, func(i, j int) bool {
	    fmt.Println(names)
	    fmt.Printf("Comparing: names[%d] = %v vs names[%d] = %v\n", i, names[i], j, names[j])
	    return names[i] < names[j] 
	})
	
	fmt.Printf("result: %v", names)
}

```

**出力結果**
```go
[Gopher Alice Vera Bob]
Comparing: names[1] = Alice vs names[0] = Gopher
[Alice Gopher Vera Bob]
Comparing: names[2] = Vera vs names[1] = Gopher
[Alice Gopher Vera Bob]
Comparing: names[3] = Bob vs names[2] = Vera
[Alice Gopher Bob Vera]
Comparing: names[2] = Bob vs names[1] = Gopher
[Alice Bob Gopher Vera]
Comparing: names[1] = Bob vs names[0] = Alice
result: [Alice Bob Gopher Vera]
```
出力結果を見ると、まず1番目の`Alice`と0番目の`Gopher`を比較しています。`Alice < Gopher`なので順番を入れ替えます。次に2番目の`Vera`と1番目の`Gopher`を比較しています。`Gopher < Vera`なので入れ替えは発生しません。というように、順番の入れ替えが発生しなくなるまで比較関数が何度も呼び出され、要素の入れ替え処理を行います。
この結果を見ると、どうやらスライスの前の方から比較していき、要素の入れ替えが発生したら、入れ替えた要素の一つ前の要素と比較というのを繰り返しているように見えます。

# sort.Sliceの他にオススメされている方法
一方で[sort.Sliceの公式ドキュメント](https://pkg.go.dev/sort#Slice)にはこのようなことが書いてありました。

>Note: in many situations, the newer slices.SortFunc function is more ergonomic and runs faster.

日本語訳すると

>多くの場合、新しいslice.SortFunc関数の方がより人間工学的で高速に実行されます。

Go1.21以降で追加された`slices.SortFunc`という関数もあるみたいです。
ここに書くと長くなってしまうため、こちらについては[[Go] slices.SortFuncで文字列を辞書順にソートする](https://qiita.com/k21071/items/c9762e63554182d1adde)で解説したいと思います。

# まとめ
本記事では、Go言語で文字列を辞書順にソートする方法を紹介しました。
Goの標準パッケージである`sort.Slice`を使えば、任意のスライスを指定したルールで並び替えることができます。
加えて、`sort.Slice`の中で処理がどのように行われているのかを実際にログを出力して確認してみました。
ログを出力してみると、実際にどんな風に処理されているのかなということを実感できて理解が深まりました。