|
|||
Previous < |
Contents ^
|
Next >
|
if
statements,
assignments, and other details, it's difficult to write examples of
classes. Throughout our top-down description, we kept coming across
low-level details we needed to cover so that the example code would
make sense.
So, we came up with another grand plan (they don't call us pragmatic
for nothing). We'd still describe Ruby starting at the top. But before
we did that, we'd add a short chapter that described all the common
language features used in the examples along with the special vocabulary
used in Ruby, a kind of minitutorial to bootstrap us into the rest of
the book.
Song
, you'd have separate instances for popular hits such
as ``Ruby Tuesday,'' ``Enveloped in Python,'' ``String of Pearls,''
``Small talk,'' and so on. The word object is used
interchangeably with class instance (and being lazy typists, we'll
probably be using the word ``object'' more frequently).
In Ruby, these objects are created by calling a constructor, a special
method associated with a class. The standard constructor is called
new
.
song1 = Song.new("Ruby Tuesday") song2 = Song.new("Enveloped in Python") # and so on |
"gin joint".length
|
» |
9
|
"Rick".index("c")
|
» |
2
|
-1942.abs
|
» |
1942
|
sam.play(aSong)
|
» |
"duh dum, da dum de dum ..."
|
number = Math.abs(number) // Java code |
abs
to a number object and let it do the work.
number = number.abs |
strlen(name)
, while in Ruby it's name.length
, and so
on. This is part of what we mean when we say that Ruby is a genuine OO
language.
def sayGoodnight(name) result = "Goodnight, " + name return result end # Time for bed... puts sayGoodnight("John-Boy") puts sayGoodnight("Mary-Ellen") |
#
character and run to the end of the line. Code layout is
pretty much up to you; indentation is not significant.
Methods are defined with the keyword def
, followed by the method
name (in this case, ``sayGoodnight
'') and the method's
parameters between parentheses. Ruby doesn't use braces to delimit
the bodies of compound statements and definitions. Instead, you simply
finish the body with the keyword end
. Our method's body is
pretty simple. The first line concatenates the literal string
``Goodnight,'' to the parameter name
and assigns the result to
the local variable result. The next line returns that result to the
caller. Note that we didn't have to declare the variable result
;
it sprang into existence when we assigned to it.
Having defined the method, we call it twice. In both cases we pass the
result to the method puts
, which simply outputs its argument
followed by a newline.
Goodnight, John-Boy Goodnight, Mary-Ellen |
puts sayGoodnight("John-Boy")
'' contains two method calls,
one to sayGoodnight
and the other to puts
. Why does one call
have its arguments in parentheses while the other doesn't? In this
case it's purely a matter of taste. The following lines are all
equivalent.
puts sayGoodnight "John-Boy" puts sayGoodnight("John-Boy") puts(sayGoodnight "John-Boy") puts(sayGoodnight("John-Boy")) |
\n
'', which is replaced with a newline character.
When a
string containing a newline is output, the ``\n
'' forces a
line break.
puts "And Goodnight,\nGrandma" |
And Goodnight, Grandma |
#{
expression
}
is replaced by the value of
expression. We could use this to rewrite our previous method.
def sayGoodnight(name) result = "Goodnight, #{name}" return result end |
name
and substitutes it into the string. Arbitrarily complex
expressions are allowed in the #{...}
construct. As a
shortcut, you don't need to supply the braces when the expression is
simply a global, instance, or class variable. For more
information on strings, as well as on the other Ruby standard types, see
Chapter 5, which begins on page 47.
Finally, we could simplify this method some more. The value returned
by a Ruby method is the value of the last expression evaluated, so we
can get rid of the return
statement altogether.
def sayGoodnight(name) "Goodnight, #{name}" end |
Example variable and class names
|
a = [ 1, 'cat', 3.14 ] # array with three elements
|
||
# access the first element
|
||
a[0]
|
» |
1
|
# set the third element
|
||
a[2] = nil
|
||
# dump out the array
|
||
a
|
» |
[1, "cat", nil]
|
Array.new
.
empty1 = [] empty2 = Array.new |
%w
does just
what we want.
a = %w{ ant bee cat dog elk }
|
||
a[0]
|
» |
"ant"
|
a[3]
|
» |
"dog"
|
instSection = { 'cello' => 'string', 'clarinet' => 'woodwind', 'drum' => 'percussion', 'oboe' => 'woodwind', 'trumpet' => 'brass', 'violin' => 'string' } |
instSection['oboe']
|
» |
"woodwind"
|
instSection['cello']
|
» |
"string"
|
instSection['bassoon']
|
» |
nil
|
nil
when
indexed by a key it doesn't contain. Normally this is convenient, as
nil
means false
when used in conditional expressions.
Sometimes you'll want to change this default. For example, if you're
using a hash to count the number of times each key occurs, it's
convenient to have the default value be zero. This is easily done by
specifying a default value when you create a new, empty
hash.
histogram = Hash.new(0)
|
||
histogram['key1']
|
» |
0
|
histogram['key1'] = histogram['key1'] + 1
|
||
histogram['key1']
|
» |
1
|
if
statements
and while
loops. Java, C, and Perl programmers may well get
caught by the lack of braces around the bodies of these
statements. Instead, Ruby uses the keyword end
to signify the end
of a body.
if count > 10 puts "Try again" elsif tries == 3 puts "You lose" else puts "Enter a number" end |
while
statements are terminated with end
.
while weight < 100 and numPallets <= 30 pallet = nextPallet() weight += pallet.weight numPallets += 1 end |
if
or while
statement is just a single expression. Simply
write the expression, followed by if
or while
and the
condition.
For example, here's a simple if
statement.
if radiation > 3000 puts "Danger, Will Robinson" end |
puts "Danger, Will Robinson" if radiation > 3000 |
while
loop such as
while square < 1000 square = square*square end |
square = square*square while square < 1000 |
/Perl|Python/ |
|
'').
You can use parentheses within patterns, just as you can in arithmetic
expressions, so you could also have written this pattern as
/P(erl|ython)/ |
/ab+c/
matches a
string containing an ``a'' followed by one or more ``b''s, followed by
a ``c''. Change the plus to an asterisk, and /ab*c/
creates a
regular expression that matches an ``a'', zero or more ``b''s, and a
``c''.
You can also match one of a group of characters within a pattern. Some
common examples are character classes such as ``\s
'', which
matches a whitespace character (space, tab, newline, and so on),
``\d
'', which matches any digit, and ``\w
'', which matches
any character that may appear in a typical word. The single character
``.'' (a period) matches any character.
We can put all this together to produce some useful regular
expressions.
/\d\d:\d\d:\d\d/ # a time such as 12:34:56 /Perl.*Python/ # Perl, zero or more other chars, then Python /Perl\s+Python/ # Perl, one or more spaces, then Python /Ruby (Perl|Python)/ # Ruby, a space, and either Perl or Python |
=~
'' can be used to match a string against a
regular expression. If the pattern is found in the string, =~
returns its starting position, otherwise it returns nil
. This means
you can use regular expressions as the condition in if
and
while
statements. For example, the following code fragment writes
a message if a string contains the text 'Perl' or 'Python'.
if line =~ /Perl|Python/ puts "Scripting language mentioned: #{line}" end |
line.sub(/Perl/, 'Ruby') # replace first 'Perl' with 'Ruby' line.gsub(/Python/, 'Ruby') # replace every 'Python' with 'Ruby' |
do
...end
.
{ puts "Hello" } # this is a block do # club.enroll(person) # and so is this person.socialize # end # |
yield
statement. The following example shows this
in action. We define a method that calls yield
twice. We then
call it, putting a block on the same line, after the call (and after
any arguments to the method).[Some people like to think of
the association of a block with a method as a kind of parameter
passing. This works on one level, but it isn't really the whole
story. You might be better off thinking of the block and the method
as coroutines, which
transfer control back and forth between themselves.]
def callBlock yield yield end callBlock { puts "In the block" } |
In the block In the block |
puts "In the block"
) is executed
twice, once for each call to yield
.
You can provide parameters to the call to
yield
: these will be passed to the block. Within the block, you
list the names of the arguments to receive these parameters between
vertical bars (``|'').
def callBlock yield , end callBlock { |, | ... } |
a = %w( ant bee cat dog elk ) # create an array a.each { |animal| puts animal } # iterate over the contents |
ant bee cat dog elk |
Array
class's each
iterator that we used in the previous example. The each
iterator loops through every element in the array,
calling yield
for each one. In pseudo code, this might look like:
# within class Array... def each for each element yield(element) end end |
each
method and supplying a block. This block would be called for
each element in turn.
[ 'cat', 'dog', 'horse' ].each do |animal| print animal, " -- " end |
cat -- dog -- horse -- |
5.times { print "*" } 3.upto(6) {|i| print i } ('a'..'e').each {|char| print char } |
*****3456abcde |
each
.
puts
writes each
of its arguments, adding a newline after each. print
also writes
its arguments, but with no newline. Both can be used to write to any
I/O object, but by default they write to the console.
Another output method we use a lot is printf
, which prints
its arguments under the control of a format string (just like
printf
in C or Perl).
printf "Number: %5.2f, String: %s", 1.23, "hello" |
Number: 1.23, String: hello |
"Number: %5.2f, String: %s"
tells printf
to substitute in a floating point number
(allowing five characters in total, with two after the decimal point)
and a string.
There are many ways to read input into your program. Probably the most
traditional is to use the routine gets
, which returns the next
line from your program's standard input stream.
line = gets print line |
gets
routine has a side effect: as well as returning the line
just read, it also stores it into the global variable
$_
. This variable is special, in that it is used as the
default argument in many circumstances. If you call print
with no
argument, it prints the contents of $_
. If you write an if
or while
statement with just a regular expression as the
condition, that expression is matched against $_
. While viewed
by some purists as a rebarbative barbarism, these abbreviations can
help you write some concise programs. For example, the following
program prints all lines in the input stream that contain the word
``Ruby.''
while gets # assigns line to $_ if /Ruby/ # matches against $_ print # prints $_ end end |
ARGF.each { |line| print line if line =~ /Ruby/ } |
ARGF
, which represents the
input stream that can be read by a program.
Previous < |
Contents ^
|
Next >
|