Did you know ... | Search Documentation: |
Predicate foreach/2 |
foreach(Generator, Goal) :- findall(Goal, Generator, Goals), maplist(call, Goals).
The actual implementation uses findall/3 on a template created from the variables shared between Generator and Goal. Subsequently, it uses every instance of this template to instantiate Goal, call Goal and undo only the instantiation of the template and not other instantiations created by running Goal. Here is an example:
?- foreach(between(1,4,X), dif(X,Y)), Y = 5. Y = 5. ?- foreach(between(1,4,X), dif(X,Y)), Y = 3. false.
The predicate foreach/2
is mostly used if Goal performs backtrackable destructive
assignment on terms. Attributed variables (underlying constraints) are
an example. Another example of a backtrackable data structure is in library(hashtable)
.
If we care only about the side effects (I/O, dynamic database, etc.) or
the truth value of Goal, forall/2
is a faster and simpler alternative. If Goal instantiates its
arguments it is will often fail as the argument cannot be instantiated
to multiple values. It is possible to incrementally grow an
argument:
?- foreach(between(1,4,X), member(X, L)). L = [1,2,3,4|_].
Note that SWI-Prolog up to version 8.3.4 created copies of Goal using copy_term/2 for each iteration, this makes the current implementation unable to properly handle compound terms (in Goal’s arguments) that share variables with the Generator. As a workaround you can define a goal that does not use compound terms, like in this example:
mem(E,L) :- % mem/2 hides the compound argument from foreach/2 member(r(E),L). ?- foreach( between(1,5,N), mem(N,L)).
The "foreach" example is confusing because "dif/2" freezes until a decision can be made as to whether the two logical variables are "dif" for sure.
Try:
ineach(X,Y) :- format("X=~w, Y=~w\n", [X,Y]), ((nonvar(X), nonvar(Y)) -> X<Y ; format("Not sufficiently instantiated\n")). % and then ?- foreach(between(1,4,X), ineach(X,Y)). X=1, Y=_24670 Not sufficiently instantiated X=2, Y=_24670 Not sufficiently instantiated X=3, Y=_24670 Not sufficiently instantiated X=4, Y=_24670 Not sufficiently instantiated true. % but if we set refine Y to something known before the call: ?- Y=3,foreach(between(1,4,X), ineach(X,Y)). X=1, Y=3 X=2, Y=3 X=3, Y=3 false.
Actually, I believe that permutation example is wrong. It only works when the items of a list are unique. The following incorrectly returns true:
?- perms([1,1,3],[1,2,3]). true.
And because it's comparing in only one direction, swapping the above arguments causes it to return false:
?- perms([1,2,3],[1,1,3]). false.
Permit me to give a different example:
:- use_module(library(yall)). isSymmetricList(List) :- length(List, Size), foreach(between(1, Size, I), call({Size,List}/[I1]>>( I2 is Size - I1 + 1, nth1(I1, List, Elem), nth1(I2, List, Elem) ),I) ).
I've imported yall, because this is the best form I've found for most times I use foreach. Technically you can define another rule that expresses the lambda, but that's cluttery. Maybe there's another way to do it, but I don't know what it is.
You want a nice example of foreach? Here you are the most declarative permutation generator on Earth...
perms(Src,Res)
:-length(Src,N)
,length(Res,N)
,
foreach(member(X,Src),member(X,Res))
.