Version 20 of iterators

Updated 2002-09-04 16:59:41

CLU's iterators correspond to what Python and other languages call generators. CLU iterators were modeled on Alphard's generators [L1 ].

IPL-v generators and Lisp mapping functions are related to Alphard's generators.

Can someone explain the idea of iterator/generator here?

It seems to me that the idea is that rather than explicitly listing an entire range of values (that a variable can take within a loop, etc.) one lists the beginning and ending value of a range plus the incremental factor and the language generates the value as needed. How is this different than using a for loop though?

An iterator is richer than a mere index. An iterator carries the container reference and the element's position with it. You can use 'em without for loops. The iterator controls access to the element. -- Todd Coram

RS So would the "loop variable" in a foreach come closer to the iterator concept?

Yes, but iterators are most useful when you have different container types (or classes). foreach iterates over lists. A useful iterator interface deals with strings, lists, trees, vectors, etc. You need OO, interfaces or inherent polymorphism to take full advantage of iterators. Since Tcl primarily deals with lists and arrays, there is no great need for such polymorphic operations. Right? Now, VFS gives us a (loose) approximate of stream iterators which are very useful abstractions... -- Todd Coram

Also, iterators/generators control what comes nextin an iteration. foreach is predetermined: it has already been decided what element you will get on each iteration. If my container wants to return random values as I step through it, it can determine to do so at anytime. -- Todd Coram

Here is a contrived example of an Itcl based iterator.

Eric Boudaillier: I have begin to write a C extension that deals with iterator (or generator ?). This extension defines the iterate command, which works like foreach, but takes the description of the thing to iterate instead of list. This description is a list containing the type of the iterator and the arguments. At this time, four types are defined: numeric, list, string and channel. Here is a little example:

     Common Tcl construction                     Same using [iterate] command

  for {set i 0} {$i<=$max} {incr i} {        iterate i [list numeric 0 $max] {
     ...                                        ...
  }                                          }

  while {[gets $chan line] != -1} {          iterate line [list channel $chan] {
     ...                                        ...
  }                                          }

  foreach c [split $string {}] {             iterate c [list string $string] {
     ...                                        ...
  }                                          }

  foreach e [lreverse $list] {               iterate e [list list $list end 0] {
     ...                                        ...
  }                                          }

iterate command provides a cleaner syntax, and can be faster and consume less memory. And also, you can combine different type of iterator. Moreover, some iterator can be infinite:

  # returns the number of lines in $chan.
  proc wc-l {chan} {
     iterate count [list numeric 1 {}] line [list channel $chan] {}
     return $count
  }

-eric


RS: This looks like a great unification to me - however, if the list-bundling of the second argument could be given up, it would be even more pleasing to write and read:

 iterate i    -numeric "0 $max"   {...}
 iterate line -channel $chan      {...}
 iterate c    -string $string     {...}
 iterate e    -list $list         {...}
 iterate rev  -list $list {end 0} {...}
 iterate count -numeric 0 {} line -channel $chan {}

Granted that the last argument is the body, a variable number of control args could be easily processed. The dash before the type indicator might set it off more clearly. However, I'm not sure whether all iterations possible with for or foreach can be replaced with this construct.


By supplying a type to the iteration, I think you lose a great deal of power that typeless iteration gives you. C++ style iteration is what make generic programming such a nice capability. Typelessness allows me to define generic functions that don't care about type. How about an iterator command that generates an iterator for the supplied type?

  set it1 [iterator -string "hello world how are you?"]
  set it2 [iterator -chan $chan1]
  set it3 [iterator -list {{hello world} {how are you}}

The generator iterator would be a proc: So,

  proc wc-w {it} {
    set count 1
    while {[$it] != {}} {
      incr count
    }
    return $count
  }

  wc-w $it1 # -- returns 5
  wc-w $it2 # returns the number of words in the file
  wc-w $it3 # -- returns 2 

And the iterator command would be a perfect usage of Closures!

-- Todd Coram

See Iterator using Closures for an example of the above... -- Todd Coram

Category Concept


Eric Boudaillier: Iterator are not typed: just see the description list of iterators as a constructor. At the C level, I have a structure like Tcl_ObjType:

 typedef
 struct Tcl_IteratorType {
     char                     *name;
     Tcl_IteratorCreateProc   *createProc;
     Tcl_IteratorFreeProc     *freeProc;
     Tcl_IteratorGetProc      *getProc;
     Tcl_IteratorNextProc     *nextProc;
     Tcl_IteratorIsFiniteProc *finiteProc;
 } Tcl_IteratorType;

[iterator] command just looks for the iterator type, and then call the iterator type functions. This implementation is simple, and make iterator lifetime only in [iterator] command. I have tried other implementation with a Tcl_Obj internal representation, but I did'nt know how to handle some cases:

  set it1  [iterator string "Hello, World"]
  set it2  [iterator channel $chan]
  set it1copy $it1
  set it2copy $it2
  iterate c $it1 line $it2 {
  }
  # where is $it1copy ? at the start of the string ?
  # at least, $it2copy is at the same position of $it2.

Also, [iterator] don't know the args to pass to Tcl_IteratorCreateProc, so this is difficult to break the description list, which can also take options:

  iterate v [list numeric 0 9 -loop true] {e1 e2} [list list $list] {
    ...
  }

But of course, all these lists can be created by a command.

-eric


Iterators are big in Ruby.