I see. Now I read
?- foreach(between(1, 5, M), member(M, L))
as
find L such that for all M satsifying between(1, 5, M)
member(M, L)
holds.
EDIT:
Following your comment, I have tested a toy foreach
.
I have seen too many possible issues on this toy approach using numbervars.
% ?- toy_foreach(between(1,5,J), member(J, L), [L], Ws).
%@ L = A,
%@ Ws = [[1, 2, 3, 4, 5|_]] .
toy_foreach(A, B, Vs, Ws):- numbervars(Vs),
findall(B, A, Bs),
hzip(Vs, Ws, Zip),
subst_numbervars(Zip, Bs, Cs),
maplist(call, Cs).
%
hzip([],[],[]).
hzip([X|Xs],[Y|Ys],[X-Y|Zs]):-hzip(Xs, Ys, Zs).
%
subst_numbervars(_, X, Y):- (var(X); atomic(X)), !, Y = X.
subst_numbervars(M, '$VAR'(I), A):-!, memberchk('$VAR'(I)-A, M).
subst_numbervars(M, X, Y):- functor(X, F, N), functor(Y, F, N),
subst_numbervars(N, M, X, Y).
%
subst_numbervars(0, _, _, _):-!.
subst_numbervars(J, M, X, Y):- I is J - 1,
arg(J, X, A),
arg(J, Y, B),
subst_numbervars(M, A, B),
subst_numbervars(I, M, X, Y).
EDIT: A much shorter version using copy_term/2
instead of numbervars
, which I think, almost the same as the current foreach
, provided that “source” and “target” variables
are explicitly given. I am not sure that this assumption could be dropped.
% ?- toy_foreach1(between(1,5,J), member(J, L), [J], [L]).
%@ L = [1, 2, 3, 4, 5|_] .
toy_foreach1(A, B, Vs, Ws):-
findall(Vs, A, Sol),
copy_of_goal(Sol, Vs, Ws, B, Cs),
maplist(call, Cs).
%
copy_of_goal([], _, _, _, []).
copy_of_goal([A|As], Vs, Ws, B, [G|Gs]):-
copy_term(Vs+Ws+B, A+Ws+G),
copy_of_goal(As, Vs, Ws, B, Gs).
EDIT. Using closures of package pac, copy_of_goal clauses avove are included in a maplist call:
% ?- yaforeach(between(1,5,J), member(J, L), [J], [L]).
%@ L = [1, 2, 3, 4, 5|_] .
yaforeach(A, B, Vs, Ws):-
findall(Vs, A, Sols),
maplist(pred([Vs, Ws, B],
([A]:- copy_term(Vs+Ws+B, A+Ws+G), call(G))),
Sols).
% ?- listing(yaforeach).
%@ yaforeach(A, B, C, D) :-
%@ findall(C, A, E),
%@ '__aux_maplist/2_math:pac#96+3'(E, C, D, B).
%@
%@ true.
% ?- listing('__aux_maplist/2_math:pac#96+3').
%@ '__aux_maplist/2_math:pac#96+3'([], _, _, _).
%@ '__aux_maplist/2_math:pac#96+3'([A|B], C, D, E) :-
%@ 'pac#96'(C, D, E, A),
%@ '__aux_maplist/2_math:pac#96+3'(B, C, D, E).
%@
%@ true.
% ?- listing('pac#96').
%@ 'pac#96'(A, B, C, D) :-
%@ copy_term(A+B+C, D+B+E),
%@ call(E).
%@
%@ true.
EDIT: foreach_by_assert/4
is a test version of foreach/2
without using findall/3
and copy_term/2
, but using assert/1
. It is not beautiful because of using assert
, but I have not strong opinion against using assert
. Anyway it is a test code.
% ?- foreach_by_assert(between(1,5,J), member(J, L), [J], [L]).
%@ L = [1, 2, 3, 4, 5|_].
foreach_by_assert(Gen, Con, Vs, Ws):-
retractall('$consume'(_,_)),
assert(( '$consume'(Vs, Ws):- Con)),
Stash = '$STASH'(Ws),
( call(Gen),
arg(1, Stash, U),
once('$consume'(Vs, U)),
nb_setarg(1, Stash, U),
fail
; arg(1, Stash, Ws)
).
EDIT: A benchmark test shows that foreach
by copy_term
is faster than that by assert
. I expected the opposite.
% ?- N=100, K=1000, nopac(time(repeat(N, once(foreach_by_copy(between(1,K,J), member(J, L), [J], [L]))))).
%@ % 50,751,501 inferences, 2.570 CPU in 2.575 seconds (100% CPU, 19748888 Lips)
%@ N = 100,
%@ K = 1000.
% ?- N=100, K=1000, nopac(time(repeat(N, once(foreach_by_assert(between(1,K,J), member(J, L), [J], [L]))))).
%@ % 50,450,626 inferences, 3.569 CPU in 3.579 seconds (100% CPU, 14136215 Lips)
%@ N = 100,
%@ K = 1000.
EDIT: This is a complete final version of foreach/2
of mine, though I am not sure yet that I understand the foreach
requirement. I understand simply foreach(P, Q) logically as
exist Ys forall Xs (P → Q), where Ys = (VQ + VP) \ VP (set subtraction), Xs = (VP+VQ)\Ys (+ set union), Xs and Ys are disjoint, where VP is the set of variables of P and VQ is the set of variables of Q. I am not sure this logical interpretation is correct, also I forget for now further questions, for instance, nested foreach
.
?- foreach(between(1,5,N), member(r(N),L)).
L = [r(1), r(2), r(3), r(4), r(5)|_].
exists M exists L forall J (between(1,5, J) -> (member(J, L), member(J, M))).
?- foreach(between(1,5, J), (member(J, L), member(J, M))).
L = [1, 2, 3, 4, 5|_],
M = [1, 2, 3, 4, 5|_].
exists L forall J (between(1,5, J), between(3, 7, J) -> member(J, L))
?- foreach((between(1,5, J), between(3, 7, J)), member(J, L)).
L = [3, 4, 5|_].
exists L forall J (between(1,5, J); between(3, 7, J)) -> member(J, L))
?- foreach((between(1,5, J); between(3, 7, J)), member(J, L)).
L = [1, 2, 3, 4, 5, 6, 7|_].
%
foreach(Gen, Con):- term_variables(Gen, Vs),
sort(Vs, Vs0),
term_variables(Con, Ws),
sort(Ws, Ws0),
ord_subtr_var(Ws0, Vs0, Ws1),
once(foreach_by_copy(Gen, Con, Vs0, Ws1)).
%
foreach(Gen, Con, Vs, Ws):- once(foreach_by_copy(Gen, Con, Vs, Ws)).
% ?- N=200, K = 5, time((repeat(N, once((foreach_by_copy(between(1,5, J), member(J, L), [J], [L]), writeln(L)))))).
foreach_by_copy(A, B, Vs, Ws):-
findall(Vs, A, Sol),
copy_of_goal(Sol, Vs, Ws, B, Cs),
maplist(call, Cs).
%
copy_of_goal([], _, _, _, []).
copy_of_goal([A|As], Vs, Ws, B, [G|Gs]):-
copy_term(Vs+Ws+B, A+Ws+G),
copy_of_goal(As, Vs, Ws, B, Gs).
%
ord_subtr_var([], _, []):-!.
ord_subtr_var(X, [], X):-!.
ord_subtr_var([X|Xs], [Y|Ys], Zs):- X==Y, !,
ord_subtr_var(Xs, Ys, Zs).
ord_subtr_var([X|Xs], [Y|Ys], [X|Zs]):- X@<Y, !,
ord_subtr_var(Xs, [Y|Ys], Zs).
ord_subtr_var(Xs, [_|Ys], Zs):- ord_subtr_var(Xs, Ys, Zs).