罗马到整数重构

时间:2018-10-28 16:46:33

标签: ruby refactoring

我正在写一个方法roman_to_integer(roman_string),它将罗马数字转换为整数:'IV'到4,'XVI'到16,等等。

 ROMAN_TO_INT = {
      "I" => 1,
      "IV" => 4,
      "V" => 5,
      "IX" => 9,
      "X" => 10,
      "XL" => 40,
      "L" => 50,
      "XC" => 90,
      "C" => 100,
      "CD" => 400,
      "D" => 500,
      "CM" => 900,
      "M" => 1000
    }

    def roman_to_integer(roman_string)
      # TODO: translate roman string to integer
      number = 0
      str = roman_string.dup
      until str.size.zero?
        last_two_characters = str.slice(-2, 2)
        if ROMAN_TO_INT.key?(last_two_characters)
          number += ROMAN_TO_INT[last_two_characters]
          str.chop!
        else
          number += ROMAN_TO_INT[str.slice(-1)]
        end
        str.chop!
      end
      number
    end

如何使我的方法更短? Rubocop仅允许10行。我正在尝试,但总是以至少13结尾。

3 个答案:

答案 0 :(得分:1)

并不是真正的重构,而是一种减少行数的选项:

ROMAN_TO_INT =
  {
    i: 1,
    v: 5,
    x: 10,
    l: 50,
    c: 100,
    d: 500,
    m: 1000
  }


def roman_to_int roman
  value_map = roman.split('').map { |e| ROMAN_TO_INT[e.downcase.to_sym] }
  value_map.map.with_index do |e, idx| 
    unless value_map[idx + 1].nil?
    then
      value_map[idx + 1] > e ? -e : e
    else e
    end
  end.sum
end

roman = "MDCCLXXVI"
roman_to_int roman #=> 1776

如果罗马符号无效,它不会发出警报,例如:

roman = "VMII" # incorrect notation for 997
roman_to_int roman #=> 997

roman = "CMXCVII" # correct notation for 997
roman_to_int roman #=> 997

答案 1 :(得分:1)

如果主要目标是减少代码行数,则可以执行以下操作。

代码

H = {"VI"=>" 4", "XI"=>" 9", "LX"=>" 40", "CX"=>" 90", "DC"=>" 400", "MC"=>" 900",
     "I"=>" 1", "V"=>" 5", "X"=>" 10", "L"=>" 50", "C"=>" 100", "D"=>" 500", "M"=>" 1000"}

def roman_to_integer(roman_string)
   roman_string.reverse.gsub(Regexp.union(H.keys), H).split.sum(&:to_i)
end

示例

%w| III LXIV CCXXVI CM CMXCVIII MDCCXII |.each {|s| puts "#{s}->#{ roman_to_integer(s)}"}
  # III->3
  # LXIV->64
  # CCXXVI->226
  # CM->900
  # CMXCVIII->998
  # MDCCXII->1712

说明

正则表达式是从左到右解析的,因此第一步,我们需要将roman_string反转。这意味着我们还必须反转哈希中的键。

这使用String#gsub的形式,该形式采用哈希作为参数。请注意,H的键是按大小降序排列的。这是为什么我这样做的一个例子。假设gsub的指针位于"V",下一个字符为"I"。键的顺序将使gsub(贪心)匹配"VI"而不是"V"

对于

roman_string = "CCXXVI"

步骤如下。

k = H.keys
  #=> ["VI", "XI", "LX", "CX", "DC", "MC", "I", "V", "X", "L", "C", "D", "M"]
r = Regexp.union(H.keys)
  #=> /VI|XI|LX|CX|DC|MC|I|V|X|L|C|D|M/
t = s.gsub(r, H)
  #=> " 1 5 10 10 100 100"
a = t.split
  #=> ["1", "5", "10", "10", "100", "100"]
a.sum(&:to_i)
  # => 226

请注意,如果给予我们

ROMAN_TO_INT = { "I" => 1, "IV" => 4, "V" => 5, "IX" => 9, "X" => 10, "XL" => 40,
                 "L" => 50, "XC" => 90, "C" => 100, "CD" => 400, "D" => 500, 
                 "CM" => 900, "M" => 1000 }

我们可以如下计算H

H = ROMAN_TO_INT.map { |k,v| [k.reverse, " #{v}"] }.sort_by { |k,_| -k.size }.to_h

答案 2 :(得分:1)

一个更小的版本,使用了与iGian相同的技巧:

ROMAN_TO_INT =
{
  i: 1,
  v: 5,
  x: 10,
  l: 50,
  c: 100,
  d: 500,
  m: 1000
}

def roman_to_int(roman)
  numbers = roman.downcase.chars.map { |char| ROMAN_TO_INT[char.to_sym] }.reverse
  numbers.inject([0, 1]) do |result_number, int|
    result, number = result_number
    int >= number ? [result + int, int] : [result - int, number]
  end.first
end