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.