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

Mik*_*e C 3 ruby arrays equals case

我想比较一个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
Run Code Online (Sandbox Code Playgroud)

唯一的问题是Array case equals运算符不适用大小等于Array的元素.

ruby-1.9.2-p0 > deposit_apr([656.00,'rupees'],0.065)
N/A
Run Code Online (Sandbox Code Playgroud)

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

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

现在,一切正常:

ruby-1.9.2-p0 > deposit_apr([656.00,'rupees'],0.065)
656.0 rupees, 6.5% APR
Run Code Online (Sandbox Code Playgroud)

我错过了什么吗?

小智 8

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

宝石今天使用"没有猴子补丁"作为卖点.覆盖运营商可能不是正确的方法.猴子修补非常适合实验,但事情很容易出错.

此外,还有很多类型检查.在为Duck Typing设计的语言中,这清楚地表明需要采用不同的方法.例如,如果我传入整数值而不是浮点数会发生什么?我们得到'N/A',尽管这不太可能是我们想要的.

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

最后,因为有多个数字与相关的逻辑,似乎至少有一个值object -type class(Deposit)想要写.

为了清洁,我将假设nilAPR可以被认为是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
Run Code Online (Sandbox Code Playgroud)

一旦我们有了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"
Run Code Online (Sandbox Code Playgroud)

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