循環小数を分数に

循環小数を分数に直す@Ruby - 簡潔なQ」が面白そうだったのでやってみた。

require "rational"

def period source
  # [整数部, 小数部, 循環小数部]を取得する正規表現
  matches = source.match(/\A(-?\d+)(?:\.(\d*)(?:\[(\d+)\])?)?\Z/)
  raise ArgumentError.new("#.###[###]") unless matches
  
  # 整数部のみならそのまま返却
  return matches[1].to_i unless matches[2]
  
  # 整数部と小数部の計算
  numerator = (matches[1]+matches[2]).to_i
  denominator = 10**matches[2].size
  
  # 循環小数部の計算
  if matches[3]
    period_factor = 10**matches[3].size-1
    numerator   = numerator*period_factor   + matches[3].to_i
    denominator = denominator*period_factor
  end
  
  return numerator.quo(denominator)
end

同じことやってるのに行数めっちゃ増えてる!

テスト

上のコードを「period.rb」として保存。

require "test/unit"
require "period"

class TestPeriod < Test::Unit::TestCase
  def test_integer
    assert_equal  1, period( "1")
    assert_equal -1, period("-1")
    assert_equal  1, period( "1.")
  end
  
  def test_integer_fraction
    assert_equal  1, period( "1.0")
    assert_equal -1, period("-1.0")
  end
  
  def test_fraction
    assert_equal  11.quo(10),     period( "1.1")
    assert_equal  11.quo(10),     period( "1.100")
    assert_equal  1111.quo(1000), period( "1.111")
  end
  
  def test_period
    assert_equal  10.quo(9), period( "1.[1]")
    assert_equal  10.quo(9), period( "1.[11]")
    assert_equal  10.quo(9), period( "1.[111]")
    assert_equal  1233.quo(999), period( "1.[234]")
    assert_equal  111.quo(90),   period( "1.2[3]")
    assert_equal  1111.quo(90),  period( "12.3[4]")
  end
  
  def test_raised
    assert_raise(ArgumentError){ period("1.[]") }
  end
end

One Assertion Per Testはー?