範囲(Range)
実装されている抽象概念が豊富なこともRubyの魅力だ。
例えば「範囲」。我々がいろいろな所で半ば無意識に使う概念だが、Rubyでは範囲演算子を使ってこれを直接表現できる。
1..12
これは見た目通り1〜12の整数の範囲を表しているのだが(".."は両端終端を含み、"..."は両端終端を含まない範囲とすることができる)、このように記述した範囲は内部ではRangeクラスの定数に変換されていることになる。
Rangeクラスはそれ自体、メソッドを持っており範囲に対して行いたい操作メッセージを送信することができる。
irb(main):001:0> a = 1..12 => 1..12 irb(main):002:0> a.include?(5) => true irb(main):003:0> a.min => 1 irb(main):004:0> a.max => 12 irb(main):005:0> a.to_a => [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]
さて、ここからが真骨頂。
Rubyは独自の範囲を作ることが出来る上に、範囲を作るためにはRangeクラスを継承したクラスにする必要が無い。
単に範囲を作るだけであれば、Comparableモジュールをmixinして演算子 "<=>" と succメソッド※に対する応答を用意すれば良い。
bmi.rb
class BMI include Comparable attr :volume def initialize(volume) @volume = volume end def inspect return '痩せ' if 18.5 > @volume return '標準' if (18.5..24) === @volume return '肥満' if (25..29) === @volume return '高度肥満' if 30 <= @volume end def <=>(other) self.volume <=> other.volume end def succ BMI.new(@volume.succ) end end mybmi = BMI.new(18.5) p mybmi mybmi = BMI.new(15) .. BMI.new(27) p mybmi p mybmi.to_a
実行結果
>ruby -Ks bmi.rb 標準 痩せ..肥満 [痩せ, 痩せ, 痩せ, 痩せ, 標準, 標準, 標準, 標準, 標準, 標準, 肥満, 肥満, 肥満]
実装するのにインタフェースすら書かなくても良いのはちょっと感動。
範囲はいろいろな概念をコードに変換するのに重宝するだろう。
※succって、successorの省略かな。