2013年1月14日月曜日

Go言語でソートする


こんにちは。scarvizです。

明けましておめでとうございます。
今年もよろしくお願いします!

ということで、今回はGo言語のソートの仕方を紹介します。

「$GOROOT/src/pkg/sort/sort.go」のソースコードを参考にしました。




■あらかじめ用意されているソート関数を使う
Go言語はスライス型か自分で定義した型のソートが出来るようになっています。
スライス型でソートできるのはint,float64,stringの3種類のみです。
int32やint64、float32などは型変換するか、後で説明する「自分で定義した型のソートをする」を参考にしてください。

ソート関数を使う手順は以下になります。

1. sortをインポートする

2. それぞれのソート関数を呼び出す
intの場合はsort.Ints、float64の場合はsort.Float64s、stringの場合はsort.Stringsを呼び出します。
引数で渡したスライスが昇順になって返ってきます。


また、既にソートされているかどうかをチェックする関数もあります。
intの場合はsort.IntsAreSorted、float64の場合はsort.Float64sAreSorted、stringの場合はsort.StringsAreSortedを呼び出します。
既にソート済みの場合はtrue、未ソートの場合はfalseが返ってきます。


以下に使用例を記載します。
package main

import (
 "fmt"
 "sort"
)

func main() {
 dataInt := []int{9, 3, 6, 7, 4, 1, 2, 8, 5}
 sort.Ints(dataInt)
 fmt.Println("dataInt:", dataInt)

 dataFloat64 := []float64{9, 3, 6, 7, 4, 1, 2, 8, 5}
 sort.Float64s(dataFloat64)
 fmt.Println("dataFloat64:", dataFloat64)

 dataString := []string{"d", "g", "b", "j", "c", "i", "a", "f", "e", "h"}
 sort.Strings(dataString)
 fmt.Println("dataString:", dataString)

 var isSort bool
 isSort = sort.IntsAreSorted(dataInt)
 fmt.Println("dataInt Sorted:", isSort)

 isSort = sort.Float64sAreSorted(dataFloat64)
 fmt.Println("dataFloat64 Sorted:", isSort)

 isSort = sort.StringsAreSorted(dataString)
 fmt.Println("dataString Sorted:", isSort)
}


結果:
dataInt: [1 2 3 4 5 6 7 8 9]
dataFloat64: [1 2 3 4 5 6 7 8 9]
dataString: [a b c d e f g h i j]
dataInt Sorted: true
dataFloat64 Sorted: true
dataString Sorted: true

■自分で定義した型のソートをする
あらかじめ用意されているソート関数はインターフェースを使っているので、これを利用して、自分で定義した型のソートをする関数を作ることが出来ます。
インターフェースで定義されている関数は以下になります。

// スライスの長さを取得する
Len() int
// 要素の大小を比較してiが小さいかどうかを判断する
Less(i, j int) bool
// 要素を入れ替える
Swap(i, j int)

試しにint32がソートできるようにしてみようと思います。

1. 型を定義する
int32のスライスを型定義します。

type myInt32Slice []int32

2. インターフェースの関数を実装する
各関数を実装します。

func (p myInt32Slice) Len() int           { return len(p) }
func (p myInt32Slice) Less(i, j int) bool { return p[i] < p[j] }
func (p myInt32Slice) Swap(i, j int)      { p[i], p[j] = p[j], p[i] }

3. ソート関数を実装する
int32のスライスを渡してソートできる関数を実装します。

func myInt32s(a []int32) { sort.Sort(myInt32Slice(a)) }
func myInt32SliceAreSorted(a []int32) bool { return sort.IsSorted(myInt32Slice(a)) }

4. ソートする
int32のスライスをソートしてみます。

package main

import (
 "fmt"
 "sort"
)

type myInt32Slice []int32

func (p myInt32Slice) Len() int           { return len(p) }
func (p myInt32Slice) Less(i, j int) bool { return p[i] & lt; p[j] }
func (p myInt32Slice) Swap(i, j int)      { p[i], p[j] = p[j], p[i] }

func myInt32s(a []int32)                   { sort.Sort(myInt32Slice(a)) }
func myInt32SliceAreSorted(a []int32) bool { return sort.IsSorted(myInt32Slice(a)) }

func main() {
 dataInt32 := []int32{9, 3, 6, 7, 4, 1, 2, 8, 5}
 myInt32s(dataInt32)
 fmt.Println("dataInt32:", dataInt32)

 isSort = myInt32SliceAreSorted(dataInt32)
 fmt.Println("dataInt32 Sorted:", isSort)
}

結果:
dataInt32: [1 2 3 4 5 6 7 8 9]
dataInt32 Sorted: true

■ソートメソッドを実装する
自分で定義した型なら、ソートメソッドを用意すると便利だと思います。
以下はstringのスライスを型定義したmyStringSliceで使ってみました。

package main

import (
 "fmt"
 "sort"
)

type myStringSlice []string

func (p myStringSlice) Len() int           { return len(p) }
func (p myStringSlice) Less(i, j int) bool { return p[i] & lt; p[j] }
func (p myStringSlice) Swap(i, j int)      { p[i], p[j] = p[j], p[i] }

// ソートメソッド
func (p myStringSlice) Sort() { sort.Sort(p) }

func main() {
 var dataMyString myStringSlice
 dataMyString = []string{"d", "g", "b", "j", "c", "i", "a", "f", "e", "h"}
 dataMyString.Sort()
 fmt.Println("dataMyString:", dataMyString)
}

結果:
dataMyString: [a b c d e f g h i j]

ソートは良く使うと思うので、知っておくと便利ですね。
ちなみにお気付きかもしれませんが、Less関数を実装する時に、大小比較を逆しておくと降順でソートするようにできます。
みなさんも色々試してみてください!

1 件のコメント:

  1. そうそう。構造体のスライスをソートするようにするとめっちゃ便利と思います。
    基本的には記事通りに実装して、Less関数は構造体で定義しているもののどれを比較するか指定すればOKですね。

    返信削除