Prev - DUP - Next - TOC

What is an iterator?


An iterator isn't an original concept in ruby. It is a of feature CLU, which is popular among some people. In Lisp, iterators are often used whereas they aren't called iterators. However the concepet of iterator is an unfamiliar one for many so it should be explained in more detail.

The verb `iterate' means "do the same thing many times', you know, so `iterator' means "one which does the same thing many times'.

Writing code, we often put loops into them in various situations. In C, we use them as `for' or `while'. For example,

 char *str;
 for (str = "abcdefg"; *str; str++) {
   /* process for each chars here */
 }

The part of `for(...)' is a sort of idiom, but it requires some knowledge of internal data from us and is so irksome. A programmer is required to know about internal structure when in a simple loop; That's why we feel the language be low level. Some higher level languages have control structures to iterate. Consider the following example of `sh'. (You may ask me, "Is `sh' higher than `C'?" Ok, it is right at least in this problem...)

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

In this case, `sh' undertakes detailsome, pick up and substitute file names one by one. I think this is higher level rather than C, don't you?

But there are more problems. It is good that a language deals with iterations for built-in data types, but we are disappointed again if we must write low level loops, like in C, for data types defined by user :-( In OOP, users often define one data type after another, so this problem is serious.

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.

Some examples are showed below. Ruby's string type has some iterators, so it's apt.

 ruby> "abc".each_byte{|c| printf "<%c>", c}; print "\n"
 <a><b><c>
 nil

`each_byte' is an iterator for each character in the string. Each character is substitute into local variable `c'. This code is translated into C as follows.

 ruby> s="abc";i=0
 0
 ruby> while i<s.length
 ruby|    printf "<%c>", s[i]; i+=1
 ruby| end; print "\n"
 <a><b><c>
 nil

The way with an iterator is simpler and probably faster. Also it seems adaptable to changes of internal structure or different access strings.

Another iterator of string is `each_line' for each line.

 ruby> "a\nb\nc\n".each_line{|l| print l}
 a
 b
 c
 nil

Finding delimiters of lines, generating substrings; every irksome task is undertaken by the iterator. It's convenient.

The `for' statement appearing in the previous chapter does iteration by way of an `each' iterator. String's `each' works as same as `each_line', so let's rewrite the avobe example of `each_line' with `for'.

 ruby> for l in "a\nb\nc\n"
 ruby|   print l 
 ruby| end
 a
 b
 c
 nil

Within `for' or an iterator, we can use a control structure `retry', it retries the loop from invoking the iteration.

 ruby> c=0
 0
 ruby> for i in 0..4
 ruby|   print i
 ruby|   if i == 2 and c == 0
 ruby|     c = 1
 ruby|     print "\n"
 ruby|     retry
 ruby|   end
 ruby| end; print "\n"
 012
 01234
 nil

One can define iterators in ruby. So, there are a few restrictions, but you can write your original iterators. A definition of an iterator is common to ordinary methods.

`yield' occurs in some place in a definition of an iteretor. `yield' moves control to the block given by calling side. The following example defines an iterator `repreat' which repeats specified times as argnument.

 ruby> def repeat(num)
 ruby|   while num > 0
 ruby|     yield
 ruby|     num -= 1
 ruby|   end
 ruby| end
 nil
 ruby> repeat(3) { print "foo\n" }
 foo
 foo
 foo
 nil

With `retry', one can define an iterator which works the same as `while', but it's not practical due to slowness.

 ruby> def WHILE(cond)
 ruby|   return if not cond
 ruby|   yield
 ruby|   retry
 ruby| end
 nil
 ruby> i=0; WHILE(i<3) { print i; i+=1 }
 012nil

Do you understand what an iterator is?

When one defines a new data type, it is often convenient that one defines suitable iterators too. In this sense, above examples `repeat()' or `WHILE()' are not very useful. We will talk about practical iterators after `class' is explained in the next chapter.


Prev - DUP - Next - TOC