Prev | Contents | Next

| Ruby user's guide | Back to the simple examples |

Now let's take apart the code of some of our previous example programs.

The following appeared in the simple examples chapter.

    def fact(n)
      if n == 0
        1
      else
        n * fact(n-1)
      end
    end
    print fact(ARGV[0].to_i), "\n"

Because this is the first explanation, we examine each line individually.

### Factorials

    def fact(n)

In the first line, `def` is a statement to define a function (or, more precisely, a method; we'll talk more about what a method is in a later chapter). Here, it specifies that the function `fact` takes a single argument, referred to as `n`.

    if n == 0

The `if` is for checking a condition. When the condition holds, the next bit of code is evaluated; otherwise whatever follows the `else` is evaluated.

    1

The value of `if` is 1 if the condition holds.

    else

If the condition does not hold, the code from here to `end` is evaluated.

    n * fact(n-1)

If the condition is not satisfied, the value of `if` is the result of `n` times `fact(n-1)`.

    end

The first `end` closes the `if` statement.

    end

The second `end` closes the `def` statement.

    print fact(ARGV[0].to_i), "\n"

This invokes our `fact()` function using a value specified from the command line, and prints the result.

`ARGV` is an array which contains command line arguments. The members of `ARGV` are strings, so we must convert this into a integral number by `to_i`. Ruby does not convert strings into integers automatically like perl does.

Hmmm... what would happen if we fed this program a negative number? Do you see the problem? Can you fix it?

### Strings

Next we examine the puzzle program from the chapter on strings. As this is somewhat longer, we number the lines for reference.

    01 words = ['foobar', 'baz', 'quux']
    02 secret = words[rand(3)]
    03
    04 print "guess? "
    05 while guess = STDIN.gets
    06   guess.chop!
    07   if guess == secret
    08     print "you win\n"
    09     break
    10   else
    11     print "you lose.\n"
    12   end
    13   print "guess? "
    14 end
    15 print "the word is ", secret, ".\n"

In this program, a new control structure, `while`, is used. The code between `while` and its corresponding `end` will execute repeatedly as long as some specified condition remains true.

`rand(3)` in line 2 returns a random number in the range 0 to 2. This random number is used to extract one of the members of the array `words`.

In line 5 we read one line from standard input by the method `STDIN.gets`. If EOF (end of file) occurs while getting the line, `gets` returns `nil`. So the code associated with this `while` will repeat until it sees ^D (or ^Z under DOS), signifying the end of input.

`guess.chop!` in line 6 removes the last character from `guess`; in this case it will always be a newline character.

In line 15 we print the secret word. We have written this as a print statement with three arguments (which are printed one after the other), but it would have been equally effective to do it with a single argument, writing `secret` as `#{secret}` to make it clear that it is a variable to be evaluated, not a literal word to be printed:

    print "the word is #{secret}.\n"

### Regular expressions

Finally we examine this program from the chapter on regular expressions.

    01 st = "\033[7m"
    02 en = "\033[m"
    03
    04 while TRUE
    05   print "str> "
    06   STDOUT.flush
    07   str = gets
    08   break if not str
    09   str.chop!
    10   print "pat> "
    11   STDOUT.flush
    12   re = gets
    13   break if not re
    14   re.chop!
    15   str.gsub! re, "#{st}\\&#{en}"
    16   print str, "\n"
    17 end
    18 print "\n"

In line 4, the condition for `while` is hardwired to `true`, so it forms what looks like an infinite loop. However we put `break` statements in the 8th and 13th lines to escape the loop. These two `break`s are also an example of `if` modifiers. An "`if` modifier" executes the statement on its left hand side if and only if the specified condition is satisfied.

There is more to say about `chop!` (see lines 9 and 14). In ruby, we conventionally attach '`!`' or '`?`' to the end of certain method names. The exclamation point (`!`, sometimes pronounced aloud as "bang!") indicates something potentially destructive, that is to say, something that can change the value of what it touches. `chop!` affects a string directly, but `chop` with no exclamation point works on a copy. Here is an illustration of the difference.

    ruby> s1 = "forth"
      "forth"
    ruby> s1.chop!       # This changes s1.
      "fort"
    ruby> s2 = s1.chop   # This puts a changed copy in s2,
      "for"
    ruby> s1             # ... without disturbing s1.
      "fort"

You will later come across method names that end in a question mark (`?`, sometimes pronounced aloud as "huh?"); this indicates a "predicate" method, one that can return either `true` or `false`.

Line 15 deserves some careful attention. First, notice that `gsub!` is another so-called destructive method. It changes `str` by replacing everything matching the pattern `re` (`sub` means substitute, and the leading `g` means global, i.e., replace all matching parts in the string, not just the first one found). So far, so good; but what are we replacing the matched parts of the text with? `st` and `en` were defined in lines 1-2 as the ANSI sequences that make text color-inverted and normal, respectively. In line 15 they are enclosed in `#{}` to ensure that they are actually interpreted as such (and we do not see the variable names printed instead). Between these we see "`\\&`". This is a little tricky. Since the replacement string is in double quotes, the pair of backslashes will be interpreted as a single backslash; what `gsub!` actually sees will be "`\&`", and that happens to be a special code that refers to whatever matched the pattern in the first place. So the new string, when printed, looks just like the old one, except that the parts that matched the given pattern are highlighted in inverse video.

Prev | Contents | Next