True when the conjunction of instances of Goal created
from solutions for Generator is true. Except for term
copying, this could be implemented as below.
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)).