|
 |
CS 3723
Programming Languages |
Ruby Tutorial
|
|
The main Ruby references:
In these notes and elsewhere my principal reference is
-
Programming Ruby: The Pragmatic Programmer's Guide (PPG),
by David Thomas, Addison-Wesley, 2001, 608 pages. This covers Ruby 1.7.
Available online at the link above. Even this is a dense
and complex reference which should be fine for this course.
A newer and much larger 3rd edition (2009, 864 pages, covers
Ruby 1.9, $31.30) is available for sale
at
Amazon.com There are now a large number of Ruby books out, and
this one is not designed for beginners.
A second possible reference is:
- Free Tutorials
- Tutorialspoint Site
A great deal of information is available through further links
on this page.
Illustrating the Ruby Way:
The way that can be named is not the
true way. -- Lao Tse, Tao Te Ching
(See The
Ruby Way by Hal Fulton)
Here is a Ruby program from the PPG that resembles its counterpart
in Perl. The program prints all lines in the input stream that
contain the word "Ruby". Notice that while and
if statements (along with most others) end with
end and do not make use of {} blocks, as
in Perl. This code uses the "hidden" global variable $_,
and for this (and other) reasons is increasingly out of favor
with Ruby programmers.
while gets # assigns each input line to $_ by default
if /Ruby/ # matches against $_ by default
print # prints $_ by default
end
end
Here is another version in Ruby about which PPG says:
"The 'Ruby way' to write this would be to use
an iterator." (Iterators, a more general construct than in Perl,
are discussed three sections down on this page.)
ARGF.each { |line| print line if line =~ /Ruby/ }
PPG says: "This used the predefined object ARGF,
which represents the input stream that can be read by a program."
One-liner Introduction:
% perl -e 'print 5+2; $x = "hi"; print $x,"\n";'
7hi
% ruby -e 'print 5+2; $x = "hi"; print $x,"\n";'
7hi
% ruby -e 'print 5.+(2); x=%q|hi|; print "#{x}\n"'
7hi
%
Here the -e option on either Perl or
Ruby causes the quoted string following to be treated as
one line of program source to be interpreted.
The first two inputs show that this is a program that
both Perl and Ruby execute, both producing output 7hi.
The third line is more "Ruby-like" and includes the strange
features:
- The expression
5.+(2), is
using the method +, with parameter
2 on the integer object
5,
using .
as the means to select the method from the object.
This form is called the dot syntax and is the usual way
to invoke methods. For operators, Ruby permits the normal-looking
way also.
In Perl, the expression
5.+(2) is legal
and just means to add floating point 5 and integer 2.
For this reason, Ruby requires a digit after the decimal
point of any floating point constant, since otherwise
5.+(2) would be ambiguous in Ruby.
- The form %q is a short-hand way to insert
quotes. (It takes the place of Perl's
qq.)
- String interpolation is done in Ruby with
#{}, rather than Perl's
${} or @{}.
- Semicolons are almost never used at the end of a statement
in Ruby, though they are optional, just as in Perl.
Prefixes and Scope:
- Prefixes: In Ruby these are used to indicate scope and
not data type as in Perl. In Ruby, any variable name
can hold a reference to any type of data. Here are some
of Ruby's rules:
- A variable starting with a lower-case letter
(and with no prefix) is local in scope.
- A $ prefix means a variable that
has global scope.
- A variable starting with an upper-case letter
(and with no prefix) is a constant and
has class-wide scope.
(Constants often use all upper-case letters.)
- Class names also start with an upper-case letter.
(It turns out that class names are also constants.)
- The prefix @ is used for a variable belonging
to a particular object, that is, an instance
of a class.
- Variables starting with @@ belong to
a whole class, that is, they have class scope.
- In Ruby, the prefixes @ and @@
have nothing to do with arrays (as they do in Perl).
- Predefined Variables and Constants:
- Ruby has many of the same special "scalar variables" that
start with $ and are present in Perl. These have been
included for convenience and because many people are familiar with
them.
- For example, the variable $_ can be used in
Ruby in much the same way as in Perl (it holds the current line),
but Ruby has alternatives
to its use. We will mostly avoid these variables.
Ruby Syntax:
Ruby's syntax initially looked very strange to me, but now I understand
it better. Ruby allows needless tokens to be left out, and
Ruby programs usually don't bother with these extras, writing
in a succinct style.
Here is a page illustrating this:
Illustrative programs.
Ruby has a semicolon at the end of a statement, but a newline
can work for a statement terminator if the line is syntactically
complete. So normally one either finishes a statement at
the end of the line, or leaves something "hanging" such as
an operator. A backslash at the end also means the statement
continues to the next line.
In a similar way, for many of Ruby's constructs, such as
the if-then-else-end or the
while-do-end, a keyword at the end
of a line, such as then or do
is optional and usually omitted. (This is also illustrated in
the program above.)
Loops and Iterators
(not the same things):
Arrays:
Ruby supports arrays (indexed by integers) as well as
hashes (indexed by any object; see next section).
Unlike Perl, the special prefixes @,
@@, or $ have nothing to do with arrays.
Elements of an array can hold a reference to any object.
Arrays will grow as needed to support insertion of new elements,
even beyond the current bounds of the array.
The class Array has a large number of
flexible methods which are illustrated in the following program.
Ruby example
showing arrays |
#!/usr/local/bin/ruby
x = 3
a = [14, 3.1415926, 'kitty', "Tell me #{x} times", [2, 3, 4]]
print a[3] # Prints: Tell me 3 times
a[1, 2] # [3.1415926, 'kitty']
a[4] # [2, 3, 4]
print a[4][1] # prints: 3
b = Array.new(5, "A") # b is ["A", "A", "A", "A", "A"]
b[3] = 13 # b is ["A", "A", "A", 13, "A"]
b[6] = 8 # b is ["A", "A", "A", 13, "A", nil, 8]
b.each {|x| print x, " "} # prints: A A A 13 A nil 8
print b.length # prints: 7
print b[-1] # prints: 8, the last element
print b[-4] # prints: 13, 4 back from last element
c = Array.new # new empty array
print c.length # prints: 0
c[2] = 47
for x in 0...c.length # prints: nil nil 47
print c[x], " "
end
for x in 0...c.length do print c[x], " " end # same thing
m = %w( Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec )
m.each {|x| print x, " "} # prints: Jan Feb Mar Apr ...
m.collect! {|x| x.downcase + ':'} # applies {} block to each elt
# downcase turns caps to lowercase
# ! at end means change object itself
m.each {|x| print x, " "} # prints: jan: feb: mar: apr: ...
|
Larger Example:
This example shows a function (sqrt2) defined within
a class in one file. Then in a second file the function is used to calculate
pi using 25 terms of the following infinite product (which must be solved for pi):
Notice the statement require "math2" at the beginning of the
program to use the sqrt2 function, which makes the class
containing the function available to the other program.
The code resembles Java.
Notice also that the way the middle function is structured,
the variable m cannot be a local variable inside the
function pi, but, if it's not a parameter, it must be
global, and so must be written as $m.
The file math2.rb could have consisted of just the
definition of the function sqrt2, not embedded in a class.
Then in the file piproduct.rb, the code could just use
the function sqrt2 directly without instantiating a class
and without the variable $m.
Code for sqrt
function File: math2.rb |
Code to calculate pi
File: piproduct.rb |
Output of run
(parameter on command line) |
#!/usr/local/bin/ruby
class Math2
def sqrt2(x)
x = x.to_f # to float
if x < 0 # if undefined
nil
elsif x == 0 # sqrt(0) = 0
0.0
else
# use better init value
y = x
# use Newton's method
yold = y + 1.0
while (y-yold).abs >
0.1e-14
yold, y =
y, x/(2*y) + y/2
end
y # return answer
end
end
end
|
#!/usr/local/bin/ruby
require "math2"
def pi(n)
p = 2 # final product
b = 0 # each denom
for i in 1..n
b = $m.sqrt2(2 + b)
t = 2.0/b # term
p *= t
if i%2 == 0
print " i: ", i,
", t: ", t,
", p: ", p, "\n"
end
end
p
end
$m = Math2.new
x = ARGV[0].to_i
print "pi: ", pi(x), "\n"
|
% piproduct.rb 25
i: 2, t: 1.08239220029239
p: 3.06146745892072
i: 4, t: 1.00483857237631
p: 3.13654849054594
i: 6, t: 1.0003012720413
p: 3.14127725093277
i: 8, t: 1.00001882507178
p: 3.14157294036709
i: 10, t: 1.00000117654968
p: 3.1415914215112
i: 12, t: 1.00000007353429
p: 3.14159257658487
i: 14, t: 1.00000000459589
p: 3.14159264877699
i: 16, t: 1.00000000028724
p: 3.14159265328899
i: 18, t: 1.00000000001795
p: 3.14159265357099
i: 20, t: 1.00000000000112
p: 3.14159265358862
i: 22, t: 1.00000000000007
p: 3.14159265358972
i: 24, t: 1
p: 3.14159265358979
pi: 3.14159265358979
|
Regular Expressions (REs):
A separate page gives material about Regular Expressions,
with emphasis on Ruby's object-oriented form of REs:
Regular Expressions.
Ruby classes:
Reference, from the PPG:
This section studies classes. [Under construction, but read the
link above.]
Revision date: 2013-11-07.
(Please use ISO 8601,
the International Standard Date and Time Notation.)
|