How do I write a switch statement in Ruby?
问题:
回答1:
Ruby uses the case
expression instead.
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 compares the object in the when
clause with the object in the case
clause using the ===
operator. For example, 1..5 === x
, and not x === 1..5
.
This allows for sophisticated when
clauses as seen above. Ranges, classes and all sorts of things can be tested for rather than just equality.
Unlike switch
statements in many other languages, Ruby’s case
does not have fall-through, so there is no need to end each when
with a break
. You can also specify multiple matches in a single when
clause like when \"foo\", \"bar\"
.
回答2:
case...when
behaves a bit unexpectedly when handling classes. This is due to the fact that it uses the ===
operator.
That operator works as expected with literals, but not with classes:
1 === 1 # => true
Fixnum === Fixnum # => false
This means that if you want to do a case ... when
over an object\'s class, this will not work:
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
Will print \"It is not a string\".
Fortunately, this is easily solved. The ===
operator has been defined so that it returns true
if you use it with a class and supply an instance of that class as the second operand:
Fixnum === 1 # => true
In short, the code above can be fixed by removing the .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\')
end
I hit this problem today while looking for an answer, and this was the first appearing page, so I figured it would be useful to others in my same situation.
回答3:
It is done by case in Ruby. Also see this article on Wikipedia.
Quoted:
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
Another example:
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
On around page 123 (I am using Kindle) of The Ruby Programming Lanugage (1st Edition, O\'Reilly), it says the then
keyword following the when
clauses can be replaced with a newline or semicolon (just like in the if then else
syntax). (Ruby 1.8 also allows a colon in place of then
... But this syntax is no longer allowed in Ruby 1.9.)
回答4:
case...when
To add more examples to Chuck\'s answer:
With parameter:
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
Without parameter:
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
Please, be aware of the issue that kikito warns.
回答5:
Many programming languages, especially those derived from C, have support for the so-called Switch Fallthrough. I was searching for the best way to do the same in Ruby and thought it might be useful to others:
In C-like languages fallthrough typically looks like this:
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;
}
In Ruby, the same can be achieved in the following way:
case expression
when \'a\', \'b\', \'c\'
# Do something for a, b or c
when \'d\', \'e\'
# Do something else for d or e
end
This is not strictly equivalent, because it\'s not possible to let \'a\'
execute a block of code before falling through to \'b\'
or \'c\'
, but for the most part I find it similar enough to be useful in the same way.
回答6:
In Ruby 2.0, you can also use lambdas in case
statements, as follows:
is_even = ->(x) { x % 2 == 0 }
case number
when 0 then puts \'zero\'
when is_even then puts \'even\'
else puts \'odd\'
end
You can also create your own comparators easily using a Struct with a custom ===
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
(Example taken from \"Can procs be used with case statements in Ruby 2.0?\".)
Or, with a complete class:
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
(Example taken from \"How A Ruby Case Statement Works And What You Can Do With It\".)
回答7:
You can use regular expressions, such as finding a type of string:
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\'s case
will use the equality operand ===
for this (thanks @JimDeville). Additional information is available at \"Ruby Operators\". This also can be done using @mmdemirbas example (without parameter), only this approach is cleaner for these types of cases.
回答8:
If you are eager to know how to use an OR condition in a Ruby switch case:
So, in a case
statement, a ,
is the equivalent of ||
in an if
statement.
case car
when \'Maruti\', \'Hyundai\'
# Code here
end
Many other things you can do with a Ruby case statement
回答9:
It\'s called case
and it works like you would expect, plus lots more fun stuff courtesy of ===
which implements the tests.
case 5
when 5
puts \'yes\'
else
puts \'else\'
end
Now for some fun:
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
And it turns out you can also replace an arbitrary if/else chain (that is, even if the tests don\'t involve a common variable) with case
by leaving out the initial case
parameter and just writing expressions where the first match is what you want.
case
when x.nil?
...
when (x.match /\'^fn\'/)
...
when (x.include? \'substring\')
...
when x.gsub(\'o\', \'z\') == \'fnzrq\'
...
when Time.now.tuesday?
...
end
回答10:
Depending on your case, you could prefer to use a hash of methods.
If there is a long list of when\'s and each of them has a concrete value to compare with (not an interval), it will be more effective to declare a hash of methods and then to call the relevant method from the hash like that.
# 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])
回答11:
Since switch case
always returns a single object, we can directly print its result:
puts case a
when 0
\"It\'s zero\"
when 1
\"It\'s one\"
end
回答12:
Multi-value when and no-value case:
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
And a regular expression solution here:
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
回答13:
Ruby uses the case
for writing switch statements.
As per the Ruby Docs:
Case statements consist of an optional condition, which is in the position of an argument to
case
, and zero or morewhen
clauses. The firstwhen
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 successfulwhen
clause, ornil
if there is no such clause.A case statement can end with an
else
clause. Eachwhen
a statement can have multiple candidate values, separated by commas.
Example:
case x
when 1,2,3
puts \"1, 2, or 3\"
when 10
puts \"10\"
else
puts \"Some other number\"
end
Shorter version:
case x
when 1,2,3 then puts \"1, 2, or 3\"
when 10 then puts \"10\"
else puts \"Some other number\"
end
And as this blog by Honeybadger describes Ruby Case;
Can be used with Ranges:
case 5
when (1..10)
puts \"case statements match inclusion in a range\"
end
## => \"case statements match inclusion in a range\"
Can be used with Regex:
case \"FOOBAR\"
when /BAR$/
puts \"they can match regular expressions!\"
end
## => \"they can match regular expressions!\"
Can be used with Procs and Lambdas:
case 40
when -> (n) { n.to_s == \"40\" }
puts \"lambdas!\"
end
## => \"lambdas\"
Also, can be used with your own match classes:
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
回答14:
You can write case expressions in two different ways in ruby.
- Similar to a series of \"if\" statements
- Specify a target next to the case and each \"when\" clause is compared to the target.
1st way
age = 20
case
when age >= 21
puts \"display something\"
when 1 == 0
puts \"omg\"
else
puts \"default condition\"
end
2nd way
case params[:unknown]
when /Something/ then \'Nothing\'
when /Something else/ then \'I dont know\'
end
回答15:
Lots of great answers but I thought I would add one factoid.. If you are attempting to compare objects (Classes) make sure you have a space ship method (not a joke) or understand how they are being compared
Here is a good discussion on the topic http://www.skorks.com/2009/09/ruby-equality-and-object-comparison/
回答16:
You can do like this in more natural way,
case expression
when condtion1
function
when condition2
function
else
function
end
回答17:
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
回答18:
As stated in many of the above answers, the === operator is used under the hood on case/when statements.
Here is a few extra information about that operator.
Case equality operator: ===
Many of Ruby\'s built-in classes, such as String, Range, and Regexp, provide their own implementations of the === operator, also known as case-equality, triple equals or threequals. Because it\'s implemented differently in each class, it will behave differently depending on the type of object it was called on. Generally, it returns true if the object on the right \"belongs to\" or \"is a member of\" the object on the left. For instance, it can be used to test if an object is an instance of a class (or one of its subclasses).
String === \"zen\" # Output: => true
Range === (1..2) # Output: => true
Array === [1,2,3] # Output: => true
Integer === 2 # Output: => true
The same result can be achieved with other methods which are probably best suited for the job, such as is_a? and instance_of?.
Range Implementation of ===
When the === operator is called on a range object, it returns true if the value on the right falls within the range on the left.
(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
Remember that the === operator invokes the === method of the left-hand object. So (1..4) === 3 is equivalent to (1..4).=== 3. In other words, the class of the left-hand operand will define which implementation of the === method will be called, so the operand positions are not interchangeable.
Regexp Implementation of ===
Returns true if the string on the right matches the regular expression on the left. /zen/ === \"practice zazen today\" # Output: => true # is similar to \"practice zazen today\"=~ /zen/
The only relevant difference between the two examples above is that, when there is a match, === returns true and =~ returns an integer, which is a truthy value in Ruby. We will get back to this soon.
回答19:
$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
reference => https://www.tutorialspoint.com/ruby/ruby_if_else.htm
回答20:
I\'ve started to use:
a = \"secondcase\"
var_name = case a
when \"firstcase\" then \"foo\"
when \"secondcase\" then \"bar\"
end
puts var_name
>> \"bar\"
It helps compact code in some cases.
回答21:
No support for regular expressions in your environment? E.g. Shopify Script Editor (April, 2018):
[Error]: uninitialized constant RegExp
A workaround following a combination of methods already previously covered in here and here:
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
I used or
s in the class method statement since ||
has higher precedence than
.include?
. If you are a ruby-nazi, please imagine I used this (item.include? \'A\') || ...
instead. repl.it test.