Did you know ... Search Documentation:
SWI-Prolog owl logo Predicate forall/2
Availability:built-in
Source [semidet]forall(:Cond, :Action)
For all alternative bindings of Cond, Action can be proven. The example verifies that all arithmetic statements in the given list are correct. It does not say which is wrong if one proves wrong.
?- forall(member(Result = Formula, [2 = 1 + 1, 4 = 2 * 2]),
 Result =:= Formula).

The predicate forall/2 is implemented as \+ ( Cond, \+ Action), i.e., There is no instantiation of Cond for which Action is false.. The use of double negation implies that forall/2 does not change any variable bindings. It proves a relation. The forall/2 control structure can be used for its side-effects. E.g., the following asserts relations in a list into the dynamic database:

?- forall(member(Child-Parent, ChildPairs),
 assertz(child_of(Child, Parent))).

Using forall/2 as forall(Generator, SideEffect) is preferred over the classical failure driven loop as shown below because it makes it explicit which part of the construct is the generator and which part creates the side effects. Also, unexpected failure of the side effect causes the construct to fail. Failure makes it evident that there is an issue with the code, while a failure driven loop would succeed with an erroneous result.

 ...,
 ( Generator,
 SideEffect,
 fail
 ; true
 )

If your intent is to create variable bindings, the forall/2 control structure is inadequate. Possibly you are looking for maplist/2, findall/3 or foreach/2.

Tags are associated to your profile if you are logged in|Report abuse
Tags:
  • failure_driven_loop
LogicalCaptain said (2020年07月15日T14:38:31):0 upvotes 0 0 downvotes
Picture of user LogicalCaptain.

In particular:

Àny binding is rolled back on success because it happens behind the "\+ wall":

?- forall(true,X=1),write(X).
_4984
true.

And binding previously extant is used of course:

?- X=1,forall(true,X=1),write(X).
1
X = 1.
?- X=2,forall(true,X=1),write(X).
false.

forany/2

This should do it:

forany(Cond,Action) :-
 \+forall(Cond,\+Action).

Make sure you generator doesn't fail

If the generator fails, then forall/2 succeeds (logically correct - but maybe not what you want):

We want:

?- forall(member(X,[_,1,_]),var(X)).
false

But due to bad programming we get:

?- forall(member(X,foo),var(X)).
true.

The difference between a forall / 2 and a maplist / 2

Consider

repeated_success(X) :-
 between(0,10,X),
 between(X,10,Y),
 format("Success for X=~d, Y=~d~n",[X,Y]).

Then forall does not backtrack over the individual calls if it fails at 11:

?- forall(member(X,[8,9,10,11]),repeated_success(X)).
Success for X=8, Y=8
Success for X=9, Y=9
Success for X=10, Y=10
false.

But maplist/2 backtracks if it fails at 11:

?- maplist(repeated_success,[8,9,10,11]).
Success for X=8, Y=8
Success for X=9, Y=9
Success for X=10, Y=10
Success for X=9, Y=10
Success for X=10, Y=10
Success for X=8, Y=9
Success for X=9, Y=9
Success for X=10, Y=10
Success for X=9, Y=10
Success for X=10, Y=10
Success for X=8, Y=10
Success for X=9, Y=9
Success for X=10, Y=10
Success for X=9, Y=10
Success for X=10, Y=10
false.

Similarly, for success:

?- forall(member(X,[8,9,10]),repeated_success(X)).
Success for X=8, Y=8
Success for X=9, Y=9
Success for X=10, Y=10
true.

But

?- maplist(repeated_success,[8,9,10]).
Success for X=8, Y=8
Success for X=9, Y=9
Success for X=10, Y=10
true ;
Success for X=9, Y=10
Success for X=10, Y=10
true ;
Success for X=8, Y=9
Success for X=9, Y=9
Success for X=10, Y=10
true ;
Success for X=9, Y=10
Success for X=10, Y=10
true ;
Success for X=8, Y=10
Success for X=9, Y=9
Success for X=10, Y=10
true ;
Success for X=9, Y=10
Success for X=10, Y=10
true.

