如何在Ruby中编写Switch语句

如何用Ruby编写switch语句?

1057157 次浏览

它是使用Ruby中的#0完成的。另请参阅维基百科上的“Switch语句”。

引用:

case nwhen 0puts 'You typed zero'when 1, 9puts 'n is a perfect square'when 2puts 'n is a prime number'puts 'n is an even number'when 3, 5, 7puts 'n is a prime number'when 4, 6, 8puts 'n is an even number'elseputs 'Only single-digit numbers are allowed'end

另一个例子:

score = 70
result = case scorewhen 0..40 then "Fail"when 41..60 then "Pass"when 61..70 then "Pass with Merit"when 71..100 then "Pass with Distinction"else "Invalid Score"end
puts result

在我的Kindle上Ruby编程语言(第一版,O'Reilly)的第123页左右,它说when子句后面的then关键字可以替换为换行符或分号(就像if then else语法一样)。(Ruby 1.8也允许冒号代替then,但Ruby 1.9不再允许这种语法。)

Ruby使用#0表达式代替。

case xwhen 1..5"It's between 1 and 5"when 6"It's 6"when "foo", "bar""It's either foo or bar"when String"You passed a string"else"You gave me #{x} -- I have no idea what to do with that."end

Ruby使用===运算符将when子句中的对象与case子句中的对象进行比较。例如,1..5 === x,而不是x === 1..5

这允许如上所述的复杂的when子句。范围、类和各种东西都可以测试,而不仅仅是相等。

与许多其他语言中的switch语句不同,Ruby的case没有失败,因此无需以break结束每个when。您还可以在单个when子句中指定多个匹配项,例如when "foo", "bar"

case...when在处理类时的行为有点出乎意料。这是由于它使用了===运算符。

该运算符按预期使用文字,但不使用类:

1 === 1           # => trueFixnum === Fixnum # => false

这意味着如果你想在对象的类上执行case ... when,这将不起作用:

obj = 'hello'case obj.classwhen Stringprint('It is a string')when Fixnumprint('It is a number')elseprint('It is not a string or number')end

将打印“它不是字符串或数字”。

幸运的是,这很容易解决。===运算符已经定义,如果您将其与类一起使用并提供该类的实例作为第二个操作数,它将返回true

Fixnum === 1 # => true

简而言之,上面的代码可以通过从case obj.class中删除.class来修复:

obj = 'hello'case obj  # was case obj.classwhen Stringprint('It is a string')when Fixnumprint('It is a number')elseprint('It is not a string or number')end

我今天在寻找答案时遇到了这个问题,这是第一个出现的页面,所以我想这对我同样情况下的其他人很有用。

案…当

要向Chuck的回答添加更多示例:

带参数:

case awhen 1puts "Single value"when 2, 3puts "One of comma-separated values"when 4..6puts "One of 4, 5, 6"when 7...9puts "One of 7, 8, but not 9"elseputs "Any other thing"end

不带参数:

casewhen b < 3puts "Little than 3"when b == 3puts "Equal to 3"when (1..10) === bputs "Something in closed range of [1..10]"end

请注意kikito警告的“如何在Ruby中编写Switch语句”。

您可以使用正则表达式,例如查找类型的字符串:

case foowhen /^(true|false)$/puts "Given string is boolean"when /^[0-9]+$/puts "Given string is integer"when /^[0-9\.]+$/puts "Given string is float"elseputs "Given string is probably string"end

Ruby的case将为此使用相等操作数===(感谢@JimDeville)。“ruby运算符”中提供了更多信息。这也可以使用@mmdemirbas示例(没有参数)来完成,只有这种方法对于这些类型的情况更清晰。

在Ruby 2.0中,您还可以在case语句中使用lambdas,如下所示:

is_even = ->(x) { x % 2 == 0 }
case numberwhen 0 then puts 'zero'when is_even then puts 'even'else puts 'odd'end

您还可以使用带有自定义===的结构轻松创建自己的比较器

Moddable = Struct.new(:n) dodef ===(numeric)numeric % n == 0endend
mod4 = Moddable.new(4)mod3 = Moddable.new(3)
case numberwhen mod4 then puts 'multiple of 4'when mod3 then puts 'multiple of 3'end

(示例取自“可以在Ruby 2.0中与case语句一起使用吗?”。

或者,有一个完整的类:

class Vehicledef ===(another_vehicle)self.number_of_wheels == another_vehicle.number_of_wheelsendend
four_wheeler = Vehicle.new 4two_wheeler = Vehicle.new 2
case vehiclewhen two_wheelerputs 'two wheeler'when four_wheelerputs 'four wheeler'end

(示例取自“Ruby case语句如何工作以及您可以用它做什么”。

很多很好的答案,但我想我会添加一个事实…如果你试图比较对象(类),请确保你有一个太空船方法(不是开玩笑)或了解它们是如何被比较的

Ruby等式和对象比较”是关于这个话题的一个很好的讨论。

由于switch case总是返回单个对象,我们可以直接打印它的结果:

puts case awhen 0"It's zero"when 1"It's one"end

许多编程语言,特别是那些源自C的编程语言,都支持所谓的开关故障。我在Ruby中寻找最好的方法来做同样的事情,并认为它可能对其他人有用:

在类C语言中,失败通常看起来像这样:

switch (expression) {case 'a':case 'b':case 'c':// Do something for a, b or cbreak;case 'd':case 'e':// Do something else for d or ebreak;}

在Ruby中,同样可以通过以下方式实现:

case expressionwhen 'a', 'b', 'c'# Do something for a, b or cwhen 'd', 'e'# Do something else for d or eend

这不是严格等价的,因为不可能让'a''b''c'之前执行代码块,但在大多数情况下,我发现它足够相似,以同样的方式有用。

多值时和无值情况:

print "Enter your grade: "grade = gets.chompcase gradewhen "A", "B"puts 'You pretty smart!'when "C", "D"puts 'You pretty dumb!!'elseputs "You can't even use a computer!"end

这里有一个正则表达式的解决方案:

print "Enter a string: "some_string = gets.chompcasewhen some_string.match(/\d/)puts 'String has numbers'when some_string.match(/[a-zA-Z]/)puts 'String has letters'elseputs 'String has no numbers or letters'end

如果您想知道如何在Ruby Switch中使用OR条件:

因此,在case语句中,,相当于if语句中的||

case carwhen 'Maruti', 'Hyundai'# Code hereend

见“Ruby case语句如何工作以及您可以用它做什么”。

我开始使用:

a = "secondcase"
var_name = case awhen "firstcase" then "foo"when "secondcase" then "bar"end
puts var_name>> "bar"

在某些情况下,它有助于压缩代码。

根据您的情况,您可能更喜欢使用方法的哈希。

如果有一长串when,并且它们中的每一个都有一个具体的值来比较(而不是一个区间),那么声明一个方法的哈希,然后像这样从哈希中调用相关的方法会更有效。

# Define the hashmenu = {a: :menu1, b: :menu2, c: :menu2, d: :menu3}
# Define the methodsdef menu1puts 'menu 1'end
def menu2puts 'menu 2'end
def menu3puts 'menu3'end
# Let's say we case by selected_menu = :aselected_menu = :a
# Then just call the relevant method from the hashsend(menu[selected_menu])

它被称为case,它的工作方式就像你期望的那样,加上===提供的更多有趣的东西,它实现了测试。

case 5when 5puts 'yes'elseputs 'else'end

现在一些乐趣:

case 5 # every selector below would fire (if first)when 3..7    # OK, this is nicewhen 3,4,5,6 # also nicewhen Fixnum  # orwhen Integer # orwhen Numeric # orwhen Comparable # (?!) orwhen Object  # (duhh) orwhen Kernel  # (?!) orwhen BasicObject # (enough already)...end

事实证明,你也可以用case替换任意的if/else链(也就是说,即使测试不涉及公共变量),方法是省略初始的case参数,只编写第一个匹配是你想要的表达式。

casewhen x.nil?...when (x.match /'^fn'/)...when (x.include? 'substring')...when x.gsub('o', 'z') == 'fnzrq'...when Time.now.tuesday?...end
puts "Recommend me a language to learn?"input = gets.chomp.downcase.to_s
case inputwhen 'ruby'puts "Learn Ruby"when 'python'puts "Learn Python"when 'java'puts "Learn Java"when 'php'puts "Learn PHP"else"Go to Sleep!"end

你可以用更自然的方式,

case expressionwhen condtion1functionwhen condition2functionelsefunctionend

正如上述许多答案所述,===运算符在case/when语句的引擎盖下使用。

以下是有关该操作员的其他信息:

等号操作符:===

Ruby的许多内置类,如String、Range和Regexp,都提供了自己的===运算符实现,也称为“大小写相等”、“三重等于”或“三等号”。因为它在每个类中的实现不同,所以它的行为会根据它被调用的对象类型而不同。通常,如果右侧的对象“属于”或“是左侧对象的成员”,则返回true。例如,它可用于测试对象是否是类(或其子类之一)的实例。

String === "zen"  # Output: => trueRange === (1..2)   # Output: => trueArray === [1,2,3]   # Output: => trueInteger === 2   # Output: => true

同样的结果可以用其他可能最适合这项工作的方法来实现,例如is_a?instance_of?

===的范围实现

当对范围对象调用===运算符时,如果右侧的值落在左侧的范围内,则返回true。

(1..4) === 3  # Output: => true(1..4) === 2.345 # Output: => true(1..4) === 6  # Output: => false
("a".."d") === "c" # Output: => true("a".."d") === "e" # Output: => false

请记住,===运算符调用左侧对象的===方法。因此(1..4) === 3等效于(1..4).=== 3。换句话说,左侧操作数的类将定义将调用===方法的哪个实现,因此操作数位置不可互换。

Regexp实现===

如果右侧的字符串与左侧的正则表达式匹配,则返回true。

/zen/ === "practice zazen today"  # Output: => true# is similar to"practice zazen today"=~ /zen/

上面两个示例之间唯一相关的区别是,当存在匹配时,===返回true,=~返回一个整数,这是Ruby中的truthy值。我们很快就会回到这一点。

Ruby使用case来编写Switch语句。

#0留档:

case语句包含一个可选条件,该条件位于参数到case的位置,以及零个或多个when子句。匹配条件的第一个when子句(或计算为布尔真值,如果条件为空)“赢”,其代码节被执行。case语句的值是成功的when子句,如果没有这样的子句,则为nil

case语句可以以else子句结尾。每个when a语句可以有多个候选值,用逗号分隔。

示例:

case xwhen 1,2,3puts "1, 2, or 3"when 10puts "10"elseputs "Some other number"end

更短的版本:

case xwhen 1,2,3 then puts "1, 2, or 3"when 10 then puts "10"else puts "Some other number"end

而“Ruby的case语句-高级技术”描述了Rubycase

可以使用范围

case 5when (1..10)puts "case statements match inclusion in a range"end
## => "case statements match inclusion in a range"

可以使用Regex

case "FOOBAR"when /BAR$/puts "they can match regular expressions!"end
## => "they can match regular expressions!"

可以使用Procs和Lambdas

case 40when -> (n) { n.to_s == "40" }puts "lambdas!"end
## => "lambdas"

也可以与您自己的匹配类一起使用:

class Successdef self.===(item)item.status >= 200 && item.status < 300endend
class Emptydef self.===(item)item.response_size == 0endend
case http_responsewhen Emptyputs "response was empty"when Successputs "response was a success"end

您可以在Ruby中以两种不同的方式编写case表达式:

  1. 类似于一系列if语句
  2. case旁边指定一个目标,并将每个when子句与目标进行比较。
age = 20casewhen age >= 21puts "display something"when 1 == 0puts "omg"elseputs "default condition"end

或:

case params[:unknown]when /Something/ then 'Nothing'when /Something else/ then 'I dont know'end
$age =  5case $agewhen 0 .. 2puts "baby"when 3 .. 6puts "little child"when 7 .. 12puts "child"when 13 .. 18puts "youth"elseputs "adult"end

有关更多信息,请参阅“Ruby-如果…否则,情况下,除非”。

您的环境中不支持正则表达式?例如Shopify脚本编辑器(2018年4月):

[错误]:未初始化的常量RegExp

遵循之前在这里这里中已经介绍过的方法组合的解决方法:

code = '!ADD-SUPER-BONUS!'
class StrContainsdef self.===(item)item.include? 'SUPER' or item.include? 'MEGA' or\item.include? 'MINI' or item.include? 'UBER'endend
case code.upcasewhen '12345PROMO', 'CODE-007', StrContainsputs "Code #{code} is a discount code!"when '!ADD-BONUS!'puts 'This is a bonus code!'elseputs 'Sorry, we can\'t do anything with the code you added...'end

我在class方法语句中使用了or,因为||的优先级高于.include?.

如果您仍然喜欢使用||尽管#1在这种情况下更可取,您可以这样做:(item.include? 'A') || ...。您可以在这个repl.it中测试它。

我们可以为多个条件编写Switch语句。

举个例子,

x = 22
CASE xWHEN 0..14 THEN puts "#{x} is less than 15"WHEN 15 THEN puts "#{x} equals 15"WHEN 15 THEN puts "#{x} equals 15"WHEN 15..20 THEN puts "#{x} is greater than 15"ELSE puts "Not in the range, value #{x} "END

when子句中强调逗号(,)至关重要。它充当if语句的||,也就是说,它在when子句的分隔表达式之间进行比较而不是比较。请参阅以下case语句:

x = 3case xwhen 3, x < 2 then 'apple'when 3, x > 2 then 'orange'end=> "apple"

x不小于2,但返回值是"apple"。为什么?因为x是3,并且因为',`` acts as an||, it did not bother to evaluate the expressionx<2'。

你可能认为要执行,你可以在下面这样做,但它不起作用:

case xwhen (3 && x < 2) then 'apple'when (3 && x > 2) then 'orange'end=> nil

它不起作用,因为(3 && x > 2)的计算结果为true,而Ruby获取True值并将其与x===进行比较,这不是true,因为x是3。

要进行&&比较,您必须将case视为if/else块:

casewhen x == 3 && x < 2 then 'apple'when x == 3 && x > 2 then 'orange'end

在Ruby Programming Language的书中,Matz说后一种形式是简单的(并且不经常使用的)形式,它只不过是if/elsif/else的替代语法。然而,不管它是否经常使用,我看不出有任何其他方法可以为给定的when子句附加多个&&表达式。

case语句运算符类似于其他语言中的switch

这是C中switch...case的语法:

switch (expression)​{case constant1:// statementsbreak;case constant2:// statementsbreak;...default:// default statements}

这是Ruby中case...when的语法:

case expressionwhen constant1, constant2 #Each when statement can have multiple candidate values, separated by commas.# statementsnext # is like continue in other languageswhen constant3# statementsexit # exit is like break in other languages...else# statementsend

例如:

x = 10case xwhen 1,2,3puts "1, 2, or 3"exitwhen 10puts "10" # it will stop here and execute that lineexit # then it'll exitelseputs "Some other number"end

有关详细信息,请参阅#0留档。

我更喜欢用case+

number = 10
case numberwhen 1...8 then # ...when 8...15 then # ...when 15.. then # ...end

如果您需要“小于”或“大于”:

case xwhen 1..5"It's between 1 and 5"when 6"It's 6"when 7..1.0/0"It's equal or greater than 7"when -1.0/0..0"It's equal or less than 0"end

1.0/0等于Float::INFINITY,所以你可以使用你喜欢的。

在Ruby 2.6之后您可以使用无尽的范围在Ruby 2.7之后,您还可以使用无始山脉,例如:

case xwhen 1..5"It's between 1 and 5"when 6"It's 6"when (7..)"It's equal or greater than 7"when (..0)"It's equal or less than 0"end

Ruby支持case表达式。

类匹配:

case e = StandardError.new("testing")when Exception then puts "error!"else puts "ok!"end # => error!

多值匹配:

case 3when 1,2,3 then puts "1..3"when 4,5,6 then puts "4..6"else puts "?"end # => 1..3

正则表达式评估:

case "monkey"when /mon/ then puts "banana"else puts "?"end # => banana

Ruby在2.7中引入了模式匹配

这是超级强大的功能

它也使用case,但有另一种语法

还有查找模式功能

users ={ users:[{ user: 'user', email: 'user@user.com' },{ user: 'admin', email: 'admin@admin.com' },]}
case usersin users: [*, { user:, email: /admin/ => admin_email }, *]puts admin_emailelseputs "No admin"end
# will print admin@admin.com

与通常的case不同,如果条件不匹配,NoMatchingPatternError将被抛出。所以你可能不得不使用else分支