roman numerals



# お題

アラビア数字 (arabic numerals) を受け取って, ローマ数字 (roman numerals) を返す method を書け.
(<https://qiita.com/daddygongon/items/2d0a73a51ddab2d9da1b> にある課題)


# 今回書いた code

とりあえず解説より先に, 今回書いた code を貼っとく.

```ruby

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 を出力

思うがままに書いたらこうなる.

```ruby

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 を出力

素直に書くとこうなった.

```ruby

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` 書くのめんどくさくなったので, ちょっとループで書いてみた.
あと, 関数内で出力するのではなく, 返り値を返すようにした.

```ruby

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` にアラビア数字とローマ数字の対応全てを書くのはめんどくさすぎる.
なので, こうした.

```ruby

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` に改良.

```ruby

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` をある程度整理して, こうなった.

```ruby

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 に突っ込んだ.

```ruby

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

```

もちろん期待通りの出力になってる.


# 参考資料

-   [roman\_numerals](https://qiita.com/daddygongon/items/2d0a73a51ddab2d9da1b)
-   [C++ プログラミング：アラビア数字をローマ数字に変換する](https://eigo.rumisunheart.com/2018/03/30/integer-to-roman-number-converter/)



------
 - **source** ~/Lecture/multiscale_simulation/grad_members_20f/members/gagagagazelle/docs/roman_numerals.org