Ruby offers several conditional statements and control structures for managing the flow of your applications. In the previous chapter you had a sneak peak at the if and for statements; here they are introduced more formally, among others useful statements.
The following illustrates the usage of the if conditional statement in Ruby:
if temperature < 0 puts "Freezing!" end
Visual Basic programmers will find this syntax quite natural, and it shouldn't be too foreign to C# programmers either.
Any code between the if and the end line is executed unless the tested expression evaluates to false or nil. The if statement also accepts the then token separator (as opposed to the newline alone), so you could have written the same code as follows:
if (temperature < 0) then puts "Freezing!" end
Parenthesis around expressions within conditional statements are optional in Ruby, but it may not be a bad idea to include them when they're helpful in clarifying the meaning of the code. Or even as a single line:
if temperature < 0 then puts "Freezing!" end
Ruby 1.8 allows semicolons as token separators as well, but Ruby 1.9 doesn't.
Listing 4-1 shows a recursive version of the factorial in Ruby.
# Naive, recursive implementation of the factorial in Ruby def fact(n) if n <= 1 1 else n * fact(n-1) end end n = (ARGV[0] || 10).to_i puts fact(n) |
Copy the code into a fact.rb file (or whatever you wish to call it), and then run ruby fact.rb. The result should be 3628800, which is the factorial of 10. You can also pass an argument to the program by running (for example) ruby fact.rb 5, obtaining 120.
Line 11 shows you a little trick: n = (ARGV[0] || 10).to_i. ARGV is an array of arguments passed to the program, so ARGV[0] retrieves the first argument as a string or nil if no arguments were passed. ARGV[0] || 10 returns the first argument if different than nil or 10 if nil. The result is then converted to an integer (because ARGV[0] is a string when it exists). This idiom is an easy way to specify a default number when no arguments are passed to the program, while still accepting an argument from the user.
Ruby also offers the possibility of using the if statement as an expression modifier. So you can rewrite the line from the earlier example as:
puts "Freezing!" if temperature < 0
This notation offers a clear gain in readability for simple one-liners (try reading it out loud), but for non-trivial statements it's far better to use the "traditional" multiple-line if.
if statements may also include the optional elsif and else clauses using the following syntax:
if expression1 code elsif expression2 code elsif expression3 code else code end
From now on, in these snippets, code is used as a single, generic term that indicates one or more lines of code.
There can be several elsif clauses but only one final else. Please pay attention to the peculiar syntax of the elsif clause. It's not ElseIf of VB, nor else if of C#.
Perhaps surprisingly, Ruby also offers an unless statement:
unless sold_out place_order end
As you can imagine, unless is the opposite of if, and the preceding code delimited within unless and end is executed only when the condition tested is nil or false. unless can also be used as an expression/statement modifier:
place_order unless sold_out
unless supports an else clause, but unlike the if statement, it doesn't accept elseif clauses. Depending on the situation, you may opt for if or prefer unless; Ruby provides both for the sake of convenience and readability.
Though if and unless have been viewed as "statements" so far, in Ruby there's a twist: they too are expressions. Unlike C# and VB, in Ruby everything is an expression, so even if and unless return values. If you run the following snippet, the variable state is set to the returning value of the if expression, in this particular case "Gas," before being printed to the console:
h2o_temp = 130 state = if h2o_temp < 0 "Solid" elsif h2o_temp > 100 "Gas" else "Liquid" end puts state
Like C#, and many other C-derived languages, Ruby also supports the ternary operator. The ?: operator returns the first value unless the condition is false or nil, in which case the second value is returned:
value = condition ? val1 : val2
The preceding line is a much more succinct way of writing the following if statement:
value = if condition val1 else val2 end
For example, you could write:
ticket = (age < 18) ? "child" : "adult"
In most languages, whenever you need to verify multiple conditions, you'd employ the "switch" statement. Ruby calls it the case statement and it's far closer to what Visual Basic developers — as opposed to C# ones — are accustomed to. That said, Ruby's case statement is much more powerful than what you may expect at first.
There are two ways of using the statement. The first, without passing an argument, is equivalent to using an if statement with a series of elsif clauses:
case when condition1 code when condition2 code when condition3 code when condition4 code else code end
And the second, which passes an argument:
case expression when value1 code when value2 code when value3 code when value4 code else code end
In both usages, the case statement can have multiple when clauses, and an optional else clause for code that should be executed if no other previous condition has been successful. This is equivalent to the default clause in C# and the Case Else in VB.NET.
Please note that case has no fall-through behavior, so if one of the conditions is met, no other attempts to evaluate other conditions are carried out by the interpreter. This implies that, just as in VB.NET, there's no need for break statements of any sort. Still similarly to Visual Basic, it is possible to combine multiple conditions on the same line by separating them with a comma:
case expression when value1, value, value3 code when value4, value5 code
when value6, value7, value8 code when value9 code else code end
Just like if and unless, the case statement returns the last evaluated expression if any code is executed, or nil if no condition was successful and no else clause was defined. Conversely, like if and unless, the when clause supports the then token separator as well.
The case statement in Ruby is very powerful because it's able to use not only integers, strings, or what other languages consider "primitive types," but any type of expression, even when the value is determined during runtime. What's more, it works by using the so-called "case equality" operator (that is, ===). Though several classes make no distinction between this operator and the simple equality one (that is, ==), many classes implement the case equality operator in a somewhat intuitive and logical manner. The class Regexp implements the === operator to check if there is a match for the given pattern, whereas the Range class implements it in order to check whether the element is contained within the given range, and so forth. For example, the following snippet prints "Senior" to the console.
age = 85 puts case age when 0..12 then "Child" when 13..19 then "Teenager" when 20..65 then "Adult" else "Senior" end
Another common idiom takes advantage of the equality operator implemented by the Class class. This verifies that the given expression is an instance of the specified class:
Numeric === 3 # true String === "Rails" # true Array === {} # false, because an empty hash is not an instance of Array
Within the context of the case statement, you could then write the following:
case my_expression when Numeric # Handle number code when String # Handle String code when Array # Handle Array code when Hash # Handle Hash code
when TrueClass, FalseClass # Handle Boolean code else # Handle any other type code end
18.118.12.232