Conditionals

The most common control structure, in any programming language, is the conditional. This is a way of telling the computer to conditionally execute some code: to execute it only if some condition is satisfied. The condition is an expression—if it evaluates to any value other than false or nil, then the condition is satisfied.

Ruby has a rich vocabulary for expressing conditionals. The syntax choices are described in the subsections that follow. When writing Ruby code, you can choose the one that seems most elegant for the task at hand.

if

The most straightforward of the conditionals is if. In its simplest form, it looks like this:

if expression
  code
end

The code between if and end is executed if (and only if) the expression evaluates to something other than false or nil. The code must be separated from the expression with a newline or semicolon or the keyword then.[5] Here are two ways to write the same simple conditional:

# If x is less than 10, increment it
if x < 10                     # newline separator
  x += 1
end
if x < 10 then x += 1 end     # then separator

You can also use then as the separator token, and follow it with a newline. Doing so makes your code robust; it will work even if the newline is subsequently removed:

if x < 10 then
  x += 1
end

Programmers who are used to C, or languages whose syntax is derived from C, should note two important things about Ruby’s if statement:

  • Parentheses are not required (and typically not used) around the conditional expression. The newline, semicolon, or then keyword serves to delimit the expression instead.

  • The end keyword is required, even when the code to be conditionally executed consists of a single statement. The modifier form of if, described below, provides a way to write simple conditionals without the end keyword.

else

An if statement may include an else clause to specify code to be executed if the condition is not true:

if expression
  code
else
  code
end

The code between the if and else is executed if expression evaluates to anything other than false or nil. Otherwise (if expression is false or nil), the code between the else and end is executed. As in the simple form of if, the expression must be separated from the code that follows it by a newline, a semicolon, or the keyword then. The else and end keywords fully delimit the second chunk of code, and no newlines or additional delimiters are required.

Here is an example of a conditional that includes an else clause:

if data         # If the array exists
  data << x     #   then append a value to it.
else            # Otherwise...
  data = [x]    #   create a new array that holds the value.
end             # This is the end of the conditional.

elsif

If you want to test more than one condition within a conditional, you can add one or more elsif clauses between an if and an else. elsif is a shortened form of “else if.” Note that there is only one e in elsif. A conditional using elsif looks like this:

if expression1
  code1
elsif expression2
  code2
      .
      .
      .
elsif expressionN
  codeN
else
  code
end

If expression1 evaluates to anything other than false or nil, then code1 is executed. Otherwise, expression2 is evaluated. If it is anything other than false or nil, then code2 is executed. This process continues until an expression evaluates to something other than false or nil, or until all elsif clauses have been tested. If the expression associated with the last elsif clause is false or nil, and the elsif clause is followed by an else clause, then the code between else and end is executed. If no else clause is present, then no code is executed at all.

elsif is like if: the expression must be separated from the code by a newline, a semicolon, or a then keyword. Here is an example of a multiway conditional using elsif:

if x == 1
  name = "one"
elsif x == 2
  name = "two"
elsif x == 3 then name = "three"
elsif x == 4; name = "four"
else
  name = "many"
end

Return value

In most languages, the if conditional is a statement. In Ruby, however, everything is an expression, even the control structures that are commonly called statements. The return value of an if “statement” (i.e., the value that results from evaluating an if expression) is the value of the last expression in the code that was executed, or nil if no block of code was executed.

The fact that if statements return a value means that, for example, the multiway conditional shown previously can be elegantly rewritten as follows:

name = if    x == 1 then "one"
       elsif x == 2 then "two"
       elsif x == 3 then "three"
       elsif x == 4 then "four"
       else              "many"
       end

if As a Modifier

When if is used in its normal statement form, Ruby’s grammar requires that it be terminated with the end keyword. For simple, single-line conditionals, this is somewhat awkward. This is just a parsing problem, and the solution is to use the if keyword itself as the delimiter that separates the code to be executed from the conditional expression. Instead of writing:

if expression then code end

we can simply write:

code if expression

When used in this form, if is known as a statement (or expression) modifier. If you’re a Perl programmer, you may be accustomed to this syntax. If not, please note that the code to execute comes first, and the expression follows. For example:

puts message if message    # Output message, if it is defined

This syntax places more emphasis on the code to be executed, and less emphasis on the condition under which it will be executed. Using this syntax can make your code more readable when the condition is a trivial one or when the condition is almost always true.

