next up previous index
Next: Array Notation Up: ECLiPSe-specific Language Features Previous: Structure Notation   Index

Subsections


Loop/Iterator Constructs

Many types of simple iterations are inconvenient to write in the form of recursive predicates. ECLiPSe therefore provides a logical iteration construct do/2, which can be understood either by itself or by its translation to an equivalent recursion.

A simple example is the traversal of a list

main :-
        write_list([1,2,3]).

    write_list([]).
    write_list([X|Xs]) :-
        writeln(X),
        write_list(Xs).
which can be written as follows without the need for an auxiliary predicate:
main :-
        ( foreach(X, [1,2,3]) do
            writeln(X)
        ).
This looks very much like a loop in a procedural language. However, due to the relational nature of logic programming, the same foreach- construct can be used not only to control iteration over an existing list, but also to build a new list during an iteration. For example
main :-
        ( foreach(X, [1,2,3]), foreach(Y, Negatives) do
            Y is -X
        ),
        writeln(Negatives).
will print [-1, -2, -3].

The general form of a do-loop is

( IterationSpecs do Goals )
and it corresponds to a call to an auxiliary recursive predicate of the form
    do__n(...).
    do__n(...) :- Goals, do__n(...).

The IterationSpecs determine the number of times the loop is executed (i.e. the termination condition), and the way information is passed into the loop, from one iteration to the next, and out of the loop.

IterationSpecs is one (or a comma-separated sequence) of the following:

fromto(First,In,Out,Last)

iterate Goals starting with In=First until Out=Last. In and Out are local variables in Goals. For all but the first iteration, the value of In is the same as the value of Out in the previous iteration.

foreach(X,List)

iterate Goals with X ranging over all elements of List. X is a local variable in Goals. Can also be used for constructing a list.

foreacharg(X,Struct)

iterate Goals with X ranging over all elements of Struct. X is a local variable in Goals. Cannot be used for constructing a term.

for(I,MinExpr,MaxExpr)

iterate Goals with I ranging over integers from MinExpr to MaxExpr. I is a local variable in Goals. MinExpr and MaxExpr can be arithmetic expressions. Can be used only for controlling iteration, ie. MaxExpr cannot be uninstantiated.

for(I,MinExpr,MaxExpr,Increment)

same as before, but Increment can be specified (it defaults to 1).

count(I,Min,Max)

iterate Goals with I ranging over integers from Min up to Max. I is a local variable in Goals. Can be used for controlling iteration as well as counting, ie. Max can be a variable.

param(Var1,Var2,...)

for declaring variables in Goals global, ie shared with the context. CAUTION: By default, variables in Goals are local!

Note that fromto/4 is the most general specifier (subsuming the functionality of all the others), but foreach/2, foreacharg/2, count/3, for/3 and param/N are convenient shorthands.

More than one iteration specifier can be given in one do-loop, and the order in which they are written does not matter. In the case of several iteration specifiers, typically not all of them will impose a termination condition on the loop (e.g. foreach with an uninstantiated list, or count with an uninstantiated maximum do not impose a termination condition), but at least one of them should do so. If several specifiers impose termination conditions, these conditions must coincide, i.e. specify the same number of iterations.

Syntactically, the do-operator binds like the semicolon, ie. less than comma. That means that the whole do-construct should always be enclosed in parentheses (see examples).

Unless you use :-pragma(noexpand) or :-dbgcomp, the do-construct is compiled into an efficient auxiliary predicate named do__nnn, where nnn is a unique integer. This will be visible during debugging. To make debugging easier, it is possible to give the loop a user-defined name by adding loop_name(Name) to the iteration specifiers. Name must be an atom, and is used as the name of the auxiliary predicate into which the loop is compiled (instead of do__nnn). The name should therefore not clash with other predicate names in the same module.

Examples

Iterate over list

foreach(X,[1,2,3]) do writeln(X).

Maplist (construct a new list from an existing list)

(foreach(X,[1,2,3]), foreach(Y,List) do Y is X+3).

Sumlist

(foreach(X,[1,2,3]), fromto(0,In,Out,Sum) do Out is In+X).

Reverse list

(foreach(X,[1,2,3]), fromto([],In,Out,   Rev) do Out=[X|In]). % or:
(foreach(X,[1,2,3]), fromto([],In,[X|In],Rev) do true).

Iterate over integers from 1 up to 5

for(I,1,5) do writeln(I). % or:
count(I,1,5) do writeln(I).

Make list of integers [1,2,3,4,5]

(for(I,1,5), foreach(I,List) do true). % or:
(count(I,1,5), foreach(I,List) do true).

Make a list of length 3

(foreach(_,List), for(_,1,3) do true). % or:
(foreach(_,List), count(_,1,3) do true).

Get the length of a list

(foreach(_,[a,b,c]), count(_,1,N) do true).

Actually, the length/2 builtin is (almost)

length(List, N) :- (foreach(_,List), count(_,1,N) do true).

Filter list elements

(foreach(X,[5,3,8,1,4,6]), fromto(List,Out,In,[]) do
    X>3 -> Out=[X|In] ; Out=In).

Iterate over structure arguments

(foreacharg(X,s(a,b,c,d,e)) do writeln(X)).

Collect args in list (bad example, use =.. if you really want to do that!)

(foreacharg(X,s(a,b,c,d,e)), foreach(X,List) do true).

Collect args reverse

(foreacharg(X,s(a,b,c,d,e)), fromto([],In,[X|In],List) do true).

or like this:

S = s(a,b,c,d,e), functor(S, _, N),
(for(I,N,1,-1), foreach(A,List), param(S) do arg(I,S,A)).

The following two are equivalent

foreach(X,[1,2,3])        do             writeln(X).
fromto([1,2,3],In,Out,[]) do In=[X|Out], writeln(X).

The following two are equivalent

count(I,1,5)     do            writeln(I).
fromto(0,I0,I,5) do I is I0+1, writeln(I).

Some examples for nested loops. Print all pairs of list elements:

Xs = [1,2,3,4],
( foreach(X, Xs), param(Xs) do
    ( foreach(Y,Xs), param(X) do
        writeln(X-Y)
    )
).
and the same without symmetries:
Xs = [1,2,3,4],
( fromto(Xs, [X|Xs1], Xs1, []) do
    ( foreach(Y,Xs1), param(X) do
        writeln(X-Y)
    )
).
Find all pairs of list elements and collect them in a result list:
pairs(Xs, Ys, Zs) :-
    (
        foreach(X,Xs),
        fromto(Zs, Zs4, Zs1, []),
        param(Ys)
    do
        (
            foreach(Y,Ys),
            fromto(Zs4, Zs3, Zs2, Zs1),
            param(X)
        do
            Zs3 = [X-Y|Zs2]
        )
    ).


next up previous index
Next: Array Notation Up: ECLiPSe-specific Language Features Previous: Structure Notation   Index
Warwick Harvey
2004-08-07