(1..7).to_a.sort { |a, b| a[0] b[0] } ?

イテレータ解説である
PHPer 向け Ruby のイテレータ解説 - まちゅダイアリー(2007-10-11)
を読んでいて、気になる表記を見かけた。

ちなみに、イテレータの説明のために array_map メソッドを自作したけど、これらのメソッドは Array クラス(がインクルードしているEnumerableモジュール)にてあらかじめ用意されている。なので、自分で array_map メソッドを作らなくてもそのまま以下のように書ける。

arr = [1,2,3,4,5,6,7]

arr.map {|item| item * 3 }
arr.map {|item| item + 3 }
arr.map {|item| item % 3 }

arr.select {item| item > 10 }
arr.sort {|a,b| a[0] <=> b[0] }

よくやるsortのイテレータ

sort {|a,b| a <=> b }

なので、[0]が有るか無いかの違い。
で、実際にやってみるとこうなる。

pry(main)> arr = [1,2,3,4,5,6,7]
=> [1, 2, 3, 4, 5, 6, 7]
pry(main)> arr.sort {|a,b| a[0] <=> b[0] }
=> [4, 2, 6, 1, 5, 3, 7]

…なんなの、この並び方は!?

pry(main)> arr.sort {|a,b| a[1] <=> b[1] }
=> [1, 5, 4, 3, 2, 6, 7]
pry(main)> arr.sort {|a,b| a[0] <=> b[1] }
=> [4, 1, 7, 2, 6, 5, 3]

???

{ |a, b| a[0] <=> b[0] }?

ブロックとともに呼び出された時には、要素同士の比較をブロックを用いて行います。 ブロックに2つの要素を引数として与えて評価し、その結果で比較します。
instance method Array#sort (Ruby 1.9.3)

つまり

a[0] <=> b[0]

の比較が行われている。
なんだろ、これ。
当初、私はa, bそれぞれが複数要素を持つのかと思った。つまり、条件によってブロックが要素を複数取り出したりするのかなと考えてみた*1。でも違った。

結論

instance method Fixnum#[]

self[nth] -> Fixnum[permalink]

nth 番目のビット(最下位ビット(LSB)が 0 番目)が立っている時 1 を、そうでなければ 0 を返します。

  • [PARAM] nth:

何ビット目を指すかの数値

  • [RETURN]

1 か 0
instance method Fixnum#[] (Ruby 1.9.3)

つまり2進数にしてn番目のビットで比較を行っていたわけ。整数クラスに[]が定義されてることを知らなかったし、そんな定義をしているとは思わなかった。

ではStringクラスでは?

ここからは脱線ですw

instance method String#[]

self[nth] -> String | nil[permalink]
slice(nth) -> String | nil

nth 番目の文字を返します。 nth が負の場合は文字列の末尾から数えます。 つまり、 self.size + nth 番目の文字を返します。
nth が範囲外を指す場合は nil を返します。
instance method String#[] (Ruby 1.9.3)

そういえば文字列の文字を返すんですね。なので次の結果もすぐわかる。

pry(main)> arr = ["g", "f", "e", "d", "c", "b", "a"]
=> ["g", "f", "e", "d", "c", "b", "a"]
pry(main)> arr.sort{ |a, b| a <=> b }
=> ["a", "b", "c", "d", "e", "f", "g"]
pry(main)> arr.sort{ |a, b| a[0] <=> b[0] }
=> ["a", "b", "c", "d", "e", "f", "g"]
pry(main)> arr.sort{ |a, b| a[1] <=> b[1] }
=> ["g", "f", "e", "d", "c", "b", "a"]
pry(main)> arr.sort{ |a, b| a[1] <=> b[0] }
ArgumentError: comparison of String with String failed

sort{ |a, b| a[1] <=> b[1] }はnilnilを比較しているのでソートしない。
sort{ |a, b| a[1] <=> b[0] }でエラーが出るのはStringとnilを比較しようとしているからですね。

納得しました^_^。

*1:こんな器用なことはしてくれないようだw