Ruby supports the following conditionals:
if - perform a block if true
unless - perform a block if false
case - perform options when a condition is true.
All three also accept an else block, and if supports an elsif block. If and unless both work as code modifiers (i.e. the statement can be written the other way around) and in all three cases code execution continues beyond the statement once it has been completed.
Here's an example of the structure of each:
print "Please enter an integer "
value = gets.chomp.to_i
print "Testing the number #{value}\n"
# normal if, with elif and else
if value > 5
puts "Value is greater than 5"
elsif value > 2
puts "Greater that 2 but less than 5"
else
puts "Less than or equal to 2"
end
# an if that's a code modifier
puts "That's very big" if value > 10
# An unless and else
unless value > 5
puts "Value is not greater than 5"
else
puts "That's greater that 5"
end
# an unless that's a code modifier
puts "That's not very big" unless value > 10
# Case - any one condition (or 'else' if none
# of the conditions matches exactly)
case value
when 1,21,31
suffix = "st"
when 2,22
suffix = "nd"
when 3,23
suffix = "rd"
else
suffix = "th"
end
puts "It is the #{value}#{suffix} of the month"
Let's run some tests on that:
earth-wind-and-fire:~/ruby/r104 grahamellis$ ruby condi.rb
Please enter an integer 2
Testing the number 2
Less than or equal to 2
Value is not greater than 5
That's not very big
It is the 2nd of the month
earth-wind-and-fire:~/ruby/r104 grahamellis$ ruby condi.rb
Please enter an integer 14
Testing the number 14
Value is greater than 5
That's very big
That's greater that 5
It is the 14th of the month
earth-wind-and-fire:~/ruby/r104 grahamellis$ ruby condi.rb
Please enter an integer 7
Testing the number 7
Value is greater than 5
That's greater that 5
That's not very big
It is the 7th of the month
earth-wind-and-fire:~/ruby/r104 grahamellis$
You'll note that my case takes a series of possible options on each when clause, and that it does NOT have a break at the end of each of the conditions, which you would expect to find in most switch type statements - case has been very neatly implemented in Ruby!
LOOPS IN RUBY
Ruby provides you with the basic while and unless loops and iterators. Let's get an example of while and unless out of the way first before looking at the much more useful, high level and efficient operators.
A "while" loop defines that a statement or block of code is to be rerun time and time again while a condition remains true, and then execution is to continue after the statement or block. An "until" loop says that the block is to be performed until the condition goes true (i.e. while it is false).
Just like the "if" and "unless", "while" and "until" are written with an 'end' key-worded block, or as modified after a piece of code.
Sample code:
# Examples of Ruby loop structures
top = 4
# A while loop, counting up
now = 0
while now < top
puts "while #{now} .. #{top}"
now += 1
end
# While written in one line
now = 0
print "#{now+=1} " while now < top
print "\n"
# An until loop
now = 0
until now > top
puts "until #{now} .. #{top}"
now += 1
end
# Until written in one line
needed = 1
teams = 19
needed *= 2 until needed >= teams
print "You have #{teams} teams so ideally ",
"need #{needed}.\n"
When that code is run, here are the results:
earth-wind-and-fire:~/ruby/r104 grahamellis$ ruby loopy.rb
while 0 .. 4
while 1 .. 4
while 2 .. 4
while 3 .. 4
1 2 3 4
until 0 .. 4
until 1 .. 4
until 2 .. 4
until 3 .. 4
until 4 .. 4
You have 19 teams so ideally need 32.
earth-wind-and-fire:~/ruby/r104 grahamellis$
ITERATORS
The method "each" when run on what we call a collection object will cause a block of code to be run on each of the elements of the collection object in turn. Yes, that's rather different to many other languages (though it does parallel foreach in PHP and for in Python).
You can write a loop that looks reasonably conventional to step up through a series of numbers - example coming up - but in addition you can write far clearer and more elegant loops to process all the members of a collection in turn, without having to refer to them by their position number all the time.
# Some for loops that might almost look familiar
# From 1 to 20
for k in 1..20
print k," "
end
print "\n"
# From 1, stopping short of 20
for k in 1...20
print k," "
end
print "\n"
# An iterator object revealed!
(5..15).each do |k|
print k," "
end
print "\n"
Here's how it runs.
earth-wind-and-fire:~/ruby/r104 grahamellis$ ruby ddd.rb
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
5 6 7 8 9 10 11 12 13 14 15
earth-wind-and-fire:~/ruby/r104 grahamellis$
Note those .. and ... operators, and the subtle difference - in the first case, you're returned an iterator that runs up from the first to the last number, but in the second case the iterator stops one short of the last number. This means that is you have (say) a collection of 10 items numbered from 0 to 9 - which is quite common - you'll be able to write 0...10 or even 0...coll.length and not have to worry about a -1 correction.
COMPARATORS IN RUBY
There are multiple comparators in Ruby, and they're all adjusted dynamically to suit the objects on which they're being run. The three you'll need to remember will be:
== - regular equality test, true if objects have same value
eql? - method true if objects have same value AND TYPE
equal? - true if both object have the same ID (i.e. same obj)
A further test - === - is used by the case statement and can be independently overridden if you wish later on, and the <=> operator returns -1, 0 or +1 when comparing two objects, for less than, equal or greater than.
Here's a demonstration of the comparisons, showing some differences:
first = 10
second = 10.0
puts "Comparing #{first} with #{second}"
puts "two = signs" if first==second
puts "three = signs" if first===second
puts "eql? method" if first.eql?second
puts "equal? method" if first.equal?second
second=first
puts "Comparing #{first} with #{second}"
puts "two = signs" if first==second
puts "three = signs" if first===second
puts "eql? method" if first.eql?second
puts "equal? method" if first.equal?second
And here's how it runs:
earth-wind-and-fire:~/ruby/r104 grahamellis$ ruby ctors.rb
Comparing 10 with 10.0
two = signs
three = signs
Comparing 10 with 10
two = signs
three = signs
eql? method
equal? method
earth-wind-and-fire:~/ruby/r104 grahamellis$
Ruby also supports lazy operators - "and" means "and if true", and "or" means "and if false". The ? : operator from other languages is supported too.
stock = 0..5
for number in stock
num = number
# "and" is lazy. If the first part is false, then
# the whole expression will be false - so ruby doesn't
# bother to go on to the second test nor the assignment
# it includes.
num == 0 and num = "none"
# "? ... :" is an "if true x else y" type operator.
word = num == 1 ? "is" : "are"
print "There #{word} #{num} in stock\n"
end
We run that and get:
earth-wind-and-fire:~/ruby/r104 grahamellis$ ruby lazyops.rb
lazyops.rb:12: warning: found = in conditional, should be ==
There are none in stock
There is 1 in stock
There are 2 in stock
There are 3 in stock
There are 4 in stock
There are 5 in stock
earth-wind-and-fire:~/ruby/r104 grahamellis$
so perhaps the lazy and / or aren't such a good idea!
See also
Ruby course - UK, Ireland, Europe