せかいや

いまいるここを、おもしろく http://sekai-in-the-box.appspot.com/

【Ruby】Rubyで実装されているRangeクラス? Ruby1.9で範囲がFloat,Timeのときにinclude?がエラーになる理由

 

大江戸Ruby会議での村田さんの発表資料のなかに
こんなスクショがありました。
f:id:sekaiya:20131023081346j:plain


来年の四月に4回目が開催されるらしく、
どんな会議なのかなと思って資料を覗いていたという経緯だよ。
資料一覧はこちら

 
なんだろう?
なぜRangeがRubyで記述されているのかな

そのあとのパワポにもStringがRubyで記述されている。
なんだか変。
Railsとかのライブラリのクラス?
既存クラスをオープンしているということなのかな?


やっぱりRangeはcで記述されていると思うんだよね。。
f:id:sekaiya:20131023081229j:plain

時間切れ!
いってきます!

(追記)
secondlifeさんにコメントを頂いたよ。感激だ!
f:id:sekaiya:20131023232853j:plain

なるほど!
RubyがOSSとして提供しているコードではなくて、
プロジェクト独自(クックパッド独自)のコードなのか。
どうりでこのメソッド名でぐぐっても何もでないわけだ。

パワポの最後のほうに、モンキーパッチ云々と書いていたのはそういう意味か。
このパワポでは、パッチのコードを抜粋して見せてくれてたんだ。

 
モンキーパッチのディレクトリ
f:id:sekaiya:20131024004136j:plain

あれ?
そもそもこのパッチ(Range.rb)はどこに当てるんだろう?
Ruby1.8?1.9?

 
Ruby1.8にパッチを当てる(書いてるコードは1.8文法)⇒今まで正常だったのがWARNが出るようになる
Ruby1.9にパッチを当てる(書いてるコードは1.8文法)⇒エラーが発生してしまうところをWARNが出るようになる

なるほど。。。?
Ruby1.8、Ruby1.9どちらもに当てたら、
挙動が同じになるから移行しやすいのではないかな。。?
だからこのディレクトリにも同じものが2つづつ(1.8と1.9に)存在しているのかも。
(パワポのコードを見る限り)RUBY_VERSIONが1.8台の時に
1.8フォルダ下のファイルを読み込む実装(だと思う)。

ちょっとこれ以上は良く分からない。あくまで予想。
でもとりあえず、「Rubyで実装されているRangeクラス」の秘密は分かった。
 
せっかくなのでこのVerUpに伴う内容も調べてみよう。

 

Ruby1.9でinclude?がエラー?

確かに、Ruby2.0では範囲がTimeだとエラーになる。

now = Time.now 
future = now + (60 * 60 * 24)
p RUBY_VERSION
(now..future).include?(now)

■実行結果

"2.0.0"
time.rb:4:in `each': can't iterate from Time (TypeError)

 
これはどうしてかはオライリー本に書いてある。

Ruby1.9のinclude?とmember?は範囲の端点が数値ならRuby1.8のときと同様に連続範囲メンバテストを行うが、端点が数値でなければ離散範囲メンバテストを行う

つまり、Timeは数値ではないので、離散形式でテストされる。
しかしTimeを用いた範囲は離散範囲ではない。
 

離散範囲の説明↓

Stringクラスがsuccメソッドを定義しており、a.succがb b.succがc だからである。このように反復処理できる範囲を離散範囲という。

日時は「反復できない」。
だからエラーメッセージも

`each': can't iterate from Time

なんだね。

 
替わりにcover?を使えば良い(宇宙船演算子<=>を用いた範囲確認方法)。

<=> メソッドによる演算により範囲内かどうかを判定するには Range#cover? を使用してください。

http://doc.ruby-lang.org/ja/1.9.3/method/Range/i/include=3f.html

なるほど。


 

aliasによる既存メソッドの拡張

パワポに載っているこのコード、

alias include_without_warn? include?
alias include? include_with_warn? 

こうやってaliasを2文書く理由は、既存のinclude? を、
include_without_warn?メソッドにてバックアップを取りつつ、
拡張する(include_with_warn?で定義した振る舞いを行う)ため。
既存のメソッドに追加するときによくあるイディオムみたい。
以前に勉強したところ。詳しくはこちら。



ふむふむ。
バージョンアップ毎の変化がすごいなー。
SIerJavaマイグレーションと訳が違う。
エネルギーがないと追いつけなさそう。。!