Review

A wide variety of applications from different domains need different
levels of organization. We have seen the fundamentals of Ruby in
Part 1, and now we jump on to the next
level of organization.

Regular Expressions

In Ruby, a regular expression is quoted by '/' as in Perl and awk rather than
by quotation marks.
Regular expressions have an efficient expressive
power, whenever you deal with patterns (as in pattern matching). Also
some methods convert a string into a regular expression.

print "abcdef" =~ /de/,"\n"
print "aaaaaa" =~ /d/,"\n"
^D
3
FALSE

The operator `=~' is a matching operator with respect to regular
expressions. It returns the position in a string where a match was
found, or nil if the pattern did not match. It is interesting to see
that regular expressions share a particular kind of vocabulary as shown
below:

[ ] range specification. (e.g., [a-z] means a letter in range of from a to z)
\w letter or digit. same as [0-9A-Za-z_]
\W neither letter nor digit
\s blank character. same as [ \t\n\r\f]
\S non-space character.
\d digit character. same as [0-9].
\D non digit character.
\b word boundary (outside of range specification).
\B non word boundary.
\b back space (0x08) (inside of range specification)
* zero or more times repetition of followed expression
+ one or more times repetition of followed expression
{m,n} at least n times, but not more than m times repetition
of followed expression
? at least 0 times, but not more than 1 times repetition
of followed expression
| either preceding or next expression may match
( ) grouping

For example, `^f[a-z]+' means "f followed by repetition of letters in
range from `a' to `z'. Now what if we want check whether a string fits a
given description say for example: "Starts with lower case `f', which
is immediately followed by exactly one upper case letter, and
optionally more junk after that, as long as there are no more lower
case characters. You will have to write a dozen lines in C, right?
Admit it; you can hardly help yourself. In Ruby you just have to
request the string to be matched with the regular expression
/^f[A-Z](^[a-z])*$/. This ability of regular expressions in string
matching is often used in UNIX environment, typical example is `grep'.
Let us get acquainted with regular expressions. Consider the program
given below:

The program requires inputs twice, once for a string and once for a
regular expression. The test is performed for the string against the regular
expression, and matched parts are highlighted in reverse video. Note
that this requires an ANSI terminal since it uses reverse video
escape sequences. Do not mind the details of the program.

str>foobar
pat>^fo+
foobar
~~~

We see that foo is reversed. Note that ``~~~'' is just for text-based
browsers. We shall experiment with different inputs.

Iterators

Note the abstraction provided by C's for(...) syntax to create loops,
but in fact, the programmer has to know the internal structure of a
string to test *str with the null character.

Flexible support for iteration is one of the few features that mark a
high-level language. Consider the following shell script (/bin/sh):

for i in *.[ch]; do
# ... something to do for each file
done

All the C source and header files in the current directory are,
processed, and the command shell handles the details of picking up and
substituting file names one by one. Isn't this working at a higher
level than C? What do you think ?

Considering the fact that, it is fine to provide iterators in a
programming language for built-in data types, but it is a
disappointment if we have to write low-level loops to iterate our
dat types. In OOP, this can be a serious problem, since users often
define one data type after another.

To solve above matters, every OOP language has elaborate ways to make
iterations easy, for example some languages provide class controlling
iteration, etc. On the other hand, ruby allows us to define control
structures directly. In term of ruby, such user-defined control
structures are called iterators.

Let us see few examples:

"abc".each_byte{|c| printf "%c\n", c}
^D
a
b
c

Here, each_byte is an iterator for each character in the string. A
local variable `c' is being used here, and each character is being
substituted into it. This can be translated into something that looks
a lot like C code ...

s="abc"
i=0
while i < s.length
printf "%c\n",s[i]
i+=1
end
^D
a
b
c

... however, the each_byte iterator is simpler conceptually and is
more likely to continuously work even if the string class happens to
be radically modified in the near future. One benefit of the iterators
is their tendency of robustness in the face of such changes, and I
think that is a characteristic of a good code.

Another iterator of string is each_line.

"a\nb\nc\n".each_line{|l| print l}
^D
a
b
c

Every irksome task like finding delimiters for lines, generating
sub strings etc. are undertaken by iterators.

Now, let's rewrite this example with for statement.

for l in "a\nb\nc\n"
print l
end
^D
a
b
c

The for statement does iteration by way of an each iterator. String's
each works the same as each_line as seen in the previous example.

Current iteration can be done or retried again from the top, by using
a control structure `retry' in conjunction with an iterated loop.
See below:

c = 0
for i in 0..4
print i
if i==2 and c==0
c = 1
print "\n"
retry
end
end
^D
012
01234

The definition of an iterator may have an occurrence of `yield', which
moves control to the block of code that is passed to the iterator (we
will see more of this later). The example below defines the iterator
repeat, which repeats a block of code the number of times specified in
an argument.

