Common Lisp/External libraries/Iterate

Iterate provides an English language like iteration mechanism much like the Common Lisp LOOP facility. This raises the question - why use iterate when you have LOOP built in? The best reason for using Iterate is for its extensibility. LOOP is quite static, but Iterate can be extended to iterate over your own data structures, for instance.

Standard use edit

As you will see, the syntax of Iterate is very much like LOOP. In fact, it is often a good idea to think of Iterate as LOOP with parentheses. The parentheses are purely aesthetic. The main idea behind the parentheses is to provide hints to text editors for how the syntax should be formatted and to make it easier to write extensions.

  • FOR loops
;; Collect the values from 0 to 9
(iter (for i below 10)
      (collect i))

=> (0 1 2 3 4 5 6 7 8 9)

;; Collect the values from -5 to 10 stepping by PI
(iter (for i from -5 to 10 by pi)
      (collect i)

=> (-5.0d0 -1.8584073464102069d0 1.2831853071795862d0 4.424777960769379d0
 7.5663706143591725d0)

;; Find the number in the file that maximizes the square of itself.
(iter (for number in-file #p"file")
      (finding number maximizing (expt number 2)))

For a list of all iterate constructs, use the function DISPLAY-ITERATE-CLAUSES.

Extending Iterates Functionality edit

In order to extend Iterate you need to write a new driver.

Using DEFCLAUSE-SEQUENCE and DEFMACRO-DRIVER edit

DEFMACRO-CLAUSE edit

Iterate “gotchas” edit

Binding done by the iterate in the FOR driver is actually done via a SET. This means that you have to pay extra attention to these bound variables when you collect a series of closures. Unless you explicitly make a lexical binding, the closure will close over the variable that iterate is mutating. In the end, you will get a collection of closures that all have the binding that iterate exited on.

Don't do this:

(defparameter *closures*
  (iter (for number below 10)
        (collecting (lambda () number)) ))

==> *CLOSURES*

(funcall (first *closures*))

==> 10

Do this:

(defparameter *closures*
  (iter (for number below 10)
        (collecting (let ((number number))
                      (lambda () number)) )))

==> *CLOSURES*

(funcall (first *closures*))

==> 0

You should note that LOOP behaves the same way.

Further reading edit