@taichinoさんがPythonなら不等式を連結できることに喜んでいて,たまたまここに書いてるのを読んでたから「それRubyでもできるよ」って反応した.
@taichino 不等式の連結はたしかRubyならRangeを使えば同じようなことできますね
— チキンとタイカレーさん (@yashigani) 2013年3月5日
「どうなってんの?」って,聞かれたれたけどRuby力低くてなんで動いてるのか答えられなかった.けれども,調べているうちに理解できたのでメモ.
このコードを元に考えてみる.
n = 5 status = case n when 1..10 :first when 11..20 :second end puts status # => :first
これ初見でも直感的に動作は理解はできるけど,実際書けって言われたら絶対無理.どうなってるのか分解してみる.
1..10
いうまでもないけど,1..10はRangeオブジェクトを返す.ここもオブジェクトなのが大事.これはメソッドなのかな?そこまでは調べられてない.
case
caseが値返しててまず「なんじゃこら」ってなるけどRubyのcaseは式なので値が返ってくる.(式ってなんやって人は黙ってHaskellを…)この式から返ってくる値は最後に評価した値らしい.メソッドの動きと同じ.そしてwhen節に与えられた式とcaseに与えられた値を===で評価して,真ならその節を評価する.
つまり,caseの動作は以下のコードと同じになる.
n = 5 status = if (1..10) === n :first elsif (11..20) === n :second end puts status # => :first
あ,Rubyではもちろんifも式.
===ってなに?
===ってなんや?Rangeオブジェクトのリファレンスを見てみると,Range#===はRange#include?と同じ動きをするこようだ.include?なら一目で動作がわかる.
(1..10) === nとかどう見てもメソッド呼び出しじゃないだろバカめ!と思うかもしれないが,Rubyではメソッド引数の()は省略できる.更にメソッド名が記号の場合,メソッドの前のドットさえも省略できる.Haskellの中置みたいな感じ.
つまり,以下のように書き換えできる.
n = 5 status = if (1..10).include?(n) :first elsif (11..20).include?(n) :second end puts status # => :first
ここまでくると動作の仕組みは誰の目にも明らかだ.caseがなんで動いてるのか理解できたぞ.
感想
適宜===さえ実装してやれば,自分で定義したクラスにもcaseが適用できるのがいいと思う.既にあるものに関しても差し替えてやればいいし(List#===は与えられたリストと同値かを返すけど,要素を含んでいるかという動作に書き換えるのは簡単).
にしても,Rubyはシンタックスシュガーが多くてわからないことが多い.ドキュメントにもなんの説明もなく平気で書いてあるし.けど,どういう理屈で動作しているのか,きちんと調べていくと発見があっておもしろい.
まとめ
- Pythonじゃなくても不等式重ねられる.Rubyのはかなり柔軟に使えそう
===は便利.たぶんもっと他の場所でも使ってる- Rubyは暗黙知っぽいのが多いけど,なんなのか想像しつつ読むとおもしろい
- 仕組みを調べると言語自体の理解が深まる気がする