
+IterationSpecs do +Goals

   Execute Goals iteratively according to IterationSpecs.



Arguments
   IterationSpecs      a comma-separated sequence of iteration specifiers
   Goal                a goal (atom or compound term)

Type
   Control

Description

    This is a meta-predicate for writing simple iterations without
    the need for an auxiliary recursive predicate.

    A do-loop corresponds to a call to an auxiliary recursive
    predicate of the form
	do__n(...).
	do__n(...) :- Goals, do__n(...).
    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.

    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,StructOrArray)
    	iterate Goals with X ranging over all arguments of StructOrArray.
	X is a local variable in Goals.
	Cannot be used for constructing a term.

    foreacharg(X,StructOrArray,Idx)
	same as before, but Idx is set to the argument position of X in
	StructOrArray, i.e. arg(Idx, StructOrArray, X) is true.
	X and Idx are local variables in Goals.

    foreachelem(X,Array)
	like foreacharg/2, but iterates over all elements of an array
	of arbitrary dimension.  The order is the natural order, i.e.
	if Array = []([](a, b, c), [](d, e, f)), then for successive
	iterations X is bound in turn to a, b, c, d, e and f.
	X is a local variable in Goals.
	Cannot be used for constructing a term.

    foreachelem(X,Array,Idx)
	same as before, but Idx is set to the index position of X in
	Array, i.e. subscript(Array, Idx, X) is true.
	X and Idx are local variables in Goals.

    foreachindex(Idx,Array)
	like foreachelem/3, but returns just the index position and not the
	element.  Idx is a local variable in Goals.

    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, i.e. MaxExpr cannot be uninstantiated. 

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

    multifor(List,MinList,MaxList)
	like for/3, but allows iteration over multiple indices (saves
	writing nested loops).  Each element of List takes a value
	between the corresponding elements in MinList and MaxList.
	Successive iterations go through the possible combinations of
	values for List in lexicographic order.  List is a local
	variable in Goals.  MinList and MaxList must be either
	lists of arithmetic expressions evaluating to integers, or
	arithmetic expressions evaluating to integers (in the latter case
	they are treated as lists containing the (evaluated) integer
	repeated an appropriate number of times).  If none of List,
	MinList and MaxList is a list of fixed length at compile time then
	either MinList or MaxList must be a list of fixed length at call
	time (so that it is known how many indices are to be iterated).
	All lists must be the same length and must not be empty.

    multifor(List,MinList,MaxList,IncrementList)
	same as before, but IncrementList can be specified (i.e. how
	much to increment each element of List by).  IncrementList must
	be either a list of arithmetic expressions evaluating to non-zero
	integers, or an arithmetic expression evaluating to a non-zero
	integer (in which case all elements are incremented by this amount).
	IncrementList 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,
	i.e. 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!

    loop_name(Name)
	This specifier does not affect the semantics of the loop. It allows
	to give the loop a name, mainly for debugging purposes. Name must be
	an atom, and is used as the name of the auxiliary predicate into which
	the loop may be compiled. The name should therefore not clash with
	other predicate names in the same module.
    
    Note that fromto/4 is the most general specifier, but foreach/2,
    foreacharg/2,3, foreachelem/2,3, foreachindex/2, count/3, for/3,4,
    multifor/3,4 and param/N are convenient shorthands.
    
    There are three ways to combine the above specifiers in a single do loop:
    
    IterSpec1, IterSpec2 ("synchronous iteration")
	This is the normal way to combine iteration specifiers: simply
	provide a comma-separated sequence of them.  The specifiers are
	iterated synchronously; that is, they all take their first "value"
	for the first execution of Goals, their second "value" for the
	second execution of Goals, etc.  The order in which they are written
	does not matter, and the set of local variables in Goals is the
	union of those of IterSpec1 and IterSpec2.

	When multiple iteration specifiers are given in this way, typically
	not all of them will impose a termination condition on the loop
	(e.g. foreach with an uninstantiated list and
	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, then these
	conditions must coincide, i.e. specify the same number of
	iterations.

    IterSpec1 * IterSpec2 ("cross product")
	This iterates over the cross product of IterSpec1 and IterSpec2.
	The sequence of iteration is to iterate IterSpec2 completely for a
	given "value" of IterSpec1 before doing the same with the next
	"value" of IterSpec1, and so on.  The set of local variables in
	Goals is the union of those of IterSpec1 and IterSpec2.

    IterSpec1 >> IterSpec2 ("nested iteration")
	Like ( IterSpec1 do ( IterSpec2 do Goals ) ), including with respect
	to scoping.  The local variables in Goals are those of IterSpec2; in
	particular, those of IterSpec1 are not available unless IterSpec2
	passes them through, e.g. using a param.
	Similarly, the only "external" variables available as inputs to
	IterSpec2 are the locals of IterSpec1; variables from outside the
	loop are not available unless passed through by IterSpec1, e.g.
	using a param.
    
    
    Syntax: The do-operator binds like the semicolon, i.e. less than comma.
    That means that the whole do-construct should always be bracketed.
    
    Unless you use :-pragma(noexpand) or :-dbgcomp, the do-construct is
    compiled into an efficient auxiliary predicate. By default, the name of
    this predicate is do__nnn (where nnn is a unique integer), unless you
    have explicitly specified a name using the loop_name(Name) specifier.


Modules
   This predicate is sensitive to its module context (tool predicate, see @/2).

Fail Conditions
   Fails if Goal fails, or if two IterationSpecs specify a different number of iterations

Resatisfiable
   Resatisfiable if Goal is resatisfiable

Exceptions
     4 --- IterationSpecs insufficiently instantiated
   123 --- Ill-formed IterationSpecs

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]).