Even though the condition is written last, it is evaluated first. If it evaluates to anything other than false or nil, then the code is evaluated, and its value is used as the return value of the modified expression. Otherwise, the code is not executed, and the return value of the modified expression is nil. Obviously, this syntax does not allow any kind of else clause.

To use if as a modifier, it must follow the modified statement or expression immediately, with no intervening line break. Inserting a newline into the previous example turns it into an unmodified method invocation followed by an incomplete if statement:

puts message        # Unconditional
if message          # Incomplete!

The if modifier has very low precedence and binds more loosely than the assignment operator. Be sure you know just what expression you are modifying when you use it. For example, the following two lines of code are different:

y = x.invert if x.respond_to? :invert
y = (x.invert if x.respond_to? :invert)

In the first line, the modifier applies to the assignment expression. If x does not have a method named invert, then nothing happens at all, and the value of y is not modified. In the second line, the if modifier applies only to the method call. If x does not have an invert method, then the modified expression evaluates to nil, and this is the value that is assigned to y.

An if modifier binds to the single nearest expression. If you want to modify more than one expression, you can use parentheses or a begin statement for grouping. But this approach is problematic because readers don’t know that the code is part of a conditional until they reach the bottom. Also, using an if modifier in this way gives up the conciseness that is the primary benefit of this syntax. When more than one line of code is involved, you should typically use a traditional if statement rather than an if modifier. Compare the following three side-by-side alternatives:

if expression     begin                (
  line1             line1                line1
  line2             line2                line2
end               end if expression    ) end if expression

Note that an expression modified with an if clause is itself an expression that can be modified. It is therefore possible to attach multiple if modifiers to an expression:

# Output message if message exists and the output method is defined
puts message if message if defined? puts

Repeating an if modifier like this is hard to read, however, and it makes more sense to combine the two conditions into a single expression:

puts message if message and defined? puts

unless

unless, as a statement or a modifier, is the opposite of if: it executes code only if an associated expression evaluates to false or nil. Its syntax is just like if, except that elsif clauses are not allowed:

# single-way unless statement
unless condition
  code
end

# two-way unless statement
unless condition
  code
else
  code
end

# unless modifier
code unless condition

The unless statement, like the if statement, requires that the condition and the code are separated by a newline, a semicolon, or the then keyword. Also like if, unless statements are expressions and return the value of the code they execute, or nil if they execute nothing:

# Call the to_s method on object o, unless o is nil
s = unless o.nil?                        # newline separator
  o.to_s
end
s = unless o.nil? then o.to_s end        # then separator

For single-line conditionals like this, the modifier form of unless is usually clearer:

s = o.to_s unless o.nil?

Ruby has no equivalent of the elsif clause for an unless conditional. You can still write a multiway unless statement, however, if you’re willing to be a little more verbose:

unless x == 0
  puts "x is not 0"
else
  unless y == 0
    puts "y is not 0"
  else
    unless z == 0
      puts "z is not 0"
    else
      puts "all are 0"
    end
  end
end

case

The case statement is a multiway conditional. There are two forms of this statement. The simple (and infrequently used) form is nothing more than an alternative syntax for if/elsif/else. These two side-by-side expressions are equivalent:

name = case                           name = if    x == 1 then "one"
       when x == 1 then "one"                elsif x == 2 then "two"
       when x == 2 then "two"                elsif x == 3 then "three"
       when x == 3 then "three"              elsif x == 4 then "four"
       when x == 4 then "four"               else "many"
       else "many"                           end
       end

As you can see from this code, the case statement returns a value, just as the if statement does. As with the if statement, the then keyword following the when clauses can be replaced with a newline or semicolon:[*]

case 
when x == 1
  "one"
when x == 2 
  "two"
when x == 3
  "three"
end

The case statement tests each of its when expressions in the order they are written until it finds one that evaluates to true. If it finds one, it evaluates the statements that come between that when and the following when, else, or end. The last expression evaluated becomes the return value of the case statement. Once a when clause that evaluates to true has been found, no other when clauses are considered.

The else clause of a case statement is optional, but if it appears, it must come at the end of the statement, after all when clauses. If no when clause is true, and there is an else clause, then the code between else and end is executed. The value of the last expression evaluated in this code becomes the value of the case statement. If no when clause is true and there is no else clause, then no code is executed and the value of the case statement is nil.

