Iterators

The :std/iter library provides iterator support; see the Guide for an introduction.

usage

(import :std/iter)

Macros

for

usage

(for <bind> body ...)
(for (<bind> ...) body ...)

bind := (<pattern> <iterator-expr>)
        (<pattern> <iterator-expr> when <filter-expr>)
        (<pattern> <iterator-expr> unless <filter-expr>)

pattern       := match pattern
iterator-expr := expression producing an iterable object
filter-expr   := boolean filter expression, with pattern bindings visible

for iterates one or more iterable objects in parallel, matching the values produced to the binding pattern, and evaluates the body for each value. For each iterable object an iterator is constructed using (iter ...); the iteration completes as soon as one of the iterators completes.

for*

usage

(for* (<bind> ...) body ...)

for* iterates one or more iterables sequentially.

for/collect

usage

(for/collect <bind> body ...)
(for/collect (<bind> ...) body ...)

for/collect iterates in parallel and collects the values of body for each iteration into a list.

for/fold

usage

(for/fold <iv-bind> <bind> body ...)
(for/fold <iv-bind> (<bind> ...) body ...)

iv-bind := (<id> <expr>)

for/fold folds one or more iterables in parallel. The seed of the fold is bound to id, with initial value expr, and is updated as the value of the body for each iteration. The result of the fold is the result of the final iteration.

Iterator Constructors

iter

usage

(iter obj)
=> <iterator>

This is the fundamental iterator constructor for iterable objects. If the object is already an iterator then it is returned; otherwise the generic :iter is applied.

:iter

usage

(:iter obj)
=> iterator

(defmethod (:iter (obj type))
  ...)
=> iterator

Generic iterator constructor. The library defines the method for basic types: lists, vectors, strings, hash-tables, ports, and procedures which are iterated as coroutines; objects without any method binding dispatch to the :iter object method.

in-range

usage

(in-range count [start = 0] [step = 1])
  count := fixnum
  start, step := number
=> iterator

Creates an iterator that yields count values starting from start and incrementing by step.

in-naturals

usage

(in-naturals [start = 1] [step = 1])
  start, step := number
=> iterator

Creates an infinite iterator that iterates over the naturals starting from start and incrementing by step.

in-hash

usage

(in-hash ht)
  ht : hash-table
=> iterator

Creates an iterator that yields the key/value pairs (as two values) for each association in the hash table. This is the same as (:iter <hash-table>).

in-hash-keys

usage

(in-hash-keys ht)
  ht : hash-table
=> iterator

Creates an iterator that yields the keys for each association in the hash table.

in-hash-values

usage

(in-hash-values ht)

Creates an iterator that yields the values for each association in the hash table.

in-input-port

usage

(in-input-port port [read])
  port := input-port
=> iterator

Creates an iterator that yields the values read with read from the port. The unary version is the same as (:iter port).

in-input-lines

usage

(in-input-lines port)
  port := input-port
=> iterator

Same as (in-input-port port read-line).

in-input-chars

usage

(in-input-chars port)
  port := input-port
=> iterator

Same as (in-input-port port read-char).

in-input-bytes

usage

(in-input-bytes port)
  port := input-port
=> iterator

Same as (in-input-port port read-u8).

in-coroutine

usage

(in-coroutine proc arg ...)
  proc := coroutine procedure
=> iterator

Creates an iterator that applies (proc arg ...) in a coroutine. The unary version is the same as (:iter <procedure>).

in-cothread

usage

(in-cothread proc arg ...)
  proc := coroutine procedure
=> iterator

Creates an iterator that applies (proc arg ...) in a cothread.

Iterator Protocol

iterator

(defstruct iterator (e next fini))

e    := iterator value
next := lambda (iterator)
fini := lambda (iterator)

This is the type of iterator objects:

  • The element e is the state associated with the iterator
  • The procedure next advances the iterator and returns the current value; iter-end signals the end of the iteration.
  • The procedure fini finalizes the iterator. It is invoked at the end of the iteration by the for family of macros.

Note that the finilizer is not automatically invoked if the iteration fails with an exception. If the iterator has hard state associated (eg a thread or some other expensive resource), then a will should be attached to it.

iter-end

(def iter-end ...)

Special object signalling the end of iteration.

iter-end?

usage

(iter-end? obj)
=> boolean

Returns true if the object is the end of iteration object.

iter-next!

usage

(iter-next! it)
  it := iterator
=> any

Advances the iterator and returns the current value.

iter-fini!

usage

(iter-fini! it)
  it := iterator

Finalizes the iterator.

yield

usage

(yield val ...)

Yields one or more values from a coroutine procedure associated with an iterator. This is the yield defined in :std/coroutine.

Examples

Here is the definition for an iterator that produces a constant value, using the iterator protocol:

(def (iter-const val)
  (make-iterator val iterator-e))

Here is a definition of the list iterator using the iterator protocol:

(def (iter-list lst)
  (def (next it)
    (with ((iterator e) it)
      (match e
        ([hd . rest]
         (set! (iterator-e it) rest)
         hd)
        (else iter-end))))
  (make-iterator lst next))

Here is a definition of the list iterator using coroutines:

(def (iter-list lst)
  (def (iterate lst)
    (let lp ((rest lst))
      (match rest
        ([hd . rest]
         (yield hd)
         (lp rest))
        (else (void)))))
  (in-coroutine iterate lst))

Note that this is just an example for illustration purposes; you don't need to provide an iterator for lists as iter will construct one for you.