Example

Computing/Verifying prime numbers using forall/2

From http://rosettacode.org/wiki/Primality_by_trial_division#Prolog

prime(2).
prime(N) :-
 between(3, inf, N),
 1 is N mod 2, % odd
 M is floor(sqrt(N+1)), % round-off paranoia
 Max is (M-1) // 2, % integer division
 forall( between(1, Max, I), N mod (2*I+1) > 0 ).

Example

Using forall as looping construct to generate side-effects, here a rolling sine wave (works on ANSI terminals because we need to move the cursor to the topleft corner of the drawing N+1 after drawing N has been printed)

% Print a sine wave over a 2*Pi interval vertically using "Lines" lines. The amplitude is
% "Amplitude" characters around the vertical ZERO line (which here is not written using "|"
% but using the last digit of the function's offset, which here is the same as the drawing
% number)
sinus(Lines,Amplitude,Offset,Last) :-
 MaxLine is Lines - 1,
 forall(
 between(0,MaxLine,Line), % loop over the lines (i.e. the x values)
 (
 XReal is ( Line + Offset ) * ( 2.0 * pi ) / MaxLine,
 YReal is sin(XReal),
 Height is round(Amplitude*YReal), % round to nearest, but outward from 0
 
 BlankCount is min(Amplitude,Amplitude + Height),
 string_of(' ',BlankCount,BlankString),
 
 StarCount is abs(Height),
 string_of('*',StarCount,StarsString),
 
 format("\x1B[K",[]), % ANSI sequence to clear current line
 
 write_line(Offset,Height,StarsString,BlankString),
 format("~n",[]), % End with newline (can't skip this otherwise the line isn't flushed out)
 
 (
 (Last == false, Line == MaxLine) % If last line but not last drawing, move the cursor back up
 ->
 MoveLeft is BlankCount + StarCount + 1,
 MoveUp is MaxLine + 1,
 format("\x1B[~dD",[MoveLeft]), % ANSI sequence to move the cursor leftwards
 format("\x1B[~dA",[MoveUp]) % ANSI sequence to move the cursor upwards
 ;
 true
 )
 )
 ).
 
write_line(Offset,Height,StarsString,BlankString) :-
 Of is Offset mod 10, % instead of '|', write "Offset mod 10" at the X axis for visibility
 format("~s",[BlankString]),
 (Height < 0
 ->
 format("~s~d",[StarsString,Of]) % "negative sinus values"
 ;
 format("~d~s",[Of,StarsString])). % "positive sinus values"
string_of(Char,Length,String) :-
 length(Chars,Length),
 maplist(=(Char),Chars),
 string_chars(String,Chars).
 
wave(Lines,Amplitude) :-
 MaxDrawings = 10000,
 forall(
 between(0,MaxDrawings,DrawingNumber), % between 0..MaxDrawings drawings
 (
 Offset = DrawingNumber, % the function offset happens to be the same as the drawing number
 (MaxDrawings == DrawingNumber
 -> LastDrawing = true
 ; LastDrawing = false),
 sinus(Lines,Amplitude,Offset,LastDrawing), % draw single image
 ((LastDrawing) -> format("~n",[]) ; sleep(0.05)) % slow down a bit between drawings
 )
 ).

Try it for some old-school microcomputer era vibes:

?- wave(30,30).
******************************5
 *****************************5
 ***************************5
 ***********************5
 ******************5
 *************5
 ******5
 5
 5******
 5*************
 5******************
 5***********************
 5***************************
 5*****************************
 5******************************
 5******************************
 5****************************
 5*************************
 5*********************
 5***************
 5**********
 5***
 ***5
 **********5
 ***************5
 *********************5
 *************************5
 ****************************5
******************************5
******************************5
login to add a new annotation post.

AltStyle によって変換されたページ (->オリジナル) /