#! /usr/bin/env ruby #-----------------------------------------------# # Learn to Program # # by Chris Pine # # Copyright (c) 2003-2016 # # chris@pine.fm # #-----------------------------------------------# require 'cgi' require 'stringio' LINK_ADDR = '/LearnToProgram/' FRLTP_ADDR = 'https://pragprog.com/titles/ltp3/learn-to-program-third-edition/' RUBY_DL_ADDR = 'https://www.ruby-lang.org/en/downloads/' TRANSLATIONS = [ # ['Japanese' , 'Shin Nishiyama' , 'http://www1.tf.chiba-u.jp/~shin/tutorial/'], ['French' , 'Jean-Pierre ANGHEL', 'http://www.ruby-doc.org/docs/ApprendreProgrammer/Apprendre_a_Programmer.pdf'], # ['Russian' , 'Mikhail Shokhirev' , 'http://www.shokhirev.com/mikhail/ruby/ltp/title.html'], ['Danish' , 'Gunner Carstens' , 'http://gcarst.googlepages.com/komgodtigang'], ['Br. Portuguese', 'Fabio Akita et al.', 'http://aprendaaprogramar.rubyonrails.com.br/'], ['Bosnian' , 'Rusmir Gadžo' , 'http://sites.google.com/site/rubynabosanskom/'], ['Turkish' , 'Niyazi ATEŞ' , 'https://www.niyaziates.com.tr/ruby-book'], # ['Greek' , 'Ηλίας Μαργαριτίδης' , 'http://www.imargar.gr/wp-content/uploads/2011/06/egxeiridiometafrasi.pdf'], ['German' , 'Anja Stiedl' , 'https://moccasoft.com/papers-ruby-tutorial/'], ['Korean' , 'engfordev' , 'http://opentutorials.org/module/11'], ['Spanish' , 'rubysur' , 'http://rubysur.org/aprende.a.programar/'], ['Spanish' , "David O' Rojo" , 'http://goo.gl/3UcZi'], ['Italian' , 'Duccio Armenise' , 'https://corsidia.com/materia/programmazione/imparare-a-programmare'], ].sort class LearnToProgramTutorial @@HLINE = '
 
' # If you decide to change coloring or formatting of this tutorial, # you'll want to change these to something appropriate. @@NUMBER_COLOR = 'green' @@STRING_COLOR = 'red' @@KEYWORD_COLOR = 'blue' @@INPUT_DESC = 'dotted box' @@OUTPUT_DESC = 'grey box' # Delimiter for input to sample code, since it comes out # right alongside the output. Must not be html escapable. @@INPUT = "%%%'f'o'o'" def initialize @depth = 0 @page = [] end def getChapter(method) CHAPTERS.each do |chap, title, meth| return chap if (method == meth) end 'main' end def selfLink(chap = nil) LINK_ADDR+'chap_'+(chap ? getChapter(chap) : '')+'.html' end def makeLink(name, methodName) ''+name+'' end def puts(string, escapeThis=false) if escapeThis string = escapeHTML string end @page << ' '*@depth+string end def escapeHTML(string) string.gsub(/[&\"<>]/, CGI::Util::TABLE_FOR_ESCAPE_HTML__) end def escapeOutputNotInput(output) md = /#{@@INPUT}.*?#{@@INPUT.reverse}/.match output if md escapeHTML(md.pre_match) + escapeHTML(md[0]).sub(/#{@@INPUT}/,'').sub(/#{@@INPUT.reverse}/,'') + escapeOutputNotInput(md.post_match) else escapeHTML output end end def syntaxColor(str) # str has probably already been html-escaped. lines = str.split(/\n/) # L2Pcomment # L2Pstring # L2Pnumber # L2Pkeyword # L2Pdefinition lines.collect! do |line| #line += ' ' # for splitting... Do we need this? md = /'|#/.match line if md # Comment or string. syntaxColor(md.pre_match) + if (md[0] == '#') '' + md[0] + md.post_match + '' else # Big string time... md2 = /(.*?)(^|[^\\])((\\\\)*)'/.match md.post_match if (md2) md[0] + '' + $1 + $2 + $3 + '' + "'" + syntaxColor(md2.post_match) else md[0] end end else # No comment nor string. keywords = %w[__FILE__ and end in or self unless __LINE__ begin ensure redo super until BEGIN break do false next rescue then when END case else for nil retry true while alias elsif if not return undef yield] keywords.each do |keyword| line.gsub!(/(\W|^)(#{keyword})(\W|$)/) do $1+''+$2+''+$3 end end ['def', 'class', 'module'].each do |keyword| line.gsub!(/(\W|^)(#{keyword}) +([\w?]+)/) do $1 + '' + $2 + '' + ' ' + $3 + '' end end line.gsub!(/(^|[-{\[( ^+%*\/?;])(\d+(\.\d+)?|\.\d+)/) do $1+''+$2+'' end line end end lines.join "\n" end def input(str) str = escapeHTML str str.gsub!(/ /, ' ') ''+str+'' end def code(str) str = escapeHTML str str.gsub!(/ /, ' ') str = syntaxColor str ''+str+'' end def output(str) str = escapeHTML str str.gsub!(/ /, ' ') ''+str+'' end # This is the cool part... def executeCode(code, input) # Wrap code to catch errors and to stop SystemExit. code = <<-END_CODE begin #{code} rescue SystemExit rescue Exception => error puts error.inspect end END_CODE strIO = StringIO.new if !input.empty? input = input.join("\n")+"\n" input = StringIO.new(input, "r") class << strIO; self; end.module_eval do ['gets', 'getc', 'read'].each do |meth| define_method(meth) do |*params| inStr = input.method(meth).call(*params) puts @@INPUT+inStr.chomp+(@@INPUT.reverse) # Echo input. inStr end end end end # Pass these methods to strIO: kernelMethods = ['puts', 'putc', 'gets'] # Swap out Kernel methods... kernelMethods.each do |meth| Kernel.module_eval "alias __temp__tutorial__#{meth}__ #{meth}" Kernel.module_eval do define_method(meth) do |*params| strIO.method(meth).call(*params) end end end begin strIO.instance_eval code rescue Exception => error # Catch parse errors. return error.inspect end # ...and swap them back in. kernelMethods.each do |meth| Kernel.module_eval "alias #{meth} __temp__tutorial__#{meth}__" end strIO.string end # Tags (or similar) def para(attributes = {}, &block) method_missing(:p, attributes, &block) end def prog(execute = [], remark = nil, fakeOutput = nil, &block) if !execute return progN(&block) end run = {:input => execute} run[:remark ] = remark if remark run[:fakeOutput] = fakeOutput if fakeOutput progN(run, &block) end def progN(*trialRuns) code = yield # Trim leading whitespace. lines = code.split $/ numSpaces = lines[0].length - lines[0].sub(/ */, '').length lines.each do |line| line.sub!(/ {0,#{numSpaces}}/, '') end code = lines.join($/) prettyCode = syntaxColor(escapeHTML(code)) # Spit it out. puts '
'+prettyCode+'
' trialRuns.each do |run| if run[:fakeOutput] puts '
'+escapeHTML(run[:fakeOutput])+'
' end if run[:remark] puts '

'+run[:remark]+'

