roman numerals
問題
アラビア数字(arabic numerals)を受け取って,ローマ数字(roman numerals)を返すmethodを書きなさい.
各ローマ数字は以下のように対応する.
roman num | arabic num |
---|---|
I | 1 |
V | 5 |
X | 10 |
L | 50 |
C | 100 |
D | 500 |
M | 1000 |
解法
基本的にローマ数字各種に対応するアラビア数字で順に割っていくのが正攻法だろう.ただし,ローマ数字はアラビア数字における 4 や 9 においては少し特殊な挙動をする.計算式でうまいことやろうとするとif 文を乱用しそう.
今回は簡潔で美しいプログラムにすべく,アラビア数字各桁における 4 や 9 についてはあらかじめ配列に登録しておく.とはいったものの配列を用意するとなるとローマ数字とアラビア数字の 2 つ配列を用意しないといけない.これでは簡潔で美しいプログラムではなくなってしまう.
そこで授業内では触れられていないが,Ruby ならではの便利な hash を活用する.hash を簡単に説明すると Python でいう辞書型である.2 つの配列に対し,不規則だが対応関係があるものについては hash と each を活用すれば簡潔なプログラムにできる.プログラミングスクールのメンターをしていたときに学んだことがこんなところで活かせるとは.
2 つの配列を Hash で
配列を用いたら
arabic_value = [1000,900,500,400,100,90,50,40,10,9,5,4,1]
roman_value = ["M","CM","D","CD","C","XC","L","XL","X","IX","V","IV","I"]
と 2 行になってしまうところを,ハッシュを用いれば
value_sets = {"1000"=>"M","900"=>"CM","500"=>"D","400"=>"CD","100"=>"C","90"=>"XC","50"=>"L","40"=>"XL","10"=>"X","9"=>"IX","5"=>"V","4"=>"IV","1"=>"I"}
こんな感じで 1 行にでき,視覚的にも直感で理解しやすくなる.
実装
それでは,hash を用いてアラビア数字をローマ数字に変換するメソッド to_roman を実装してみる.hash の key は アラビア数字なので,先頭から順に key で割っていき,商の回数分ローマ数字である value を代入すれば良い.これを hash の先頭から each で繰り返せば良いので,プログラムは以下のようになる.
def to_roman(num)
str = ""
value_sets = {"1000"=>"M","900"=>"CM","500"=>"D","400"=>"CD","100"=>"C","90"=>"XC","50"=>"L","40"=>"XL","10"=>"X","9"=>"IX","5"=>"V","4"=>"IV","1"=>"I"}
value_sets.each do |arabic,roman|
quotient = num/arabic.to_i
num = num%arabic.to_i
str << roman*quotient
end
str
end
puts to_roman(ARGV[0].to_i)
想像以上に短いプログラムにできた.
発展問題
Integer class を拡張して,
999.to_roman #=>CMXCIX
のように実行することで,ローマ数字を返すようにしたい.
ここまできたらもう簡単.
実装
こんな感じ.
class Integer
def to_roman
num = self
str = ""
value_sets = {"1000"=>"M","900"=>"CM","500"=>"D","400"=>"CD","100"=>"C","90"=>"XC","50"=>"L","40"=>"XL","10"=>"X","9"=>"IX","5"=>"V","4"=>"IV","1"=>"I"}
value_sets.each do |arabic,roman|
quotient = num/arabic.to_i
num = num%arabic.to_i
str << roman*quotient
end
str
end
end
puts ARGV[0].to_i.to_roman
動作確認
忘れずに動作確認しておく.
$ ruby roman_numerals.rb 23
XXIII
$ ruby roman_numerals.rb 169
CLXIX
$ ruby roman_numerals.rb 1997
MCMXCVII
$ ruby roman_numerals.rb 2020
MMXX
問題なさそう.
参考ページ
今回参考にしたページはこちら.roman numerals
- source ~/grad_members_20f/members/e79a93e5b7b1/posts/class/roman_numerals.org