如果你的 Prolog 有某种不可回溯的赋值,比如 SWI-Prolog 'global' variables,你可以用这种方式实现(当心,简单的代码):
:- meta_predicate solutions(0, ?).
:- meta_predicate solutions(+, 0, ?).
solutions(G, L) :-
solutions(G, G, L).
solutions(P, G, L) :-
( nb_current(solutions_depth, C) -> true ; C=1 ),
D is C+1,
nb_setval(solutions_depth, D),
atom_concat(solutions_depth_, D, Store),
nb_setval(Store, []),
( G,
nb_getval(Store, T),
nb_setval(Store, [P|T]),
fail
; nb_getval(Store, R)
),
nb_delete(Store),
nb_setval(solutions_depth, C),
reverse(R, L).
使用“全局”变量可以更有效地执行 WRT 动态数据库(断言/撤回),并且(在 SWI-prolog 中)甚至可以在多线程应用程序中使用。
编辑
感谢@false 评论,在 reverse/2之前移动了剪切,并为可重入调用引入了一个堆栈:例如
?- solutions(X-Ys,(between(1,3,X),solutions(Y,between(1,5,Y),Ys)),S).
S = [1-[1, 2, 3, 4, 5], 2-[1, 2, 3, 4, 5], 3-[1, 2, 3, 4, 5]].
编辑
这是solutions/3 的变体,按顺序构建结果列表,以避免最终的reverse/2 调用。将结果添加到列表尾部有点棘手......
solutions(P, G, L) :-
( nb_current(solutions_depth, C) -> true ; C=1 ),
D is C+1,
nb_setval(solutions_depth, D),
atom_concat(solutions_depth_, D, Store),
( G,
( nb_current(Store, U/B) -> B = [P|R], Q = U/R ; Q = [P|T]/T ),
nb_setval(Store, Q),
fail
; ( nb_current(Store, L/[]) -> true ; L = [] )
),
nb_delete(Store),
nb_setval(solutions_depth, C).