A when clause within a case statement may have more than one (comma-separated) expression associated with it. If any one of these expressions evaluates to true, then the code associated with that when is executed. In this simple form of the case statement, the commas aren’t particularly useful and act just like the || operator:

case
when x == 1, y == 0 then  "x is one or y is zero"  # Obscure syntax
when x == 2 || y == 1 then "x is two or y is one"  # Easier to understand
end

All the case examples we’ve seen so far demonstrate the simpler, less common form of the statement. case is really more powerful than this. Notice that in most of the examples, the left side of each when clause expression is the same. In the common form of case, we factor this repeated lefthand expression of the when clause and associate it with the case itself:

name = case x
       when 1             # Just the value to compare to x
         "one"
       when 2 then "two"  # Then keyword instead of newline
       when 3; "three"    # Semicolon instead of newline
       else "many"        # Optional else clause at end
       end

In this form of the case statement, the expression associated with the case is evaluated once, and then it’s compared to the values obtained by evaluating the when expression. The comparisons are performed in the order in which the when clauses are written, and the code associated with the first matching when is executed. If no match is found, the code associated with the else clause (if there is one) is executed. The return value of this form of the case statement is the same as the return value of the simpler form: the value of the last expression evaluated, or nil if no when or else matches.

The important thing to understand about the case statement is how the values of the when clauses are compared to the expression that follows the case keyword. This comparison is done using the === operator. This operator is invoked on the value of the when expression and is passed the value of the case expression. Therefore, the case statement above is equivalent to the following (except that x is only evaluated once in the code above):

name = case
       when 1 === x then "one"
       when 2 === x then "two"
       when 3 === x then "three"
       else "many"
       end

=== is the case equality operator. For many classes, such as the Fixnum class used earlier, the === operator behaves just the same as ==. But certain classes define this operator in interesting ways. The Class class defines === so that it tests whether the righthand operand is an instance of the class named by the lefthand operand. Range defines this operator to test whether the value on the right falls within the range on the left. Regexp defines it so that it tests whether the text on the right matches the pattern on the left. In Ruby 1.9,Symbol defines === so that it tests for symbol or string equality. With these definitions of case equality, we are able to write interesting case statements like the following:

# Take different actions depending on the class of x
puts case x
     when String then "string"
     when Numeric then "number"
     when TrueClass, FalseClass then "boolean"
     else "other"
     end

# Compute 2006 U.S. income tax using case and Range objects
tax = case income
      when 0..7550
        income * 0.1
      when 7550..30650
        755 + (income-7550)*0.15
      when 30650..74200
        4220 + (income-30655)*0.25
      when 74200..154800
        15107.5 + (income-74201)*0.28
      when 154800..336550
        37675.5 + (income-154800)*0.33
      else
        97653 + (income-336550)*0.35
      end

# Get user's input and process it, ignoring comments and exiting
# when the user enters the word "quit"
while line=gets.chomp do  # Loop, asking the user for input each time
  case line
  when /^s*#/            # If input looks like a comment...
      next                #   skip to the next line.
  when /^quit$/i          # If input is "quit" (case insensitive)...
    break                 #   exit the loop.
  else                    # Otherwise...
    puts line.reverse     #   reverse the user's input and print it.
  end
end

A when clause can have more than one expression associated with it. Multiple expressions are separated by commas, and the === operator is invoked on each one. That is, it is possible to trigger the same block of code with more than one value:

def hasValue?(x)         # Define a method named hasValue?
  case x                 # Multiway conditional based on value of x
  when nil, [], "", 0    # if nil===x || []===x || ""===x || 0===x then
    false                #   method return value is false
  else                   # Otherwise
    true                 #   method return value is true
  end
end

The ?: Operator

The conditional operator ?:, described earlier in Conditional: ?:, behaves much like an if statement, with ? replacing then and : replacing else. It provides a succinct way to express conditionals:

def how_many_messages(n) # Handle singular/plural 
  "You have " + n.to_s + (n==1 ? " message." : " messages.")
end


[5] Ruby 1.8 also allows a colon, but this syntax is no longer legal in 1.9.

[*] Ruby 1.8 also allows a colon in place of then, as it does for the if statement. But this syntax is no longer allowed in Ruby 1.9.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.118.142.166