Friday, January 21, 2011

Ruby Scripting


We'll deal with variables in much more depth when we talk about classes and objects. For now, let's just say your basic local variable names should start with either a lower case letter or an underscore, and should contain upper or lower case letters, numbers, and underscore characters. Global variables start with a $.

Program flow:-

Ruby includes a pretty standard set of looping and branching constructs: if, while and case
For example, here's if in action:
a = 10 * rand

if a < 5 puts "#{a} less than 5" elsif a > 7
puts "#{a} greater than 7"
puts "Cheese sandwich!"
[As in other languages, the rand function generates a random number between 0 and 1]
There will be plenty more time to discuss conditional statements in later chapters. The above example should be pretty clear.
Ruby also includes a negated form of if called unless which goes something like
unless a > 5
puts "a is less than or equal to 5"
puts "a is greater than 5"
Generally speaking, Ruby keeps an if statement straight as long as the conditional (if ...) and the associated code block are on separate lines. If you have to smash everything together on one line, you'll need to place the then keyword after the conditional
if a < 5 then puts "#{a} less than 5" end if a < 5 then puts "#{a} less than 5" else puts "#{a} greater than 5" end Note that the if statement is also an expression; its value is the last line of the block executed. Therefore, the line above could also have been written as puts(if a < 5 then "#{a} less than 5" else "#{a} greater than 5" end) Ruby has also adopted the syntax from Perl where if and unless statements can be used as conditional modifiers after a statement. For example puts "#{a} less than 5" if a < 5 puts "Cheese sandwich" unless a == 4 while behaves as it does in other languages -- the code block that follows is run zero or more times, as long as the conditional is true while a > 5
a = 10*rand
And like if, there is also a negated version of while called until which runs the code block until the condition is true.
Finally there is the case statement which we'll just include here with a brief example. case is actually a very powerful super version of the if ... elsif... system
a = (10*rand).round
#a = rand(11) would do the same

case a
when 0..5
puts "#{a}: Low"
when 6
puts "#{a}: Six"
puts "#{a}: Cheese toast!"
There are some other interesting things going on in this example, but here the case statement is the center of attention.


In keeping with Ruby's all-object-oriented-all-the-time design, functions are typically referred to as methods. No difference. We'll cover methods in much more detail when we get to objects and classes. For now, basic method writing looks something like this:
# Demonstrate a method with func1.rb
def my_function( a )
puts "Hello, #{a}"
return a.length

len = my_function( "Giraffe" )
puts "My secret word is #{len} long"
$ func1.rb
Hello, Giraffe
My secret word is 7 long
Methods are defined with the def keyword, followed by the function name. As with variables, local and class methods should start with a lower case letter.
In this example, the function takes one argument (a) and returns a value. Note that the input arguments aren't typed (i.e. a need not be a string) ... this allows for great flexibility but can also cause a lot of trouble. The function also returns a single value with the return keyword. Technically this isn't necessary -- the value of the last line executed in the function is used as the return value -- but more often than not using return explicitly makes things clearer.
As with other languages, Ruby supports both default values for arguments and variable-length argument lists, both of which will be covered in due time. There's also support for code blocks, as discussed below.


One very important concept in Ruby is the code block. It's actually not a particularly revolutionary concept -- any time you've written if ... { ... } in C or Perl you've defined a code block, but in Ruby a code block has some hidden secret powers...
Code blocks in Ruby are defined either with the keywords do..end or the curly brackets {..}
print "I like "
print "code blocks!"

print "Me too!"
One very powerful usage of code blocks is that methods can take one as a parameter and execute it along the way.
[ed note: the Pragmatic Programmers actually want to point out that it's not very useful to describe it this way. Instead, the block of code behaves like a 'partner' to which the function occasionally hands over control]
The concept can be hard to get the first time it's explained to you. Here's an example:
$ irb --simple-prompt
>> 3.times { puts "Hi!" }
=> 3
Surprise! You always thought 3 was just a number, but it's actually an object (of type Fixnum) As its an object, it has a member function times which takes a block as a parameter. The function runs the block 3 times.
Blocks can actually receive parameters, using a special notation |..|. In this case, a quick check of the documentation for times shows it will pass a single parameter into the block, indicating which loop it's on:
$ irb --simple-prompt
>> 4.times { |x| puts "Loop number #{x}" }
Loop number 0
Loop number 1
Loop number 2
Loop number 3
=> 4
The times function passes a number into the block. The block gets that number in the variable x (as set by the |x|), then prints out the result.
Functions interact with blocks through the yield. Every time the function invokes yield control passes to the block. It only comes back to the function when the block finishes. Here's a simple example:
# Script block2.rb

def simpleFunction

simpleFunction { puts "Hello!" }
$ block2.rb
The simpleFunction simply yields to the block twice -- so the block is run twice and we get two times the output. Here's an example where the function passes a parameter to the block:
# Script block1.rb

def animals
yield "Tiger"
yield "Giraffe"

animals { |x| puts "Hello, #{x}" }
$ block1.rb
Hello, Tiger
Hello, Giraffe
It might take a couple of reads through to figure out what's going on here. We've defined the function "animals" -- it expects a code block. When executed, the function calls the code block twice, first with the parameter "Tiger" then again with the parameter "Giraffe". In this example, we've written a simple code block which just prints out a greeting to the animals. We could write a different block, for example:
animals { |x| puts "It's #{x.length} characters long!" }
which would give:
It's 5 characters long!
It's 7 characters long!
Two completely different results from running the same function with two different blocks.
There are many powerful uses of blocks. One of the first you'll come across is the each function for arrays -- it runs a code block once for each element in the array -- it's great for iterating over lists.

No comments:

Post a Comment