Iteration Cheat Sheet#
Simple iteration with while
and until
#
while (condition?)
// do something while the test condition? is true
end;
until (condition?)
// do something until the test condition? is true
end;
The for
loop#
The for
loop can be used in many different ways. We demonstrate some of the
most commonly used features here:
Iterating over a collection#
for (element in collection)
// do something with element
end;
Iterating over a range#
for (count from 0 below num)
// do work
// count ranges from 0 to the integer below num
end;
for (column from 1 to 3)
// do work
// count ranges from 1 to 3, inclusive.
end;
// Changing the stepping and going in reverse
for (index from stop - 1 to start by -1)
// index will start at 'stop - 1' and end at the
// value of 'start', decrementing by 1 with each
// iteration of the loop.
end;
Iterating over a table#
The easiest way to iterate over a table is to use an extension to
the standard for
loop that Open Dylan supports:
for (value keyed-by key in table)
// do work
end;
If you want to directly access the keys of the table, you can use
key-sequence
:
for (key in table.key-sequence)
// do work
end;
Breaking out of a loop#
Breaking out of a loop is just like any other non-local exit in Dylan.
Combine any loop with a block
expression:
let result = block (exit-block)
while (~done())
if (got-error?())
exit-block(1);
end;
end;
2
end;
In the example, if the loop ends naturally because done()
returns true,
then the result is 2
because the while
exits naturally and 2
is the last expression in the block. If got-error?
returns true, the
result is 1
because that was the value passed to exit-block
.
Collection Functions#
When working with a collection, some additional operations are available that remove the need for explicit iteration over the collection.
In all of these, the function passed in can be any of:
An existing function.
An escaped operator name (
\+
for example).A locally defined method.
The result of a method that returns a function such as
curry
rcurry
or other functional operations.
do
#
do
iterates over one or more collections, performing side effects:
do(method (x)
format-out("%s\n", x)
end,
#[1, 2, 3])
map
, map-as
, map-into
#
map
iterates over one or more collections, applying a function and
returns the results in a new collection. map-as
and map-into
allow control over the way that the results are returned.
let type-bindings = map(generate-type-binding, all-var-specs);
let strings = map(curry(as, <string>), names);
let c-direct-superclasses = map-as(<list>, convert, direct-superclasses(c));
reduce
, reduce1
#
reduce
combines the elements of a collection and a seed value into
a single value by repeatedly applying a binary function.
reduce1
is similar to reduce
, except that the first value of
the collection is used as the seed value.
reduce(\*, 1, dimensions(x))
reduce1(\+, #(1, 2, 3, 4, 5))
reduce
is often combined with map
operations:
reduce(\+, 0, map(size, qqs))
Iteration with Tail Recursion#
The iterate
macro in the common-dylan
library is another powerful way to do
iteration. It relies on the fact that Dylan implementations are required to optimize
tail recursion.
let x = 7;
let factorial = iterate loop (n = x, total = 1)
if (n < 2)
total
else
loop(n - 1, n * total) // tail call = iteration
end
end;
Here iterate
creates a local method, loop
, with two parameters n
and
total
which calls itself recursively until n < 2
is true and then it returns the
value of total
. It then calls the method with the parameter default values, x
and
1
.
You could of course do the same thing with a local method yourself but the iterate
macro makes it more concise.