使用数组的带有多个变量的 Ruby case 语句

发布于 2024-10-15 02:06:15 字数 1027 浏览 2 评论 0 原文

我想比较一个 case 语句的多个变量,目前我认为覆盖数组的 case 等于运算符 (===) 是最好的方法。这是最好的方法吗?

这是一个示例用例:

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

我错过了什么吗?

I'd like to compare multiple variables for a case statement, and am currently thinking overriding the case equals operator (===) for Array is the best way to do it. Is this the best way?

Here is an example use case:

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

The only problem is the Array case equals operator doesn't apply the case equal to the elements of the Array.

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

It will if I override, but am not sure what I'd be breaking if I did:

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

Now, it all works:

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

Am I missing something?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

西瓜 2024-10-22 02:06:15

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

今天的 Gems 使用 “无猴子补丁”作为卖点。覆盖运算符可能不是正确的方法。猴子补丁非常适合实验,但很容易出错。

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

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

最后,由于有多个具有关联逻辑的数字,因此似乎至少有一个 要写入的值对象类型类(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可能表示有一个对象需要创建。

I found this question because I was looking to run a case statement on multiple variables, but, going through the following, came to the conclusion that needing to compare multiple variables might suggest that a different approach is needed. (I went back to my own code with this conclusion, and found that even a Hash is helping me write code that is easier to understand.)

Gems today use "no monkey patching" as a selling point. Overriding an operator is probably not the right approach. Monkey patching is great for experimentation, but it's too easy for things to go awry.

Also, there's a lot of type-checking. In a language that is designed for Duck Typing, this clearly indicates the need for a different approach. For example, what happens if I pass in integer values instead of floats? We'd get an 'N/A', even though that's not likely what we're looking for.

You'll notice that the example given in the question is difficult to read. We should be able to find a way to represent this logic more clearly to the reader (and to the writer, when they revisit the code again in a few months and have to puzzle out what's going on).

And finally, since there are multiple numbers with associated logic, it seems like there's at least one value object-type class (Deposit) that wants to be written.

For cleanliness, I'm going to assume that a nil APR can be considered a 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

Once we have our Deposit object, we can implement the print logic without needing case statements at all.

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"

Conclusion: If you're comparing multiple variables in a case statement, use that as a smell to suggest a deeper design issue. Multiple-variable cases might indicate that there's an object that wants to be created.

绳情 2024-10-22 02:06:15

如果您担心更改 Array 行为会破坏某些内容(当然这是一个合理的担心),那么只需将修改后的运算符放入 Array 的子类中即可。

If you are worried about breaking something by changing Array behavior, and certainly that's a reasonable worry, then just put your revised operator in a subclass of Array.

吝吻 2024-10-22 02:06:15

这绝对不是最好的方法。更重要的是 - 您不应该重新定义标准类的方法,因为核心功能可能依赖于它 - 然后享受调试的乐趣。

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

如果您知道除了一堆浮点数和字符串之外不会将任何其他内容传递给该方法 - 为什么您需要所有这些检查?

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

it's definitely not the best way. even more - you should not redefine methods of standart classes as core functionality may depend on it - have fun debugging then.

defensive style is nice(with lot of type checks and whatnot) but it usually hurts performance and readability.

if you know that you will not pass anything else than bunch of floats and strings to that method - why do you need all those checks for?

IMO use exception catching and fix the source of problem, don't try to fix the problem somewhere in the middle

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文