如何在Ruby中编写switch语句


Answers:


2666

Ruby使用case表达式代替。

case x
when 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 === xx === 1..5

when如上所述,这允许使用复杂的条款。可以测试范围,类和各种各样的东西,而不仅仅是相等性。

不像switch其他许多语言的语句,Ruby的case没有落空,所以没有必要结束每when一个break。您还可以在单​​个when子句中指定多个匹配项,例如when "foo", "bar"


12
您也可以对传递的参数进行正则表达式:/ thisisregex / 下一行 显示“ This is找到的匹配项1#{$ 1}”末尾
Automatico

8
同样值得注意的是,可以通过将whenand return语句放在同一行when "foo" then "bar"
上来

9
重要提示:不像switch其他许多语言的语句,Ruby的case没有落空,所以没有必要结束每when一个break
janniks

3
这么多的高票,甚至没有提到关键字then。另请参阅其他答案。
克林特·帕奇

442

case...when处理类时,行为异常。这是由于它使用了===运算符。

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

1 === 1           # => true
Fixnum === Fixnum # => false

这意味着,如果要对case ... when对象的类进行操作,则将无法使用:

obj = 'hello'
case obj.class
when String
  print('It is a string')
when Fixnum
  print('It is a number')
else
  print('It is not a string or number')
end

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

幸运的是,这很容易解决。该===运营商已定义,以便它返回true,如果你有一个类中使用它,并提供该类作为第二个操作数的实例:

Fixnum === 1 # => true

简而言之,可以通过删除以下代码来修复上面的代码.class

obj = 'hello'
case obj  # was case obj.class
when String
  print('It is a string')
when Fixnum
  print('It is a number')
else
  print('It is not a string or number')
end

我今天在寻找答案时遇到了这个问题,这是第一个出现的页面,因此我认为它对处于相同情况的其他人很有用。


obj ='hello'; case obj; 当“你好”然后把“这是你好”结束时
Sugumar Venkatesan

具有.class部分有趣的是,谢谢。当然,这是完全适当的行为(尽管我可以看到认为可以打印的常见错误It is a string)……您正在测试某个任意对象的,而不是对象本身。因此,举例来说:case 'hello'.class when String then "String!" when Class then "Class!" else "Something else" end在结果:"Class!"该方法适用于相同的1.class{}.class等滴药.class,我们得到"String!"或者"Something else"这些不同的值。
lindes

219

这是case在Ruby中使用完成的。另请参阅Wikipedia上的“ Switch语句 ”。

引:

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

另一个例子:

score = 70

