Ruby中的罗马数字

时间:2014-09-29 04:21:02

标签: ruby

在我最近尝试的编码挑战中,我发现这是将数字转换为罗马数字的替代解决方案。我真的不明白这段代码是如何工作的。我只知道divmod做了什么,但我很困惑。

class Integer
  def to_roman
    roman_arr = {
      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"
    } 
    num = self

    roman_arr.reduce("") do |res, (arab, roman)|
      whole_part, num = num.divmod(arab)
      res << roman * whole_part
    end
  end
end

3 个答案:

答案 0 :(得分:7)

reduce / fold是与命令式语言中的循环结构等效的函数式编程。红宝石能够兼顾两者。

foo.reduce("") { |a, i| a + i }相当于

a = ""
foo.each {|i| a = a + i}
a

num = self行将实例(接收to_roman方法的数字)保存在局部变量中,以便您可以在传递给reduce的块中使用它。

答案 1 :(得分:4)

我在代码中添加了一些解释,希望现在很清楚。 你永远不会这样写它,你发布的代码很好。

class Integer

  def to_roman_explained
    roman_arr = {
      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"
    }

    remaining = self  # the integer on which this method is called
    empty_string = "" # startvalue of result

    return_value = roman_arr.inject(empty_string) do |result, (arab, roman)| 
      # inject = reduce, for each element of our hash
      # arab and roman are the key and value part of the hash elements, result is result from previous iteration
      p [result, arab, roman,remaining.divmod(arab)] # lets's see what happens
      # number of times the remaining can be divided with the value of this roman, the remaining becomes the rest
      whole_part, remaining = remaining.divmod(arab) 
      result << roman * whole_part # if whole_part == 0 nothing happens for this roman
    end

    return return_value

  end
end

puts 555.to_roman_explained

# gives

# ["", 1000, "M", [0, 555]]
# ["", 900, "CM", [0, 555]]
# ["", 500, "D", [1, 55]] first time the integer is dividable by the value of the roman
# ["D", 400, "CD", [0, 55]] our result now has the roman D
# ["D", 100, "C", [0, 55]]
# ["D", 90, "XC", [0, 55]]
# ["D", 50, "L", [1, 5]] etc
# ["DL", 40, "XL", [0, 5]]
# ["DL", 10, "X", [0, 5]]
# ["DL", 9, "IX", [0, 5]]
# ["DL", 5, "V", [1, 0]] etc
# ["DLV", 4, "IV", [0, 0]]
# ["DLV", 1, "I", [0, 0]]
# DLV

答案 2 :(得分:1)

如果事先扭转数字,我发现它更容易,例如:

def new_roman(arabic_number)
  symbols = {0=>["I","V"],1=>["X","L"],2=>["C","D"],3=>["M"]}
  reversed_digits = arabic_number.to_s.split(//).reverse
  romans =[]
  reversed_digits.length.times do |i|
    if reversed_digits[i].to_i< 4
      romans<<(symbols[i][0]*reversed_digits[i].to_i)
    elsif reversed_digits[i].to_i == 4
      romans<<(symbols[i][0]+ symbols[i][1])
    elsif reversed_digits[i].to_i == 9
      romans<<(symbols[i][0] + symbols[i+1][0])
    else
      romans<<(symbols[i][1] + (symbols[i][0]*((reversed_digits[i].to_i)-5)))
    end
    end
  romans.reverse.join("")
end