LoginSignup
9
0

More than 1 year has passed since last update.

Rubyでローマ数字を求める

Last updated at Posted at 2020-12-16

ローマ数字(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'] を照らし合わせれば理解できます。
    • 最後に出来上がったローマ数字 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

動いた!!!!!


9
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
9
0