SWI 序言 foreach/2

SWI Prolog foreach/2

考虑

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

?- 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]].

我想得到后一个结果,但是使用 member 是表达 "all the contents".

的更简洁的方式

我试过添加一些日志记录来调试,但我无法弄清它的正反面。

?- 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].

还有更复杂的日志记录和修改:

?- 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].

这最后一点让我怀疑生成器中的任何变量(foreach 的第一项)在 foreach 被调用时被有效地深度复制,并且副本在每个循环中重置。它会保留已经绑定的结构,但是在 foreach 中统一它们中的任何结构只会统一副本,而不是原始结构。这是一个正确的推论吗?关于 foreach 的运作方式,还有其他我应该知道的信息吗?文档非常薄。

(我确实想出了一个方法来做我本来想做的事情,我想:

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))).

只是有点笨拙。不过,也许特异性很好。)

为 x64-win64 使用 SWI-Prolog 版本 8.0.3。

The documentation 实际上说的是你认为正在发生的事情:

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.

foreach/2 的用处不多; forall/2 是,我相信,更常用,但这些都不是标准的。查看 forall/2 的文档,我注意到这条注释:

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.

果然,我们可以用 maplist/2 和 lamba 表达式来做你想做的事:

?- 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]].

您也可以使用 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]].

请注意,在后一种情况下,结果不是 L,而是 Xs。

我认为这些中的任何一个都是比您提出的代码更好的选择,虽然我认为 aggregate 应该更频繁地使用,但我觉得它看起来有点复杂。