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