如何用Ruby编写switch语句

How to write a switch statement in Ruby

如何用Ruby编写switch语句?


Ruby使用case表达式。

1
2
3
4
5
6
7
8
9
10
11
12
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 === x,而不是x === 1..5

这就允许了上述复杂的when条款。范围、类和各种各样的东西都可以测试,而不仅仅是平等。

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


处理类时,case...when的行为有点出乎意料。这是因为它使用了===操作符。

该运算符按预期处理文本,但不处理类:

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

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

1
2
3
4
5
6
7
8
9
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')
end

将打印"它不是字符串"。

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

1
Fixnum === 1 # => true

简而言之,可以通过删除.class来修复上述代码:

1
2
3
4
5
6
7
8
9
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')
end

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


它是由Ruby中的case完成的。另请参阅维基百科上的这篇文章。

引用:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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

另一个例子:

1
2
3
4
5
6
7
8
9
10
11
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

在Ruby编程语言(第1版,O'Reilly)的第123页(我使用的是Kindle)上,它说when子句后面的then关键字可以用换行符或分号替换(就像在if then else语法中一样)。(Ruby1.8还允许冒号代替then……但Ruby1.9不再允许使用这种语法。)


案件…何时

要在查克的答案中添加更多示例:

带参数:

1
2
3
4
5
6
7
8
9
10
11
12
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

无参数:

1
2
3
4
5
6
7
8
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警告的问题。


许多编程语言,特别是那些从C派生的语言,都支持所谓的switch fallthrough。我在寻找在Ruby中实现这一点的最佳方法,并认为它可能对其他人有用:

在类C语言中,fallthrough通常如下所示:

1
2
3
4
5
6
7
8
9
10
11
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中,可以通过以下方式实现这一点:

1
2
3
4
5
6
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''c'之前执行一个代码块,但在大多数情况下,我发现它非常相似,可以以同样的方式使用。


在Ruby2.0中,还可以在case语句中使用lambda,如下所示:

1
2
3
4
5
6
7
is_even = ->(x) { x % 2 == 0 }

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

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

1
2
3
4
5
6
7
8
9
10
11
12
13
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是否可以与Ruby2.0中的case语句一起使用?")

或者,有一个完整的类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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语句的工作方式以及您可以使用它做什么"。)


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

1
2
3
4
5
6
7
8
9
10
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 operators"提供了更多信息。这也可以使用@mmdemirbas示例(不带参数)完成,只有这种方法对于这些类型的情况更为清晰。


如果您想知道如何在Ruby开关案例中使用或条件:

因此,在case语句中,,if语句中的||等价。

1
2
3
4
case car
   when 'Maruti', 'Hyundai'
      # Code here
end

使用Ruby case语句还可以做其他许多事情


它被称为case,它的工作方式和您预期的一样,加上更多有趣的东西,由===来实现测试。

1
2
3
4
5
6
case 5
  when 5
    puts 'yes'
  else
    puts 'else'
end

现在,为了好玩:

1
2
3
4
5
6
7
8
9
10
11
12
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

结果表明,您也可以用case替换任意的if/else链(即,即使测试不涉及公共变量),方法是去掉初始的case参数,然后只在需要第一个匹配的地方编写表达式。

1
2
3
4
5
6
7
8
9
10
11
12
case
  when x.nil?
    ...
  when (x.match /'^fn'/)
    ...
  when (x.include? 'substring')
    ...
  when x.gsub('o', 'z') == 'fnzrq'
    ...
  when Time.now.tuesday?
    ...
end


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

如果有一个长的when's列表,并且每个when's都有一个要比较的具体值(而不是间隔),那么声明方法的散列,然后从散列中调用相关的方法会更有效。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# 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])

多值时无值情况:

1
2
3
4
5
6
7
8
9
10
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

这里有一个正则表达式解:

1
2
3
4
5
6
7
8
9
10
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


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

1
2
3
4
5
6
puts case a
     when 0
       "It's zero"
     when 1
       "It's one"
     end

Ruby使用case编写switch语句。

根据Ruby文档:

Case statements consist of an optional condition, which is in the
position of an argument to case, and zero or more when clauses.
The first when clause to match the condition (or to evaluate to
Boolean truth, if the condition is null)"wins", and its code stanza
is executed. The value of the case statement is the value of the
successful when clause, or nil if there is no such clause.

A case statement can end with an else clause. Each when a
statement can have multiple candidate values, separated by commas.

例子:

1
2
3
4
5
6
7
8
case x
when 1,2,3
  puts"1, 2, or 3"
when 10
  puts"10"
else
  puts"Some other number"
end

较短版本:

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

正如这篇由霍尼贝格撰写的博客所描述的,红宝石盒子;

可用于范围:

1
2
3
4
5
6
case 5
when (1..10)
  puts"case statements match inclusion in a range"
end

## =>"case statements match inclusion in a range"

可与regex一起使用:

1
2
3
4
5
6
case"FOOBAR"
when /BAR$/
  puts"they can match regular expressions!"
end

## =>"they can match regular expressions!"

可与Procs和Lambda一起使用:

1
2
3
4
5
6
case 40
when -> (n) { n.to_s =="40" }
  puts"lambdas!"
end

## =>"lambdas"

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
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

您可以用Ruby以两种不同的方式编写case表达式。

  • 类似于一系列"if"语句
  • 在事例旁边指定一个目标,并将每个"when"子句与目标进行比较。
  • 第一条道路

    1
    2
    3
    4
    5
    6
    7
    8
    9
    age = 20
    case
    when age >= 21
    puts"display something"
    when 1 == 0
    puts"omg"
    else
    puts"default condition"
    end

    第二条道路

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


    你可以更自然地这样做,

    1
    2
    3
    4
    5
    6
    7
    8
    case expression
    when condtion1
       function
    when condition2
       function
    else
       function
    end

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

    这里有一个关于这个话题的很好的讨论http://www.skorks.com/2009/09/ruby-equality-and-object-comparison/


    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    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

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

    这里有一些关于那个接线员的额外信息。

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

    Ruby的许多内置类(如string、range和regexp)都提供了自己的==运算符实现,也称为case equality、triple equals或threeequals。因为它在每个类中的实现方式不同,所以根据调用对象的类型,它的行为会有所不同。通常,如果右边的对象"属于"或"是"左边的对象的成员,则返回true。例如,它可以用来测试对象是否是类(或其子类)的实例。

    1
    2
    3
    4
    String ==="zen"  # Output: => true
    Range === (1..2)   # Output: => true
    Array === [1,2,3]   # Output: => true
    Integer === 2   # Output: => true

    同样的结果也可以通过其他可能最适合该工作的方法来实现,例如"是吗?"还有一个例子?.

    范围实施===

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

    1
    2
    3
    4
    5
    6
    (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/="今日练习zazen"输出:=>真类似于"今天练习zazen"=~/zen/

    上面两个例子之间唯一相关的区别是,当有匹配时,==返回true,=~返回整数,这是Ruby中的一个真实值。我们很快就会回到这里。


    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    $age =  5
    case $age
    when 0 .. 2
       puts"baby"
    when 3 .. 6
       puts"little child"
    when 7 .. 12
       puts"child"
    when 13 .. 18
       puts"youth"
    else
       puts"adult"
    end

    参考=>https://www.tutorialspoint.com/ruby/rubyu ifu else.htm


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

    例如,

    1
    2
    3
    4
    5
    6
    7
    8
    9
    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

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

    [Error]: uninitialized constant RegExp

    以下方法组合的解决方案已经在这里和这里介绍过:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    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

    我在类方法语句中使用了ors,因为||的优先级高于.include?。如果你是一个红宝石纳粹,请想象我用了这个(item.include? 'A') || ...。IT测试。


    我开始使用:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    a ="secondcase"

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

    puts var_name
    >>"bar"

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


    在when子句中强调逗号","是if语句的一个,这很重要,也就是说,它在when子句的定界表达式之间进行OR比较,而不是AND比较。所以请检查下面的case语句。显然,x不小于2,但返回值是'apple'。为什么?因为x是3,并且由于','作为一个,所以不需要计算表达式'x<2'。

    1
    2
    3
    4
    5
    6
    x = 3
    case x
      when 3, x < 2 then 'apple'
      when 3, x > 2 then 'orange'
    end
     =>"apple"

    您可能认为要执行和,可以在下面这样做。但它不起作用。这是因为(3&x>2)的计算结果是真的,Ruby接受真值并将其与x进行比较,得出的值为==,显然不是真的,因为x是3。

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

    要进行比较,您必须像对待病例一样对待,否则将阻止:

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

    在Ruby编程语言书中,Matz说后一种形式是简单(并且很少使用)的形式,它只是if/elsif/else的一种可选语法。但是,无论它是否经常使用,我都看不到为给定的"when"子句附加多个表达式的任何其他方法。