如果我写这个:
case v
when String
puts :String
else
puts :other
end
如果我v
设置为"some string"
,我会得到"字符串"。
如果我将v
设置为String
,我会得到"其他"。
我应该如何"切换"包含多个类对象之一的变量?
这如何遵循公认的"最小意外"计算原则?
请不要告诉我给"类"类打补丁。
我同意===
(case
使用的测试)可能会令人困惑。
它通常被称为"大小写相等运算符",但===
既不是自反的,也不是对称的,也不是传递的,所以它的行为根本不像相等:
String === "test"
# true
"test" === String
# false
String === String
# false
String === Class
# false
Class === String
# true
Class === Class
# true
我读过的最好的描述a === b
来自@JörgWMittag的回答:
">如果我有一个标有标签的抽屉a
把b
放在那个抽屉里有意义吗?">
对于您的问题,您可以编写:
v = String
case v
when Class
puts "#{v} is a Class, let's investigate some more"
# another case, if statements or hash lookup...
else
puts :other
end
我应该如何"切换"包含多个类对象之一的变量?
与Module#<=
:
case v
when String then "instance of string"
when ->(c) { c <= String } then "class derived from String"
end
这如何遵循公认的"最小意外"计算原则?
完全。
您可以使用不带对象的case
:
case
when v == String
puts 'v is String class'
when v.is_a?(String)
puts 'v is an instance of String'
else
puts 'v is something else'
end
这类似于if
-elsif
-else
表达式。
Ruby 和其他一些函数式语言中的案例表达式与它们的命令式开关语句表亲完全不同。
正如其他人所提到的,案例表达式使用===
方法,这是一种模式定义方法。在a === b
范式中,a
是描述可能实例集合的模式。b
是一个可能适合该模式/集合的实例。将a === b
视为:
"A"描述"B"吗?
或
">A"包含"B"吗?
一旦你理解了这一点,String === String #=> false
就不足为奇了,因为String
是Class
的一个实例,所以它符合Class
模式。
大小写表达式和 switch 语句之间的另一个独特区别是,大小写表达式就是:表达式。这意味着你可以做这样很酷的事情:
puts case v.name
when "String"
:String
else
:other
end if v.is_a? Class
仅当v
是一个类时,此代码才会执行,然后打开v.name
并根据类的名称puts
您想要的任何内容。
由于 ruby 本质上是一种鸭型语言,因此在变量中有一个类并需要打开它是极其罕见的。如果您对case
没有您希望的那么优雅感到沮丧,请向我们提供有关您的目标的更多背景信息,我们也许能够提出一个优雅的解决方案,避免一起切换。