result = case score
   when 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编程语言(第1版,O'Reilly)》的第123页左右,它说thenwhen子句后面的关键字可以用换行符或分号代替(就像if then else语法中一样)。(Ruby 1.8还允许使用冒号代替then,但Ruby 1.9中不再允许使用此语法。)


38
when (-1.0/0.0)..-1 then "Epic fail"
Andrew Grimm

这是我使用的答案,因为我要根据大小写转换的结果定义一个变量。type = #{score}我不必说每行,而只需复制您所做的事情。更优雅,我也更喜欢
单线

我知道这与答案的本质无关,但是4也是一个完美的平方。
尼克·摩尔

109

案例...当

Chuck的答案中添加更多示例:

带参数:

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

没有参数:

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

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


谢谢,这有助于在一行上具有多个选项。我一直在尝试使用or
sixty4bit,2014年

73

许多编程语言(尤其是从C派生的编程语言)都支持所谓的Switch Fallthrough。我一直在寻找在Ruby中执行相同操作的最佳方法,并认为这可能对其他人有用:

在类似C的语言中,穿透通常看起来像这样:

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

在Ruby中,可以通过以下方式实现相同的目的:

case expression
when 'a', 'b', 'c'
  # Do something for a, b or c
when 'd', 'e'
  # Do something else for d or e
end

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


72

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

is_even = ->(x) { x % 2 == 0 }

case number
when 0 then puts 'zero'
when is_even then puts 'even'
else puts 'odd'
end

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

Moddable = Struct.new(:n) do
  def ===(numeric)
    numeric % n == 0
  end
end

mod4 = Moddable.new(4)
mod3 = Moddable.new(3)

case number
when mod4 then puts 'multiple of 4'
when mod3 then puts 'multiple of 3'
end

(示例摘自“ procs是否可以与Ruby 2.0中的case语句一起使用? ”。)

或者,使用完整的课程:

class Vehicle
  def ===(another_vehicle)
    self.number_of_wheels == another_vehicle.number_of_wheels
  end
end

four_wheeler = Vehicle.new 4
two_wheeler = Vehicle.new 2

case vehicle
when two_wheeler
  puts 'two wheeler'
when four_wheeler
  puts 'four wheeler'
end

(示例摘自“ Ruby Case语句如何工作以及您可以如何使用它 ”。)


52

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

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

Ruby case===为此使用相等操作数(感谢@JimDeville)。其他信息可在“ Ruby运算符 ”中获得。也可以使用@mmdemirbas示例(不带参数)完成此操作,只有这种方法对于此类情况更干净。



33

这就是所谓的case,它的工作原理与您期望的一样,外加更多有趣的东西,===这些东西可以实现测试。

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

现在找点乐子:

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

事实证明,您也可以替换掉任意的if / else链(即,即使测试不涉及公共变量),也case可以省略初始case参数,而只在需要第一个匹配项的地方编写表达式。

case
  when x.nil?
    ...
  when (x.match /'^fn'/)
    ...
  when (x.include? 'substring')
    ...
  when x.gsub('o', 'z') == 'fnzrq'
    ...
  when Time.now.tuesday?
    ...
end

23

Ruby使用case编写开关语句。

根据case文档:

Case语句包含一个可选条件,该条件位于和的参数的位置case以及零个或多个when子句。when匹配条件的第一个子句(或如果条件为null则求值为布尔真)“获胜”,并执行其代码节。case语句的值是成功when子句的值,或者nil如果没有这样的子句的话。

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

例:

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

短版:

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

正如“ Ruby的案例陈述-高级技术 ”所描述的Ruby case;

可以与Ranges一起使用:

case 5
when (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 40
when -> (n) { n.to_s == "40" }
  puts "lambdas!"
end

## => "lambdas"

另外,可以与您自己的匹配类一起使用:

class Success
  def self.===(item)
    item.status >= 200 && item.status < 300
  end
end

class Empty
  def self.===(item)
    item.response_size == 0
  end
end

case http_response
when Empty
  puts "response was empty"
when Success
  puts "response was a success"
end

22

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

如果存在一个很长的whens 列表,并且每个s具有要比较的具体值(而不是一个间隔),则声明方法的哈希,然后像这样从哈希中调用相关的方法将更为有效。

# Define the hash
menu = {a: :menu1, b: :menu2, c: :menu2, d: :menu3}

# Define the methods
def menu1
  puts 'menu 1'
end

def menu2
  puts 'menu 2'
end

def menu3
  puts 'menu3'
end

# Let's say we case by selected_menu = :a
selected_menu = :a

# Then just call the relevant method from the hash
send(menu[selected_menu])

21

由于switch case始终返回单个对象,因此我们可以直接打印其结果:

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

20

多值时间和无值情况:

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

还有一个正则表达式解决方案:

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

2
为什么不只是case some_string, when /\d/, (stuff), when /[a-zA-Z]/, (stuff), end(在哪里,意味着换行)
Doorknob 2014年

2
哦,这个答案已经包含了第一部分,而且很多答案已经提到了正则表达式。坦白说,这个答案没有增加任何新内容,我正在投票删除它。
门把手2014年

@DoorknobofSnow这表明您可以在开关情况下使用Regex解决方案和逗号分隔的值。不知道为什么该解决方案会给您带来极大的痛苦。
2014年

因此,如果他们获得“ F”(合法)等级,则他们的错是您的代码缺少案例?
2014年

我喜欢这种幽默,而且它表明您可以将字符串与大小写匹配,这一点我很喜欢。
2015年

13

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

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

要么:

case params[:unknown]
when /Something/ then 'Nothing'
when /Something else/ then 'I dont know'
end

尽管您的代码可能会回答该问题,但是您应该至少添加简短说明,以说明代码的作用以及如何解决最初的问题。
user1438038 '17

以后我会考虑这个技巧。
ysk

10

您可以更自然地做到这一点,

case expression
when condtion1
   function
when condition2
   function
else
   function
end

9

很多不错的答案,但我想我会添加一个模拟对象。

Ruby平等和对象比较 ”是对该主题的一个很好的讨论。


7
作为参考,“ space-ship”方法为<=>,用于根据比较结果分别返回小于,等于,大于还是不可比较,返回-1、0、1或nil。Ruby的Comparable模块文档对此进行了解释。
Tin Man

7

如以上许多答案所述,===运算符用于case/ when语句的内幕。

这是有关该运算符的其他信息:

大小写相等运算符: ===

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

String === "zen"  # Output: => true
Range === (1..2)   # Output: => true
Array === [1,2,3]   # Output: => true
Integer === 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。换句话说,左侧操作数的类将定义===将调用该方法的哪种实现,因此操作数位置不可互换。

正则表达式的实现 ===

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

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

上面两个示例之间唯一相关的区别是,如果存在匹配项,则===返回true并=~返回整数,这是Ruby中的真值。我们将尽快回到这一点。


5
puts "Recommend me a language to learn?"
input = gets.chomp.downcase.to_s

case input
when 'ruby'
    puts "Learn Ruby"
when 'python'
    puts "Learn Python"
when 'java'
    puts "Learn Java"
when 'php'
    puts "Learn PHP"
else
    "Go to Sleep!"
end

1
如果您提供解释说明为什么这是首选解决方案并说明其工作原理,那么它会有所帮助。我们要教育,而不仅仅是提供代码。
锡人


1

我已经开始使用:

a = "secondcase"

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

puts var_name
>> "bar"

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


1
此类代码通常应使用Hash而不是case语句来完成。
汤姆·罗德

当该开关变大时,使用散列会更快。
锡人

1

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

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

结合之前在此处此处已经介绍的方法的一种解决方法:

code = '!ADD-SUPER-BONUS!'

class StrContains
  def self.===(item)
    item.include? 'SUPER' or item.include? 'MEGA' or\
    item.include? 'MINI' or item.include? 'UBER'
  end
end

case code.upcase
when '12345PROMO', 'CODE-007', StrContains
  puts "Code #{code} is a discount code!"
when '!ADD-BONUS!'
  puts 'This is a bonus code!'
else
  puts 'Sorry, we can\'t do anything with the code you added...'
end

or在类方法语句中使用s,因为||它的优先级高于 .include?。如果您是红宝石纳粹分子,请想象我用了它(item.include? 'A') || ...repl.it测试。


1

,when子句中强调逗号()非常重要。它充当||一个的if说法,那就是它的OR比较,而不是该的分隔符的表达式之间的比较when条款。请参阅以下案例声明:

x = 3
case x
  when 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'。

您可能会认为要执行AND,您可以在下面执行以下操作,但这不起作用:

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

它不工作,因为(3 && x > 2)计算结果为真实的,和Ruby取真值,并将其与x===,这是不正确的,因为x是3。

做一个&&比较,你将不得不对待caseif/ else块:

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

在Ruby编程语言的书,马茨说,这后一种形式是简单的(和不经常使用)的形式,这无非是一个替代语法if/ elsif/ else。但是,无论是否经常使用它,我都看不到任何其他方式可以&&为给定when子句附加多个表达式。


在我看来,这似乎不是良好的编码风格。使用罕见的备用语法会造成不必要的混淆。为什么不正常使用if...elsif?似乎您正在尝试混合一个case语句和一个条件。为什么?只需将条件放在 when块中,例如。when 3; ( x < 2 ) ? 'apple' : 'orange'
sondra.kinsey,

0

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

例如,

x = 22

CASE x
  WHEN 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

1
这行不通;红宝石的关键字(例如casewhenend)是区分大小写的,并且不能是大写这样。
sondra.kinsey,

NoMethodError (undefined method CASE'for main:Object)`。正如@ sondra.kinsey所说,您不能使用大写。Ruby会认为它是恒定的。
锡人

0

case声明运营商像switch其他语言。

这是switch...caseC语言的语法:

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

这是case...whenRuby中的语法:

case expression
  when constant1, constant2 #Each when statement can have multiple candidate values, separated by commas.
     # statements 
     next # is like continue in other languages
  when constant3
     # statements 
     exit # exit is like break in other languages
  .
  .
  .
  else
     # statements
end

例如:

x = 10
case x
when 1,2,3
  puts "1, 2, or 3"
  exit
when 10
  puts "10" # it will stop here and execute that line
  exit # then it'll exit
else
  puts "Some other number"
end

有关更多信息,请参见case文档。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.