% reverse list (even shorter)
?- (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)).

% iterate over integers from 1 up to 5
?- (count(I,1,5) do writeln(I)).

% iterate over integers from 5 down to 1
?- (for(I,5,1,-1) do writeln(I)).

% make list of integers [1,2,3,4,5]
?- (for(I,1,5), foreach(I,List) do true).

% make a list of length 3
?- (foreach(_,List), for(_,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).

% iterate [I,J] over [1,1], [1,2], [1,3], [2,1], ..., [3,3]:
?- (multifor([I,J],1,3) do writeln([I,J])).

% similar, but have different start/stop values for I and J:
?- (multifor([I,J], [2,1], [4,5]) do writeln([I,J])).

% similar, but only do odd values for the second variable:
?- (multifor(List, [2,1], [4,5], [1,2]) do writeln(List)).

% 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)).

% rotate args in a struct
?- S0 = s(a,b,c,d,e), functor(S0, F, N), functor(S1, F, N),
    (foreacharg(X,S0,I), param(S1, N) do I1 is (I mod N)+1, arg(I1,S1,X)).

% flatten an array into a list
?- (foreachelem(X,[]([](5,1,2),[](3,3,2))), foreach(X,List) do true).

% transpose a 2D array
?- A = []([](5,1,2),[](3,3,2)), dim(A, [R,C]), dim(T, [C,R]),
    (foreachelem(X,A,[I,J]), param(T) do subscript(T, [J,I], X)).

% same, using foreachindex
?- A = []([](5,1,2),[](3,3,2)), dim(A, [R,C]), dim(T, [C,R]),
    (foreachindex([I,J],A), param(A, T) do
     subscript(A, [I,J], X), subscript(T, [J,I], X)).

% 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)
	)
    ).
% or
?- Xs = [1,2,3,4],
    ( foreach(X, Xs) * foreach(Y, Xs) 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)
	)
    ).
% or
?- Xs = [1,2,3,4],
    ( fromto(Xs, [X|Xs1], Xs1, []) >> ( 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]
        )
    ).
% or
pairs(Xs, Ys, Zs) :-
    (
	foreach(X, Xs) * foreach(Y, Ys),
	foreach(Z, Zs)
    do
	Z = X-Y
    ).


% Flatten a 2-dimensional matrix into a list:
flatten_matrix(Mat, Xs) :-
    dim(Mat, [M,N]),
    (
	for(I,1,M),
	fromto(Xs, Xs4, Xs1, []),
	param(Mat,N)
    do
	(
	    for(J,1,N),
	    fromto(Xs4, [X|Xs2], Xs2, Xs1),
	    param(Mat,I)
	do
	    subscript(Mat, [I,J], X)
	)
    ).

% Same using * to avoid nesting:
flatten_matrix(Mat, Xs) :-
    dim(Mat, [M,N]),
    (
	for(I, 1, M) * for(J, 1, N),
	foreach(X, Xs),
	param(Mat)
    do
	subscript(Mat, [I,J], X)
    ).

% Same using multifor to avoid nesting:
flatten_matrix(Mat, Xs) :-
    dim(Mat, [M,N]),
    (
	multifor([I,J], 1, [M,N]),
	foreach(X, Xs),
	param(Mat)
    do
	subscript(Mat, [I,J], X)
    ).

% Same for an array of arbitrary dimension:
flatten_array(Array, Xs) :-
    dim(Array, Dims),
    (
	multifor(Idx, 1, Dims),
	foreach(X, Xs),
	param(Array)
    do
	subscript(Array, Idx, X)
    ).

% Same but returns the elements in the reverse order:
flatten_array(Array, Xs) :-
    dim(Array, Dims),
    (
	multifor(Idx, Dims, 1, -1),
	foreach(X, Xs),
	param(Array)
    do
	subscript(Array, Idx, X)
    ).

% Flatten nested lists one level (cf. flatten/2 which flattens completely):
?- List = [[a,b],[[c,d,e],[f]],[g]],
    (foreach(Xs,List) >> foreach(X,Xs), foreach(X,Ys) do true).

% Iterate over all ordered pairs of integers 1..4 (param(I) required to make
% I available in body of loop):
?- (for(I,1,4) >> (for(J,I+1,4), param(I)) do writeln(I-J)).

% Same for general 1..N (param(N) required to make N available to second for):
?- N=4,
    ((for(I,1,N), param(N)) >> (for(J,I+1,N), param(I)) do writeln(I-J)).


See Also
   pragma / 1
