ローマ数字(roman numerals)
アラビア数字を入力としてローマ数字を返すメソッドを作成する。
回答(リファクタリング前)
無駄が多いし長くて見ない方がいいレベルなので折り畳んでます。
def getNums()
lines = readlines
len = lines.length
(0..len-1).each do |i|
lines[i] = lines[i].chomp.to_i
end
return lines
end
class Integer
def to_roman
sym = ['I', 'V', 'X', 'L', 'C', 'D', 'M']
roman = ''
to_arr(4).each_with_index do |num, i|
case i
when 0 then
roman += sym[6] * num
when 1 then
case num
when 0..3 then
roman += sym[4] * num
when 4 then
roman += sym[4] + sym[5]
when 5..8 then
roman += sym[5] + sym[4] * (num-5)
when 9 then
roman += sym[4] + sym[6]
end
when 2 then
case num
when 0..3 then
roman += sym[2] * num
when 4 then
roman += sym[2] + sym[3]
when 5..8 then
roman += sym[3] + sym[2] * (num-5)
when 9 then
roman += sym[2] + sym[4]
end
when 3 then
case num
when 0..3 then
roman += sym[0] * num
when 4 then
roman += sym[0] + sym[1]
when 5..8 then
roman += sym[1] + sym[0] * (num-5)
when 9 then
roman += sym[0] + sym[2]
end
end
end
roman
end
def to_arr(n)
arr = []
(Math.log10(self).to_i + 1).upto(n-1) do
arr.unshift(0)
end
arr + digits.reverse
end
end
if $PROGRAM_NAME == __FILE__
getNums().each do |i|
puts i.to_roman
end
end
回答(リファクタリング後)
def getNums()
lines = readlines
(0..lines.length-1).each do |i|
lines[i] = lines[i].chomp.to_i
end
lines
end
class Integer
def to_roman
sym = ['I', 'V', 'X', 'L', 'C', 'D', 'M']
roman = ''
base = sym.length-1
to_arr(4).each_with_index do |num, i|
case num
when 0..3 then
roman += sym[base-i*2] * num
when 4 then
roman += sym[base-i*2] + sym[base-i*2+1]
when 5..8 then
roman += sym[base-i*2+1] + sym[base-i*2] * (num-5)
when 9 then
roman += sym[base-i*2] + sym[base-(i-1)*2]
end
end
roman
end
def to_arr(n)
arr = Array.new(n - Math.log10(self).to_i - 1, 0)
arr + digits.reverse
end
end
if $PROGRAM_NAME == __FILE__
getNums().each do |i|
# puts "#{i} => #{i.to_roman}"
printf("%4d => %s\n", i, i.to_roman)
end
end
解説
アラビア数字の入力受け取り
def getNums()
lines = readlines
(0..lines.length-1).each do |i|
lines[i] = lines[i].chomp.to_i
end
lines
end
ここは標準入力から入力を受け取る部分です。
- 複数行の入力を対象としているので、改行区切りで入力できます。
- 改行コードを取り除いた文字列から Integer にキャストして、lines に配列として格納していきます。
実行部分
if $PROGRAM_NAME == __FILE__
getNums().each do |i|
# puts "#{i} => #{i.to_roman}"
printf("%4d => %s\n", i, i.to_roman)
end
end
ここはプログラムを $ ruby roman_numerals.rb
等で実行した場合に実行される部分です。
- 先程の
getNums()
で入力を受け取り、その返りオブジェクト(配列) に対して標準出力します。 - この後の解説で記述しますが、Integerクラスにオーバーライドしているので、
(Integer).to_roman
で文字列としてローマ数字が取得できます。
ローマ数字を返すメソッド
class Integer
def to_roman
sym = ['I', 'V', 'X', 'L', 'C', 'D', 'M']
roman = ''
base = sym.length-1
to_arr(4).each_with_index do |num, i|
case num
when 0..3 then
roman += sym[base-i*2] * num
when 4 then
roman += sym[base-i*2] + sym[base-i*2+1]
when 5..8 then
roman += sym[base-i*2+1] + sym[base-i*2] * (num-5)
when 9 then
roman += sym[base-i*2] + sym[base-(i-1)*2]
end
end
roman
end
def to_arr(n)
arr = Array.new(n - Math.log10(self).to_i - 1, 0)
arr + digits.reverse
end
end
ここが今回の実装部分になります。
- Integer クラスにオーバーライドするような実装にしています。
4要素の配列へ(to_arr(n))
- ローマ数字は 1〜3999 を表現できます。
to_arr(n)
- 入力されたアラビア数字を各桁で分けて配列化するメソッドです。
-
n
は最大桁数を意味し、その桁より少ない数字に対しては 0 埋めされます。
- 例
n = 8
n.to_arr(4) # [0, 0, 0, 8]
ローマ数字へ
- ローマ数字の各シンボルに関しては Wikipedia 等を参照してください。
- 組み立てるローマ数字は文字列の変数
roman
に格納していきます。-
to_arr(4).each_with_index
-
self
は省略可能なので省略して記述しています。 -
to_arr(4)
で 4 要素の配列に変換してから、それぞれの桁に対して処理をおこないます。
-
-
case
文によって場合分けします。 ローマ数字で重要なのは 4 と 9 です。- それぞれの数字に対しての処理は上記 Wikipedia のローマ数字の概念と配列
sym = ['I', 'V', 'X', 'L', 'C', 'D', 'M']
を照らし合わせれば理解できます。
- それぞれの数字に対しての処理は上記 Wikipedia のローマ数字の概念と配列
- 最後に出来上がったローマ数字
roman
を出力します。
-
- 例
n = 8
n.to_roman # "VIII"
n = 1999
n.to_roman # "MCMXCIX"
実行結果
まず入力として以下のテキストファイル(in.txt)を用意します。
1
2
4
5
6
9
10
11
14
15
19
38
42
49
51
97
99
439
483
499
732
961
999
1999
これを入力として与えた時の実行結果が以下。
$ ruby roman_numerals.rb < in.text
1 => I
2 => II
4 => IV
5 => V
6 => VI
9 => IX
10 => X
11 => XI
14 => XIV
15 => XV
19 => XIX
38 => XXXVIII
42 => XLII
49 => XLIX
51 => LI
97 => XCVII
99 => XCIX
439 => CDXXXIX
483 => CDLXXXIII
499 => CDXCIX
732 => DCCXXXII
961 => CMLXI
999 => CMXCIX
1999 => MCMXCIX
動いた!!!!!