組込みじゃないんかい
Goは素晴らしい言語なのでソートのためにsort
パッケージを呼ぶことができるのです。手間かけてimportしたソートにはぬくもりがありますね。
sortパッケージはこちら。
src/sort/sort.go - The Go Programming Language
基本的な使い方
ググればたくさんでてきますが、こちらがすごくわかりやすかったです。ありがとうございました。
なにもしなくてもstirng, float64, int型は以下のような感じでソートできるそうです。ここではint型についてだけ書いてますが残りの型もお察しの感じです
sort.Ints(slices) sort.IntSlice(slices).Sort() sort.Sort(sort.IntSlice(a))
これらは全部同じ結果になります。IntSlice
、StringSlice
、Float64Slice
はそれぞれ[]int
、[]string
、[]float64
のエイリアスで、sort.Interface
インターフェースの各メソッドとsort.Sort()
を実装しています。sort.Ints()
の中身も、
// Ints sorts a slice of ints in increasing order. func Ints(a []int) { Sort(IntSlice(a)) }
のようにsort.Sortを内部的に呼んでいるだけなので、結局スライスをIntSlice
なりでキャストするだけで使えるって感じでしょうか。
降順は専用の別の関数があって
sort.Sort(sort.Reverse(sort.IntSlice(s)))
とすればいいです。
type reverse struct { // This embedded Interface permits Reverse to use the methods of // another Interface implementation. Interface } // Less returns the opposite of the embedded implementation's Less method. func (r reverse) Less(i, j int) bool { return r.Interface.Less(j, i) } // Reverse returns the reverse order for data. func Reverse(data Interface) Interface { return &reverse{data} }
reverse構造体がsort.InterfaceをまるまるパクっていてLess
に渡すときにiとjを逆に渡しているという、理性ではわかるけど実際に呼び出すとき非常にめんどくさい書き方と毎回思ってます。代案はありません。
ちなみにどういうソートを使っているか、なんですが、データ量によって内部でアルゴリズムを切り替えているらしいです。解読は諦めましたが。
複数のキーでソートする
C#でいうとこのOrderBy().ThenBy()...
、pythonでいうとこのsortedlist = sorted(unsortedlist, key=lambda x:(x[0], x[2]))
みたいなやつをやりたいですがGoにはメソッドチェーンなんて存在しないので関数型をやりくりする必要があります。
ドキュメントのサンプル( https://golang.org/pkg/sort/#example__sortMultiKey のexample_SortMultiKeyってやつ)をみるとOrderBy
関数にsort.Interface用のLess
メソッドを可変長引数で複数わたせるようにしておき、ターゲットとなるスライスとそれらメソッド郡をまとめた別の構造体multiSorter
からソートをしている様子です(わかりづらい説明)。実際のLessの実装はmainでやっていますがたいていどこのサンプルをみてもそうやっていました。なるほど。
OrderByを作ってそっちにLessメソッド群を渡すのがなんか複雑でやりたくねぇなと思ったので、自分はsort.Sort()にそのまま渡すのが見た目的にシンプルだしいいやってことでそんな感じでやってみました。備忘録がわりにおいときます。
package main import ( "fmt" "time" "sort" ) type profile struct{ name string class int height int data time.Time threesize [3]int } // lessfuncのエイリアス type lessFunc func(i, j *profile)bool // ターゲットになるスライスとlessfuncのスライスをまとめた構造体 type idleSorter struct{ idle []profile lessfunc []lessFunc } // にsort.Interfaceを定義 func (is idleSorter)Len()int{ return len(is.idle) } func (is idleSorter)Swap(i, j int){ is.idle[i], is.idle[j] = is.idle[j], is.idle[i] } func (is idleSorter)Less(i, j int)bool{ var k int // 最後にkつかうのでfor簡易文ではなくここで宣言 p, q := &is.idle[i], &is.idle[j] for k=0; k<len(is.lessfunc)-1; k++{ less := is.lessfunc[k] // less(p, q)が真 -> swapが発生 偽だと発生しない switch{ case less(p, q): return true case less(q, p): return false } // p==qならもっかいkを回すことになる } // すべてのlessfuncにおいてp==qならばそんなんもう同値ってことだしどっちでもかまわんからk番目を返す return is.lessfunc[k](p, q) } // ターゲットになるやつ var aqours = []profile{ {name: "黒澤ルビィ", class: 1, height: 154, data: setData(9, 21), threesize: [3]int{76,56,79}}, {name: "国木田花丸", class: 1, height: 152, data: setData(3, 4), threesize: [3]int{83,57,83}}, {name: "津島善子", class: 1, height: 156, data: setData(7, 13), threesize: [3]int{79, 58, 80}}, {name: "高海千歌", class: 2, height: 157, data: setData(8, 1), threesize: [3]int{82,59,83}}, {name: "桜内梨子", class: 2, height: 160, data: setData(9, 19), threesize: [3]int{80, 58, 82}}, {name: "渡辺曜", class: 2, height: 157, data: setData(4, 17), threesize: [3]int{82, 57, 81}}, {name: "黒澤ダイヤ", class: 3, height: 162, data: setData(1, 1), threesize: [3]int{80,57, 80}}, {name: "松浦果南", class: 3, height: 162, data: setData(2, 10), threesize: [3]int{83,58,84}}, {name: "小原鞠莉", class: 3, height: 163, data: setData(6, 13), threesize: [3]int{87,60,84}}, } func setData(month, day int) time.Time{ t := time.Date(0000, time.Month(month), day, 0, 0, 0, 0, time.Local) return t } func main(){ // lessfuncを実装 byBast := func(p1, p2 *profile)bool{ return p1.threesize[0]<p2.threesize[0] } byClass := func(p1, p2 *profile)bool{ return p1.class<p2.class } // 逆順も同じようにここで実装する byBastDescending := func(p1, p2 *profile)bool{ return p1.threesize[0]>p2.threesize[0] } // 誕生日 time.TimeもBefore/Afterで比較してboolを返せる byDate := func(p1, p2 *profile)bool{ return p1.data.Before(p2.data) } fmt.Println("おっぱい昇順") tes := idleSorter{idle: aqours, lessfunc: []lessFunc{byBast}} sort.Sort(tes) for i, v := range aqours{ fmt.Printf("[%d] name: %s bastsize: %d\n", i+1, v.name, v.threesize[0]) } fmt.Println("誕生日昇順") sort.Sort(idleSorter{idle: aqours, lessfunc: []lessFunc{byDate}}) for i, v := range aqours{ fmt.Printf("[%d] name: %-7s birthday: %s\n", i+1, v.name, v.data.Format("1月2日")) } fmt.Println("学年昇順でおっぱい降順") sort.Sort(idleSorter{idle: aqours, lessfunc: []lessFunc{byClass, byBastDescending}}) for i, v := range aqours{ fmt.Printf("[%d] name: %-7s class: %d bast: %d\n", i+1, v.name, v.class, v.threesize[0]) } }
ちなみにこのサンプルコードのところに、
Note that it can call the less functions twice per call. We could change the functions to return -1, 0, 1 and reduce the number of calls for greater efficiency: an exercise for the reader.
とあったのですがやりかたがさっぱり思いつきません。an exercise for the reader.(これは読者への宿題だよ☆)じゃねえよ
sort.Sliceを使ってみる
ついでに、Go1.8から実装されたこちらのメソッドを使ってみます。
func Slice(slice interface{}, less func(i, j int) bool)
とのことで、第二引数の無名関数によっていちいちless用の関数を作って渡してやる手間が省けます。素晴らしい。
The sort is not guaranteed to be stable. For a stable sort, use SliceStable.
とのことなのですが安定ソートってなんでしょう。。。わからんので無視します
fmt.Println("sort.Sliceを使ってみる おっぱいがでかい順->尻がでかい順") sort.Slice(aqours, func(i, j int)bool{ // でかい順なので不等号の向きに注意 p, q := aqours[i], aqours[j] if p.threesize[0]>q.threesize[0]{ return true }else if p.threesize[0]<q.threesize[0]{ return false }else{ return p.threesize[2]>q.threesize[2] } }) for i, v := range aqours{ fmt.Printf("[%d] name: %-7s bast: %d hip: %d\n", i+1, v.name, v.threesize[0], v.threesize[2]) }
3年生のみなさん強いですね。。
おわりに
バストの英語表記がbastじゃなくてbustっていま知りました。