1
votes

Consider

?- length(L, 4), foreach(member(M,L),(length(M,4))).
L = [_28602, _28608, _28614, _28620].

vs

?- length(L, 4), foreach(between(1,4,I),call({L}/[I]>>(nth1(I,L,M),length(M,4)),I)).
L = [[_6010, _6016, _6022, _6028], [_6358, _6364, _6370, _6376], [_6754, _6760, _6766, _6772], [_7198, _7204, _7210, _7216]].

I'd like to get the latter result, but using member is a tidier way of expressing "all the contents".

I've tried adding some logging to debug, but I can't make heads or tails of it.

?- length(L, 4), foreach((member(M,L),write(M),writeln(L)),(length(M,4))).
_3400[_3400,_4060,_4066,_4072]
_3400[_4054,_3400,_4066,_4072]
_3400[_4054,_4060,_3400,_4072]
_3400[_4054,_4060,_4066,_3400]
L = [_4054, _4060, _4066, _4072].

And yet more convoluted logging and modification:

?- length(L, 3), foreach((member(M,L),write((m,M," ")),write((l,L," ")),length(M,3),write((m,M," ")),writeln((l,L))),(true)).
m,_60830, l,[_60830,_61872,_61878], m,[_61944,_61950,_61956], l,[[_61944,_61950,_61956],_61872,_61878]
m,_60830, l,[_61866,_60830,_61878], m,[_61944,_61950,_61956], l,[_61866,[_61944,_61950,_61956],_61878]
m,_60830, l,[_61866,_61872,_60830], m,[_61944,_61950,_61956], l,[_61866,_61872,[_61944,_61950,_61956]]
L = [_61866, _61872, _61878].

This last, in particular, leads me to suspect that any variable in the generator (the first term of foreach) is effectively deep-copied at the time foreach is called, and the copy is reset on each loop. It'll keep structures that are already bound, but unifying any of them inside the foreach just unifies the copy, not the original. Is that a correct inference? Is there any other info I should know about how foreach operates? The documentation is pretty slim.

(I did figure out a way to do what I originally wanted, I think:

loop(Gen, Cond) :-
    aggregate(bag(X),call(Gen,X),L),maplist(Cond,L).

?- length(L,4),loop({L}/[X]>>(member(X,L)),[X]>>(length(X,4))).

It's just a bit clunky. Perhaps the specificity is good, though.)

Using SWI-Prolog version 8.0.3 for x64-win64.

1

1 Answers

0
votes

The documentation actually says what you believe is happening:

Each member of the conjunction is a copy of Goal, where the variables it shares with Generator are filled with the values from the corresponding solution.

One doesn't see a lot of use of foreach/2; forall/2 is, I believe, more commonly used but neither of these is standard. Looking at the docs for forall/2, I notice this note:

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.

Sure enough, we can do what you're trying to do here with maplist/2 and a lamba expression:

?- length(L, 4), maplist([X]>>(length(X,4)), L).
L = [[_9462, _9468, _9474, _9480], 
     [_9558, _9564, _9570, _9576], 
     [_9654, _9660, _9666, _9672], 
     [_9750, _9756, _9762, _9768]].

You can also do this with findall/3:

?- length(L, 4), findall(X, (member(X, L), length(X, 4)), Xs).
L = [_10432, _10438, _10444, _10450],
Xs = [[_10580, _10586, _10592, _10598], 
      [_10550, _10556, _10562, _10568], 
      [_10520, _10526, _10532, _10538], 
      [_10490, _10496, _10502, _10508]].

Notice that in the latter case here, the result isn't L, it's Xs.

I would consider either of these a better option than the code you came up with, which just looks a bit convoluted to me, although in general I think aggregate should be used more often.