' end output = escapeOutputNotInput(executeCode(code,run[:input])) puts '
'+$/+output+'
' 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 '<'+methodName+attribString+' />' else puts '<'+methodName+attribString+'>' @depth += 1 blockReturn = yield puts blockReturn if (blockReturn.kind_of?(String)) @depth -= 1 puts '' 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 <<-'END_CODE' # Here is some 'Ruby' code. # 5 is better than 6. # def wilma do end if not in, dude. #' '#This shouldn\'t cause any problems.' 'Neither # should this\\' 'do end if elsif else case when then while def class' 'or and not next in' 'to 3 or not to 3, that is 3.7' '' + 'x'+''+''+'..' '' +'o' 8 0.09 9.9 5.times {} puts 'I love chips.' # yo 'g' puts 5.02 + 8 + 0.002 # s'up, muva jimmy = ['yoyoyo', 66] jimmy.each do |item| puts item.inspect end puts case 'pumpkin' when String then 'yep' when Fixnum then 'nope' else 'maybe' end def yummy if (4 <= 5) 'Yummm!' elsif (4 == 5) 'Huh?' else while (1 == 2) puts 'What?' end end end class JustSomeClass def initialize @var = 5 end end puts Math::PI # Should work. puts PI # Shouldn't work. END_CODE end para {'Here\'s some code with input and output:'} prog ['"Chris"', '&26', ''] do <<-END_CODE puts 'yoyo...wuddup?' puts 'NAME:' name = gets.chomp puts 'AGE:' age = gets.chomp puts 'FAVORITE COLOR' color = gets.chomp puts 'Hello, '+name+', the '+age+'-year-old '+color+' lover.' END_CODE end para do <<-END_PARAGRAPH Hello there. I love #{input 'inputting VARIOUS things'}. I also get a kick out of #{code 'coding various things'}. There's such a thrill in seeing all of the exciting #{output 'output you can get'} from a well-written program. END_PARAGRAPH end h2 {'A Few Things to Try'} ul do li {"Write a program which asks for a person's first name, then middle, then last. Finally, it should greet the person using their full name."} li {"Write a program which asks for a person's favorite number. Have your program add one to the number, then suggest the result as a bigger and better favorite number. (Do be tactful about it, though.)"} end para do <<-END_PARAGRAPH Once you have finished those two programs (and any others you would like to try), let's learn some more (and some more about) #{makeLink('methods', :generateMethods)}. END_PARAGRAPH end end # # SETUP # def generateSetup para do <<-END_PARAGRAPH When you program a computer, you have to "speak" in a language your computer understands: a programming language. There are lots and lots of different languages out there, and many of them are excellent. In this tutorial I chose to use my favorite programming language, Ruby. END_PARAGRAPH end para do <<-END_PARAGRAPH Aside from being my favorite, Ruby is also the easiest programming language I have seen (and I've seen quite a few). In fact, that's the real reason I'm writing this tutorial: I didn't decide to write a tutorial, and then choose Ruby because it's my favorite; instead, I found Ruby to be so easy that I decided there really ought to be a good beginner's tutorial which uses it. It's Ruby's simplicity which prompted this tutorial, not the fact that it's my favorite. (Writing a similar tutorial using another language, like C++ or Java, would have required hundreds and hundreds of pages.) But don't think that Ruby is a beginner's language just because it is easy! It is a powerful, professional-strength programming language if ever there was one. END_PARAGRAPH end para do <<-END_PARAGRAPH When you write something in a human language, what is written is called text. When you write something in a computer language, what is written is called 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 <<-END_PARAGRAPH If you come across something you don't understand, or you have a question which wasn't answered, write it down and keep reading! It's quite possible that the answer will come in a later chapter. However, if your question was not answered by the last chapter, I will tell you where you can go to ask it. There are lots of wonderful people out there more than willing to help; you just need to know where they are. END_PARAGRAPH end para do <<-END_PARAGRAPH But first we need to download and install Ruby onto your computer. END_PARAGRAPH end h2 {'Windows Installation'} para do <<-END_PARAGRAPH The Windows installation of Ruby is a breeze. First, you need to 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 <<-END_PARAGRAPH In order to program, you need to be able to write programs and to run programs. To do this, you will need a text editor and a command line. My favorite text editor is Sublime Text. END_PARAGRAPH end para do <<-END_PARAGRAPH It would also be a good idea to create a folder somewhere to keep all of your programs. Make sure that when you save a program, you save it into this folder. END_PARAGRAPH end para do <<-END_PARAGRAPH To get to your command line, select Command Prompt from the Accessories folder in your start menu. You will want to navigate to the folder where you are keeping your programs. Typing #{input 'cd ..'} will take you up one folder, and #{input 'cd foldername'} would put you inside the folder named foldername. To see all of the folders in your current folder, type #{input 'dir /ad'}. END_PARAGRAPH end para do <<-END_PARAGRAPH And that's it! You're all set to #{makeLink 'learn to program', :generateNumbers}. END_PARAGRAPH end h2 {'Macintosh Installation'} para do <<-END_PARAGRAPH If you have Mac OS X 10.2 (Jaguar) or later, then you already have Ruby on your system! What could be easier? END_PARAGRAPH end para do <<-END_PARAGRAPH In order to program, you need to be able to write programs and to run programs. To do this, you will need a text editor and a command line. END_PARAGRAPH end para do <<-END_PARAGRAPH Your command line is accessible through the Terminal application (found in Applications/Utilities). END_PARAGRAPH end para do <<-END_PARAGRAPH For a text editor, you can use whatever one you are familiar or comfortable with. My favorite text editor is 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 <<-END_PARAGRAPH And that's it! You're all set to #{makeLink 'learn to program', :generateNumbers}. END_PARAGRAPH end h2 {'Linux Installation'} para do <<-END_PARAGRAPH First, you will want to check and see if you have Ruby installed already. Type #{input 'which ruby'}. If it says something like #{output '/usr/bin/which: no ruby in (...)'}, then you need to 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 <<-END_PARAGRAPH If you are the root user, then you probably don't need any instructions for installing Ruby. If you aren't, you might want to ask your system administrator to install it for you. (That way everyone on that system could use Ruby.) END_PARAGRAPH end para do <<-END_PARAGRAPH Otherwise, you can just install it so that only you can use it. Move the file you downloaded to a temporary directory, like $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 <<-END_PARAGRAPH Configure your installation by typing #{input './configure --prefix=$HOME'}). Next type #{input 'make'}, which will build your Ruby interpreter. This might take a few minutes. After that is done, type #{input 'make install'} to install it. END_PARAGRAPH end para do <<-END_PARAGRAPH Next, you'll want to add $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 <<-END_PARAGRAPH And that's it! You're all set to #{makeLink 'learn to program', :generateNumbers}. END_PARAGRAPH end end # # NUMBERS # def generateNumbers para do <<-END_PARAGRAPH Now that you've gotten everything #{makeLink('setup', :generateSetup)}, let's write a program! Open up your favorite text editor and type in the following: END_PARAGRAPH end prog false do <<-END_CODE puts 1+2 END_CODE end para do <<-END_PARAGRAPH Save your program (yes, that's a program!) as #{input 'calc.rb'} (the .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 <<-END_PARAGRAPH So what's going on in that program? I'm sure you can guess what the #{code '1+2'} does; our program is basically the same as: END_PARAGRAPH end prog false do <<-END_CODE puts 3 END_CODE end para do <<-END_PARAGRAPH #{code 'puts'} simply writes onto the screen whatever comes after it. END_PARAGRAPH end h2 {'Integer and Float'} para do <<-END_PARAGRAPH In most programming languages (and Ruby is no exception) numbers without decimal points are called integers, and numbers with decimal points are usually called floating-point numbers, or more simply, floats. END_PARAGRAPH end para do <<-END_PARAGRAPH Here are some integers: END_PARAGRAPH end prog false do <<-END_CODE 5 -205 9999999999999999999999999 0 END_CODE end para do <<-END_PARAGRAPH And here are some floats: END_PARAGRAPH end prog false do <<-END_CODE 54.321 0.001 -205.3884 0.0 END_CODE end para do <<-END_PARAGRAPH In practice, most programs don't use floats; only integers. (After all, no one wants to look at 7.4 emails, or browse 1.8 webpages, or listen to 5.24 of their favorite songs...) Floats are used more for academic purposes (physics experiments and such) and for 3D graphics. Even most money programs use integers; they just keep track of the number of pennies! END_PARAGRAPH end h2 {'Simple Arithmetic'} para do <<-END_PARAGRAPH So far, we've got all the makings of a simple calculator. (Calculators always use floats, so if you want your computer to act just like a calculator, you should also use floats.) For addition and subtraction, we use + 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 <<-END_CODE puts 1.0 + 2.0 puts 2.0 * 3.0 puts 5.0 - 8.0 puts 9.0 / 2.0 END_CODE end para do <<-END_PARAGRAPH (The spaces in the program are not important; they just make the code easier to read.) Well, that wasn't too surprising. Now let's try it with integers: END_PARAGRAPH end prog [], 'Mostly the same, right?' do <<-END_CODE puts 1+2 puts 2*3 puts 5-8 puts 9/2 END_CODE end para do <<-END_PARAGRAPH Uh... except for that last one! But when you do arithmetic with integers, you'll get integer answers. When your computer can't get the "right" answer, it always rounds down. (Of course, #{output '4'} is the right answer in integer arithmetic for #{code '9/2'}; just maybe not the answer you were expecting.) END_PARAGRAPH end para do <<-END_PARAGRAPH Perhaps you're wondering what integer division is good for. Well, let's say you're going to the movies, but you only have $9. Here in Portland, you can see a movie at the Bagdad for 2 bucks. How many movies can you see there? #{code '9/2'}... #{output '4'} movies. 4.5 is definitely 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 <<-END_PARAGRAPH So now experiment with some programs of your own! If you want to write more complex expressions, you can use parentheses. For example: END_PARAGRAPH end prog do <<-END_CODE puts 5 * (12-8) + -15 puts 98 + (59872 / (13*8)) * -52 END_CODE end h2 {'A Few Things to Try'} para do puts 'Write a program which tells you:' end ul do li {'how many hours are in a year?'} li {'how many minutes are in a decade?'} li {'how many seconds old are you?'} li {'how many chocolates do you hope to eat in your life?
'+ '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 <<-END_PARAGRAPH When you're done playing around with numbers, let's have a look at some #{makeLink('letters', :generateLetters)}. END_PARAGRAPH end end # # LETTERS # def generateLetters para do <<-END_PARAGRAPH So we've learned all about #{makeLink('numbers', :generateNumbers)}, but what about letters? words? text? END_PARAGRAPH end para do <<-END_PARAGRAPH We refer to groups of letters in a program as 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 <<-END_CODE 'Hello.' 'Ruby rocks.' '5 is my favorite number... what is yours?' 'Snoopy says #%^?&*@! when he stubs his toe.' ' ' '' END_CODE end para do <<-END_PARAGRAPH As you can see, strings can have punctuation, digits, symbols, and spaces in them... more than just letters. That last string doesn't have anything in it at all; we would call that an empty string. END_PARAGRAPH end para do <<-END_PARAGRAPH We have been using #{code 'puts'} to print numbers; let's try it with some strings: END_PARAGRAPH end prog do <<-END_CODE puts 'Hello, world!' puts '' puts 'Good-bye.' END_CODE end para do <<-END_PARAGRAPH That worked out well. Now try some strings of your own. END_PARAGRAPH end h2 {'String Arithmetic'} para do <<-END_PARAGRAPH Just as you can do arithmetic on numbers, you can also do arithmetic on strings! Well, sort of... you can add strings, anyway. Let's try to add two strings and see what #{code 'puts'} does with that. END_PARAGRAPH end prog do <<-END_CODE puts 'I like' + 'apple pie.' END_CODE end para do <<-END_PARAGRAPH Whoops! I forgot to put a space between #{code "'I like'"} and #{code "'apple pie.'"}. Spaces don't matter usually, but they matter inside strings. (It's true what they say: computers don't do what you want them to do, only what you tell them to do.) Let's try that again: END_PARAGRAPH end prog do <<-END_CODE puts 'I like ' + 'apple pie.' puts 'I like' + ' apple pie.' END_CODE end para do <<-END_PARAGRAPH (As you can see, it didn't matter which string I added the space to.) END_PARAGRAPH end para do <<-END_PARAGRAPH So you can add strings, but you can also multiply them! (By a number, anyway.) Watch this: END_PARAGRAPH end prog [], '(Just kidding... it really does this:)', 'batting her eyes' do <<-END_CODE puts 'blink ' * 4 END_CODE end para do <<-END_PARAGRAPH If you think about it, this makes perfect sense. After all, #{code '7*3'} really just means #{code '7+7+7'}, so #{code "'moo'*3"} just means #{code "'moo'+'moo'+'moo'"}. END_PARAGRAPH end h2 {"#{code '12'} vs #{code "'12'"}"} para do <<-END_PARAGRAPH Before we get any further, we should make sure we understand the difference between numbers and digits. #{code '12'} is a number, but #{code "'12'"} is a string of two digits. END_PARAGRAPH end para do <<-END_PARAGRAPH Let's play around with this for a while: END_PARAGRAPH end prog do <<-END_CODE puts 12 + 12 puts '12' + '12' puts '12 + 12' END_CODE end para do <<-END_PARAGRAPH How about this: END_PARAGRAPH end prog do <<-END_CODE puts 2 * 5 puts '2' * 5 puts '2 * 5' END_CODE end para do <<-END_PARAGRAPH These examples were pretty straightforward. However, if you're not too careful with how you mix your strings and your numbers, you might run into... END_PARAGRAPH end h2 {'Problems'} para do <<-END_PARAGRAPH At this point you may have tried out a few things which didn't work. If not, here are a few: END_PARAGRAPH end prog do <<-END_CODE puts '12' + 12 puts '2' * '5' END_CODE end para do <<-END_PARAGRAPH Hmmm... an error message. The problem is that you can't really add a number to a string, or multiply a string by another string. It doesn't make any more sense than does this: END_PARAGRAPH end prog false do <<-END_CODE puts 'Betty' + 12 puts 'Fred' * 'John' END_CODE end para do <<-END_PARAGRAPH Something else to be aware of: you can write #{code "'pig'*5"} in a program, since it just means #{code '5'} sets of the string #{code "'pig'"} all added together. However, you 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 <<-END_PARAGRAPH Finally, what if I want a program to print out #{output 'You\'re swell!'}? We can try this: END_PARAGRAPH end prog false do <<-END_CODE puts 'You're swell!' END_CODE end para do <<-END_PARAGRAPH Well, 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 <<-END_CODE puts 'You\\'re swell!' END_CODE end para do <<-END_PARAGRAPH The backslash is the escape character. In other words, if you have a backslash and another character, they are sometimes translated into a new character. The only things the backslash escapes, though, are the apostrophe and the backslash itself. (If you think about it, escape characters must always escape themselves.) A few examples are in order here, I think: END_PARAGRAPH end prog do <<-END_CODE puts 'You\\'re swell!' puts 'backslash at the end of a string: \\\\' puts 'up\\\\down' puts 'up\\down' END_CODE end para do <<-END_PARAGRAPH Since the backslash does 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 <<-END_PARAGRAPH If you have any other questions, just #{makeLink('keep reading', :generateVariables)}! I couldn't answer every question on this page, after all. END_PARAGRAPH end end # # VARIABLES # def generateVariables para do <<-END_PARAGRAPH So far, whenever we have #{code 'puts'}ed a string or a number, the thing we #{code 'puts'}ed is gone. What I mean is, if we wanted to print something out twice, we would have to type it in twice: END_PARAGRAPH end prog do <<-END_CODE puts '...you can say that again...' puts '...you can say that again...' END_CODE end para do <<-END_PARAGRAPH It would be nice if we could just type it in once and then hang on to it... store it somewhere. Well, we can, of course—otherwise, I wouldn't have brought it up! END_PARAGRAPH end para do <<-END_PARAGRAPH To store the string in your computer's memory, we need to give the string a name. Programmers often refer to this process as 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 <<-END_CODE myString = '...you can say that again...' puts myString puts myString END_CODE end para do <<-END_PARAGRAPH Whenever you tried to do something to #{code 'myString'}, the program did it to #{code "'...you can say that again...'"} instead. You can think of the variable #{code 'myString'} as "pointing to" the string #{code "'...you can say that again...'"}. Here's a slightly more interesting example: END_PARAGRAPH end prog do <<-END_CODE name = 'Patricia Rosanna Jessica Mildred Oppenheimer' puts 'My name is ' + name + '.' puts 'Wow! ' + name + ' is a really long name!' END_CODE end para do <<-END_PARAGRAPH Also, just as we can 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 <<-END_CODE composer = 'Mozart' puts composer + ' was "da bomb", in his day.' composer = 'Beethoven' puts 'But I prefer ' + composer + ', personally.' END_CODE end para do <<-END_PARAGRAPH Of course, variables can point to any kind of object, not just strings: END_PARAGRAPH end prog do <<-END_CODE var = 'just another ' + 'string' puts var var = 5 * (1+2) puts var END_CODE end para do <<-END_PARAGRAPH In fact, variables can point to just about anything... except other variables. So what happens if we try? END_PARAGRAPH end prog do <<-END_CODE var1 = 8 var2 = var1 puts var1 puts var2 puts '' var1 = 'eight' puts var1 puts var2 END_CODE end para do <<-END_PARAGRAPH So first, when we tried to point #{code 'var2'} to #{code 'var1'}, it really pointed to #{code '8'} instead (just like #{code 'var1'} was pointing to). Then we had #{code 'var1'} point to #{code "'eight'"}, but since #{code 'var2'} was never really pointing at #{code 'var1'}, it stays pointing at #{code '8'}. END_PARAGRAPH end para do <<-END_PARAGRAPH So now that we've got variables, numbers, and strings, let's learn how to #{makeLink 'mix them all up', :generateConversion}! END_PARAGRAPH end end # # CONVERSION # def generateConversion para do <<-END_PARAGRAPH We've looked at a few different kinds of objects (#{makeLink 'numbers', :generateNumbers} and #{makeLink 'letters', :generateLetters}), and we made #{makeLink 'variables', :generateVariables} to point to them; the next thing we want to do is to get them all to play nicely together. END_PARAGRAPH end para do <<-END_PARAGRAPH We've seen that if we want a program to print #{output '25'}, the following does not work, because you can't add numbers and strings: END_PARAGRAPH end prog false do <<-END_CODE var1 = 2 var2 = '5' puts var1 + var2 END_CODE end para do <<-END_PARAGRAPH Part of the problem is that your computer doesn't know if you were trying to get #{output '7'} (#{code '2 + 5'}), or if you wanted to get #{output '25'} (#{code "'2' + '5'"}). END_PARAGRAPH end para do <<-END_PARAGRAPH Before we can add these together, we need some way of getting the string version of #{code 'var1'}, or to get the integer version of #{code 'var2'}. END_PARAGRAPH end h2 {'Conversions'} para do <<-END_PARAGRAPH To get the string version of an object, we simply write #{code '.to_s'} after it: END_PARAGRAPH end prog do <<-END_CODE var1 = 2 var2 = '5' puts var1.to_s + var2 END_CODE end para do <<-END_PARAGRAPH Similarly, #{code 'to_i'} gives the integer version of an object, and #{code 'to_f'} gives the float version. Let's look at what these three methods do (and don't do) a little more closely: END_PARAGRAPH end prog do <<-END_CODE var1 = 2 var2 = '5' puts var1.to_s + var2 puts var1 + var2.to_i END_CODE end para do <<-END_PARAGRAPH Notice that, even after we got the string version of #{code 'var1'} by calling #{code 'to_s'}, #{code 'var1'} was always pointing at #{code '2'}, and never at #{code "'2'"}. Unless we explicitly reassign #{code 'var1'} (which requires an #{code '='} sign), it will point at #{code '2'} for the life of the program. END_PARAGRAPH end para do <<-END_PARAGRAPH Now let's try some more interesting (and a few just weird) conversions: END_PARAGRAPH end prog do <<-END_CODE puts '15'.to_f puts '99.999'.to_f puts '99.999'.to_i puts '' puts '5 is my favorite number!'.to_i puts 'Who asked you about 5 or whatever?'.to_i puts 'Your momma did.'.to_f puts '' puts 'stringy'.to_s puts 3.to_i END_CODE end para do <<-END_PARAGRAPH So, this probably gave some surprises. The first one is pretty standard, giving #{output '15.0'}. After that, we converted the string #{code "'99.999'"} to a float and to an integer. The float did what we expected; the integer was, as always, rounded down. END_PARAGRAPH end para do <<-END_PARAGRAPH Next, we had some examples of some... 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 <<-END_PARAGRAPH Finally, we saw that our last two conversions did nothing at all, just as we would expect. END_PARAGRAPH end h2 {'Another Look at '+(code 'puts')} para do <<-END_PARAGRAPH There's something strange about our favorite method... Take a look at this: END_PARAGRAPH end prog do <<-END_CODE puts 20 puts 20.to_s puts '20' END_CODE end para do <<-END_PARAGRAPH Why do these three all print the same thing? Well, the last two should, since #{code '20.to_s'} 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 <<-END_PARAGRAPH Well, here's the big secret behind our friend, #{code 'puts'}: Before #{code 'puts'} tries to write out an object, it uses #{code 'to_s'} to get the string version of that object. In fact, the s in #{code 'puts'} stands for string; #{code 'puts'} really means put string. END_PARAGRAPH end para do <<-END_PARAGRAPH This may not seem too exciting now, but there are many, 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 <<-END_PARAGRAPH In the meantime, we have a few more methods for you, and they allow us to write all sorts of fun programs... END_PARAGRAPH end h2 {'The Methods '+(code 'gets')+' and '+(code 'chomp')} para do <<-END_PARAGRAPH If #{code 'puts'} means 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 <<-END_PARAGRAPH From you! Well, from your keyboard, anyway. Since your keyboard only makes strings, that works out beautifully. What actually happens is that #{code 'gets'} just sits there, reading what you type until you press Enter. Let's try it out: END_PARAGRAPH end prog ['Is there an echo in here?'] do <<-END_CODE puts gets END_CODE end para do <<-END_PARAGRAPH Of course, whatever you type in will just get repeated back to you. Run it a few times and try typing in different things. END_PARAGRAPH end para do <<-END_PARAGRAPH Now we can make interactive programs! In this one, type in your name and it will greet you: END_PARAGRAPH end prog ['Chris'], 'Eek! I just ran it—I typed in my name, and this is what happened:' do <<-END_CODE puts 'Hello there, and what\\'s your name?' name = gets puts 'Your name is ' + name + '? What a lovely name!' puts 'Pleased to meet you, ' + name + '. :)' END_CODE end para do <<-END_PARAGRAPH Hmmm... it looks like when I typed in the letters 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 <<-END_CODE puts 'Hello there, and what\\'s your name?' name = gets.chomp puts 'Your name is ' + name + '? What a lovely name!' puts 'Pleased to meet you, ' + name + '. :)' END_CODE end para do <<-END_PARAGRAPH Much better! Notice that since #{code 'name'} is pointing to #{code 'gets.chomp'}, we don't ever have to say #{code 'name.chomp'}; #{code 'name'} was already #{code 'chomp'}ed. END_PARAGRAPH end h2 {'A Few Things to Try'} ul do li {"Write a program which asks for a person's first name, then middle, then last. Finally, it should greet the person using their full name."} li {"Write a program which asks for a person's favorite number. Have your program add one to the number, then suggest the result as a bigger and better favorite number. (Do be tactful about it, though.)"} end para do <<-END_PARAGRAPH Once you have finished those two programs (and any others you would like to try), let's learn some more (and some more about) #{makeLink('methods', :generateMethods)}. END_PARAGRAPH end end # # METHODS # def generateMethods para do <<-END_PARAGRAPH So far we've seen a number of different methods, #{code 'puts'} and #{code 'gets'} and so on (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 <<-END_PARAGRAPH But really, that 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 <<-END_PARAGRAPH So, did you take the quiz? Good. Well, I'm sure you remembered the methods #{code 'puts'}, #{code 'gets'}, and #{code 'chomp'}, since we just covered those. You probably also got our conversion methods, #{code 'to_i'}, #{code 'to_f'}, and #{code 'to_s'}. However, did you get the other four? Why, it's none other than our old arithmetic buddies #{code '+'}, #{code '-'}, #{code '*'}, and #{code '/'}! END_PARAGRAPH end para do <<-END_PARAGRAPH So as I was saying, just as every verb needs a noun, so every method needs an object. It's usually easy to tell which object is performing the method: it's what comes right before the dot, like in our #{code 'clock.tick'} example, or in #{code '101.to_s'}. Sometimes, however, it's not quite as obvious; like with the arithmetic methods. As it turns out, #{code '5 + 5'} is really just a shortcut way of writing #{code '5.+ 5'}. For example: END_PARAGRAPH end prog do <<-END_CODE puts 'hello '.+ 'world' puts (10.* 9).+ 9 END_CODE end para do <<-END_PARAGRAPH It isn't very pretty, so we won't ever write it like that; however, it's important to understand what is 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 <<-END_PARAGRAPH And, of course, we still have #{code 'puts'} and #{code 'gets'} to explain. Where are their objects? In English, you can sometimes leave out the noun; for example, if a villain yells "Die!", the implicit noun is whoever he is yelling at. In Ruby, if I say #{code "puts 'to be or not to be'"}, what I am really saying is #{code "self.puts 'to be or not to be'"}. So what is #{code 'self'}? It's a special variable which points to whatever object you are in. We don't even know how to be 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 <<-END_CODE iCantBelieveIMadeAVariableNameThisLongJustToPointToA3 = 3 puts iCantBelieveIMadeAVariableNameThisLongJustToPointToA3 self.puts iCantBelieveIMadeAVariableNameThisLongJustToPointToA3 END_CODE end para do <<-END_PARAGRAPH If you didn't entirely follow all of that, that's OK. The important thing to take away from all of this is that every method is being done by some object, even if it doesn't have a dot in front of it. If you understand that, then you're all set. END_PARAGRAPH end h2 {'Fancy String Methods'} para do <<-END_PARAGRAPH Let's learn a few fun string methods. You don't have to memorize them all; you can just look up this page again if you forget them. I just want to show you a 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 <<-END_PARAGRAPH So, our first string method is #{code 'reverse'}, which gives a backwards version of a string: END_PARAGRAPH end prog do <<-END_CODE var1 = 'stop' var2 = 'stressed' var3 = 'Can you pronounce this sentence backwards?' puts var1.reverse puts var2.reverse puts var3.reverse puts var1 puts var2 puts var3 END_CODE end para do <<-END_PARAGRAPH As you can see, #{code 'reverse'} doesn't reverse the original string; it just makes a new backwards version of it. That's why #{code 'var1'} is still #{code "'stop'"} even after we called #{code 'reverse'} on #{code 'var1'}. END_PARAGRAPH end para do <<-END_PARAGRAPH Another string method is #{code 'length'}, which tells us the number of characters (including spaces) in the string: END_PARAGRAPH end prog ['Christopher David Pine'] do <<-END_CODE puts 'What is your full name?' name = gets.chomp puts 'Did you know there are ' + name.length + ' characters in your name, ' + name + '?' END_CODE end para do <<-END_PARAGRAPH Uh-oh! Something went wrong, and it looks like it happened sometime after the line #{code 'name = gets.chomp'}... Do you see the problem? See if you can figure it out. END_PARAGRAPH end para do <<-END_PARAGRAPH The problem is with #{code 'length'}: it gives us a number, but we want a string. Easy enough, we'll just throw in a #{code 'to_s'} (and cross our fingers): END_PARAGRAPH end prog ['Christopher David Pine'] do <<-END_CODE puts 'What is your full name?' name = gets.chomp puts 'Did you know there are ' + name.length.to_s + ' characters in your name, ' + name + '?' END_CODE end para do <<-END_PARAGRAPH No, I did not know that. 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 <<-END_PARAGRAPH Did you do it? Good! It's nice to program, isn't it? After a few more chapters, though, you'll be amazed at what you can do. END_PARAGRAPH end para do <<-END_PARAGRAPH So, there are also a number of string methods which change the case (uppercase and lowercase) of your string. #{code 'upcase'} changes every lowercase letter to uppercase, and #{code 'downcase'} changes every uppercase letter to lowercase. #{code 'swapcase'} switches the case of every letter in the string, and finally, #{code 'capitalize'} is just like #{code 'downcase'}, except that it switches the first character to uppercase (if it is a letter). END_PARAGRAPH end prog do <<-END_CODE letters = 'aAbBcCdDeE' puts letters.upcase puts letters.downcase puts letters.swapcase puts letters.capitalize puts ' a'.capitalize puts letters END_CODE end para do <<-END_PARAGRAPH Pretty standard stuff. As you can see from the line #{code "puts ' a'.capitalize"}, the method #{code 'capitalize'} only capitalizes the first 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 <<-END_PARAGRAPH The last of the fancy string methods we'll look at are for visual formatting. The first one, #{code 'center'}, adds spaces to the beginning and end of the string to make it centered. However, just like you have to tell #{code 'puts'} what you want it to print, and #{code '+'} what you want it to add, you have to tell #{code 'center'} how wide you want your centered string to be. So if I wanted to center the lines of a poem, I would do it like this: END_PARAGRAPH end prog do <<-END_CODE lineWidth = 50 puts( 'Old Mother Hubbard'.center(lineWidth)) puts( 'Sat in her cupboard'.center(lineWidth)) puts( 'Eating her curds an whey,'.center(lineWidth)) puts( 'When along came a spider'.center(lineWidth)) puts( 'Which sat down beside her'.center(lineWidth)) puts('And scared her poor shoe dog away.'.center(lineWidth)) END_CODE end para do <<-END_PARAGRAPH Hmmm... I don't think that's how that nursery rhyme goes, but I'm too lazy to look it up. (Also, I wanted to line up the #{code '.center lineWidth'} part, so I put in those extra spaces before the strings. This is just because I think it is prettier that way. Programmers often have strong feelings about what is pretty in a program, and they often disagree about it. The more you program, the more you will come into your own style.) Speaking of being lazy, laziness isn't always a bad thing in programming. For example, see how I stored the width of the poem in the variable #{code 'lineWidth'}? This was so that if I want to go back later and make the poem wider, I only have to change the very top line of the program, instead of every line which does centering. With a very long poem, this could save me a lot of time. That kind of laziness is really a virtue in programming. END_PARAGRAPH end para do <<-END_PARAGRAPH So, about that centering... you may have noticed that it isn't quite as beautiful as what a word processor would have done. If you really want perfect centering (and maybe a nicer font), then you should just use a word processor! Ruby is a wonderful tool, but no tool is the right tool for every job. END_PARAGRAPH end para do <<-END_PARAGRAPH The other two string formatting methods are #{code 'ljust'} and #{code 'rjust'}, which stand for 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 <<-END_CODE lineWidth = 40 str = '--> text <--' puts str.ljust lineWidth puts str.center lineWidth puts str.rjust lineWidth puts str.ljust(lineWidth/2) + str.rjust(lineWidth/2) END_CODE end h2 {'A Few Things to Try'} ul do li {"Write an Angry Boss program. It should rudely ask what you want. Whatever you answer, the Angry Boss should yell it back to you, and then fire you. For example, if you type in #{input 'I want a raise.'}, it should yell back #{output 'WHADDAYA MEAN "I WANT A RAISE."?!? YOU\'RE FIRED!!'}"} li do para do <<-END_PARAGRAPH So here's something for you to do in order to play around more with #{code 'center'}, #{code 'ljust'}, and #{code 'rjust'}: Write a program which will display a Table of Contents so that it looks like this: END_PARAGRAPH end puts '
' +
             '                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 <<-END_PARAGRAPH (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 <<-END_PARAGRAPH There aren't nearly as many number methods as there are string methods (though I still don't know them all off the top of my head). Here, we'll look at the rest of the arithmetic methods, a random number generator, and the #{code 'Math'} object, with its trigonometric and transcendental methods. END_PARAGRAPH end h2 {'More Arithmetic'} para do <<-END_PARAGRAPH The other two arithmetic methods are #{code '**'} (exponentiation) and #{code '%'} (modulus). So if you want to say "five squared" in Ruby, you would write it as #{code '5**2'}. You can also use floats for your exponent, so if you want the square root of 5, you could write #{code '5**0.5'}. The modulus method gives you the remainder after division by a number. So, for example, if I divide 7 by 3, I get 2 with a remainder of 1. Let's see it working in a program: END_PARAGRAPH end prog do <<-END_CODE puts 5**2 puts 5**0.5 puts 7/3 puts 7%3 puts 365%7 END_CODE end para do <<-END_PARAGRAPH From that last line, we learn that a (non-leap) year has some number of weeks, plus one day. So if your birthday was on a Tuesday this year, it will be on a Wednesday next year. You can also use floats with the modulus method. Basically, it works the only sensible way it could... but I'll let you play around with that. END_PARAGRAPH end para do <<-END_PARAGRAPH There's one last method to mention before we check out the random number generator: #{code 'abs'}. It just takes the absolute value of the number: END_PARAGRAPH end prog do <<-END_CODE puts((5-2).abs) puts((2-5).abs) END_CODE end h2 {'Random Numbers'} para do <<-END_PARAGRAPH Ruby comes with a pretty nice random number generator. The method to get a randomly chosen number is #{code 'rand'}. If you call #{code 'rand'} just like that, you'll get a float greater than or equal to #{code '0.0'} and less than #{code '1.0'}. If you give #{code 'rand'} an integer (#{code '5'} for example), it will give you an integer greater than or equal to #{code '0'} and less than #{code '5'} (so five possible numbers, from #{code '0'} to #{code '4'}). END_PARAGRAPH end para do <<-END_PARAGRAPH Let's see #{code 'rand'} in action. END_PARAGRAPH end prog do <<-END_CODE puts rand puts rand puts rand puts(rand(100)) puts(rand(100)) puts(rand(100)) puts(rand(1)) puts(rand(1)) puts(rand(1)) puts(rand(99999999999999999999999999999999999999999999999999999999999)) puts('The weatherman said there is a '+rand(101).to_s+'% chance of rain,') puts('but you can never trust a weatherman.') END_CODE end para do <<-END_PARAGRAPH Note that I used #{code 'rand(101)'} to get back numbers from #{code '0'} to #{code '100'}, and that #{code 'rand(1)'} always gives back #{code '0'}. Not understanding the range of possible return values is the biggest mistake I see people make with #{code 'rand'}; even professional programmers; even in finished products you can buy at the store. I even had a CD player once which, if set on "Random Play," would play every song but the last one... (I wonder what would have happened if I had put in a CD with only one song on it?) END_PARAGRAPH end para do <<-END_PARAGRAPH Sometimes you might want #{code 'rand'} to return the 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 <<-END_CODE srand 1776 puts(rand(100)) puts(rand(100)) puts(rand(100)) puts(rand(100)) puts(rand(100)) puts '' srand 1776 puts(rand(100)) puts(rand(100)) puts(rand(100)) puts(rand(100)) puts(rand(100)) END_CODE end para do <<-END_PARAGRAPH It will do the same thing every time you seed it with the same number. If you want to get different numbers again (like what happens if you never use #{code 'srand'}), then just call #{code 'srand 0'}. This seeds it with a really weird number, using (among other things) the current time on your computer, down to the millisecond. END_PARAGRAPH end h2 {"The #{code 'Math'} Object"} para do <<-END_PARAGRAPH Finally, let's look at the #{code 'Math'} object. We might as well jump right in: END_PARAGRAPH end prog do <<-END_CODE puts(Math::PI) puts(Math::E) puts(Math.cos(Math::PI/3)) puts(Math.tan(Math::PI/4)) puts(Math.log(Math::E**2)) puts((1 + Math.sqrt(5))/2) END_CODE end para do <<-END_PARAGRAPH The first thing you noticed was probably the #{code '::'} notation. Explaining the 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 <<-END_PARAGRAPH As you can see, #{code 'Math'} has all of the things you would expect a decent scientific calculator to have. And as always, the floats are really close to being the right answers. END_PARAGRAPH end para do <<-END_PARAGRAPH So now let's #{makeLink 'flow', :generateFlowControl}! END_PARAGRAPH end end # # FLOW CONTROL # def generateFlowControl para do <<-END_PARAGRAPH Ahhhh, flow control. This is where it all comes together. Even though this chapter is shorter and easier than the #{makeLink 'methods', :generateMethods} chapter, it will open up a whole world of programming possibilities. After this chapter, we'll be able to write truly interactive programs; in the past we have made programs which 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 <<-END_PARAGRAPH Let's rush through this part so we can get to the next section, 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 '<'}, like this: END_PARAGRAPH end prog do <<-END_CODE puts 1 > 2 puts 1 < 2 END_CODE end para do <<-END_PARAGRAPH No problem. Likewise, we can find out if an object is greater-than-or-equal-to another (or less-than-or-equal-to) with the methods #{code '>='} and #{code '<='} END_PARAGRAPH end prog do <<-END_CODE puts 5 >= 5 puts 5 <= 4 END_CODE end para do <<-END_PARAGRAPH And finally, we can see if two objects are equal or not using #{code '=='} (which means "are these equal?") and #{code '!='} (which means "are these different?"). It's important not to confuse #{code '='} with #{code '=='}. #{code '='} is for telling a variable to point at an object (assignment), and #{code '=='} is for asking the question: "Are these two objects equal?" END_PARAGRAPH end prog do <<-END_CODE puts 1 == 1 puts 2 != 1 END_CODE end para do <<-END_PARAGRAPH Of course, we can compare strings, too. When strings get compared, they compare their lexicographical ordering, which basically means their dictionary ordering. #{code 'cat'} comes before #{code 'dog'} in the dictionary, so: END_PARAGRAPH end prog do <<-END_CODE puts 'cat' < 'dog' END_CODE end para do <<-END_PARAGRAPH There's a catch, though: the way computers usually do things, they order capital letters as coming before lowercase letters. (That's how they store the letters in fonts, for example: all the capital letters first, then the lowercase ones.) This means that it will think #{code "'Zoo'"} comes before #{code "'ant'"}, so if you want to figure out which word would come first in a real dictionary, make sure to use #{code 'downcase'} (or #{code 'upcase'} or #{code 'capitalize'}) on both words before you try to compare them. END_PARAGRAPH end para do <<-END_PARAGRAPH One last note before 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 <<-END_PARAGRAPH Branching is a simple concept, but powerful. In fact, it's so simple that I bet I don't even have to explain it at all; I'll just show you: END_PARAGRAPH end run1 = {:input => ['Chris']} run2 = {:input => ['Chewbacca'], :remark => 'But if we put in a different name...'} progN run1, run2 do <<-END_CODE puts 'Hello, what\\'s your name?' name = gets.chomp puts 'Hello, ' + name + '.' if name == 'Chris' puts 'What a lovely name!' end END_CODE end para do <<-END_PARAGRAPH And that is branching. If what comes after the #{code 'if'} is #{code 'true'}, we run the code between the #{code 'if'} and the #{code 'end'}. If what comes after the #{code 'if'} is #{code 'false'}, we don't. Plain and simple. END_PARAGRAPH end para do <<-END_PARAGRAPH I indented the code between the #{code 'if'} and the #{code 'end'} just because I think it's easier to keep track of the branching that way. Almost all programmers do this, regardless of what language they are programming in. It may not seem much help in this simple example, but when things get more complex, it makes a big difference. END_PARAGRAPH end para do <<-END_PARAGRAPH Often, we would like a program to do one thing if an expression is #{code 'true'}, and another if it is #{code 'false'}. That's what #{code 'else'} is for: END_PARAGRAPH end run1 = {:input => ['Chris']} run2 = {:input => ['Ringo'], :remark => 'Now let\'s try a different name...'} progN run1, run2 do <<-END_CODE puts 'I am a fortune-teller. Tell me your name:' name = gets.chomp if name == 'Chris' puts 'I see great things in your future.' else puts 'Your future is... Oh my! Look at the time!' puts 'I really have to go, sorry!' end END_CODE end para do <<-END_PARAGRAPH Branching is kind of like coming to a fork in the code: Do we take the path for people whose #{code "name == 'Chris'"}, or #{code 'else'} do we take the other path? END_PARAGRAPH end para do <<-END_PARAGRAPH And just like the branches of a tree, you can have branches which themselves have branches: END_PARAGRAPH end run1 = {:input => ['chris', 'yes']} run2 = {:input => ['Chris'], :remark => 'Fine, I\'ll capitalize it...'} progN run1, run2 do <<-END_CODE puts 'Hello, and welcome to 7th grade English.' puts 'My name is Mrs. Gabbard. And your name is...?' name = gets.chomp if name == name.capitalize puts 'Please take a seat, ' + name + '.' else puts name + '? You mean ' + name.capitalize + ', right?' puts 'Don\\'t you even know how to spell your name??' reply = gets.chomp if reply.downcase == 'yes' puts 'Hmmph! Well, sit down!' else puts 'GET OUT!!' end end END_CODE end para do <<-END_PARAGRAPH Sometimes it might get confusing trying to figure out where all of the #{code 'if'}s, #{code 'else'}s, and #{code 'end'}s go. What I do is write the #{code 'end'} 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 <<-END_CODE puts 'Hello, and welcome to 7th grade English.' puts 'My name is Mrs. Gabbard. And your name is...?' name = gets.chomp if name == name.capitalize else end END_CODE end para do <<-END_PARAGRAPH Then I filled it in with comments, stuff in the code the computer will ignore: END_PARAGRAPH end prog false do <<-END_CODE puts 'Hello, and welcome to 7th grade English.' puts 'My name is Mrs. Gabbard. And your name is...?' name = gets.chomp if name == name.capitalize # She's civil. else # She gets mad. end END_CODE end para do <<-END_PARAGRAPH Anything after a #{code '#'} is considered a comment (unless, of course, you are in a string). After that, I replaced the comments with working code. Some people like to leave the comments in; personally, I think well-written code usually speaks for itself. I used to use more comments, but the more "fluent" in Ruby I become, the less I use them. I actually find them distracting much of the time. It's a personal choice; you'll find your own (usually evolving) style. So my next step looked like this: END_PARAGRAPH end prog false do <<-END_CODE puts 'Hello, and welcome to 7th grade English.' puts 'My name is Mrs. Gabbard. And your name is...?' name = gets.chomp if name == name.capitalize puts 'Please take a seat, ' + name + '.' else puts name + '? You mean ' + name.capitalize + ', right?' puts 'Don\\'t you even know how to spell your name??' reply = gets.chomp if reply.downcase == 'yes' else end end END_CODE end para do <<-END_PARAGRAPH Again, I wrote down the #{code 'if'}, #{code 'else'}, and #{code 'end'} all at the same time. It really helps me keep track of "where I am" in the code. It also makes the job seem easier because I can focus on one small part, like filling in the code between the #{code 'if'} and the #{code 'else'}. The other benefit of doing it this way is that the computer can understand the program at any stage. Every one of the unfinished versions of the program I showed you would run. They weren't finished, but they were working programs. That way I could test it as I wrote it, which helped to see how it was coming along and where it still needed work. When it passed all of the tests, that's how I knew I was done! END_PARAGRAPH end para do <<-END_PARAGRAPH These tips will help you write programs with branching, but they also help with the other main type of flow control: END_PARAGRAPH end h2 {'Looping'} para do <<-END_PARAGRAPH Often, you'll want your computer to do the same thing over and over again—after all, that's what computers are supposed to be so good at. END_PARAGRAPH end para do <<-END_PARAGRAPH When you tell your computer to keep repeating something, you also need to tell it when to stop. Computers never get bored, so if you don't tell it to stop, it won't. We make sure this doesn't happen by telling the computer to repeat certain parts of a program #{code 'while'} a certain condition is true. This works very similarly to how #{code 'if'} works: END_PARAGRAPH end prog ['Hello?', 'Hi!', 'Very nice to meet you.', 'Oh... how sweet!', 'bye'] do <<-END_CODE command = '' while command != 'bye' puts command command = gets.chomp end puts 'Come again soon!' END_CODE end para do <<-END_PARAGRAPH And that's a loop. (You may have noticed the blank line at the beginning of the output; it's from the first #{code 'puts'}, before the first #{code 'gets'}. How would you change the program to get rid of this first line. Test it! Did it work exactly like the program above, other than that first blank line?) END_PARAGRAPH end para do <<-END_PARAGRAPH Loops allow you to do all kinds of interesting things, as I'm sure you can imagine. However, they can also cause problems if you make a mistake. What if your computer gets trapped in an infinite loop? If you think this may have happened, just hold down the Ctrl key and press C. END_PARAGRAPH end para do <<-END_PARAGRAPH Before we start playing around with loops, though, let's learn a few things to make our job easier. END_PARAGRAPH end h2 {'A Little Bit of Logic'} para do <<-END_PARAGRAPH Let's take a look at our first branching program again. What if my wife came home, saw the program, tried it out, and it didn't tell her what a lovely name she had? Well... she probably wouldn't care. But I'd care! So let's rewrite it: END_PARAGRAPH end prog ['Katy'] do <<-END_CODE puts 'Hello, what\\'s your name?' name = gets.chomp puts 'Hello, ' + name + '.' if name == 'Chris' puts 'What a lovely name!' else if name == 'Katy' puts 'What a lovely name!' end end END_CODE end para do <<-END_PARAGRAPH It works... but it isn't a very pretty program. Why not? Well, the best rule I ever learned in programming was the 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 <<-END_CODE puts 'Hello, what\\'s your name?' name = gets.chomp puts 'Hello, ' + name + '.' if (name == 'Chris' or name == 'Katy') puts 'What a lovely name!' end END_CODE end para do <<-END_PARAGRAPH Much better. In order to make it work, I used #{code 'or'}. The other 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 <<-END_CODE iAmChris = true iAmPurple = false iLikeFood = true iEatRocks = false puts (iAmChris and iLikeFood) puts (iLikeFood and iEatRocks) puts (iAmPurple and iLikeFood) puts (iAmPurple and iEatRocks) puts puts (iAmChris or iLikeFood) puts (iLikeFood or iEatRocks) puts (iAmPurple or iLikeFood) puts (iAmPurple or iEatRocks) puts puts (not iAmPurple) puts (not iAmChris ) END_CODE end para do <<-END_PARAGRAPH The only one of these which might trick you is #{code 'or'}. In English, we often use "or" to mean "one or the other, but not both." For example, your mom might say, "For dessert, you can have pie or cake." She did 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 <<-END_PARAGRAPH When you finish those, take a break! You've learned a lot already. Congratulations! Are you surprised at the number of things you can tell a computer to do? A few more chapters and you'll be able to program just about anything. Seriously! Just look at all the things you can do now that you couldn't do without looping and branching. END_PARAGRAPH end para do <<-END_PARAGRAPH Now let's learn about a new kind of object, one which keeps track of lists of other objects: #{makeLink 'arrays', :generateArrays}. END_PARAGRAPH end end # # ARRAYS AND ITERATORS # def generateArrays para do <<-END_PARAGRAPH Let's write a program which asks us to type in as many words as we want (one word per line, continuing until we just press Enter on an empty line), and which then repeats the words back to us in alphabetical order. OK? END_PARAGRAPH end para do <<-END_PARAGRAPH So... first we'll—uh... um... hmmm... Well, we could—er... um... END_PARAGRAPH end para do <<-END_PARAGRAPH You know, I don't think we can do it. We need a way to store an unknown amount of words, and how to keep track of them all together, so they don't get mixed up with other variables. We need to put them in some sort of a list. We need arrays. END_PARAGRAPH end para do <<-END_PARAGRAPH An array is just a list in your computer. Every slot in the list acts like a variable: you can see what object a particular slot points to, and you can make it point to a different object. Let's take a look at some arrays: END_PARAGRAPH end prog false do <<-END_CODE [] [5] ['Hello', 'Goodbye'] flavor = 'vanilla' # This is not an array, of course... [89.9, flavor, [true, false]] # ...but this is. END_CODE end para do <<-END_PARAGRAPH So first we have an empty array, then an array holding a single number, then an array holding two strings. Next, we have a simple assignment; then an array holding three objects, the last of which is the array #{code '[true, false]'}. Remember, variables aren't objects, so our last array is really pointing to float, a 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 <<-END_PARAGRAPH To help us find a particular object in an array, each slot is given an index number. Programmers (and, incidentally, most mathematicians) start counting from zero, though, so the first slot in the array is slot zero. Here's how we would reference the objects in an array: END_PARAGRAPH end prog do <<-END_CODE names = ['Ada', 'Belle', 'Chris'] puts names puts names[0] puts names[1] puts names[2] puts names[3] # This is out of range. END_CODE end para do <<-END_PARAGRAPH So, we see that #{code 'puts names'} prints each name in the array #{code 'names'}. Then we use #{code 'puts names[0]'} to print out the "first" name in the array, and #{code 'puts names[1]'} to print the "second"... I'm sure this seems confusing, but you 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 <<-END_PARAGRAPH Finally, we tried #{code 'puts names[3]'}, just to see what would happen. Were you expecting an error? Sometimes when you ask a question, your question doesn't make sense (at least to your computer); that's when you get an error. Sometimes, however, you can ask a question and the answer is 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 <<-END_PARAGRAPH If all this funny numbering of array slots is getting to you, fear not! Often, we can avoid them completely by using various array methods, like this one: END_PARAGRAPH end h2 {"The Method #{code 'each'}"} para do <<-END_PARAGRAPH #{code 'each'} allows us to do something (whatever we want) to #{code 'each'} object the array points to. So, if we want to say something nice about each language in the array below, we'd do this: END_PARAGRAPH end prog do <<-END_CODE languages = ['English', 'German', 'Ruby'] languages.each do |lang| puts 'I love ' + lang + '!' puts 'Don\\'t you?' end puts 'And let\\'s hear it for C++!' puts '...' END_CODE end para do <<-END_PARAGRAPH So what just happened? Well, we were able to go through every object in the array without using any numbers, so that's definitely nice. Translating into English, the above program reads something like: For #{code 'each'} object in #{code 'languages'}, point the variable #{code 'lang'} to the object and then #{code 'do'} everything I tell you to, until you come to the #{code 'end'}. (Just so you know, C++ is another programming language. It's much harder to learn than Ruby; usually, a C++ program will be many times longer than a Ruby program which does the same thing.) END_PARAGRAPH end para do <<-END_PARAGRAPH You might be thinking to yourself, "This is a lot like the loops we learned about earlier." Yep, it's similar. One important difference is that the method #{code 'each'} is just that: a method. #{code 'while'} and #{code 'end'} (much like #{code 'do'}, #{code 'if'}, #{code 'else'}, and all the other #{@@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 <<-END_PARAGRAPH But not #{code 'each'}; #{code 'each'} is just another array method. Methods like #{code 'each'} which "act like" loops are often called iterators. END_PARAGRAPH end para do <<-END_PARAGRAPH One thing to notice about iterators is that they are always followed by #{code 'do'}...#{code 'end'}. #{code 'while'} and #{code 'if'} never had a #{code 'do'} near them; we only use #{code 'do'} with iterators. END_PARAGRAPH end para do <<-END_PARAGRAPH Here's another cute little iterator, but it's not an array method... it's an integer method! END_PARAGRAPH end prog do <<-END_CODE 3.times do puts 'Hip-Hip-Hooray!' end END_CODE end h2 {'More Array Methods'} para do <<-END_PARAGRAPH So we've learned #{code 'each'}, but there are many other array methods... almost as many as there are string methods! In fact, some of them (like #{code 'length'}, #{code 'reverse'}, #{code '+'}, and #{code '*'}) work just like they do for strings, except that they operate on the slots of the array rather than the letters of the string. Others, like #{code 'last'} and #{code 'join'}, are specific to arrays. Still others, like #{code 'push'} and #{code 'pop'}, actually change the array. And just as with the string methods, you don't have to remember all of these, as long as you can remember where to find out about them (right here). END_PARAGRAPH end para do <<-END_PARAGRAPH First, let's look at #{code 'to_s'} and #{code 'join'}. #{code 'join'} works much like #{code 'to_s'} does, except that it adds a string in between the array's objects. Let's take a look: END_PARAGRAPH end prog do <<-END_CODE foods = ['artichoke', 'brioche', 'caramel'] puts foods puts puts foods.to_s puts puts foods.join(', ') puts puts foods.join(' :) ') + ' 8)' 200.times do puts [] end END_CODE end para do <<-END_PARAGRAPH As you can see, #{code 'puts'} treats arrays differently from other objects: it just calls #{code 'puts'} on each of the objects in the array. That's why #{code 'puts'}ing an empty array 200 times doesn't do anything; the array doesn't point to anything, so there's nothing to #{code 'puts'}. (Doing nothing 200 times is still doing nothing.) Try #{code 'puts'}ing an array containing other arrays; does it do what you expected? END_PARAGRAPH end para do <<-END_PARAGRAPH Also, did you notice that I left out the empty strings when I wanted to #{code 'puts'} a blank line? It does the same thing. END_PARAGRAPH end para do <<-END_PARAGRAPH Now let's take a look at #{code 'push'}, #{code 'pop'}, and #{code 'last'}. The methods #{code 'push'} and #{code 'pop'} are sort of opposites, like #{code '+'} and #{code '-'} are. #{code 'push'} adds an object to the end of your array, and #{code 'pop'} removes the last object from the array (and tell you what it was). #{code 'last'} is similar to #{code 'pop'} in that it tells you what's at the end of the array, except that it leaves the array alone. Again, #{code 'push'} and #{code 'pop'} actually change the array: END_PARAGRAPH end prog do <<-END_CODE favorites = [] favorites.push 'raindrops on roses' favorites.push 'whiskey on kittens' puts favorites[0] puts favorites.last puts favorites.length puts favorites.pop puts favorites puts favorites.length END_CODE end h2 {'A Few Things to Try'} ul do li {"Write the program we talked about at the very beginning of this chapter.
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 <<-END_PARAGRAPH So far we have learned quite a number of different methods. Now it's time to learn how to #{makeLink 'make our own', :generateDefMethod}. END_PARAGRAPH end end # # WRITING METHODS # def generateDefMethod para do <<-END_PARAGRAPH As we've seen, loops and iterators allow us to do the same thing (run the same code) over and over again. However, sometimes we want to do the same thing a number of times, but from different places in the program. For example, let's say we were writing a questionnaire program for a psychology student. From the psychology students I have known and the questionnaires they have given me, it would probably go something like this: END_PARAGRAPH end prog ['yes','yes','no way!','NO','yes','yes'] do <<-END_CODE puts 'Hello, and thank you for taking the time to' puts 'help me with this experiment. My experiment' puts 'has to do with the way people feel about' puts 'Mexican food. Just think about Mexican food' puts 'and try to answer every question honestly,' puts 'with either a "yes" or a "no". My experiment' puts 'has nothing to do with bed-wetting.' puts # We ask these questions, but we ignore their answers. goodAnswer = false while (not goodAnswer) puts 'Do you like eating tacos?' answer = gets.chomp.downcase if (answer == 'yes' or answer == 'no') goodAnswer = true else puts 'Please answer "yes" or "no".' end end goodAnswer = false while (not goodAnswer) puts 'Do you like eating burritos?' answer = gets.chomp.downcase if (answer == 'yes' or answer == 'no') goodAnswer = true else puts 'Please answer "yes" or "no".' end end # We pay attention to *this* answer, though. goodAnswer = false while (not goodAnswer) puts 'Do you wet the bed?' answer = gets.chomp.downcase if (answer == 'yes' or answer == 'no') goodAnswer = true if answer == 'yes' wetsBed = true else wetsBed = false end else puts 'Please answer "yes" or "no".' end end goodAnswer = false while (not goodAnswer) puts 'Do you like eating chimichangas?' answer = gets.chomp.downcase if (answer == 'yes' or answer == 'no') goodAnswer = true else puts 'Please answer "yes" or "no".' end end puts 'Just a few more questions...' goodAnswer = false while (not goodAnswer) puts 'Do you like eating sopapillas?' answer = gets.chomp.downcase if (answer == 'yes' or answer == 'no') goodAnswer = true else puts 'Please answer "yes" or "no".' end end # Ask lots of other questions about Mexican food. puts puts 'DEBRIEFING:' puts 'Thank you for taking the time to help with' puts 'this experiment. In fact, this experiment' puts 'has nothing to do with Mexican food. It is' puts 'an experiment about bed-wetting. The Mexican' puts 'food was just there to catch you off guard' puts 'in the hopes that you would answer more' puts 'honestly. Thanks again.' puts puts wetsBed END_CODE end para do <<-END_PARAGRAPH That was a pretty long program, with lots of repetition. (All of the sections of code around the questions about Mexican food were identical, and the bed-wetting question was only slightly different.) Repetition is a bad thing. Still, we can't make it into a big loop or iterator, because sometimes we have things we want to do between questions. In situations like these, it's best to write a method. Here's how: END_PARAGRAPH end prog do <<-END_CODE def sayMoo puts 'mooooooo...' end END_CODE end para do <<-END_PARAGRAPH Uh... our program didn't #{code 'sayMoo'}. Why not? Because we didn't tell it to. We told it how to #{code 'sayMoo'}, but we never actually said to do it. Let's give it another shot: END_PARAGRAPH end prog do <<-END_CODE def sayMoo puts 'mooooooo...' end sayMoo sayMoo puts 'coin-coin' sayMoo sayMoo END_CODE end para do <<-END_PARAGRAPH Ahhh, much better. (Just in case you don't speak French, that was a French duck in the middle of the program. In France, ducks say "coin-coin".) END_PARAGRAPH end para do <<-END_PARAGRAPH So we #{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 <<-END_PARAGRAPH You may have noticed that some methods (like #{code 'gets'}, #{code 'to_s'}, #{code 'reverse'}...) you can just call on an object. However, other methods (like #{code '+'}, #{code '-'}, #{code 'puts'}...) take 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 <<-END_PARAGRAPH To add a parameter to #{code 'sayMoo'} (let's say, the number of moos), we would do this: END_PARAGRAPH end prog do <<-END_CODE def sayMoo numberOfMoos puts 'mooooooo...'*numberOfMoos end sayMoo 3 puts 'oink-oink' sayMoo # This should give an error because the parameter is missing. END_CODE end para do <<-END_PARAGRAPH #{code 'numberOfMoos'} is a variable which points to the parameter passed in. I'll say that again, but it's a little confusing: #{code 'numberOfMoos'} is a variable which points to the parameter passed in. So if I type in #{code 'sayMoo 3'}, then the parameter is #{code '3'}, and the variable #{code 'numberOfMoos'} points to #{code '3'}. END_PARAGRAPH end para do <<-END_PARAGRAPH As you can see, the parameter is now 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 <<-END_PARAGRAPH If objects in Ruby are like nouns in English, and methods are like verbs, then you can think of parameters as adverbs (like with #{code 'sayMoo'}, where the parameter told us 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 <<-END_PARAGRAPH In the following program, there are two variables: END_PARAGRAPH end prog do <<-END_CODE def doubleThis num numTimes2 = num*2 puts num.to_s+' doubled is '+numTimes2.to_s end doubleThis 44 END_CODE end para do <<-END_PARAGRAPH The variables are #{code 'num'} and #{code 'numTimes2'}. They both sit inside the method #{code 'doubleThis'}. These (and all of the variables you have seen so far) are 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 <<-END_CODE def doubleThis num numTimes2 = num*2 puts num.to_s+' doubled is '+numTimes2.to_s end doubleThis 44 puts numTimes2.to_s END_CODE end para do <<-END_PARAGRAPH Undefined local variable... In fact, we 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 <<-END_PARAGRAPH This might seem inconvenient, but it's actually quite nice. While it does mean that you have no access to variables inside methods, it also means that they have no access to your variables, and thus can't screw them up: END_PARAGRAPH end prog do <<-END_CODE def littlePest var var = nil puts 'HAHA! I ruined your variable!' end var = 'You can\\'t even touch my variable!' littlePest var puts var END_CODE end para do <<-END_PARAGRAPH There are actually 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 <<-END_PARAGRAPH You may have noticed that some methods give you something back when you call them. For example, #{code 'gets'} 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 <<-END_PARAGRAPH It's important to understand the difference between methods returning a value to where the method was called, and your program outputting information to your screen, like #{code 'puts'} does. Notice that #{code '5+3'} returns #{code '8'}; it does not output #{output '8'}. END_PARAGRAPH end para do <<-END_PARAGRAPH So what does #{code 'puts'} return? We never cared before, but let's look at it now: END_PARAGRAPH end prog do <<-END_CODE returnVal = puts 'This puts returned:' puts returnVal END_CODE end para do <<-END_PARAGRAPH The first #{code 'puts'} didn't seem to return anything, and in a way it didn't; it returned #{code 'nil'}. Though we didn't test it, the second #{code 'puts'} did, too; #{code 'puts'} always returns #{code 'nil'}. Every method has to return something, even if it's just #{code 'nil'}. END_PARAGRAPH end para do <<-END_PARAGRAPH Take a quick break and write a program to find out what #{code 'sayMoo'} returned. END_PARAGRAPH end para do <<-END_PARAGRAPH Were you surprised? Well, here's how it works: the value returned from a method is simply the last line of the method. In the case of #{code 'sayMoo'}, this means it returns #{code "puts 'mooooooo...'*numberOfMoos"}, which is just #{code 'nil'} since #{code 'puts'} always returns #{code 'nil'}. If we wanted all of our methods to return the string #{code "'yellow submarine'"}, we would just need to put that at the end of them: END_PARAGRAPH end prog do <<-END_CODE def sayMoo numberOfMoos puts 'mooooooo...'*numberOfMoos 'yellow submarine' end x = sayMoo 2 puts x END_CODE end para do <<-END_PARAGRAPH So, let's try that psychology experiment again, but this time we'll write a method to ask the questions for us. It will need to take the question as a parameter, and return #{code 'true'} if they answered #{input 'yes'} and #{code 'false'} if they answered #{input 'no'}. (Even though most of the time we just ignore the answer, it's still a good idea for our method to return the answer. This way we can use it for the bed-wetting question, too.) I'm also going to shorten the greeting and the debriefing, just so this is easier to read: END_PARAGRAPH end prog ['yes','yes','no way!','NO','yes','yes','yes','yes','yes'] do <<-END_CODE def ask question goodAnswer = false while (not goodAnswer) puts question reply = gets.chomp.downcase if (reply == 'yes' or reply == 'no') goodAnswer = true if reply == 'yes' answer = true else answer = false end else puts 'Please answer "yes" or "no".' end end answer # This is what we return (true or false). end puts 'Hello, and thank you for...' puts ask 'Do you like eating tacos?' # We ignore this return value. ask 'Do you like eating burritos?' wetsBed = ask 'Do you wet the bed?' # We save this return value. ask 'Do you like eating chimichangas?' ask 'Do you like eating sopapillas?' ask 'Do you like eating tamales?' puts 'Just a few more questions...' ask 'Do you like drinking horchata?' ask 'Do you like eating flautas?' puts puts 'DEBRIEFING:' puts 'Thank you for...' puts puts wetsBed END_CODE end para do <<-END_PARAGRAPH Not bad, huh? We were able to add more questions (and adding questions is 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 <<-END_PARAGRAPH I think another example method would be helpful here. We'll call this one #{code 'englishNumber'}. It will take a number, like #{code '22'}, and return the english version of it (in this case, the string #{code "'twenty-two'"}). For now, let's have it only work on integers from #{code '0'} to #{code '100'}. END_PARAGRAPH end para do <<-END_PARAGRAPH (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 <<-END_CODE def englishNumber number # We only want numbers from 0-100. if number < 0 return 'Please enter a number zero or greater.' end if number > 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 <<-END_PARAGRAPH Well, there are certainly a few things about this program I don't like. First, it has too much repetition. Second, it doesn't handle numbers greater than 100. Third, there are too many special cases, too many #{code 'return'}s. Let's use some arrays and try to clean it up a bit: END_PARAGRAPH end prog do <<-END_CODE def englishNumber number if number < 0 # No negative numbers. return 'Please enter a number that isn\\'t negative.' end if number == 0 return 'zero' end # No more special cases! No more returns! numString = '' # This is the string we will return. onesPlace = ['one', 'two', 'three', 'four', 'five', 'six', 'seven', 'eight', 'nine'] tensPlace = ['ten', 'twenty', 'thirty', 'forty', 'fifty', 'sixty', 'seventy', 'eighty', 'ninety'] teenagers = ['eleven', 'twelve', 'thirteen', 'fourteen', 'fifteen', 'sixteen', 'seventeen', 'eighteen', 'nineteen'] # "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 # 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 <<-END_PARAGRAPH 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 <<-END_PARAGRAPH Congratulations! At this point, you are a true programmer! You have learned everything you need to build huge programs from scratch. If you have ideas for programs you would like to write for yourself, give them a shot! END_PARAGRAPH end para do <<-END_PARAGRAPH Of course, building everything from scratch can be a pretty slow process. Why spend time writing code that someone else already wrote? Would you like your program to send some email? Would you like to save and load files on your computer? How about generating web pages for a tutorial where the code samples are all automatically tested? ;) Ruby has many different #{makeLink 'kinds of objects', :generateClasses} we can use to help us write better programs faster. END_PARAGRAPH end end # # CLASSES # def generateClasses para do <<-END_PARAGRAPH So far we've seen several different kinds, or 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 <<-END_CODE a = Array.new + [12345] # Array addition. b = String.new + 'hello' # String addition. c = Time.new puts 'a = '+a.to_s puts 'b = '+b.to_s puts 'c = '+c.to_s END_CODE end para do <<-END_PARAGRAPH Because we can create arrays and strings using #{code '[...]'} and #{code "'...'"} respectively, we rarely create them using #{code 'new'}. (Though it's not really obvious from the above example, #{code 'String.new'} creates an empty string, and #{code 'Array.new'} creates an empty array.) Also, numbers are special exceptions: you can't create an integer with #{code 'Integer.new'}. You just have to write the integer. END_PARAGRAPH end h2 {"The #{code 'Time'} Class"} para do <<-END_PARAGRAPH So what's the story with this #{code 'Time'} class? #{code 'Time'} objects represent moments in time. You can add (or subtract) numbers to (or from) times to get new times: adding #{code '1.5'} to a time makes a new time one-and-a-half seconds later: END_PARAGRAPH end prog do <<-END_CODE time = Time.new # The moment I generated this web page. time2 = time + 60 # One minute later. puts time puts time2 END_CODE end para do <<-END_PARAGRAPH You can also make a time for a specific moment using #{code 'Time.mktime'}: END_PARAGRAPH end prog do <<-END_CODE puts Time.mktime(2000, 1, 1) # Y2K. puts Time.mktime(1976, 8, 3, 10, 11) # When I was born. END_CODE end para do <<-END_PARAGRAPH Notice: that's when I was born in Pacific Daylight Savings Time (PDT). When Y2K struck, though, it was Pacific Standard Time (PST), at least to us West Coasters. The parentheses are to group the parameters to #{code 'mktime'} together. The more parameters you add, the more accurate your time becomes. END_PARAGRAPH end para do <<-END_PARAGRAPH You can compare times using the comparison methods (an earlier time is 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 <<-END_PARAGRAPH Another useful class is the #{code 'Hash'} class. Hashes are a lot like arrays: they have a bunch of slots which can point to various objects. However, in an array, the slots are lined up in a row, and each one is numbered (starting from zero). In a hash, the slots aren't in a row (they are just sort of jumbled together), and you can use 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 <<-END_CODE colorArray = [] # same as Array.new colorHash = {} # same as Hash.new colorArray[0] = '#{@@STRING_COLOR}' colorArray[1] = '#{@@NUMBER_COLOR}' colorArray[2] = '#{@@KEYWORD_COLOR}' colorHash['strings'] = '#{@@STRING_COLOR}' colorHash['numbers'] = '#{@@NUMBER_COLOR}' colorHash['keywords'] = '#{@@KEYWORD_COLOR}' colorArray.each do |color| puts color end colorHash.each do |codeType, color| puts codeType + ': ' + color end END_CODE end para do <<-END_PARAGRAPH If I use an array, I have to remember that slot #{code '0'} is for strings, slot #{code '1'} is for numbers, etc. But if I use a hash, it's easy! Slot #{code "'strings'"} holds the color of the strings, of course. Nothing to remember. You might have noticed that when we used #{code 'each'}, the objects in the hash didn't come out in the same order we put them in. Arrays are for keeping things in order, not hashes. END_PARAGRAPH end para do <<-END_PARAGRAPH Though people usually use strings to name the slots in a hash, you could use any kind of object, even arrays and other hashes (though I can't think of why you would want to do this...): END_PARAGRAPH end prog false do <<-END_CODE weirdHash = Hash.new weirdHash[12] = 'monkeys' weirdHash[[]] = 'emptiness' weirdHash[Time.new] = 'no time like the present' END_CODE end para do <<-END_PARAGRAPH Hashes and arrays are good for different things; it's up to you to decide which one is best for a particular problem. END_PARAGRAPH end h2 {'Extending Classes'} para do <<-END_PARAGRAPH At the end of the last chapter, you wrote a method to give the English phrase for a given integer. It wasn't an integer method, though; it was just a generic "program" method. Wouldn't it be nice if you could write something like #{code '22.to_eng'} instead of #{code 'englishNumber 22'}? Here's how you would do that: END_PARAGRAPH end # HACK ALERT!!! (I can't get to the global namespace transparently # from inside the StringIO object in a mod_ruby script.) integerClassHack = "def to_eng if self == 5 english = 'five' else english = 'fifty-eight' end english end" Integer.module_eval integerClassHack # This is the real method definition. # The following defines a method in "another" integer class: # END HACK ALERT!!! prog do <<-END_CODE class Integer #{integerClassHack} end # I'd better test on a couple of numbers... puts 5.to_eng puts 58.to_eng END_CODE end para do <<-END_PARAGRAPH Well, I tested it; it seems to work. ;) END_PARAGRAPH end para do <<-END_PARAGRAPH So we defined an integer method by jumping into the #{code 'Integer'} class, defining the method there, and jumping back out. Now all integers have this (somewhat incomplete) method. In fact, if you didn't like the way a built-in method like #{code 'to_s'} worked, you could just redefine it in much the same way... but I don't recommend it! It's best to leave the old methods alone and to make new ones when you want to do something new. END_PARAGRAPH end para do <<-END_PARAGRAPH So... confused yet? Let me go over that last program some more. So far, whenever we executed any code or defined any methods, we did it in the default "program" object. In our last program, we left that object for the first time and went into the class #{code 'Integer'}. We defined a method there (which makes it an integer method) and all integers can use it. Inside that method we use #{code 'self'} to refer to the object (the integer) using the method. END_PARAGRAPH end h2 {'Creating Classes'} para do <<-END_PARAGRAPH We've seen a number of different classes of objects. However, it's easy to come up with kinds of objects that Ruby doesn't have. Luckily, creating a new class is as easy as extending an old one. Let's say we wanted to make some dice in Ruby. Here's how we could make the Die class: END_PARAGRAPH end prog do <<-END_CODE class Die def roll 1 + rand(6) end end # Let's make a couple of dice... dice = [Die.new, Die.new] # ...and roll them. dice.each do |die| puts die.roll end END_CODE end para do <<-END_PARAGRAPH (If you skipped the section on random numbers, #{code 'rand(6)'} just gives a random number between #{code '0'} and #{code '5'}.) END_PARAGRAPH end para do <<-END_PARAGRAPH And that's it! Objects of our very own. END_PARAGRAPH end para do <<-END_PARAGRAPH We can define all sorts of methods for our objects... but there's something missing. Working with these objects feels a lot like programming before we learned about variables. Look at our dice, for example. We can roll them, and each time we do they give us a different number. But if we wanted to hang on to that number, we would have to create a variable to point to the number. It seems like any decent die should be able to 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 <<-END_PARAGRAPH However, if we try to store the number we rolled in a (local) variable in #{code 'roll'}, it will be gone as soon as #{code 'roll'} is finished. We need to store the number in a different kind of variable: END_PARAGRAPH end h2 {'Instance Variables'} para do <<-END_PARAGRAPH Normally when we want to talk about a string, we will just call it a 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 <<-END_PARAGRAPH So instance variables are just an object's variables. A method's local variables last until the method is finished. An object's instance variables, on the other hand, will last as long as the object does. To tell instance variables from local variables, they have #{code '@'} in front of their names: END_PARAGRAPH end prog do <<-END_CODE class Die def roll @numberShowing = 1 + rand(6) end def showing @numberShowing end end die = Die.new die.roll puts die.showing puts die.showing die.roll puts die.showing puts die.showing END_CODE end para do <<-END_PARAGRAPH Very nice! So #{code 'roll'} rolls the die and #{code 'showing'} tells us which number is showing. However, what if we try to look at what's showing before we've rolled the die (before we've set #{code '@numberShowing'})? END_PARAGRAPH end prog do <<-END_CODE class Die def roll @numberShowing = 1 + rand(6) end def showing @numberShowing end end # Since I'm not going to use this die again, # I don't need to save it in a variable. puts Die.new.showing END_CODE end para do <<-END_PARAGRAPH Hmmm... well, at least it didn't give us an error. Still, it doesn't really make sense for a die to be "unrolled", or whatever #{output 'nil'} is supposed to mean here. It would be nice if we could set up our new die object right when it's created. That's what #{code 'initialize'} is for: END_PARAGRAPH end prog do <<-END_CODE class Die def initialize # I'll just roll the die, though we # could do something else if we wanted # to, like setting the die with 6 showing. roll end def roll @numberShowing = 1 + rand(6) end def showing @numberShowing end end puts Die.new.showing END_CODE end para do <<-END_PARAGRAPH When an object is created, its #{code 'initialize'} method (if it has one defined) is always called. END_PARAGRAPH end para do <<-END_PARAGRAPH Our dice are just about perfect. The only thing that might be missing is a way to set which side of a die is showing... why don't you write a #{code 'cheat'} method which does just that! Come back when you're done (and when you tested that it worked, of course). Make sure that someone can't set the die to have a #{code '7'} showing! END_PARAGRAPH end para do <<-END_PARAGRAPH So that's some pretty cool stuff we just covered. It's tricky, though, so let me give another, more interesting example. Let's say we want to make a simple virtual pet, a baby dragon. Like most babies, it should be able to eat, sleep, and poop, which means we will need to be able to feed it, put it to bed, and take it on walks. Internally, our dragon will need to keep track of if it is hungry, tired, or needs to go, but we won't be able to see that when we interact with our dragon, just like you can't ask a human baby, "Are you hungry?". We'll also add a few other fun ways we can interact with our baby dragon, and when he is born we'll give him a name. (Whatever you pass into the #{code 'new'} method is passed into the #{code 'initialize'} method for you.) Alright, let's give it a shot: END_PARAGRAPH end prog do <<-END_CODE class Dragon def initialize name @name = name @asleep = false @stuffInBelly = 10 # He's full. @stuffInIntestine = 0 # He doesn't need to go. puts @name + ' is born.' end def feed puts 'You feed ' + @name + '.' @stuffInBelly = 10 passageOfTime end def walk puts 'You walk ' + @name + '.' @stuffInIntestine = 0 passageOfTime end def putToBed puts 'You put ' + @name + ' to bed.' @asleep = true 3.times do if @asleep passageOfTime end if @asleep puts @name + ' snores, filling the room with smoke.' end end if @asleep @asleep = false puts @name + ' wakes up slowly.' end end def toss puts 'You toss ' + @name + ' up into the air.' puts 'He giggles, which singes your eyebrows.' passageOfTime end def rock puts 'You rock ' + @name + ' gently.' @asleep = true puts 'He briefly dozes off...' passageOfTime if @asleep @asleep = false puts '...but wakes when you stop.' end end private # "private" means that the methods defined here are # methods internal to the object. (You can feed # your dragon, but you can't ask him if he's hungry.) def hungry? # Method names can end with "?". # Usually, we only do this if the method # returns true or false, like this: @stuffInBelly <= 2 end def poopy? @stuffInIntestine >= 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 <<-END_PARAGRAPH 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 <<-END_PARAGRAPH We saw a few new things in that example. The first is simple: #{code 'exit'} terminates the program right then and there. The second is the word #{code 'private'} which we stuck right in the middle of our class definition. I could have left it out, but I wanted to enforce the idea of certain methods being things you can do to a dragon, and others which simply happen within the dragon. You can think of these as being "under the hood": unless you are an automobile mechanic, all you really need to know is the gas pedal, the brake pedal, and the steering wheel. A programmer might call those the 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 <<-END_PARAGRAPH Actually, for a bit more concrete example along those lines, let's talk about how you might represent a car in a video game (which happens to be my line of work). First, you would want to decide what you want your public interface to look like; in other words, which methods should people be able to call on one of your car objects? Well, they need to be able to push the gas pedal and the brake pedal, but they would also need to be able to specify how hard they are pushing the pedal. (There's a big difference between flooring it and tapping it.) They would also need to be able to steer, and again, they would need to be able to say how hard they are turning the wheel. I suppose you could go further and add a clutch, turn signals, rocket launcher, afterburner, flux capacitor, etc... it depends on what type of game you are making. END_PARAGRAPH end para do <<-END_PARAGRAPH Internal to a car object, though, there would need to be much more going on; other things a car would need are a speed, a direction, and a position (at the most basic). These attributes would be modified by pressing on the gas or brake pedals and turning the wheel, of course, but the user would not be able to set the position directly (which would be like warping). You might also want to keep track of skidding or damage, if you have caught any air, and so on. These would all be internal to your car object. END_PARAGRAPH end h2 {'A Few Things to Try'} ul do li {"Make an #{code 'OrangeTree'} class. It should have a #{code 'height'} method which returns its height, and a #{code 'oneYearPasses'} method, which, when called, ages the tree one year. Each year the tree grows taller (however much you think an orange tree should grow in a year), and after some number of years (again, your call) the tree should die. For the first few years, it should not produce fruit, but after a while it should, and I guess that older trees produce more each year than younger trees... whatever you think makes most sense. And, of course, you should be able to #{code 'countTheOranges'} (which returns the number of oranges on the tree), and #{code 'pickAnOrange'} (which reduces the #{code '@orangeCount'} by one and returns a string telling you how delicious the orange was, or else it just tells you that there are no more oranges to pick this year). Make sure that any oranges you don't pick one year fall off before the next year."} li {"Write a program so that you can interact with your baby dragon. You should be able to enter commands like #{input 'feed'} and #{input 'walk'}, and have those methods be called on your dragon. Of course, since what you are inputting are just strings, you will have to have some sort of method dispatch, where your program checks which string was entered, and then calls the appropriate method."} end para do <<-END_PARAGRAPH And that's just about all there is to it! But wait a second... I haven't told you about any of those classes for doing things like sending an email, or saving and loading files on your computer, or how to create windows and buttons, or 3D worlds, or anything! Well, there are just 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 <<-END_PARAGRAPH This is definitely one of the coolest features of Ruby. Some other languages have this feature, though they may call it something else (like closures), but most of the more popular ones don't, and it's a shame. END_PARAGRAPH end para do <<-END_PARAGRAPH So what is this cool new thing? It's the ability to take a 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 <<-END_CODE toast = Proc.new do puts 'Cheers!' end toast.call toast.call toast.call END_CODE end para do <<-END_PARAGRAPH So I created a proc (which I think is supposed to be short for "procedure", but far more importantly, it rhymes with "block") which held the block of code, then I #{code 'call'}ed the proc three times. As you can see, it's a lot like a method. END_PARAGRAPH end para do <<-END_PARAGRAPH Actually, it's even more like a method than I have shown you, because blocks can take parameters: END_PARAGRAPH end prog do <<-END_CODE doYouLike = Proc.new do |aGoodThing| puts 'I *really* like '+aGoodThing+'!' end doYouLike.call 'chocolate' doYouLike.call 'ruby' END_CODE end para do <<-END_PARAGRAPH Ok, so we see what blocks and procs are, and how to use them, but what's the point? Why not just use methods? Well, it's because there are some things you just can't do with methods. In particular, you can't pass methods into other methods (but you can pass procs into methods), and methods can't return other methods (but they can return procs). This is simply because procs are objects; methods aren't. END_PARAGRAPH end para do <<-END_PARAGRAPH (By the way, is any of this looking familiar? Yep, you've seen blocks before... when you learned about iterators. But let's talk more about that in a bit.) END_PARAGRAPH end h2 { 'Methods Which Take Procs' } para do <<-END_PARAGRAPH When we pass a proc into a method, we can control how, if, or how many times we call the proc. For example, let's say there's something we want to do before and after some code is run: END_PARAGRAPH end prog do <<-END_CODE def doSelfImportantly someProc puts 'Everybody just HOLD ON! I have something to do...' someProc.call puts 'Ok everyone, I\\'m done. Go on with what you were doing.' end sayHello = Proc.new do puts 'hello' end sayGoodbye = Proc.new do puts 'goodbye' end doSelfImportantly sayHello doSelfImportantly sayGoodbye END_CODE end para do <<-END_PARAGRAPH Maybe that doesn't appear particulary fabulous... but it is. :-) It's all too common in programming to have strict requirements about what must be done when. If you want to save a file, for example, you have to open the file, write out the information you want it to have, and then close the file. If you forget to close the file, Bad Things(tm) can happen. But each time you want to save or load a file, you have to do the same thing: open the file, do what you 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 <<-END_PARAGRAPH You can also write methods which will determine how many times, or even 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_CODE def maybeDo someProc if rand(2) == 0 someProc.call end end def twiceDo someProc someProc.call someProc.call end wink = Proc.new do puts '' end glance = Proc.new do puts '' end maybeDo wink maybeDo glance twiceDo wink twiceDo glance END_CODE end para do <<-END_PARAGRAPH These are some of the more common uses of procs which enable us to do things we simply could not have done using methods alone. Sure, you could write a method to wink twice, but you couldn't write one to just do something twice! END_PARAGRAPH end para do <<-END_PARAGRAPH Before we move on, let's look at one last example. So far the procs we have passed in have been fairly similar to each other. This time they will be quite different, so you can see how much such a method depends on the procs passed into it. Our method will take some object and a proc, and will call the proc on that object. If the proc returns false, we quit; otherwise we call the proc with the returned object. We keep doing this until the proc returns false (which it had better do eventually, or the program will crash). The method will return the last non-false value returned by the proc. END_PARAGRAPH end prog do <<-END_CODE def doUntilFalse firstInput, someProc input = firstInput output = firstInput while output input = output output = someProc.call input end input end buildArrayOfSquares = Proc.new do |array| lastNumber = array.last if lastNumber <= 0 false else array.pop # Take off the last number... array.push lastNumber*lastNumber # ...and replace it with its square... array.push lastNumber-1 # ...followed by the next smaller number. end end alwaysFalse = Proc.new do |justIgnoreMe| false end puts doUntilFalse([5], buildArrayOfSquares).inspect puts doUntilFalse('I\\'m writing this at 3:00 am; someone knock me out!', alwaysFalse) END_CODE end para do <<-END_PARAGRAPH Ok, so that was a pretty weird example, I'll admit. But it shows how differently our method acts when given very different procs. END_PARAGRAPH end para do <<-END_PARAGRAPH The #{code 'inspect'} method is a lot like #{code 'to_s'}, except that the string it returns tries to show you the ruby code for building the object you passed it. Here it shows us the whole array returned by our first call to #{code 'doUntilFalse'}. Also, you might notice that we never actually squared that #{code '0'} on the end of that array, but since #{code '0'} squared is still just #{code '0'}, we didn't have to. And since #{code 'alwaysFalse'} was, you know, always #{code 'false'}, #{code 'doUntilFalse'} didn't do anything at all the second time we called it; it just returned what was passed in. END_PARAGRAPH end h2 { 'Methods Which Return Procs' } para do <<-END_PARAGRAPH One of the other cool things you can do with procs is to create them in methods and return them. This allows all sorts of crazy programming power (things with impressive names, like 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 <<-END_PARAGRAPH In this example, #{code 'compose'} takes two procs and returns a new proc which, when called, calls the first proc and passes its result into the second proc. END_PARAGRAPH end prog do <<-END_CODE def compose proc1, proc2 Proc.new do |x| proc2.call(proc1.call(x)) end end squareIt = Proc.new do |x| x * x end doubleIt = Proc.new do |x| x + x end doubleThenSquare = compose doubleIt, squareIt squareThenDouble = compose squareIt, doubleIt puts doubleThenSquare.call(5) puts squareThenDouble.call(5) END_CODE end para do <<-END_PARAGRAPH Notice that the call to #{code 'proc1'} had to be inside the parentheses for #{code 'proc2'} in order for it to be done first. END_PARAGRAPH end h2 { 'Passing Blocks (Not Procs) into Methods' } para do <<-END_PARAGRAPH Ok, so this has been sort of academically interesting, but also sort of a hassle to use. A lot of the problem is that there are three steps you have to go through (defining the method, making the proc, and calling the method with the proc), when it sort of feels like there should only be two (defining the method, and passing the 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 <<-END_PARAGRAPH I'll show you a quick example first, then we'll talk about it. END_PARAGRAPH end # HACK ALERT!!! (I can't get to the global namespace transparently # from inside the StringIO object in a mod_ruby script.) arrayClassHack = "def eachEven(&wasABlock_nowAProc) # We start with \"true\" because arrays start with 0, which is even. isEven = true self.each do |object| if isEven wasABlock_nowAProc.call object end isEven = (not isEven) # Toggle from even to odd, or odd to even. end end" Array.module_eval arrayClassHack # This is the real method definition. # The following defines a method in "another" array class: # END HACK ALERT!!! prog do <<-END_CODE class Array #{arrayClassHack} end ['apple', 'bad apple', 'cherry', 'durian'].eachEven do |fruit| puts 'Yum! I just love '+fruit+' pies, don\\'t you?' end # Remember, we are getting the even-numbered elements # of the array, all of which happen to be odd numbers, # just because I like to cause problems like that. [1, 2, 3, 4, 5].eachEven do |oddBall| puts oddBall.to_s+' is NOT an even number!' end END_CODE end para do <<-END_PARAGRAPH So to pass in a block to #{code 'eachEven'}, all we had to do was stick the block after the method. You can pass a block into any method this way, though many methods will just ignore the block. In order to make your method 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 <<-END_PARAGRAPH If you get confused, just remember what #{code 'eachEven'} is supposed to do: call the block passed in with every other element in the array. Once you've written it and it works, you don't need to think about what it's actually doing under the hood ("which block is called when??"); in fact, that's exactly 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 <<-END_PARAGRAPH I remember one time I wanted to be able to time how long different sections of a program were taking. (This is also known as 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 <<-END_CODE def profile descriptionOfBlock, &block startTime = Time.now block.call duration = Time.now - startTime puts descriptionOfBlock+': '+duration.to_s+' seconds' end profile '25000 doublings' do number = 1 25000.times do number = number + number end # Show the number of digits in this HUGE number. puts number.to_s.length.to_s+' digits' end profile 'count to a million' do number = 0 1000000.times do number = number + 1 end end END_CODE end para do <<-END_PARAGRAPH How simple! How elegant! With that tiny method, I can now easily time any section of any program that I want to; I just throw the code in a block and send it to #{code 'profile'}. What could be simpler? In most languages, I would have to explicitly add that timing code (the stuff in #{code 'profile'}) around every section which I wanted to time. In Ruby, however, I get to keep it all in one place, and (more importantly) out of my way! END_PARAGRAPH end h2 {'A Few Things to Try'} ul do li 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 '
' +
              'Beginning "outer block"...' + $/ +
              'Beginning "some little block"...' + $/ +
              '..."some little block" finished, returning:  5' + $/ +
              'Beginning "yet another block"...' + $/ +
              '..."yet another block" finished, returning:  I like Thai food!' + $/ +
              '..."outer block" finished, returning:  false' + $/ +
              '
' 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 '
' +
              'Beginning "outer block"...' + $/ +
              '  Beginning "some little block"...' + $/ +
              '    Beginning "teeny-tiny block"...' + $/ +
              '    ..."teeny-tiny block" finished, returning:  lots of love' + $/ +
              '  ..."some little block" finished, returning:  42' + $/ +
              '  Beginning "yet another block"...' + $/ +
              '  ..."yet another block" finished, returning:  I love Indian food!' + $/ +
              '..."outer block" finished, returning:  true' + $/ +
              '
' end end para do <<-END_PARAGRAPH Well, that's about all you're going to learn from this tutorial. Congratulations! You've learned a lot! Maybe you don't feel like you remember everything, or you skipped over some parts... really, that's just fine. Programming isn't about what you know; it's about what you can figure out. As long as you know where to find out the things you forgot, you're doing just fine. I hope you don't think that I wrote all of this without looking things up every other minute! Because I did. I also got a lot of help with the code which runs all of the examples in this tutorial. But where was I looking stuff up, and who was I asking for help? #{makeLink 'Let me show you...', :generateBeyond} END_PARAGRAPH end end # # BEYOND THIS TUTORIAL # def generateBeyond para do <<-END_PARAGRAPH So where do we go now? If you have a question, who can you ask? What if you want your program to open a webpage, send an email, or resize a digital picture? Well, there are many, many places to find Ruby help. Unfortunately, that's sort of unhelpful, isn't it? :-) END_PARAGRAPH end para do <<-END_PARAGRAPH For me, there are really only three places I look for Ruby help. If it's a small question, and I think I can experiment on my own to find the answer, I use irb. If it's a bigger question, I look it up in my pickaxe. And if I just can't figure it out on my own, then I ask for help on ruby-talk. END_PARAGRAPH end h2 {'IRB: Interactive Ruby'} para do <<-END_PARAGRAPH If you installed Ruby, then you installed irb. To use it, just go to your command prompt and type #{input 'irb'}. When you are in irb, you can type in any ruby expression you want, and it will tell you the value of it. Type in #{input '1 + 2'}, and it will tell you #{output '3'}. (Note that you don't have to use #{code 'puts'}.) It's kind of like a giant Ruby calculator. When you are done, just type in #{input 'exit'}. END_PARAGRAPH end para do <<-END_PARAGRAPH There's a lot more to irb than this, but you can learn all about it in the pickaxe. END_PARAGRAPH end h2 {'The Pickaxe: "Programming Ruby"'} para do <<-END_PARAGRAPH Absolutely the Ruby book to get is "Programming Ruby, The Pragmatic Programmer's Guide", by David Thomas and Andrew Hunt (the Pragmatic Programmers). While I highly recommend picking up the 4th edition of this excellent book, with all of the latest Ruby covered, you can also get a slightly older (but still mostly relevant) version for free online. END_PARAGRAPH end para do <<-END_PARAGRAPH You can find just about everything about Ruby, from the basic to the advanced, in this book. It's easy to read; it's comprehensive; it's just about perfect. I wish every language had a book of this quality. At the back of the book, you'll find a huge section detailing every method in every class, explaining it and giving examples. I just love this book! END_PARAGRAPH end para do <<-END_PARAGRAPH There are a number of places you can get it (including the Pragmatic Programmers' own site), but my favorite place is at ruby-doc.org. That version has a nice table of contents on the side, as well as an index. (ruby-doc.org has lots of other great documentation as well, such as for the Core API and Standard Library... basically, it documents everything Ruby comes with right out of the box. Check it out.) END_PARAGRAPH end para do <<-END_PARAGRAPH And why is it called "the pickaxe"? Well, there's a picture of a pickaxe on the cover of the book. It's a silly name, I guess, but it stuck. END_PARAGRAPH end h2 {'Ruby-Talk: the Ruby Mailing List'} para do <<-END_PARAGRAPH Even with irb and the pickaxe, sometimes you still can't figure it out. Or perhaps you want to know if someone already did whatever it is you are working on, to see if you could use it instead. In these cases, the place to go is ruby-talk, the Ruby Mailing List. It's full of friendly, smart, helpful people. To learn more about it, or to subscribe, look here. END_PARAGRAPH end para do <<-END_PARAGRAPH WARNING: There's a lot of mail on the mailing list every day. I have mine automatically sent to a different mail folder so that it doesn't get in my way. If you just don't want to deal with all that mail, though, you don't have to! The ruby-talk mailing list is mirrored to the newsgroup comp.lang.ruby, and vice versa, so you can see the same messages there. Either way, you see the same messages, just in a slightly different format. END_PARAGRAPH end h2 {'Tim Toady'} para do <<-END_PARAGRAPH Something I have tried to shield you from, but which you will surely run in to soon, is the concept of TMTOWTDI (pronounced "Tim Toady"): There's More Than One Way To Do It. END_PARAGRAPH end para do <<-END_PARAGRAPH Now some will tell you what a wonderful thing TMTOWTDI is, while others feel quite differently. I don't really have strong feelings about it in general, but I think it's a terrible way to teach someone how to program. (As if learning one way to do something wasn't challenging and confusing enough!) END_PARAGRAPH end para do <<-END_PARAGRAPH However, now that you are moving beyond this tutorial, you'll be seeing much more diverse code. For example, I can think of at least five other ways to make a string (aside from surrounding some text in single quotes), and each one works slightly differently. I only showed you the simplest of the six. END_PARAGRAPH end para do <<-END_PARAGRAPH And when we talked about branching, I showed you #{code 'if'}, but I didn't show you #{code 'unless'}. I'll let you figure that one out in irb. END_PARAGRAPH end para do <<-END_PARAGRAPH Another nice little shortcut you can use with #{code 'if'}, #{code 'unless'}, and #{code 'while'}, is the cute one-line version: END_PARAGRAPH end prog do <<-END_CODE # These words are from a program I wrote to generate # English-like babble. Cool, huh? puts 'grobably combergearl thememberate' if 5 == 2**2 + 1**1 puts 'enlestrationshifter supposine' unless 'Chris'.length == 5 END_CODE end para do <<-END_PARAGRAPH And finally, there is another way of writing methods which take blocks (not procs). We saw the thing where we grabbed the block and turned it into a proc using the #{code '&block'} trick in your parameter list when you define the function. Then, to call the block, you just use #{code 'block.call'}. Well, there's a shorter way (though I personally find it more confusing). Instead of this: END_PARAGRAPH end prog do <<-END_CODE def doItTwice(&block) block.call block.call end doItTwice do puts 'murditivent flavitemphan siresent litics' end END_CODE end para do <<-END_PARAGRAPH ...you do this: END_PARAGRAPH end prog do <<-END_CODE def doItTwice yield yield end doItTwice do puts 'buritiate mustripe lablic acticise' end END_CODE end para do <<-END_PARAGRAPH I don't know... what do you think? Maybe it's just me, but... #{code 'yield'}?! If it was something like #{code 'call_the_hidden_block'} or something, that would make a lot more sense to me. A lot of people say #{code 'yield'} makes sense to them. But I guess that's what TMTOWTDI is all about: they do it their way, and I'll do it my way. END_PARAGRAPH end h2 {'THE END'} para do <<-END_PARAGRAPH Use it for good and not evil. :-) And if you found this tutorial useful (or confusing, or if you found an error), let me know! END_PARAGRAPH end end # # MAIN # def generateMain h2 { 'A Place to Start for the Future Programmer' } para do <<-END_PARAGRAPH I guess this all began back in 2002. I was thinking about teaching programming, and what a great language Ruby would be for learning how to program. I mean, we were all excited about Ruby because it was powerful, elegant, and really just fun, but it seemed to me that it would also be a great way to get into programming in the first place. END_PARAGRAPH end para do <<-END_PARAGRAPH Unfortunately, there wasn't much Ruby documentation geared for newbies at the time. Some of us in the community were talking about what such a "Ruby for the Nuby" tutorial would need, and more generally, how to teach programming at all. The more I thought about this, the more I had to say (which surprised me a bit). Finally, someone said, "Chris, why don't you just write a tutorial instead of talking about it?" So I did. END_PARAGRAPH end para do <<-END_PARAGRAPH And it wasn't very good. I had all these ideas that were good in theory, but the actual task of making a great tutorial for non-programmers was vastly more challenging than I had realized. (I mean, it seemed good to me, but I already knew how to program.) END_PARAGRAPH end para do <<-END_PARAGRAPH What saved me was that I made it really easy for people to contact me, and I always tried to help people when they got stuck. When I saw a lot of people getting stuck in one place, I'd rewrite it. It was a lot of work, but it slowly got better and better. END_PARAGRAPH end para do <<-END_PARAGRAPH A couple of years later, it was getting pretty good. :-) So good, in fact, that I was ready to pronounce it finished, and move on to something else. And right about then came an opportunity to turn the tutorial into a book. Since it was already basically done, I figured this would be no problem. I'd just clean up a few spots, add some more exercises, maybe some more examples, a few more chapters, run it by 50 more reviewers... END_PARAGRAPH end para do <<-END_PARAGRAPH It took me another year, but now I think it's really really good, mostly because of the hundreds of brave souls who have helped me write it. END_PARAGRAPH end para do <<-END_PARAGRAPH What's here on this site is the original tutorial, more or less unchanged since 2004. For the latest and greatest, you'll want to check out the book. END_PARAGRAPH end puts @@HLINE h2 { 'Thoughts For Teachers' } para do <<-END_PARAGRAPH There were a few guiding principles that I tried to stick to. I think they make the learning process much smoother; learning to program is hard enough as it is. If you're teaching or guiding someone on the road to hackerdom, these ideas might help you, too. END_PARAGRAPH end para do <<-END_PARAGRAPH First, I tried to separate concepts as much as possible, so that the student would only have to learn one concept at a time. This was difficult at first, but a little too easy after I had some practice. Some things must be taught before others, but I was amazed at how little of a precedence hierarchy there really is. Eventually, I just had to pick an order, and I tried to arrange things so that each new section was motivated by the previous ones. END_PARAGRAPH end para do <<-END_PARAGRAPH Another principle I've kept in mind is to teach only one way to do something. It's an obvious benefit in a tutorial for people who have never programmed before. For one thing, one way to do something is easier to learn than two. Perhaps the more important benefit, though, is that the fewer things you teach a new programmer, the more creative and clever they have to be in their programming. Since so much of programming is problem solving, it's crucial to encourage that as much as possible at every stage. END_PARAGRAPH end para do <<-END_PARAGRAPH I have tried to piggy-back programming concepts onto concepts the new programmer already has; to present ideas in such a way that their intuition will carry the load, rather than the tutorial. Object-Oriented programming lends itself to this quite well. I was able to begin referring to "objects" and different "kinds of objects" pretty early in the tutorial, slipping those phrases in at the most innocent of moments. I wasn't saying anything like "everything in Ruby is an object," or "numbers and strings are kinds of objects," because these statements really don't mean anything to a new programmer. Instead, I would talk about strings (not "string objects"), and sometimes I would refer to "objects", simply meaning "the things in these programs." The fact that all these things in Ruby are objects made this sort of sneakiness on my part work so well. END_PARAGRAPH end para do <<-END_PARAGRAPH Although I wanted to avoid needless OO jargon, I wanted to make sure that, if they did need to learn a word, they learned the right one. (I don't want them to have to learn it twice, right?) So I called them "strings," not "text." Methods needed to be called something, so I called them "methods." END_PARAGRAPH end para do <<-END_PARAGRAPH As far as the exercises are concerned, I think I came up with some good ones, but you can never have too many. Honestly, I bet I spent half of my time just trying to come up with fun, interesting exercises. Boring exercises absolutely kill any desire to program, while the perfect exercise creates an itch the new programmer can't help but scratch. In short, you just can't spend too much time coming up with good exercises. END_PARAGRAPH end puts @@HLINE h2 { 'About the Original Tutorial' } para do <<-END_PARAGRAPH The pages of the tutorial (and even this page) are generated by a big Ruby program, of course. :-) All of the code samples were automatically run, and the output shown is the output they generated. I think this is the best, easiest, and certainly the coolest way to make sure that all of the code I present works exactly as I say it does. You don't have to worry that I might have copied the output of one of the examples wrong, or forgotten to test some of the code; it's all been tested. END_PARAGRAPH end para do ''+ 'powered by Ruby'+ '' end puts @@HLINE h2 { 'Acknowledgements' } para do <<-END_PARAGRAPH Finally, I'd like to thank everyone on the ruby-talk mailing list for their thoughts and encouragement, all of my wonderful reviewers for their help in making the book far better than I could have alone, my dear wife especially for being my main reviewer/tester/guinea-pig/muse, Matz for creating this fabulous language, and the Pragmatic Programmers for telling me about it—and, of course, for publishing my book! END_PARAGRAPH end para do <<-END_PARAGRAPH If you notice any errors or typos, or have any comments or suggestions or good exercises I could include, please let me know. END_PARAGRAPH end end # menu helpers def menuBookLink para(:class=>'fancyMenuText') { 'Buy the improved' } para(:class=>' wideMenuText') { 'expanded' } para(:class=>'fancyMenuText') { 'version:' } puts "" img(class: 'shadowed', width: '200', height: '240', src: '/images/LTP3_cover.jpg', alt: 'Learn to Program, Second Edition') puts '' para(:class=>'fancyMenuText') { 'answers now included!' } end def menuTOC para { '« the original tutorial »' } ol(:start=>'0') do CHAPTERS.sort_by{|x| x[0]}.each do |aChapNum, aTitle, aMethod| if aChapNum < 'A' li { makeLink(aTitle, aMethod) } end end end end def menuTranslations para { '« translations »' } table do TRANSLATIONS.each do |trans| by = ('by '+trans[1]).gsub(' ',' ').gsub('-','‑') tr do td { para(class: 'translink') {"#{trans[0]}"} } td { para { by } } end end end end # # MAIN PAGE GENERATION # def generate(chap, title, generatingMethod) srand(12345 + 54321 * chap.to_i) chap = chap.sub(/^0/, '') pageTitle = if title.nil? 'Learn to Program, by Chris Pine' else title + ' - Learn to Program' end puts '' html do head do meta(charset: 'UTF-8') link(:href=>LINK_ADDR+'tutorial.css', :type=>'text/css', :rel=>'Stylesheet', :media=>'screen') link(href: 'https://fonts.googleapis.com/css?family=Libre+Baskerville', rel: 'stylesheet', type: 'text/css') link(href: 'https://fonts.googleapis.com/css?family=Source+Code+Pro&subset=latin-ext,latin', rel: 'stylesheet', type: 'text/css') title { pageTitle } script(type: 'text/javascript') do puts "" puts "var _gaq = _gaq || [];" puts "_gaq.push(['_setAccount', 'UA-28406155-1']);" puts "_gaq.push(['_trackPageview']);" puts "" puts "(function() {" puts " var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;" puts " ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';" puts " var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);" puts "})();" puts "" end end # head body do div do header do puts '' puts 'Learn to Program' puts '' end main do if chap != 'main' h1 {title} h3 {'Chapter '+chap} puts @@HLINE end method(generatingMethod).call end footer do puts @@HLINE para do '' end para {"© 2003-#{Time.now.year} Chris Pine"} script(type: 'text/javascript') do "!function(d,s,id){var js,fjs=d.getElementsByTagName(s)[0],p=/^http:/.test(d.location)?'http':'https';if(!d.getElementById(id)){js=d.createElement(s);js.id=id;js.src=p+'://platform.twitter.com/widgets.js';fjs.parentNode.insertBefore(js,fjs);}}(document, 'script', 'twitter-wjs');" end end end nav do menuBookLink img(class: 'divider', width: '150', src: '/images/divider.svg', alt: 'divider') menuTOC img(class: 'divider', width: '150', src: '/images/divider.svg', alt: 'divider') menuTranslations end end # body end # html @page.join("\n")+"\n" end end # 'format' is a hidden page for testing formatting. CHAPTERS = [ ['main' , nil , :generateMain ], ['00' , 'Getting Started' , :generateSetup ], ['01' , 'Numbers' , :generateNumbers ], ['02' , 'Letters' , :generateLetters ], ['03' , 'Variables and Assignment', :generateVariables ], ['04' , 'Mixing It Up' , :generateConversion ], ['05' , 'More About Methods' , :generateMethods ], ['06' , 'Flow Control' , :generateFlowControl ], ['07' , 'Arrays and Iterators' , :generateArrays ], ['08' , 'Writing Your Own Methods', :generateDefMethod ], ['09' , 'Classes' , :generateClasses ], ['10' , 'Blocks and Procs' , :generateBlocksProcs ], ['11' , 'Beyond This Tutorial' , :generateBeyond ], ['format', 'Formatting Page' , :generateFormattingPage], ] CHAPTERS.each do |chap, title, meth| page = LearnToProgramTutorial.new out = page.generate(chap, title, meth) filename = "#{chap}.html" filename = 'chap_'+filename if chap < 'A' filename = File.expand_path(File.join(File.dirname(__FILE__), filename)) puts "Writing #{filename}..." File.write(filename, out) end puts "Done!"