Prolog中的截断陷阱
prolog吧
全部回复
仅看楼主
吧务
level 14
端点市 楼主
《Programming In Prolog》的第4.4章节,截断的问题(Problems with the Cut)里,作者讲解了利用截断机制时可能会出的状况。内容很基础,但之前没太留意这个,只是把求解问题就得了,没考虑太多设计之外的情况。书中给出了两个示例:
示例一:将第2个列表附加到第1个列表之后,形成新的列表的代码:
append([], X, X) :- !.
append([A|B], C, [A|D]) :- append(B, C, D).
然后进行3个询问:
?- append([a,b,c],[d,e], X).
?- append([a,b,c], X, Y).
?- append(X, Y, [a,b,c]).
前2个的结果没什么问题,但第3个却出了问题,给出了这样的结果:
X=[], Y=[a,b,c].
它只给出这种情况的结果,因为在第1个参数为空,也能够匹配成功,但后面的截断会使查询就此打住。
示例二:表述人的父母数量,亚当和夏娃例外的代码:
number_of_parents(adam, 0) :- !.
number_of_parents(eve, 0) :- !.
number_of_parents(X, 2).
然后进行3个询问:
?- number_of_parents(eve, X).
?- number_of_parents(john, X).
?- number_of_parents(eve, 2).
前2个的结果没什么问题,但第3个却出了问题,给出了这样的结果:
yes
这个命题应当为假才对。因为前面的语句匹配失败后,直接与number_of_parents(X, 2).语句进行匹配,而这个语句,会对任意第1个变量,都将第2个变量匹配为2。(而且这里解释器给出警告,说这个变量只出现过1次。需要表示任意值的话,用_符号更好一些。)
书里就第2个示例给出了两个解决方案,将代码重写为:
number_of_parents(adam, N) :- !, N = 0.
number_of_parents(eve, N) :- !, N = 0.
number_of_parents(X, 2).

number_of_parents(adam, 0).
number_of_parents(eve, 0).
number_of_parents(X, 2) :- \+(X=adam), \+(X=eve).
因此,只有当你对如何使用规则语句有明确的策略时,才能可靠地使用截断。如果你要更改相关规则语句,则必须审查所有使用截断的地方。
2022年11月23日 12点11分 1
level 13
确实,初学者很容易被这个“!”坑
2022年11月24日 09点11分 2
吧务
level 14
端点市 楼主
当时看到这个内容,就想起《The Craft of Prolog》中关于将第2个列表附加到第1个列表之后,形成第3个列表的代码演示。
很容易就能写出简陋的相关代码。记得第1个代码能正向得到结果,但如果变为第3个已知,前两个未知,无法求出组合方案。第2个代码恰好相反,能反向求解,但正向是会出问题。还有这种小小玄机。
当然,Prolog自带这个谓词,它那个就很完备,正向反向都可以用。很佩服。
2022年12月04日 12点12分 3
1