A do-loop corresponds to a call to an auxiliary recursive predicate of the form
do__n(...) :- !. do__n(...) :- Goals, do__n(...).IterationSpecs specifies the termination condition, and what is being iterated over. It can be one (or a combination) of the following:
There are three ways to combine the above specifiers in a single do loop:
Syntax: The do-operator binds like the semicolon, i.e. less than comma. That means that the whole do-construct should always be parenthesised.
Do-loops can be used as a control structure in grammar rules as well: A do-loop in a grammar rule context will generate (or parse) the concatenation of the lists of symbols generated (or parsed) by each loop iteration (the grammar rule transformation effectively adds a hidden fromto-iterator to a do-loop).
Cuts in the loop body only have a local effect, i.e. they do not cut through the loop.
Unless you use :-pragma(noexpand) or the compiler's expand_goals:off option, 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.
% 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 arguments in a 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 arguments 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 arguments 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 using the product combinator:
?- 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 using the nesting combinator:
?- 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)).
% Do-loop inside a Grammar Rule
% This rule will accept/generate a list of integers from 1 to N
intlist(N) --> ( for(I,1,N) do [I] ).