'
end
nil
end
# Makes a tag.
def method_missing(methodSymbol, attributes = {})
methodName = methodSymbol.to_s
attribString = ''
attributes.each do |key, val|
raise methodName if (key.nil? || val.nil?)
attribString += ' '+key.to_s+'="'+val+'"'
end
if (!block_given?)
puts ''
else
puts ''
@depth += 1
blockReturn = yield
puts blockReturn if (blockReturn.kind_of?(String))
@depth -= 1
puts ''+methodName+'>'
end
nil
end
#
# TEST PAGE FOR FORMATTING
#
def generateFormattingPage
h1 { 'Heading 1' }
h2 { 'Heading 2' }
h3 { 'Heading 3' }
para {'Here\'s some code with fake output:'}
prog [], '...just kidding, dude...', 'FUNKADELIC!' do <'] do <bigger and better favorite number.
(Do be tactful about it, though.)"}
end
para do <Ruby.
END_PARAGRAPH
end
para do <code. I
have included lots of examples of Ruby code throughout
this tutorial, most of them complete programs you can
run on your own computer. To make the code easier to
read, I have colored parts of the code different
colors. (For example, numbers are always
#{@@NUMBER_COLOR}.)
Anything you are supposed to type in will be in a
#{input @@INPUT_DESC}, and anything a program prints
out will be in a #{output @@OUTPUT_DESC}.
END_PARAGRAPH
end
para do <download Ruby.
There might be a couple of versions to choose from; this tutorial
is using version #{RUBY_VERSION}, so make sure what you download is at
least as recent as that. (I would just get the latest
version available.) Then simply run the installation program.
It will ask you where you want to install Ruby. Unless you have
a good reason for it, I would just install it in the default
location.
END_PARAGRAPH
end
para do <Sublime Text.
END_PARAGRAPH
end
para do <foldername. To see all of the folders
in your current folder, type #{input 'dir /ad'}.
END_PARAGRAPH
end
para do <Sublime Text.
If you use TextEdit, however, make sure
you save your programs as text-only! Otherwise your programs
will not work.
END_PARAGRAPH
end
para do <download Ruby,
otherwise see what version of Ruby you are running with
#{input 'ruby -v'}. If it is older than the latest stable build
on the above download page, you might want to upgrade.
END_PARAGRAPH
end
para do <$HOME/tmp. If the name of the file is
ruby-1.6.7.tar.gz, you can open it with
#{input 'tar zxvf ruby-1.6.7.tar.gz'}. Change directory
to the directory you just created (in this example,
#{input 'cd ruby-1.6.7'}).
END_PARAGRAPH
end
para do <$HOME/bin to your
command search path by editing your $HOME/.bashrc
file. (You might have to log out and back in again for
this to take effect.) After you do that, test your installation:
#{input 'ruby -v'}. If that tells you what version of Ruby you
have, you can now delete the files
in $HOME/tmp (or wherever you put them).
END_PARAGRAPH
end
para do <.rb is what we usually put at the end of
programs written in Ruby). Now run your program by typing #{input 'ruby calc.rb'}
into your command line. It should have put a #{output '3'} on your screen.
See, programming isn't so hard, now is it?
END_PARAGRAPH
end
h2 {'Introduction to '+(code 'puts')}
para do <integers, and
numbers with decimal points are usually called
floating-point numbers,
or more simply, floats.
END_PARAGRAPH
end
para do <+ and -,
as we saw. For multiplication, we use *,
and for division we use /. Most keyboards have
these keys in the numeric keypad on the far right side.
If you have a smaller keyboard or a laptop, though, you can just use
Shift 8 and / (same key as the
? key). Let's try to expand our calc.rb program a little.
Type in the following and then run it.
END_PARAGRAPH
end
prog [], 'This is what the program returns:' do <is the right answer in integer arithmetic
for #{code '9/2'}; just maybe not the answer you were expecting.)
END_PARAGRAPH
end
para do <not the right answer in this case; they will
not let you watch half of a movie, or let half of you in to
see a whole movie... some things just aren't divisible.
END_PARAGRAPH
end
para do <'+
'Warning: This part of the program could take a while to compute!'}
end
para do
puts "Here's a tougher question:"
end
ul do
li {"If I am #{(Time.now - Time.mktime(1976,8,3)).to_i / 1000000} million seconds old, how old am I?"}
end
para do <strings. (You can
think of printed letters being strung together on a banner.)
To make it easier to see just what part of the code is in a string,
I'll color strings
#{@@STRING_COLOR}.
Here are some strings:
END_PARAGRAPH
end
prog false do <empty string.
END_PARAGRAPH
end
para do <want
them to do, only what you tell them to do.) Let's try that again:
END_PARAGRAPH
end
prog do <numbers and digits.
#{code '12'} is a number, but #{code "'12'"} is a string of two digits.
END_PARAGRAPH
end
para do <didn't work. If not, here are a few:
END_PARAGRAPH
end
prog do <can't write #{code "5*'pig'"}, since that means #{code "'pig'"}
sets of the number #{code '5'}, which is just silly.
END_PARAGRAPH
end
para do <that won't work; I won't even try to run it.
The computer thought we were done with the string.
(This is why it's nice to have a text editor which does
syntax coloring for you.) So how do we let
the computer know we want to stay in the string? We have
to escape the apostrophe, like this:
END_PARAGRAPH
end
prog do <not escape a #{code "'d'"},
but does escape itself, those last two strings are
identical. They don't look the same in the code, but in your
computer they really are the same.
END_PARAGRAPH
end
para do <this page, after all.
END_PARAGRAPH
end
end
#
# VARIABLES
#
def generateVariables
para do <assignment,
and they call the names variables. This variable can be just
about any sequence of letters and numbers, but the first character
needs to be a lowercase letter. Let's try that last program again,
but this time I will give the string the name #{code 'myString'} (though I could
just as well have named it #{code 'str'} or
#{code 'myOwnLittleString'} or #{code 'henryTheEighth'}).
END_PARAGRAPH
end
prog do <assign an object to a variable,
we can reassign a different object to that variable.
(This is why we call them variables: because what they
point to can vary.)
END_PARAGRAPH
end
prog do <does not work, because you can't add
numbers and strings:
END_PARAGRAPH
end
prog false do <don't do) a little more closely:
END_PARAGRAPH
end
prog do <unusual strings being converted
into numbers. #{code 'to_i'} ignores the first thing it doesn't understand,
and the rest of the string from that point on. So the first one
was converted to #{code '5'}, but the others, since they started with
letters, were ignored completely... so the computer just picks zero.
END_PARAGRAPH
end
para do <is #{code "'20'"}. But what
about the first one, the integer #{code '20'}? For that matter, what
does it even mean to write out the integer 20? When
you write a 2 and then a 0 on a piece of paper, you
are writing down a string, not an integer. The integer 20 is the number of
fingers and toes I have; it isn't a 2 followed by a 0.
END_PARAGRAPH
end
para do <s in
#{code 'puts'} stands for string; #{code 'puts'} really means
put string.
END_PARAGRAPH
end
para do <many kinds of objects in Ruby (you'll even learn how
to make your own!), and it's nice to know what will happen if
you try to #{code 'puts'} a really weird object,
like a picture of your grandmother, or a music file or something.
But that will come later...
END_PARAGRAPH
end
para do <put string, I'm sure you can guess
what #{code 'gets'} stands for. And just as #{code 'puts'} always
spits out strings, #{code 'gets'} will only retrieve strings. And
whence does it get them?
END_PARAGRAPH
end
para do <Enter. Let's try it out:
END_PARAGRAPH
end
prog ['Is there an echo in here?'] do <Eek! I just ran it—I typed in my name, and this is what happened:' do <C,
h, r, i,
s, and then pressed Enter, #{code 'gets'}
got all of the letters in my name and the
Enter! Fortunately, there's a method just for
this sort of thing: #{code 'chomp'}. It takes off any Enters
hanging out at the end of your string. Let's try that program again,
but with #{code 'chomp'} to help us this time:
END_PARAGRAPH
end
prog ['Chris'] do <bigger and better favorite number.
(Do be tactful about it, though.)"}
end
para do <Pop Quiz: List all
of the methods we have seen so far!
There are ten of them; the answer is below.),
but we haven't really talked about what methods are.
We know what they do, but
we don't know what they are.
END_PARAGRAPH
end
para do <is what they are: things
that do stuff. If objects (like strings,
integers, and floats) are the nouns in the Ruby
language, then methods are like the verbs.
And, just like in English, you can't have a
verb without a noun to do the verb.
For example, ticking isn't something that just
happens; a clock (or a watch or something)
has to do it. In English we would say, "The
clock ticks." In Ruby we would say
#{code 'clock.tick'} (assuming that #{code 'clock'}
was a Ruby object, of course).
Programmers might say we were "calling #{code 'clock'}'s
#{code 'tick'} method,"
or that we "called #{code 'tick'} on #{code 'clock'}."
END_PARAGRAPH
end
para do <really happening.
(On older versions of Ruby, this code might also give a warning:
#{output 'warning: parenthesize argument(s) for future version'}.
It would still run the code just fine, though.)
This also gives us a deeper understanding
of why we can do #{code "'pig'*5"} but we
can't do #{code "5*'pig'"}: #{code "'pig'*5"} is
telling #{code "'pig'"} to do the multiplying,
but #{code "5*'pig'"} is telling #{code '5'}
to do the multiplying. #{code "'pig'"} knows how
to make #{code '5'} copies of itself and
add them all together; however, #{code '5'}
will have a much more difficult time of making
#{code "'pig'"} copies of itself
and adding them together.
END_PARAGRAPH
end
para do <in
an object yet, but until we find out, we
are always going to be in a big object which
is... the whole program! And lucky for us,
the program has a few methods of its own,
like #{code 'puts'} and #{code 'gets'}.
Watch this:
END_PARAGRAPH
end
prog do <small
part of what strings can do. In fact, I
can't remember even half of the string methods myself—but
that's fine, because there are great references
on the internet with all of the string
methods listed and explained. (I will show
you where to find them at the end of this tutorial.)
Really, I don't even want to know
all the string methods; it's kind of like knowing every
word in the dictionary. I can speak English
just fine without knowing every word in
the dictionary... and isn't that really the whole
point of the dictionary? So you don't have
to know what's in it?
END_PARAGRAPH
end
para do <Note: that's the number of
characters in my name, not the number of letters
(count 'em). I guess we could write a program which
asks for your first, middle, and last names individually, and then
adds those lengths together... hey, why don't you do that! Go ahead,
I'll wait.
END_PARAGRAPH
end
para do <character, not the first
letter. Also, as we have seen before, throughout all of
these method calls, #{code 'letters'} remains unchanged. I don't mean
to belabor the point, but it's important to understand. There are
some methods which do change the associated object, but we haven't
seen any yet, and we won't for some time.
END_PARAGRAPH
end
para do <every job.
END_PARAGRAPH
end
para do <left justify and
right justify. They are similar to #{code 'center'}, except
that they pad the string with spaces on the right and left sides,
respectively. Let's take a look at all three in action:
END_PARAGRAPH
end
prog do < text ' +
' Table of Contents ' + $/ +
' ' + $/ +
'Chapter 1: Numbers page 1' + $/ +
'Chapter 2: Letters page 72' + $/ +
'Chapter 3: Variables page 118' + $/ +
''
end
end
h2 {'Higher Math'}
para do <(This section is totally optional. It assumes a fair degree
of mathematical knowledge. If you aren't interested, you
can go straight to #{makeLink 'Flow Control', :generateFlowControl}
without any problems. However, a quick look at the section
on Random Numbers might come in handy.)
END_PARAGRAPH
end
para do <same random numbers
in the same sequence on two different runs of your program. (For example, once I
was using randomly generated numbers to create a randomly generated world for a computer
game. If I found a world that I really liked, perhaps I would want to play on it
again, or send it to a friend.) In order to do this, you need to set the
seed, which you can do with #{code 'srand'}. Like this:
END_PARAGRAPH
end
prog do <scope operator (which is what that is)
is really beyond the, uh... scope of this tutorial. No pun
intended. I swear. Suffice it to say, you can use
#{code 'Math::PI'} just like you would expect to.
END_PARAGRAPH
end
para do <really close to being the right answers.
END_PARAGRAPH
end
para do <say different
things depending on your keyboard input, but after this chapter
they will actually do different things, too. But
before we can do that, we need to be
able to compare the objects in our programs. We need...
END_PARAGRAPH
end
h2 {'Comparison Methods'}
para do <Branching, where all the cool
stuff happens. So, to see if one object is greater than
or less than another, we use the methods #{code '>'}
and #{code ' 2
puts 1 < 2
END_CODE
end
para do <='} and #{code '<='}
END_PARAGRAPH
end
prog do <= 5
puts 5 <= 4
END_CODE
end
para do <lexicographical ordering,
which basically means their dictionary ordering. #{code 'cat'}
comes before #{code 'dog'} in the dictionary, so:
END_PARAGRAPH
end
prog do <Branching: The comparison
methods aren't giving us the strings #{code "'true'"} and
#{code "'false'"}; they are giving us the special objects #{code 'true'} and
#{code 'false'}. (Of course, #{code 'true.to_s'} gives us
#{code "'true'"}, which is why #{code 'puts'} printed #{code "'true'"}.)
#{code 'true'} and #{code 'false'} are used all the time in...
END_PARAGRAPH
end
h2 {'Branching'}
para do < ['Chris']}
run2 = {:input => ['Chewbacca'], :remark => 'But if we put in a different name...'}
progN run1, run2 do < ['Chris']}
run2 = {:input => ['Ringo'], :remark => 'Now let\'s try a different name...'}
progN run1, run2 do < ['chris', 'yes']}
run2 = {:input => ['Chris'], :remark => 'Fine, I\'ll capitalize it...'}
progN run1, run2 do <at the same time I write the #{code 'if'}. So
as I was writing the above program, this is how it looked
first:
END_PARAGRAPH
end
prog false do <comments, stuff
in the code the computer will ignore:
END_PARAGRAPH
end
prog false do <exactly
like the program above, other than that first blank line?)
END_PARAGRAPH
end
para do <Ctrl key and press C.
END_PARAGRAPH
end
para do <she had? Well... she probably
wouldn't care. But I'd care! So let's rewrite it:
END_PARAGRAPH
end
prog ['Katy'] do <DRY rule:
Don't Repeat Yourself. I could probably write a small
book just on why that is such a good rule. In our case, we
repeated the line #{code "puts 'What a lovely name!'"}. Why is
this such a big deal? Well, what if I made a spelling mistake
when I rewrote it? What if I wanted to change it from
#{code "'lovely'"} to #{code "'beautiful'"} on both lines?
I'm lazy, remember? Basically, if
I want the program to do the same thing when it gets
#{code "'Chris'"} or #{code "'Katy'"}, then it should really
do the same thing:
END_PARAGRAPH
end
prog ['Katy'] do <logical operators are #{code 'and'} and
#{code 'not'}. It is always a good idea to use parentheses
when working with these. Let's see how they work:
END_PARAGRAPH
end
prog do <not mean you could have them both!
A computer, on the other hand, uses #{code 'or'} to mean "one or the other,
or both." (Another way of saying it is, "at least one of
these is true.") This is why computers are more fun than
moms.
END_PARAGRAPH
end
h2 {'A Few Things to Try'}
ul do
li {'"99 bottles of beer on the wall..." Write a program
which prints out the lyrics to that beloved classic, that
field-trip favorite: "99 Bottles of Beer on the Wall."'}
li {"Write a Deaf Grandma program. Whatever you say
to grandma (whatever you type in), she should respond with
#{output 'HUH?! SPEAK UP, SONNY!'}, unless you shout it (type in
all capitals). If you shout, she can hear you (or at least
she thinks so) and yells back, #{output 'NO, NOT SINCE 1938!'} To
make your program really believable, have grandma
shout a different year each time; maybe any year at random
between 1930 and 1950. (This part is optional, and would be
much easier if you read the section on Ruby's random number
generator at the end of the #{makeLink 'methods', :generateMethods}
chapter.) You can't stop talking to grandma
until you shout #{input 'BYE'}.Hint: Don't forget about
#{code 'chomp'}! #{code "'BYE'"}with an
Enter is not the same as #{code "'BYE'"} without
one!Hint 2: Try to think about what parts
of your program should happen over and over again. All
of those should be in your #{code 'while'} loop."}
li {"Extend your Deaf Grandma program: What if grandma
doesn't want you to leave? When you shout #{input 'BYE'}, she
could pretend not to hear you. Change your previous
program so that you have to shout #{input 'BYE'} three times
in a row. Make sure to test your program:
if you shout #{input 'BYE'} three times, but not in a row, you
should still be talking to grandma."}
li {"Leap Years. Write a program which will ask for
a starting year and an ending year, and then #{code 'puts'}
all of the leap years between them (and including them,
if they are also leap years). Leap years are years divisible
by four (like 1984 and 2004). However, years divisible
by 100 are not leap years (such as 1800 and
1900) unless they are divisible
by 400 (like 1600 and 2000, which were in fact leap years).
(Yes, it's all pretty
confusing, but not as confusing as having July in the
middle of the winter, which is what would eventually
happen.)"}
end
para do <Enter on an empty line), and which then repeats
the words back to us in alphabetical order. OK?
END_PARAGRAPH
end
para do <arrays.
END_PARAGRAPH
end
para do <string, and an array. Even if we
were to set #{code 'flavor'} to
point to something else, that wouldn't change the
array.
END_PARAGRAPH
end
para do <do get used to it. You just have to really
start thinking that counting begins at zero, and
stop using words like "first" and "second".
If you go out to a five-course meal, don't talk about
the "first" course; talk about course zero
(and in your head, be thinking #{code 'course[0]'}).
You have five fingers on your right hand, and their
numbers are 0, 1, 2, 3, and 4. My wife and I are
jugglers. When we juggle six clubs, we are juggling
clubs 0-5. Hopefully in the next few months, we'll
be able to juggle club 6 (and thus be juggling seven
clubs between us). You'll know you've got it when you
start using the word "zeroth". :-) Yes, it's a real
word; ask any programmer or mathematician.
END_PARAGRAPH
end
para do <nothing. What's in slot three? Nothing.
What is #{code 'names[3]'}? #{code 'nil'}: Ruby's way
of saying "nothing". #{code 'nil'} is a special object
which basically means "not any other object." And when you
#{code 'puts nil'}, it prints out nothing. (Just a new line.)
END_PARAGRAPH
end
para do <#{@@KEYWORD_COLOR}
words) are not methods. They are a fundamental part of the Ruby
language, just like #{code '='} and parentheses; kind of
like punctuation marks in English.
END_PARAGRAPH
end
para do <iterators.
END_PARAGRAPH
end
para do <actually
change the array:
END_PARAGRAPH
end
prog do <
Hint: There's a lovely
array method which will give you a sorted version of an
array: #{code 'sort'}. Use it!"}
li {"Try writing the above program without using
the #{code 'sort'} method. A large part of programming is
solving problems, so get all the practice you can!"}
li {"Rewrite your Table of Contents program (from the chapter
on #{makeLink 'methods', :generateMethods}). Start the program
with an array holding all of the information for your Table
of Contents (chapter names, page numbers, etc.). Then print
out the information from the array in a beautifully formatted
Table of Contents."}
end
para do <how to #{code 'sayMoo'},
but we never actually said to do it.
Let's give it another shot:
END_PARAGRAPH
end
prog do <"coin-coin".)
END_PARAGRAPH
end
para do <#{code 'def'}ined
the method #{code 'sayMoo'}. (Method names, like
variable names, start with a lowercase letter.
There are a few exceptions, though, like #{code '+'}
or #{code '=='}.)
But don't methods always have to be associated with
objects? Well, yes they do, and in this case (as with
#{code 'puts'} and #{code 'gets'}), the method is just
associated with the object representing
the whole program. In the next chapter we'll see how to
add methods to other objects. But first...
END_PARAGRAPH
end
h2 {'Method Parameters'}
para do <parameters to tell the object how to
do the method. For example, you wouldn't just say
#{code '5+'}, right? You're telling #{code '5'} to
add, but you aren't telling it what
to add.
END_PARAGRAPH
end
para do <required.
After all, what is #{code 'sayMoo'} supposed to multiply
#{code "'mooooooo...'"} by if you don't give it a
parameter? Your poor computer has no idea.
END_PARAGRAPH
end
para do <how to #{code 'sayMoo'}) or sometimes as
direct objects (like with #{code 'puts'}, where the
parameter is what gets #{code 'puts'}ed).
END_PARAGRAPH
end
h2 {'Local Variables'}
para do <local variables. This means
that they live inside the method, and they cannot leave.
If you try, you will get an error:
END_PARAGRAPH
end
prog do <did
define that local variable, but it isn't local to where
we tried to use it; it's local to the method.
END_PARAGRAPH
end
para do <your variables, and thus can't screw them up:
END_PARAGRAPH
end
prog do <two variables in that little
program named #{code 'var'}: one inside #{code 'littlePest'},
and one outside of it. When we called #{code 'littlePest var'},
we really just passed the string from one #{code 'var'} to
the other, so that both were pointing to the same string.
Then #{code 'littlePest'} pointed its own local
#{code 'var'} to #{code 'nil'}, but that did nothing to the
#{code 'var'} outside the method.
END_PARAGRAPH
end
h2 {'Return Values'}
para do <returns a string (the string you typed in),
and the #{code '+'} method in #{code '5+3'}, (which is
really #{code '5.+(3)'}) returns #{code '8'}. The
arithmetic methods for numbers return numbers, and the
arithmetic methods for strings return strings.
END_PARAGRAPH
end
para do <not output
#{output '8'}.
END_PARAGRAPH
end
para do <does #{code 'puts'} return? We never cared
before, but let's look at it now:
END_PARAGRAPH
end
prog do <that at the end of
them:
END_PARAGRAPH
end
prog do <easy now), but our program
is still quite a bit shorter! It's a big improvement
— a lazy programmer's dream.
END_PARAGRAPH
end
h2 {'One More Big Example'}
para do <(NOTE: This method uses a new trick
to return from a method early using the #{code 'return'}
keyword, and introduces a new twist on branching:
#{code 'elsif'}. It should be clear in context
how these work.)
END_PARAGRAPH
end
prog do < 100
return 'Please enter a number 100 or lesser.'
end
numString = '' # This is the string we will return.
# "left" is how much of the number we still have left to write out.
# "write" is the part we are writing out right now.
# write and left... get it? :)
left = number
write = left/100 # How many hundreds left to write out?
left = left - write*100 # Subtract off those hundreds.
if write > 0
return 'one hundred'
end
write = left/10 # How many tens left to write out?
left = left - write*10 # Subtract off those tens.
if write > 0
if write == 1 # Uh-oh...
# Since we can't write "tenty-two" instead of "twelve",
# we have to make a special exception for these.
if left == 0
numString = numString + 'ten'
elsif left == 1
numString = numString + 'eleven'
elsif left == 2
numString = numString + 'twelve'
elsif left == 3
numString = numString + 'thirteen'
elsif left == 4
numString = numString + 'fourteen'
elsif left == 5
numString = numString + 'fifteen'
elsif left == 6
numString = numString + 'sixteen'
elsif left == 7
numString = numString + 'seventeen'
elsif left == 8
numString = numString + 'eighteen'
elsif left == 9
numString = numString + 'nineteen'
end
# Since we took care of the digit in the ones place already,
# we have nothing left to write.
left = 0
elsif write == 2
numString = numString + 'twenty'
elsif write == 3
numString = numString + 'thirty'
elsif write == 4
numString = numString + 'forty'
elsif write == 5
numString = numString + 'fifty'
elsif write == 6
numString = numString + 'sixty'
elsif write == 7
numString = numString + 'seventy'
elsif write == 8
numString = numString + 'eighty'
elsif write == 9
numString = numString + 'ninety'
end
if left > 0
numString = numString + '-'
end
end
write = left # How many ones left to write out?
left = 0 # Subtract off those ones.
if write > 0
if write == 1
numString = numString + 'one'
elsif write == 2
numString = numString + 'two'
elsif write == 3
numString = numString + 'three'
elsif write == 4
numString = numString + 'four'
elsif write == 5
numString = numString + 'five'
elsif write == 6
numString = numString + 'six'
elsif write == 7
numString = numString + 'seven'
elsif write == 8
numString = numString + 'eight'
elsif write == 9
numString = numString + 'nine'
end
end
if numString == ''
# The only way "numString" could be empty is if
# "number" is 0.
return 'zero'
end
# If we got this far, then we had a number somewhere
# in between 0 and 100, so we need to return "numString".
numString
end
puts englishNumber( 0)
puts englishNumber( 9)
puts englishNumber( 10)
puts englishNumber( 11)
puts englishNumber( 17)
puts englishNumber( 32)
puts englishNumber( 88)
puts englishNumber( 99)
puts englishNumber(100)
END_CODE
end
para do < 0
# Now here's a really sly trick:
hundreds = englishNumber write
numString = numString + hundreds + ' hundred'
# That's called "recursion". So what did I just do?
# I told this method to call itself, but with "write" instead of
# "number". Remember that "write" is (at the moment) the number of
# hundreds we have to write out. After we add "hundreds" to
# "numString", we add the string ' hundred' after it.
# So, for example, if we originally called englishNumber with
# 1999 (so "number" = 1999), then at this point "write" would
# be 19, and "left" would be 99. The laziest thing to do at this
# point is to have englishNumber write out the 'nineteen' for us,
# then we write out ' hundred', and then the rest of
# englishNumber writes out 'ninety-nine'.
if left > 0
# So we don't write 'two hundredfifty-one'...
numString = numString + ' '
end
end
write = left/10 # How many tens left to write out?
left = left - write*10 # Subtract off those tens.
if write > 0
if ((write == 1) and (left > 0))
# Since we can't write "tenty-two" instead of "twelve",
# we have to make a special exception for these.
numString = numString + teenagers[left-1]
# The "-1" is because teenagers[3] is 'fourteen', not 'thirteen'.
# Since we took care of the digit in the ones place already,
# we have nothing left to write.
left = 0
else
numString = numString + tensPlace[write-1]
# The "-1" is because tensPlace[3] is 'forty', not 'thirty'.
end
if left > 0
# So we don't write 'sixtyfour'...
numString = numString + '-'
end
end
write = left # How many ones left to write out?
left = 0 # Subtract off those ones.
if write > 0
numString = numString + onesPlace[write-1]
# The "-1" is because onesPlace[3] is 'four', not 'three'.
end
# Now we just return "numString"...
numString
end
puts englishNumber( 0)
puts englishNumber( 9)
puts englishNumber( 10)
puts englishNumber( 11)
puts englishNumber( 17)
puts englishNumber( 32)
puts englishNumber( 88)
puts englishNumber( 99)
puts englishNumber(100)
puts englishNumber(101)
puts englishNumber(234)
puts englishNumber(3211)
puts englishNumber(999999)
puts englishNumber(1000000000000)
END_CODE
end
para do <Ahhhh.... That's much, much better. The program is
fairly dense, which is why I put in so many comments. It
even works for large numbers... though not quite as nicely
as one would hope. For example, I think #{code "'one trillion'"}
would be a nicer return value for that last number, or even
#{code "'one million million'"} (though all three are correct).
In fact, you can do that right now...
END_PARAGRAPH
end
h2 {'A Few Things to Try'}
ul do
li {"Expand upon #{code 'englishNumber'}. First, put in
thousands. So it should return #{code "'one thousand'"}
instead of #{code "'ten hundred'"} and #{code "'ten thousand'"}
instead of #{code "'one hundred hundred'"}."}
li {"Expand upon #{code 'englishNumber'} some more.
Now put in millions, so you get #{code "'one million'"}
instead of #{code "'one thousand thousand'"}. Then try adding
billions and trillions. How high can you go?"}
li {"How about #{code 'weddingNumber'}? It should
work almost the same as #{code 'englishNumber'}, except
that it should insert the word \"and\" all over the place,
returning things like #{code "'nineteen hundred and seventy and two'"},
or however wedding invitations are supposed to look. I'd give you more
examples, but I don't fully understand it myself. You might
need to contact a wedding coordinator to help you."}
li {"\"Ninety-nine bottles of beer...\"
Using #{code 'englishNumber'} and your old program, write out the
lyrics to this song the right way this time.
Punish your computer: have it start at 9999. (Don't pick
a number too large, though, because writing all of that to
the screen takes your computer quite a while. A hundred
thousand bottles of beer takes some time; and if you pick
a million, you'll be punishing yourself as well!"}
end
para do <classes, of objects:
strings, integers, floats, arrays, and a few special objects
(#{code 'true'}, #{code 'false'}, and #{code 'nil'}) which
we'll talk about later.
In Ruby, these classes are always capitalized: #{code 'String'},
#{code 'Integer'}, #{code 'Float'}, #{code 'Array'}... etc.
In general, if we want to create a new object of a
certain class, we use #{code 'new'}:
END_PARAGRAPH
end
prog do <less than a later time),
and if you subtract one time from another, you'll get the
number of seconds between them. Play around with it!
END_PARAGRAPH
end
h2 {'A Few Things to Try'}
ul do
li {"One billion seconds... Find out the exact second you
were born (if you can). Figure out when you will turn (or
perhaps when you did turn?) one billion seconds old. Then
go mark your calendar."}
li {"Happy Birthday! Ask what year a person was born in,
then the month, then the day. Figure out how old they are
and give them a big #{output 'SPANK!'} for each birthday
they have had."}
end
h2 {"The #{code 'Hash'} Class"}
para do <any object to refer to a slot, not just
a number. It's good to use hashes when you have a bunch
of things you want to keep track of, but they don't really
fit into an ordered list. For example, the colors I use for different
parts of the code which created this tutorial:
END_PARAGRAPH
end
prog do <have
a number, and that rolling the die should change the number.
If we keep track of the die, we shouldn't also have to keep track
of the number it is showing.
END_PARAGRAPH
end
para do <string. However, we could also call
it a string object. Sometimes programmers might
call it an instance of the class #{code 'String'}, but this
is just a fancy (and rather long-winded) way of saying
string. An instance of a class is just an
object of that class.
END_PARAGRAPH
end
para do <= 8
end
def passageOfTime
if @stuffInBelly > 0
# Move food from belly to intestine.
@stuffInBelly = @stuffInBelly - 1
@stuffInIntestine = @stuffInIntestine + 1
else # Our dragon is starving!
if @asleep
@asleep = false
puts 'He wakes up suddenly!'
end
puts @name + ' is starving! In desperation, he ate YOU!'
exit # This quits the program.
end
if @stuffInIntestine >= 10
@stuffInIntestine = 0
puts 'Whoops! ' + @name + ' had an accident...'
end
if hungry?
if @asleep
@asleep = false
puts 'He wakes up suddenly!'
end
puts @name + '\\'s stomach grumbles...'
end
if poopy?
if @asleep
@asleep = false
puts 'He wakes up suddenly!'
end
puts @name + ' does the potty dance...'
end
end
end
pet = Dragon.new 'Norbert'
pet.feed
pet.toss
pet.walk
pet.putToBed
pet.rock
pet.putToBed
pet.putToBed
pet.putToBed
pet.putToBed
END_CODE
end
para do <Whew! Of course, it would be nicer if this was
an interactive program, but you can do that part later.
I was just trying to show the parts directly relating to
creating a new dragon class.
END_PARAGRAPH
end
para do <public interface to your car.
How your airbag knows when to deploy, however, is internal to
the car; the typical user (driver) doesn't need to know about
this.
END_PARAGRAPH
end
para do <method dispatch, where your program checks
which string was entered, and then calls the appropriate method."}
end
para do <so many classes
you can use that I can't possibly show you them all; I don't
even know what most of them are! What I can tell
you is where to find out more about them, so you can learn
about the ones you want to program with. Before I send you
off, though, there is just one more feature of Ruby you should
know about, something most languages don't have, but which I
simply could not live without:
#{makeLink 'blocks and procs', :generateBlocksProcs}.
END_PARAGRAPH
end
end
#
# BLOCKS AND PROCS
#
def generateBlocksProcs
para do <closures), but most of the
more popular ones don't, and it's a shame.
END_PARAGRAPH
end
para do <block of code (code in between #{code 'do'}
and #{code 'end'}), wrap it up in an object (called a
proc), store it in a variable or pass it to a
method, and run the code in the block whenever you feel
like (more than once, if you want). So it's kind of like
a method itself, except that it isn't bound to an object
(it is an object), and you can store it or pass
it around like you can with any object. I think it's example
time:
END_PARAGRAPH
end
prog do <really want to do, then close the file.
It's tedious and easy to forget. In Ruby, saving (or loading) files works
similarly to the code above, so you don't have to worry about anything but
what you actually want to save (or load). (In the next chapter I'll show you
where to find out how to do things like save and load files.)
END_PARAGRAPH
end
para do <if to call a proc. Here's a method which will call the proc passed in
about half of the time, and another which will call it twice:
END_PARAGRAPH
end
prog do <'
end
glance = Proc.new do
puts ''
end
maybeDo wink
maybeDo glance
twiceDo wink
twiceDo glance
END_CODE
end
para do <something twice!
END_PARAGRAPH
end
para do <lazy evaluation, infinite data structures,
and currying),
but the fact is that I almost never do this in practice, nor
can I remember seeing anyone else do this in their code. I think
it's the kind of thing you don't usually end up having to do in Ruby,
or maybe Ruby just encourages you to find other solutions; I don't
know. In any case, I will only touch on this briefly.
END_PARAGRAPH
end
para do <block right into the method, without using
a proc at all), since most of the time you don't want to use the
proc/block after you pass it into the method. Well, wouldn't you
know, Ruby has it all figured out for us! In fact, you've already
been doing it every time you use iterators.
END_PARAGRAPH
end
para do <not ignore the block, but grab it and turn it into
a proc, put the name of the proc at the end of your method's parameter
list, preceded by an ampersand (#{code '&'}). So that part is a little
tricky, but not too bad, and you only have to do that once (when you
define the method). Then you can use the method over and over again,
just like the built-in methods which take blocks, like #{code 'each'}
and #{code 'times'}. (Remember #{code '5.times do'}...?)
END_PARAGRAPH
end
para do <why we write methods like this: so we
never have to think about how they work again. We just use them.
END_PARAGRAPH
end
para do <profiling the code.) So I wrote a method which takes
the time before running the code, then it runs it, then it takes
the time again at the end and figures out the difference. I can't
find the code right now, but I don't need it; it probably
went something like this:
END_PARAGRAPH
end
prog do <Grandfather Clock. Write a method which takes a block
and calls it once for each hour that has passed today. That way, if I
were to pass in the block #{code "do puts 'DONG!' end"}, it would chime
(sort of) like a grandfather clock. Test your method
out with a few different blocks (including the one I just gave you).
Hint: You can use
#{code 'Time.now.hour'} to get the current hour.
However, this returns a number between #{code '0'} and #{code '23'},
so you will have to alter those numbers in order to get ordinary clock-face
numbers (#{code '1'} to #{code '12'})."
end
li do
para {"Program Logger. Write a method called #{code 'log'}, which
takes a string description of a block and, of course, a block. Similar to
#{code 'doSelfImportantly'}, it should #{code 'puts'} a string telling
that it has started the block, and another string at the end telling you
that it has finished the block, and also telling you what the block returned.
Test your method by sending it a code block. Inside the block, put another
call to #{code 'log'}, passing another block to it. (This is called
nesting.) In other words, your output should look something like this:"}
puts '

'
end
li do
puts "Better Logger. The output from that last logger was kind
of hard to read, and it would just get worse the more you used it. It would
be so much easier to read if it indented the lines in the inner blocks. To
do this, you'll need to keep track of how deeply nested you are every time
the logger wants to write something. To do this, use a global variable,
a variable you can see from anywhere in your code. To make a global variable,
just precede your variable name with #{code '$'}, like these:
#{code '$global'}, #{code '$nestingDepth'}, and #{code '$bigTopPeeWee'}.
In the end, your logger should output code like this:"
puts '