许多谓词本质上使用某种形式的传递闭包,但发现必须解决终止问题。为什么不使用
closure0/3
一劳永逸地解决它呢?:- meta_predicate closure0(2,?,?).
:- meta_predicate closure(2,?,?).
:- meta_predicate closure0(2,?,?,+). % internal
closure0(R_2, X0,X) :-
closure0(R_2, X0,X, [X0]).
closure(R_2, X0,X) :-
call(R_2, X0,X1),
closure0(R_2, X1,X, [X1,X0]).
closure0(_R_2, X,X, _).
closure0(R_2, X0,X, Xs) :-
call(R_2, X0,X1),
non_member(X1, Xs),
closure0(R_2, X1,X, [X1|Xs]).
non_member(_E, []).
non_member(E, [X|Xs]) :-
dif(E,X),
non_member(E, Xs).
这个定义在实现传递闭包时是否存在无法使用的情况?
为什么要使用dif/2?
回答@WouterBeek的评论,dif/2
或dif_si/2
是理想的选择,因为它们能够显示或标识潜在的问题。然而,在当前的实现中,顶层循环经常隐藏了实际的问题。考虑目标closure0(\_^_^true,a,b)
本身就非常有问题。当使用以下系统时,实际问题直接不可见。
| ?- closure0(\_^_^true,a,b). % SICStus
yes
?- closure0(\_^_^true,a,b). % SWI
true ;
true ;
true ...
两个顶层循环都没有展示我们想要看到的内容:悬挂约束。在SICStus中,我们需要一个伪变量来产生一些替换,在SWI中,查询必须用call_residue_vars/2
进行包装。这样,所有具有约束的变量现在都会显示出来。
| ?- closure0(\_^_^true,a,b), Alt=t. % SICStus
Alt = t ? ;
Alt = t,
prolog:dif(_A,a),
prolog:dif(b,_A) ? ;
Alt = t,
prolog:dif(_A,a),
prolog:dif(_B,_A),
prolog:dif(_B,a),
prolog:dif(b,_B),
prolog:dif(b,_A) ...
?- call_residue_vars(closure0(\_^_^true,a,b),Vs). % SWI
Vs = [] ;
Vs = [_G1744, _G1747, _G1750],
dif(_G1744, a),
dif(b, _G1744) ;
Vs = [_G1915, _G1918, _G1921, _G1924, _G1927, _G1930, _G1933],
dif(_G1915, a),
dif(b, _G1915),
dif(_G1921, _G1915),
dif(_G1921, a),
dif(b, _G1921) ...
nonmember/2
而不是\+ memberchk/2
?这可能有一个很好的用例,但我自己想不出来。 - Wouter Beek\+ memberchk/2
或\+member/2
会产生错误的结果。使用dif/2
或iso_dif/2
的nonmember/2
不会有这个问题。 - false