@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は暗黙知っぽいのが多いけど,なんなのか想像しつつ読むとおもしろい
- 仕組みを調べると言語自体の理解が深まる気がする