Module 01 Lab
-
Open a terminal window. Start up the interactive Ruby interpreter by typing
irb
. -
Do some arithmetic, like
4+37
or8*23
. Ruby is a very nice calculator. Note that you can use the arrow keys to move around and see expressions that you typed before. The variable_
(that’s a single underscore) always holds the value of the last expression evaluated. You can also assign the results of expressions to variables using the assignment operator = (as in Java). -
Besides printing the result of an expression when you type it, you can print several expressions using the print method (which does not print newlines after the expressions it prints) and the puts method (which does). Type
print 4, 5, 2**100
and thenputs 4, 5, 2**100
to see the differences. (What is 2**100?) Notice we are able to compute enormous numbers. Ruby has plain and big integer types, and big integers can be arbitrarily large. It switches between them automatically as it needs to. -
Ruby has large libraries of operations. For example, there are many operations in the Math module. Type
puts Math.sin(3)
,Math.log2(2**100)
(Now do you know what 2**100 is?) -
All values in Ruby are instances of classes, that is, objects. This is what is meant by saying that Ruby is a pure object-oriented language. Numeric classes have many methods built into them besides the usual operations (addition, subtraction, etc.). For example, test whether some integer is even by using the
even?
method (for example, try5.even?
). Now try that method on a floating point number. Find the absolute value (abs
) of a floating point number. Find the greatest common divisor (gcd
) of 15742 and 646 (make one of the numbers the host of the operation and the other the argument to thegcd
method). Many of the operations found in a math library in other languages are built in to the numeric classes in Ruby. -
Ruby has powerful string processing capabilities. Assign a long string to a variable, such as
s = 'Two roads diverged in a yellow wood'
. To get an idea of some of the things Ruby can do with strings, type the following and figure out what is going on:s[0] s[0..8] s[4,5] s[-1] s[-11..-1] s[0..-1] s[1000] s.reverse s*2 s.include?('in') s.next s.split s.tr(' ','+')
-
We could do more with
irb
, including typing in whole programs, but that is inconvenient, so lets switch to another environment. Typequit
to exit, and change directories (or create a directory) to a place you can put some files. -
Open some text editor and create a new file called
lab1.rb
. I recommendvi
because it is universally available and works for any text file, and is very fast to use once you learn it. You might also trynano
. There is an Eclipse plugin for Ruby as well if you want to install that. -
Type
puts "Hello world"
in a line of this file and save it. This is a complete Ruby hello program. -
Open another window in the same directory and type
ruby lab1.rb
. Note thatirb
is the interactive version of the interpreter, andruby
is the version of the interpreter that executes scripts (programs). All Ruby scripts have the extension.rb
. -
Ruby comments start with
#
and extend to the end of the line. Make a comment at the start of your script and put your name in it. -
Ruby has most of the usual control structures, including if-then, if then-else, if-then- elsif, and case (switch) statements, and while, until, and for loops, and break, continue, and return statements, but they often work a little differently from what you may be used to in Java.
-
To start with, conditional expressions do not need to be enclosed in parentheses. Also, the statements in the body of a control structure are not enclosed in curly brackets, and statements can be (and should be) separated by newlines rather than semicolons. Type the following in the editor window after the hello line (notice that we usually indent 2 spaces in Ruby):
x = -5 if x < 0 puts "negative" else puts "positive" end
Save the file and switch focus to the execution window. Run the script (hint: use the arrow keys to navigate to what you typed before).
-
Add code to the script to count down from 10 to 1 and print these numbers using a while loop. You should be able to figure out how to do this.
-
In Java you can write a short conditional on a single line if you like, with the statements separated by semicolons, but in Ruby, newlines are conventionally used to separate statements, so this is considered ugly. For example, you could write
if x < 0; puts "negative"; end
or
if x < 0 then puts "negative" end
on one line, but this is frowned on. However, Ruby allows some control structures to be used as statement modifiers, so we can just write
puts "negative" if x < 0
on a single line instead (notice the lack of semi-colons and other keywords). You can also use
unless
(which means if-not),while
, anduntil
as statement modifiers. This is encouraged because it shortens code. -
In your loop that counts down from 10 to 0, add a conditional modifier (on a single line) so that the number is printed only if it is even (remember the even? method?).
-
You probably used a statement like
k = k - 1
in your while loop to decrement the loop control variable. In Java, you would probably have writtenk--
, but Ruby does not have increment and decrement operators. However, Ruby (like Java) does have a host of compound assignment operators, so that decrement operation can be writtenk -= 1
. It is conventional in Ruby to use these operators, so change your loop to use one. -
As mentioned, Ruby has a case control structure. It is treated like a series of conditionals, so that the cases are tested in order and the first one that is true is executed. To illustrate, add the following to your script:
r = rand(100) case when r.even? print r, " is even\n" when r < 10 print r, " is small\n" else print r, " is big and odd\n" end
The
rand(n)
method generates a random integer between 0 and n-1. There are other forms of the case statement, but this illustrates its basic use. Save your script and run it again. -
By the way, if Ruby is a pure object-oriented language, then every operation should be a method. If so, then how can we have operations like print and rand that don’t seem to be attached to any object (in Ruby-speak, they don’t seem to have a receiver)? The explanation is that there is a built-in variable called self (the same as this in Java) that always has the current object as its value. When the interpreter starts up, this value is set to main, an instance of the
Object
class. Typeputs self, self.class
and run your script to verify this. -
When a method is called without an explicit receiver,
self
is used as the receiver. So whenprint
andrand
are called in our script, the receiver is self, and this works becauseprint
andrand
are methods of theObject
class (they are actually in theKernel
module, which is included in theObject
class). -
Fire up a browser and go to ruby-doc.org. This is the best online source for documentation about Ruby. Search for the
Kernel
module. Findrand
and read its description. -
Lets switch back to
irb
for a while; you can just open another window and start it up. -
Ruby has a built-in
Array
class. Arrays are indexed the same way that strings are, so we know how to do that already. We can make arrays with array literals, which list the elements in an array between square brackets. Make the array['cat', 'on', 'mat']
and assign it to the variablea
. -
Lets replace some elements. Ruby allows the same syntax for replaced elements as it does for result elements; in other words,
a[1,2]
(two elements starting at 1) can be a value or it can designate elements to be replaced. Type the following command:a[1,2] = 'is', 'on', 'the', 'mat'
and then examine the contents ofa
. -
You can insert between by indicating the index of the insertion point and that 0 elements are to be replaced. For example,
a[0,0] = 'the'
inserts'the'
at the start of the list. Try this. -
There are other ways to make arrays: for example, you can also make arrays using the classname and the
new
operator.Array.new
creates a new empty array (whose array literal is[]
by the way);Array.new(4)
creates an array with fournil
values (nil
is like a null pointer in Java);Array.new(4, true)
creates an array of four boolean values. -
Ruby has lots of operations for manipulating arrays. For example, type the following into
irb
and see what you get:a = Array.new(3, "hello") a = a + [1, 2, 3] a << true a.join(' ') a.shuffle
-
Unlike most other languages, Ruby allows different typed values in arrays, as indicated by the last example.
-
An
Array
is anEnumerable
, which means thatArray
implements the operations of theEnumerable
module. These operations are mostly iterators that invoke a block of code repeatedly. For example, ifa
is an array thena.each
invokes a block of code once for every element of arraya
. Blocks are specified between curly braces or the keywordsdo
andend
. Conventionally, blocks that fit on one line use curly braces and those that don’t usedo
andend
. Type the following intoirb
and see what happens:a.each { puts 'hi ' }
-
You should have seen “hi” printed seven times, which is how many elements there are in array a. This is not very interesting, but we can pass parameters to blocks. Parameters are listed between
|
characters at the start of a block. So now try this:a.each { | w | puts "hi #{w} " }
(Notice the string interpolation expression here:
#{w}
in the string is replaced with the value ofw
.) -
Note how much easier this is than making a loop on the index of the array. We can still get the index if we want it. Use the iterator
each_with_index
to print the words and their indices in arraya
(the index will be passed as the second block parameter). -
Iterators are extremely important in Ruby. They are the usual way to make loops. Furthermore, you can write your own iterators: any method can be passed a block of code, and the special
yield
keyword passes control, along with any parameters, to the block. -
Now lets consider another kind of collection in Ruby. A
Hash
is a map or dictionary that associates (maps) a key to a value. Each key is mapped to only a single value, but several keys can be mapped to the same value. The keys and the values can be any types. -
Type the following:
h = {} h[2] = "two" h[true] = "true" h h[2] h["true"]
The
{}
is the literal for an emptyHash
. The two assignments map the keys 2 and true to string values. Note that a value can be retrieved by its key, but a key cannot be retrieved by its value. Even thoughHash
syntax is similar toArray
syntax,Array
indices must be integers whileHash
keys can be anything, andArray
elements are in order whileHash
elements are not. -
Lets switch back to the file
lab1.rb
again. -
Methods in Ruby are created using the
def
/end
keywords. Parameters are indicated between parentheses, and return values are indicated with the keywordreturn
. For example, the following method returns true if an object has size of five or more.def has5?(a) return 5 <= a.size end
Notice that because Ruby is dynamically typed, this will work on anything with a
size
method. -
In your lab file write a method
genArray(n)
to create and return an array with the numbers 1 to n in it. Add the lineprint genArray(10), "\n"
after your method definition. Run your program. -
Ruby methods can have default parameters, which means that if you leave out the parameter, it will still have a value in the method. These are specified like assignment statements, for example
def f(x=5) ...
makes 5 the default value of parameterx
. Make the default value of n 10 ingenArray
and add the lineprint genArray(), "\n"
to your file. Run the program. -
Ruby is quite cavalier about arguments: you don’t need to enclose them in parentheses (but not doing so makes your code harder to read). This also means that if a method has no arguments, then the parentheses can be omitted. This makes your code easier to read. Add the line
print genArray, "\n"
to your file. Run the program. -
One other kind of collection that will be useful in P1 is the
Range
. Try creating ranges inirb
using the following examples as starting points:(1..5) (0..5) (1..100) (-5..5)
-
You can convert a range to an array using the
to_a
method. Try it inirb
. -
Re-write the
genArray
method to use a range. You should now be able to implement this method using a single line of code. -
In Ruby, you don’t need to explicitly
return
a value; the last expression evaluated in the method will be its return value if there is no explicitreturn
. For instance, we could have omitted thereturn
keyword in our definition of thehas5?
method:def has5?(a) 5 <= a.size end
Remove the
return
keyword from yourgenArray
definition and verify that the behavior is unchanged. -
You can easily filter an array or range using the
select
method. Like theeach
method, theselect
method takes a block that is run for each element of the array or range, and it returns an array made up of all of the elements for which the block evaluated totrue
. Try the following examples to see what they do:(1..20).select { |x| x.odd? } (1..20).select { |x| x.even? } (1..20).select { |x| x % 5 == 0 }
-
Modify your
genArray
method one last time so that it only generates numbers that are divisible by ten. It should still be implementable using a single line of code.
Congratulations! You have learned enough of Ruby to write simple programs. You should now begin working on the project for this module.
This lab was originally written by Dr. Chris Fox.