LoginSignup
6
0

More than 3 years have passed since last update.

roman numerals

Last updated at Posted at 2020-12-15

お題

アラビア数字 (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
6
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6
0