By now, I hope you must have got an idea about iterators. There are a
few restrictions, but you can write your original iterators; and in
fact, whenever you define a new data type, it is often convenient to
define suitable iterators to go with it. In this sense this, the above
examples `repeat() and `MYWHILE()' are not very useful. We will talk
about practical iterators after we have a better understanding of what
classes are.

Object Oriented Thinking

`Object Oriented' is indeed a very catchy phrase. Ruby claims to
be an object oriented scripting language; but what does 'object
oriented' exactly mean?

There have been a variety of answers to that question, all of which
probably boil down to about the same thing. Before arguing and summing
our definitions too quickly, let's think for a moment about the
traditional programming paradigm.

Traditionally, a programming problem is attacked by coming up with
some kinds of data representations, and procedures that
operate on that data. We can associate terms inert, passive,and
helpless with `data' under this model and that the data sits
completely at the mercy of a large procedural body with which we
associate terms active, logical, and all-powerful.

The problem with this approach is that programs are written by
programmers, who are only human and can only keep so much detail clear
in their heads at any one time. As a project gets larger, its
procedural core grows to the point where it is difficult to remember how
the whole thing works. Minor lapses of thinking and typographical
errors become more likely to result in well-concealed bugs. Complex
and unintended interactions begin to emerge within the procedural core,
and maintaining it becomes like trying to carry around an angry squid
without letting any tentacles touch your face. There are guidelines
for programming that can help to minimize and localize bugs within this
traditional paradigm, but there is a better solution that involves
fundamentally changing the way we work.

What object-oriented does is to let us delegate most of he mundane
and repetitive logical work to the data itself; we can then
change our concept of data from passive to active. Put
another way,

we stop treating each piece of data as a box with an open lid that
lets us reach in and throw things around.

We start treating each piece of data as a working machine with a
closed lid and a few well marked switches and dials.

What is described above as a "machine" may be very simple or complex
on the inside; we can't tell from the outside, and we don't allow
ourselves to open up the machine (except when we are absolutely sure
something is wrong with its design), so we are required to do things
like flip the switches and read the dials to interact with the data.
Once the machine is built, we don't want to have to think about how it
operates.

You might think we are just making more work for ourselves,but this
approach tends to do a nice job of preventing all kinds of things from
going wrong.

Let's start with an example that is to simple to be of practical value,
but should illustrate at least part of the concept. My 2-wheeler has a
trip meter. Its job is to keep track of the distance it has travelled
since the last time its reset button was pushed. How would we model
this in a programming language? In C, the trip meter would just be a
numeric variable, possibly of type float. The program would manipulate
the variable by increasing its value in small increments, with
occasional resets to zero when appropriate. What's wrong with that? A
bug in the program would assign a bogus value to the variable, for any
number of unexpected reasons. Anyone who has programmed in C knows what
it is like to spend hours or days tracking down such a bug whose cause
seems absurdly simple once it has been found. (The moment of finding the
bug is commonly indicated by the sound of a loud slap to the forehead.)

In object-oriented context, the same problem can be attacked in a
different manner. A programmer designing a trip meter is supposed not to
ask "which of the familiar data-types comes closest to resembling the
thing" but instead be interested in "how exactly is this thing supposed
to act?" The difference winds up being a profound one. It is necessary
to spend a little bit of time deciding exactly what an odometer is for,
and how the outside world expects to interact with it. We decide to
build a little machine with controls that allows us to increment it,
reset it, read its value, and nothing else.

We don't provide a way for a trip meter to be assigned arbitrary values;
why? because we all know trip meters don't work that way. There are
only a few things you should be able to do with a trip meter, and those
are all we allow. Thus, if something else in the program mistakenly
tries to place some other value (say, the target temperature of the
vehicle's climate control) into the trip meter, there is an immediate
indication of what went wrong. We are told when running the program
(or possibly while compiling, depending on the nature of the language)
that we are not allowed to assign arbitrary values to Trip meter
objects. The message might not be exactly that clear, but it will be
reasonably close to that. It doesn't prevent the error, does it? But
it quickly points us in the direction of the cause. This is only one
of several ways in which OO programming can save a lot of wasted time.

We commonly take one step of abstraction above this, because it turns
out to be as easy to build a factory that makes machines as it is to
make an individual machine. We aren't likely to build a single
trip meter directly; rather, we arrange for any number of trip meters to
be built from a single pattern. The pattern (or if you like, the
trip meter factory) corresponds to what we call a class, and an
factory) corresponds to an object. Most OO languages require
a class to be defined before we can have a new kind of object, but
ruby does not.

I would like to emphasize on the fact that the use of an OO language
will not enforce proper OO design. Indeed it is possible in any
language to write code that is unclear, sloppy, ill-conceived, buggy,
and wobbly all over. What ruby does for you (as opposed, especially, to
C++) is to make the practice of OO programming feel natural enough
that even when you are working on a small scale you don't feel a
necessity to resort to ugly code to save effort. We will be discussing
the ways in which ruby accomplishes that admirable goal as this guide
progresses; the next topic will be the "switches and dials" (object
methods) and from there we'll move on to the "factories" (classes). Are
you still with us?