【Ruby】Rubyで実装されているRangeクラス? Ruby1.9で範囲がFloat,Timeのときにinclude?がエラーになる理由
大江戸Ruby会議での村田さんの発表資料のなかに
こんなスクショがありました。
来年の四月に4回目が開催されるらしく、
どんな会議なのかなと思って資料を覗いていたという経緯だよ。
資料一覧はこちら
なんだろう?
なぜRangeがRubyで記述されているのかな
そのあとのパワポにもStringがRubyで記述されている。
なんだか変。
Railsとかのライブラリのクラス?
既存クラスをオープンしているということなのかな?
やっぱりRangeはcで記述されていると思うんだよね。。
時間切れ!
いってきます!
(追記)
secondlifeさんにコメントを頂いたよ。感激だ!
なるほど!
RubyがOSSとして提供しているコードではなくて、
プロジェクト独自(クックパッド独自)のコードなのか。
どうりでこのメソッド名でぐぐっても何もでないわけだ。
パワポの最後のほうに、モンキーパッチ云々と書いていたのはそういう意味か。
このパワポでは、パッチのコードを抜粋して見せてくれてたんだ。
モンキーパッチのディレクトリ
あれ?
そもそもこのパッチ(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?で定義した振る舞いを行う)ため。
既存のメソッドに追加するときによくあるイディオムみたい。
以前に勉強したところ。詳しくはこちら。
ふむふむ。
バージョンアップ毎の変化がすごいなー。
SIerのJavaマイグレーションと訳が違う。
エネルギーがないと追いつけなさそう。。!
メモ
前にインタプリタ、処理系、組み込みって概念を考えた。これはメモ。