id:rahaema:20080708, id:rahaema:20080709 でcoerce規約について書きましたが、まだまだ理解が不十分なので引き続き取り組んでみました。
理解をするために、何か作ろうと思ったのですが、いい題材が思い浮かばず…。結局、「帯分数(mixed number)」を取り扱うことにしました。
練習用なので、加算しかできません。
mixed_number.rb
def MixedNumber(integer, fraction) MixedNumber.new(integer, fraction) end class MixedNumber < Numeric attr_accessor :integer, :fraction def initialize(integer, fraction) @integer = integer @fraction = fraction end def + (other) if other.kind_of?(MixedNumber) integer = @integer + other.integer fraction = @fraction + other.fraction if fraction.numerator > fraction.denominator # 仮分数のとき q, r = fraction.numerator. divmod(fraction.denominator) integer += q fraction = Rational(r, fraction.denominator) end MixedNumber.new(integer, fraction) elsif other.kind_of?(Rational) self + MixedNumber.new(0, other) elsif other.kind_of?(Integer) self + MixedNumber.new(0, Rational(other, 1)) elsif other.kind_of?(Float) Float(self) + other else x, y = other.coerce(self) x + y end end def coerce(other) if other.kind_of?(Integer) return MixedNumber.new(0, Rational(other, 1)), self elsif other.kind_of?(Rational) up = @integer * @fraction.denominator + @fraction.numerator return other, Rational(up, @fraction.numerator) else return other, self.to_f end end def to_f @integer + @fraction.to_f end def to_s @integer.to_s + "+" + @fraction.to_s end def inspect sprintf("MixedNumber(%s, %s)", @integer.inspect, @fraction.inspect) end end
実行してみます。Test::Unitフレームワークの勉強をかねて、以下のようなテストケースを作成してみました(本来なら、テストケースを先に作る)。
./test/test_mixed_number.rb
$:.unshift File.join(File.dirname(__FILE__), "..") require 'mixed_number' require 'rational' require 'test/unit' class TestMixedNumber < Test::Unit::TestCase def test_plus mixednum1 = MixedNumber(2, Rational(3, 5)) # 2+3/5 mixednum2 = MixedNumber(1, Rational(3, 4)) # 1+3/4 # MixedNumber + MixedNumber assert_equal("4+7/20", (mixednum1 + mixednum2).to_s) # MixedNumber + Integer assert_equal("7+3/5", (mixednum1 + 5).to_s) # MixedNumber + Float assert_equal("6.1", (mixednum1 + 3.5).to_s) # Integer + MixedNumber assert_equal("7+3/5", (5 + mixednum1).to_s) # Float + MixedNumber assert_equal("6.1", (3.5 + mixednum1).to_s) # MixedNumber + Rational assert_equal("3+1/5", (mixednum1 + Rational(3,5)).to_s) # Rational + MixedNumber assert_equal("74/15", (Rational(3,5) + mixednum1).to_s) end end
実行結果
mmasa@debian:~/work/ruby/mixed_number/test$ ruby test_mixed_number.rb Loaded suite test_mixed_number Started . Finished in 0.001096 seconds. 1 tests, 7 assertions, 0 failures, 0 errors
学習のポイントは、
- (良い題材かどうか分かりませんが、)「帯分数」を題材にして、coerce規約について理解する
- Test::Unitフレームワークを使ってみる
でしたが、実際、作ってみて、coerce規約についての理解が少し進んだと思います。