お題
アラビア数字 (arabic numerals) を受け取って, ローマ数字 (roman numerals) を返す method を書け.
(https://qiita.com/daddygongon/items/2d0a73a51ddab2d9da1b にある課題)
今回書いた code
とりあえず解説より先に, 今回書いた code を貼っとく.
def to_roman (arabic_numerals)
pair_arabic_roman = [[1,"I"],[4,"IV"],[5,"V"],[9,"IX"],[10,"X"],[40,"XL"],[50,"L"],
[90,"XC"],[100,"C"],[400,"CD"],[500,"D"],[900,"CM"],[1000,"M"]]
roman_numerals = ""
pair_arabic_roman.reverse_each do |p_a, p_r|
while arabic_numerals >= p_a do
arabic_numerals = arabic_numerals - p_a
roman_numerals = roman_numerals + p_r
end
end
return roman_numerals
end
if $PROGRAM_NAME == __FILE__
arabic_numerals = ARGV[0].to_i
print "#{arabic_numerals}\t : #{to_roman(arabic_numerals)}\n"
end
(Integer class に組み込んだ版はページ最後に貼ってる)
解説
まず, 作成する method 概要を整理する
- 関数名 : 何でも良い (ここでは to_roman としておく)
- 入力 : アラビア数字 (arabic numerals)
- 出力 : ローマ数字 (roman numerals)
では, さっそく作っていく
1 に対して I を出力
思うがままに書いたらこうなる.
def to_roman (arabic_numerals)
if arabic_numerals==1
print "I\n"
else
print "hoge\n"
end
end
if $PROGRAM_NAME == __FILE__
arabic_numerals = ARGV[0].to_i
print "#{arabic_numerals}\n"
to_roman(arabic_numerals)
end
もちろん実行通る.
ちなみに 1 以外の場合に "hoge"
を出力してるのは何となく.
2 に対して II, 4 に対して IV を出力
素直に書くとこうなった.
def to_roman (arabic_numerals)
if arabic_numerals==1
print "I\n"
elsif arabic_numerals==2
print "II\n"
elsif arabic_numerals==4
print "IV\n"
else
print "hoge\n"
end
end
if $PROGRAM_NAME == __FILE__
arabic_numerals = ARGV[0].to_i
print "#{arabic_numerals}\n"
to_roman(arabic_numerals)
end
もちろん実行通る.
5 に対して V を返す
elsif
書くのめんどくさくなったので, ちょっとループで書いてみた.
あと, 関数内で出力するのではなく, 返り値を返すようにした.
def to_roman (arabic_numerals)
pair_arabic_roman = [[1,"I"],
[2,"II"],
[4,"IV"],
[5,"V"]]
pair_arabic_roman.each do |arabic, roman|
if arabic_numerals==arabic
return roman
end
end
return "hoge"
end
if $PROGRAM_NAME == __FILE__
arabic_numerals = ARGV[0].to_i
print "#{arabic_numerals}\n"
print "#{to_roman(arabic_numerals)}\n"
end
もちろん実行通る.
1 ~ 10 に対して ローマ数字を返す
対応表 pair_arabic_roman
にアラビア数字とローマ数字の対応全てを書くのはめんどくさすぎる.
なので, こうした.
def to_roman (arabic_numerals)
pair_arabic_roman = [[1,"I"],[2,"II"],[4,"IV"],[5,"V"],[6,"VI"],[9,"IX"],[10,"X"]]
roman_numerals = ""
pair_arabic_roman.reverse_each do |arabic, roman| # reverse each にした
if arabic_numerals >= arabic
arabic_numerals = arabic_numerals - arabic
roman_numerals = roman_numerals + roman
end
end
return roman_numerals
end
if $PROGRAM_NAME == __FILE__
arabic_numerals = ARGV[0].to_i
print "#{arabic_numerals}\n"
print "#{to_roman(arabic_numerals)}\n"
end
roman_numerals
の初期値を ""
にして, どんどん文字列を連結していく作戦.
文字列連結作戦は, このサイト (https://eigo.rumisunheart.com/2018/03/30/integer-to-roman-number-converter/) に書いてるのを見て参考にした.
-
3 の場合 : 3 = 2 + 1 なので,
""
は"II"
になり, その後"III"
になる. -
8 の場合 : 8 = 6 + 2 なので,
""
は"VI"
になり, その後"VIII"
になる.
1 ~ 20 に対して
if
のところを while
に改良.
def to_roman (arabic_numerals)
pair_arabic_roman = [[1,"I"],[2,"II"],[4,"IV"],[5,"V"],[6,"VI"],[9,"IX"],[10,"X"]]
roman_numerals = ""
pair_arabic_roman.reverse_each do |p_a, p_r|
while arabic_numerals >= p_a do
arabic_numerals = arabic_numerals - p_a
roman_numerals = roman_numerals + p_r
end
end
return roman_numerals
end
if $PROGRAM_NAME == __FILE__
arabic_numerals = Range.new(1,20)
arabic_numerals.each do |i|
print "#{i}\t : #{to_roman(i)}\n"
end
end
ここまで来たらほぼ完成.
ちなみに Range
は python でもよく見るあれ.
1 ~ 2000 に対して
対応表 pair_arabic_roman
をある程度整理して, こうなった.
def to_roman (arabic_numerals)
pair_arabic_roman = [[1,"I"],[4,"IV"],[5,"V"],[9,"IX"],[10,"X"],[40,"XL"],[50,"L"],
[90,"XC"],[100,"C"],[400,"CD"],[500,"D"],[900,"CM"],[1000,"M"]]
roman_numerals = ""
pair_arabic_roman.reverse_each do |p_a, p_r|
while arabic_numerals >= p_a do
arabic_numerals = arabic_numerals - p_a
roman_numerals = roman_numerals + p_r
end
end
return roman_numerals
end
if $PROGRAM_NAME == __FILE__
arabic_numerals = [51,97,99,439,483,499,500,732,961,999,1000,1999,2000]
arabic_numerals.each do |i|
print "#{i}\t : #{to_roman(i)}\n"
end
end
もちろん実行通った. やったぜ.
こんな感じで, 冒頭に載せた code が出来上がった.
発展課題
整数 Integer class を拡張して,
999.to_roman #=> CMXCIX
と返すようにする.
さっき作った method を素直に Integer class に突っ込んだ.
class Integer
def to_roman
arabic_numerals = self
pair_arabic_roman = [[1,"I"],[4,"IV"],[5,"V"],[9,"IX"],[10,"X"],[40,"XL"],[50,"L"],
[90,"XC"],[100,"C"],[400,"CD"],[500,"D"],[900,"CM"],[1000,"M"]]
roman_numerals = ""
pair_arabic_roman.reverse_each do |p_a, p_r|
while arabic_numerals >= p_a do
arabic_numerals = arabic_numerals - p_a
roman_numerals = roman_numerals + p_r
end
end
return roman_numerals
end
end
if $PROGRAM_NAME == __FILE__
a = ARGV[0].to_i
print "#{a}\t : #{a.to_roman}\n"
end
もちろん期待通りの出力になってる.
参考資料
- source ~/Lecture/multiscale_simulation/grad_members_20f/members/gagagagazelle/docs/roman_numerals.org