如何减少if语句?

时间:2015-09-06 19:13:38

标签: ruby

通过Chris Pine的学习编程并开始编号到罗马数字转换项目。下面的代码可以工作,但是如果(和结束)语句的话,这些代码都非常难看。但是,当我使用elsif时程序没有响应(似乎冻结)。任何想法都会有所帮助!

def calc input

roman_numeral = ''

while true
if input >= 1000 
  roman_numeral += 'M' * (input / 1000)
  input = input - (1000 * (input / 1000))

  if input <= 999 || input >= 500
  roman_numeral += 'D' * (input / 500)
  input = input - (500 * (input / 500))

  if input <= 499 || input >= 100
  roman_numeral += 'C' * (input / 100)
  input = input - (100 * (input / 100))

  if input <= 99 || input >= 50
  roman_numeral += 'L' * (input / 50)
  input = input - (50 * (input / 50))

  if input <= 49 || input >= 10
  roman_numeral += 'X' * (input / 10)
  input = input - (10 * (input / 10))

  if input <= 9 || input >= 5
  roman_numeral += 'V' * (input / 5)
  input = input - (5 * (input / 5))

  if input <= 4 || input >= 1
  roman_numeral += 'I' * (input / 1)
  input = input - (1 * (input / 1))

  puts roman_numeral

  break
end
end
end
end
end
end
end
end
end


puts 'Give me a number, any number:'
input = gets.chomp.to_i
calc(input)

3 个答案:

答案 0 :(得分:3)

将方法Enumerable#find与数组一起使用很方便:

ARR = [[1000,'M'], [ 500,'D'], [100,'C'], [50,'L'], [10,'X'], [5,'V'], [1,'I']]

def which(input)
  ARR.find { |v,_| input >= v }
end

which(2)    #=> [1, "I"] 
which(7)    #=> [5, "V"] 
which(17)   #=> [10, "X"] 
which(77)   #=> [50, "L"] 
which(777)  #=> [500, "D"] 
which(7777) #=> [1000, "M"] 

假设您要将整数转换为罗马数字,请考虑使用方法Fixnum#divmod。假设整数是2954,并且您已经确定有两个"M"和一个"D"(因此您的罗马数字字符串的开头是{{ 1}}),"MMD"遗留下来。然后:

454

告诉您剩下c, rem = 454.divmod(100) #=>[4, 54] c #=> 4 rem #=> 54 的{​​{1}}有四个。

然而,有四个"C"被写为54(而非"C"),因此您可能需要使用以下哈希值:

"CD"

"CCCC"&#39;的数量转换为罗马数字。在这里,您可以将REP = {..., "C"=>["C", "CC", "CCC", "CD"], ...} 追加到"C"REP["C"][4-1] #=> "CD"

答案 1 :(得分:1)

Cary Swoveland的答案是减少if块嵌套的绝佳方法。

他的回答告诉你下一个数字,但不是多少(如你的代码中)。将它组合在一起的一种自然方法是使用递归函数调用:

class Romans
  def self.calc(input, acc = "")
    raise ArgumentError.new("Roman Numerals must be positve") if input < 0
    raise ArgumentError.new("Roman Numerals must be integers") if ! input.is_a? Integer

    return acc if input == 0 
    amount, numeral = which(input)
    acc += numeral
    input -= amount
    calc(input, acc)
  end

  @@ARR = [[1000,'M'], [ 500,'D'], [100,'C'], [50,'L'], [10,'X'], [5,'V'], [1,'I']]
  def self.which(input)
    @@ARR.find { |v,_| input >= v }
  end
end

使用中:

pry(main)> (1..10).each{|i| puts "#{i}=> #{Romans.calc(i)}"}
1=> I
2=> II
3=> III
4=> IIII
5=> V
6=> VI
7=> VII
8=> VIII
9=> VIIII
10=> X

pry(main)> [Random.rand(1..100000)].each{|i| puts "#{i}=> #{Romans.calc(i)}"}
63124=> MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMCXXIIII

请注意,ruby没有TCO,因此会使堆栈数量足够大 - 但如果您需要800万的罗马数字版本,您可能需要编写一些新的字母。

答案 2 :(得分:0)

这是一个使用字符串乘法的。例如:('M'* 3)='MMM'

def to_roman(number)
  raise 'Must use positive numbers.' if number <= 0
  roman = ''

  roman << 'M' * (number        / 1000)
  roman << 'D' * (number % 1000 /  500)
  roman << 'C' * (number %  500 /  100)
  roman << 'L' * (number %  100 /   50)
  roman << 'X' * (number %   50 /   10)
  roman << 'V' * (number %   10 /    5)
  roman << 'I' * (number %    5 /    1)

  roman
end

puts to_roman(1234) # <-- test

参考:学习Chris Pine的第二课程