使用Array的多个变量的Ruby case语句

时间:2011-01-29 18:59:17

标签: ruby arrays case equals

我想比较一个case语句的多个变量,并且我正在考虑重写case equals operator(===)for Array是最好的方法。这是最好的方法吗?

以下是一个用例示例:

def deposit_apr deposit,apr 
  # deposit: can be nil or 2 length Array of [nil or Float, String]  
  # apr: can be nil or Float     
  case [deposit,apr] 
    when [[Float,String],Float] 
      puts "#{deposit[0]} #{deposit[1]}, #{apr*100.0}% APR"
    when [[nil,String],Float] 
      puts "#{apr*100.0}% APR on deposits greater than 100 #{deposit[1]}"
    when [[Float,String],nil] 
      puts "#{deposit[0]} #{deposit[1]}"
    else 
      puts 'N/A' 
  end
end

唯一的问题是数组大小写等于运算符不适用大小等于数组元素的情况。

ruby-1.9.2-p0 > deposit_apr([656.00,'rupees'],0.065)
N/A

如果我覆盖它,但我不确定如果我做了什么我会破坏:

class Array
  def ===(other)
    result = true
    self.zip(other) {|bp,ap| result &&= bp === ap}
    result
  end
end

现在,一切正常:

ruby-1.9.2-p0 > deposit_apr([656.00,'rupees'],0.065)
656.0 rupees, 6.5% APR

我错过了什么吗?

3 个答案:

答案 0 :(得分:8)

我发现了这个问题,因为我希望对多个变量运行case语句,但是,通过以下内容得出结论,需要比较多个变量可能表明需要采用不同的方法。 (我用这个结论回到了我自己的代码,发现即使是Hash也帮我编写更容易理解的代码。)

Gems今天使用"no monkey patching" as a selling point。覆盖运营商可能不是正确的方法。猴子修补非常适合实验,但事情很容易出错。

此外,还有很多类型检查。在为Duck Typing设计的语言中,这清楚地表明需要采用不同的方法。例如,如果我传入整数值而不是浮点数会发生什么?我们得到的是“不适合”,即使这不太可能是我们正在寻找的。

您会注意到问题中给出的示例很难阅读。我们应该能够找到一种方法来更清楚地向读者(以及作者,当他们在几个月后再次重新访问代码并且不得不弄清楚正在发生什么时)表达这种逻辑。

最后,由于有多个数字与关联逻辑,似乎至少有一个value object类型类(Deposit)想要写入。

为了清洁起见,我将假设nil APR可被视为0.0%APR。

class Deposit
  def initialize(amount, unit='USD', options={})
    @amount = amount.to_f # `nil` => 0.0
    @unit   = unit.to_s   # Example assumes unit is always present
    @apr    = options.fetch(:apr, 0.0).to_f # `apr: nil` => 0.0
  end
end

一旦我们有了Deposit对象,我们就可以实现打印逻辑而不需要case语句。

class Deposit

  # ... lines omitted

  def to_s
    string = "#{@amount} #{@unit}"
    string << ", #{@apr * 100.0}% APR" if @apr > 0.0
    string
  end
end

d = Deposit.new(656.00, 'rupees', apr: 0.065)
d.to_s
# => "656.0 rupees, 6.5% APR"

e = Deposit.new(100, 'USD', apr: nil)
e.to_s
# => "100.0 USD"

f = Deposit.new(100, 'USD')
f.to_s
# => "100.0 USD"

结论:如果您要对案例陈述中的多个变量进行比较,请将其用作气味来暗示更深层次的设计问题。多变量case可能表示有一个想要创建的对象。

答案 1 :(得分:2)

如果您担心通过更改Array行为来破坏某些内容,当然这是一个合理的担心,那么只需将修改后的运算符放在Array的子​​类中。

答案 2 :(得分:-1)

绝对不是最好的方式。甚至更多 - 你不应该重新定义标准类的方法,因为核心功能可能依赖于它 - 然后进行有趣的调试。

防御风格很好(有很多类型检查和诸如此类的东西),但它通常会损害性能的可读性。

如果你知道除了一堆浮点数和字符串之外你不会传递任何其他东西 - 为什么你需要所有这些检查?

IMO使用异常捕获并修复问题的根源,不要尝试在中间某处修复问题