Ruby can be used as a fully object oriented language, in which case you’d create classes and objects to accomplish everything. However, it can be used quite nicely with only the objects and classes that ship with Ruby, in which case it can be used as a procedural language, except that functions are typically methods of the program’s variables.
If that doesn’t make any sense to you, don’t worry, it’s just a way of saying that Ruby can be very easy to learn and use.
Even if you want to become a Ruby expert, you need to learn the basic functionality before you can become a Ruby OOP ninja. This tutorial gives you those basics.
Hello World
This is the simplest possible Ruby program, hello.rb. As you’d expect, it prints “Hello World” on the screen. Be sure to set it executable.
#!/usr/bin/ruby
print “Hello World\n”
Although this program works as expected, it goes against the philosophy of Ruby because it’s not object oriented. But as a proof of concept that Ruby’s working on your computer, it’s just fine.
Besides print, there’s also a puts keyword. The difference is that puts automatically inserts a newline at the end of the string being printed, whereas print does not. In other words, puts is more convenient, but print is necessary if separate statements print to the same line. Througout this tutorial we’ll use both print and puts.
Loops
Let’s count to 10…
#!/usr/bin/ruby
for ss in 1…10
print ss, ” Hello\n”;
end
The elipses (…) indicate the range through which to loop. The for is terminated by an end. You don’t need braces for a loop. Whew!
The following is the output:
[slitt@mydesk slitt]$ ./loop.rb
1 hello
2 hello
3 hello
4 hello
5 hello
6 hello
7 hello
8 hello
9 hello
[slitt@mydesk slitt]$
Notice that it stops on 9. The number following the elipses causes termination at the top of the loop. The 1…10 means 1 TO BUT NOT INCLUDING 10, it does NOT mean 1 through 10. Please remember this when using Ruby loops.
NOTE
There are actually two versions of the elipses operator, the three period version as shown previously, and the two period version. The two period version is inclusive. In other words, 1…3 means 1 up to but not including 3, while 1..3 means one through 3.
By using the appropriate version of the elipses operator you can save having to code convoluted end conditions.
Now let’s iterate through an array.
#!/usr/bin/ruby
presidents = ["Ford", "Carter", "Reagan", "Bush1", "Clinton", "Bush2"]
for ss in 0…presidents.length
print ss, “: “, presidents[ss], “\n”;
end
We defined an array of presidents using a Perl like syntax (except we used brackets instead of parens), and we iterated from 0 (Ruby is 0 based, like most languages), through the final subscript in the presidents array. Remember, the triple dot stops before executing the final number, which is why it doesn’t count to 6. If you had wanted it to count to 6 (which in this case would have walked off the end of the array), you would have used the double dot. The output of the preceding code follows:
[slitt@mydesk slitt]$ ./loop.rb
0: Ford
1: Carter
2: Reagan
3: Bush1
4: Clinton
5: Bush2
[slitt@mydesk slitt]$
Now lets iterate backwards through the array, using the fact that array[-1] is the last item, array[-2] is the second to last, etc:
#!/usr/bin/ruby
presidents = ["Ford", "Carter", "Reagan", "Bush1", "Clinton", "Bush2"]
for ss in 0…presidents.length
print ss, “: “, presidents[presidents.length - ss - 1], “\n”;
end
The preceding program produces the following output:
[slitt@mydesk slitt]$ ./hello.rb
0: Bush2
1: Clinton
2: Bush1
3: Reagan
4: Carter
5: Ford
[slitt@mydesk slitt]$
Of course, the preceding was a very contrived example just to demonstrate negative subscripts. Note that the subscripts no longer match the presidents, which is probably not what you want. You probably want to do it like you’d do it in any language — tweak the subscript to the end value and decrease it:
#!/usr/bin/ruby
presidents = ["Ford", "Carter", "Reagan", "Bush1", "Clinton", "Bush2"]
for ss in 0…presidents.length
ss_tweaked = presidents.length - ss - 1
print ss_tweaked, “: “, presidents[ss_tweaked], “\n”
end
If you’re familiar with C, Pascal or Perl, you’re probably dissappointed you couldn’t just use presidents.length…0. Backwards iteration doesn’t work in Ruby — it must iterate up.
Iterators and Blocks
Another way to loop through an array is to use an iterator (in red in the following code) and a block (in blue in the following code:
#!/usr/bin/ruby
presidents = ["Ford", "Carter", "Reagan", "Bush1", "Clinton", "Bush2"]
presidents.each {|prez| puts prez}
In the preceding code, the block argument (prez) contains the current array element, and everything else until the closing brace contains code to operate on the block argument. The block argument is always enclosed in vertical lines (pipe symbols). The following is the output of the preceding code:
[slitt@mydesk slitt]$ ./hello.rb
Ford
Carter
Reagan
Bush1
Clinton
Bush2
[slitt@mydesk slitt]$
The block needn’t be on one line:
#!/usr/bin/ruby
presidents = ["Ford", "Carter", "Reagan", "Bush1", "Clinton", "Bush2"]
presidents.each {
|prez|
puts prez
}
As shown in the previous examples, you can define the block by enclosing it in curly braces. You can also define it by enclosing it in a do and an end, where the do replaces the opening brace, and the end replaces the closing brace:
#!/usr/bin/ruby
presidents = ["Ford", "Carter", "Reagan", "Bush1", "Clinton", "Bush2"]
presidents.each do
|prez|
puts prez
end
Personally, I greatly prefer the do/end syntax for multiline blocks, because as a Perl/C/C++ guy I have a very different perception of braces than their limited use in Ruby, and also because of all the brace placement religious wars I’ve endured (I’m a Whitesmith type guy myself). However, on short single line blocks, using the braces saves valuable line space. From what I understand, the methods are interchangeable in features and performance, with one small exception…
Speaking of performance, if you declare the block argument outside the block (in other words, make it a local variable), performance improves because Ruby needn’t recreate a variable every iteration. HOWEVER, the loop messes with the value of the variable, so it’s best to use a specific variable for that purpose, and do not use it for other purposes within the subroutine. Here’s an example of using a local variable as a block argument:
#!/usr/bin/ruby
i = -99
puts “Before: ” + i.to_s
(1..10).each{|i| puts i}
puts “After : ” + i.to_s
[slitt@mydesk slitt]$ ./loop.rb
Before: -99
1
2
3
4
5
6
7
8
9
10
After : 10
[slitt@mydesk slitt]$
If you use a local variable for a block argument, do so only in loops with huge numbers of iterations, and use only variables that are specifically intended to serve as block arguuments and nothing else.
A Difference Between {} and do/end
As mentioned, there’s one small difference between brace enclosed blocks and do/end enclosed blocks: Braces bind tighter. Watch this:
#!/usr/bin/ruby
my_array = ["alpha", "beta", "gamma"]
puts my_array.collect {
|word|
word.capitalize
}
puts “======================”
puts my_array.collect do
|word|
word.capitalize
end
[slitt@mydesk slitt]$ ./test.rb
Alpha
Beta
Gamma
======================
alpha
beta
gamma
[slitt@mydesk slitt]$
The braces bound tightly like this:
puts (my_array.collect {|word| word.capitalize})
Whereas do/end bind more loosely, like this:
puts (my_array.collect) do |word| word.capitalize} end
Note that the latter represents a syntax error anyway, and I’ve found no way to coerce do/end into doing the right thing simply by using parentheses. However, by assigning the iterator’s results to a new array, that array can be used. It’s one more variable and one more line of code. If the code is short, use braces. If it’s long, the added overhead is so small a percentage that it’s no big deal:
#!/usr/bin/ruby
my_array = ["alpha", "beta", "gamma"]
puts my_array.collect {
|word|
word.capitalize
}
puts “======================”
new_array = my_array.collect do
|word|
word.capitalize
end
puts new_array
[slitt@mydesk slitt]$ ./test.rb
Alpha
Beta
Gamma
======================
Alpha
Beta
Gamma
[slitt@mydesk slitt]$
Generally speaking, if you want to directly use the result of iterators, use braces. For longer blocks, do/end is more readable, and the overhead for the extra variable and line of code is trivial.
while Loops
All the loops previously discussed looped through either an array or a set of numbers. Sometimes you need a more generic loop. That’s when you use a while loop:
#!/usr/bin/ruby
ss = 4
while ss > 0
puts ss
ss -= 1
end
puts “======================”
while ss < 5
puts ss
ss += 1
break if ss > 2
end
puts “======================”
ss = 5
while ss > 0
puts ss
ss -= 2
if ss == 1
ss += 5
end
end
[slitt@mydesk slitt]$ ./loop.rb
4
3
2
1
======================
0
1
2
======================
5
3
6
4
2
[slitt@mydesk slitt]$
The first while loop iterated from 4 down to 1, quitting when ss became 0 and hit the while condition. The second loop was intended to iterate up to 4 and quit when 5 was encountered, but a break statement inside the loop caused it to terminate after printing 2 and then incrementing to 3. This demonstrates the break statement.
The third loop was intended to loop from 5 down to 1, quitting after printing 1 and then decrementing. However, the statement in the body of the loop added 5 when it reached 1, pushing it back up to 6, so it had to count down again. On the second countdown, the numbers were even, so it didn’t trigger the if statement. This shows that unlike Pascal, it’s OK to tamper with the loop variable inside the loop.
Read the rest of this entry »