Ruby is a structured language, executes blocks of statements, which are delimited by the words "begin .. end" or "then .. end". After a conditional instruction the "then" statement can be omitted, but not if the statements are on the same line as the condition.
A block returns a value, which is the result of last executed expression.
The if , then, else statement:
if a==0 then b=77 end # if on a single line, "then" is mandatory if boolean-expression then statements elsif boolean-expression then statements else statements end
The if statement has a return value: the result of the last executed statement, or nil:
name = if x == 1 then "one" elsif x == 2 then "two" elsif x == 3 then "three" elsif x == 4 then "four" else "many" end
unless, same as: "if not .."
unless a==0 then b=77 end unless boolean-expression then statements else statements end
The case statement, for multiple conditions; the general form is:
case target-expr when comparison , comparison ... then statements when comparison , comparison ... then statements else # optional else statements end
The case statement is equivalent to the "if elsif .. end" statement:
Case statement Equivalent if-else statement 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
A more concise syntax; where the x value to test is not repeated at each condition:
name = case x when 1 # new line as a separator "one" when 2 then "two" # then used as a separator when 3; "three" # semicolon as a separator else "many" end
In the case statement, the "===" equality is often used. Also ranges are often used in case statements:
case 74.95 when 1...50 then puts "low" when 50...75 then puts "medium" when 75...100 then puts "high" end
The blocks executed many times are between the "do .. end" statements, or curly brackets. The "do" keyword can be omitted. The "do" (or bracket) beginning the loop block must be on the same line as the "loop" keyword.
The infinite loop:
loop do statements end loop { a+=1 ; print(a) ; break if a>3 } # loop on a single line
The while loop:
while boolean-expression do statements end x = 0 while x < 10 do puts x = x + 1 end # loop on a single line
The until loop (same as: while not):
until boolean-expression do statements end
while and until loops executed at least once, with the condition at the end of the block:
x = 10 begin # Starts the conditional block, executed at least once puts x x = x - 1 end until x == 0 # condition evaluated at the end a=10 begin a+=1 end while a<3
The for loop: this loop returns, at each iteration, an element of a sequence; the sequence is often an array or an hash. The "do" keyword is optional if the subsequent statements are on the following lines:
for name, name in sequence do statements end a=[1,2,3,4] for i in a print i end for i in a ; print i ;end for i in a do print i ;end for i in (1..3) do print i ; end # range usage in the loop hash = {:a=>1, :b=>2, :c=>3} for key,value in hash puts "#{key} => #{value}" end
Statements altering the loop flow:
- break : terminates loop immediately; can have an optional argument that is is the loop result
- next : goes to the next iteration.
- redo : repeats the iteration, without testing again the condition.
- retry : repeats the iteration, testing again the condition.
Example:
for i in a break if i==3 next if i==2 print i redo if i==1 # this is an infinite loop end
catch, throw :
These statements alter the loop behavior; are similar to the trowing of an exception, and can be used to exit from an inner block in a deeply nested structure; it's an involved substitute for a simpler "goto" statement; but the "goto" statement has not been included in Ruby: it is strictly forbidden by the structured programming dogmas, so we need this construct, which enables us to write true "spaghetti code" in structured programming.
The catch statement defines a block, which is interrupted by a throw statement. The catch block has a label, and the throw send to the end of the block with the label:
for matrix in data do # A deeply nested data structure. catch :label do # begin of the catch block # the block has the label: ":label" for row in matrix do for value in row do throw :label unless value # Break out of two loops at once, statements # otherwise, executes these statements. end end end # end of the catch block end
The raise and rescue statements are used for exception handling; the "raise" statement throws an exception, which is an instance of the "Exception" class. There are many pre-defined exception classes, the default being "RuntimeError".
The "rescue" statements define blocks which are executed if an exception of a specified class is raised. The last raised exception is saved in the global variable: "$!" .
The general form of the "begin ... rescue .. end" block is:
begin ... raise exception_name rescue Exception_class,Exception_class => local_name_for_exception .... (block executed if given the exceptions have been raised ) rescue Exception_class,Exception_class => local_name_for_exception .... (block executed if given the exceptions have been raised ) else ... block executed if no exception in main block ensure .... block executed in any case end
The rescue statement can give to the exception a local name, to be used in the rescue block itself.
The rescue block can have a "retry" statement which re-executes the block after the "begin" statement.
The optional block after the last rescue statement, defined by an "else" statement, is executed if there are no exceptions. If, in the main block, a return, next or break statement is encountered, the "else" block is skipped.
The final, optional, "ensure" block is executed in every case.
If a new exception is raised in a rescue block the old exception is discarded and replaced by the new exception.
Below some examples of the raise statement:
raise "message error" if n < 1 raise RuntimeError.new("message error") if n < 1 raise RuntimeError.exception("message error") if n < 1
An example of a simple "begin ... rescue .. end" block:
begin raise "negative value" if x<0 # raise the exceptions y=Math::sqrt(x) rescue => ex # Stores the exception in the local variable ex puts "#{ex.class}: #{ex.message}" # Handle exception end
In the following example more rescue blocks catch different exceptions; a single rescue block can catch more types of exceptions:
begin y=Math::sqrt(x) rescue DomainError => ex puts "Try again with a value >= 1" rescue TypeError,ArgumentError => ex puts "Try again with an integer" end
The rescue statement can be also used to give an alternate value to a variable:
y = Math::sqrt(x) rescue 0
In "postfix expressions" a condition is after the statements to be executed; postfix expressions are typical of Ruby, which tries to mimic a natural language with its syntax. The general form of a "postfix expressions" is:
expr if bool-expr # the same as: "if expr then ... end" ) expr unless bool-expr # the same as "unless bool-expr then ... end") Examples:: exit if not str or not str[0] a=1 if a.nil?
There are postfix expressions also for the "while" and "until" loops, with condition and loop statements to be executed on the same line. In these cases the condition is tested before the first iteration:
x = 0 puts x = x + 1 while x < 10 # The while condition is at the end a = [1,2,3] puts a.pop until a.empty? # The until condition is at the end