Ruby で循環小数を扱う
以前にも同様の試みをしたのですが(参照)、コードを始めから書き直しました。以前のは何か自分でもよくわからない、面倒なことをしているので。
作ったのは Rational#to_rec_decimal と String#to_r で、前者は Rational(分数)を(String で表される)循環小数に、後者はその逆で(String で表される)循環小数を Rational(分数)に直します。ともに負数もサポートします。String#to_r は同名の組み込みメソッドをオーバーライドしています。
例。
irb(main):001:0> require_relative 'rec_decimal' => true irb(main):002:0> Rational(25, 17).to_rec_decimal => "1.(4705882352941176)" irb(main):003:0> Rational(12, 7).to_rec_decimal => "1.(714285)" irb(main):004:0> Rational(55, 48).to_rec_decimal => "1.1458(3)" irb(main):005:0> "0.23(41)".to_r => (1159/4950) irb(main):006:0> "7.(3)".to_r => (22/3) irb(main):007:0> Rational(-1, 3).to_rec_decimal => "-0.(3)" irb(main):008:0> "-2.45(7)".to_r => (-553/225)
コード。
rec_decimal.rb
class Rational #分数を循環小数に直す def to_rec_decimal #String{f, result}, Rational{num, ra} #Integer{i, remainder, deno, place[], rems[], idx} >> String f, num = (self < 0) ? ["-", -self] : ["", self] i = num.to_i result = f + i.to_s ra = num - i return result if ra.zero? result += "." remainder = ra.numerator deno = ra.denominator place = [] rems = [] begin rems << remainder place << remainder * 10 / deno remainder = remainder * 10 % deno return result + place.join if remainder.zero? end while not (idx = rems.find_index(remainder)) place.insert(idx, "(") result + place.join + ")" end end class String #循環小数を分数に直す alias :__to_r__ :to_r def to_r #String{st}, Rational{result}, m, f, l >> Rational st = delete(" ") return st.__to_r__ unless (m = /^([^\d]?)(\d+)\.(\d*)\((\d+)\)$/.match(st)) f = (m[1] == "-") ? -1 : 1 result = (m[2] + "." + m[3]).__to_r__ l = m[4].length result += Rational(m[4].to_i, 10 ** l - 1) * Rational(1, 10) ** m[3].length result * f end end
Gem 化
いろいろ使っているコードを Gem 化した中に入れておきました。
kaki-utils | RubyGems.org | your community gem host
インストールは $ gem install kaki-utils で。使うときは
require 'kaki/utils/rec_decimal'
でどうぞ。