给定一组通过谓词parent/2表示父子关系的事实,用谓词pred1/2和pred2/2定义关系“祖先”(祖先)时有什么区别,如下所示?
pred1(X,Z) :- parent(X,Z).
pred1(X,Z) :- parent(X,Y), pred1(Y,Z).
pred2(X,Z) :- parent(X,Z).
pred2(X,Z) :- parent(Y,Z), pred2(X,Y).
给定一组通过谓词parent/2表示父子关系的事实,用谓词pred1/2和pred2/2定义关系“祖先”(祖先)时有什么区别,如下所示?
pred1(X,Z) :- parent(X,Z).
pred1(X,Z) :- parent(X,Y), pred1(Y,Z).
pred2(X,Z) :- parent(X,Z).
pred2(X,Z) :- parent(Y,Z), pred2(X,Y).
主要区别在于两者的终止属性,前提是关系中有一些循环。为了理解这一点,我将使用一个故障切片,它将程序缩减到它的本质 wrt 终止。
pred1(X,Z) :- false , parent(X,Z)。 pred1(X, Z ) :- 父级(X,Y), pred1(Y, Z )。% Z没有影响pred2(X,Z) :- false , parent(X,Z)。 pred2( X ,Z) :- 父级 (Y,Z), pred2( X ,Y)。% X没有影响
对于pred1/2
,第二个参数对终止完全没有影响,而在 中pred2/2
,第一个参数没有影响。要看到这一点,请尝试使用以下事实的原始定义:
父母(一,一)。 ?- pred1(b, _), false。% 终止 ?- pred2(b, _), false。% 循环 ?- pred1(_, b), false。% 循环 ?- pred2(_, b), false。% 终止
参见closure/3
避免此类循环的一般方法。
为了完整起见,这里是传递闭包的另一种变体,它具有一些概念上的优势:
pred3(X,Z) :- parent(X,Z).
pred3(X,Z) :- pred3(X,Y), pred3(Y,Z).
唉,它的终止特性最差。事实上,它永远不会终止,正如以下片段所证明的那样:
pred3(X,Z) :- false , parent(X,Z)。 pred3(X,Z) :- pred3(X,Y), false ,pred3(Y,Z)。
在这个片段中,第一个参数只被传递。所以,不管参数是什么,程序总是会循环!
?- pred3(b,b), false。% 循环