首先,我已经阅读了关于在Prolog中使用cuts的所有其他帖子,并且确实看到了与使用它们相关的问题。然而,对于我来说仍然存在一些不清楚的地方,我想一劳永逸地解决这个问题。
在下面的简单示例中,我们通过递归迭代列表并检查每个第二个元素是否等于1。在这样做时,递归过程可能会以以下任一基本情况之一结束:空列表或剩余一个元素的列表。
在这种情况下,我总是在第二个基本情况(即表示列表中仅剩一个元素的情况)中使用显式的剪枝运算符,如下所示,以消除冗余的选择点。
现在我们得到:
然而,需要再强调一遍,这种切割行为之所以能正确地运作,是因为规则的顺序。如果有人将基本情况重新排列回如下原始形式:
在这些情况下,我总是在第二个基本情况中使用单个剪切符号,因为只有我一个人经过我的代码,而且我已经习惯了这种用法。然而,在我的另一个SO帖子的回答中,有人告诉我这不是推荐使用剪切符号的方式,我应该尽量避免使用它。
这就引出了我的双重问题:
- 如果一个剪切符号,无论其出现在规则的位置如何,都会改变行为但不改变解决方案(就像上面的例子一样),那么它是否仍被认为是不良实践? - 如果我想要摆脱像上面例子中典型的多余选择点,以使谓词完全确定,除了使用剪切符号之外,还有其他推荐的方法来完成这个目标吗?
提前感谢!
在下面的简单示例中,我们通过递归迭代列表并检查每个第二个元素是否等于1。在这样做时,递归过程可能会以以下任一基本情况之一结束:空列表或剩余一个元素的列表。
base([]).
base([_]).
base([_,H|T]) :- H =:= 1, base(T).
当执行时:
?- time(base([1])).
% 0 inferences, 0.000 CPU in 0.000 seconds (74% CPU, 0 Lips)
true ;
% 2 inferences, 0.000 CPU in 0.000 seconds (83% CPU, 99502 Lips)
false.
?- time(base([3,1,3])).
% 2 inferences, 0.000 CPU in 0.000 seconds (79% CPU, 304044 Lips)
true ;
% 2 inferences, 0.000 CPU in 0.000 seconds (84% CPU, 122632 Lips)
false.
在这种情况下,我总是在第二个基本情况(即表示列表中仅剩一个元素的情况)中使用显式的剪枝运算符,如下所示,以消除冗余的选择点。
base([]).
base([_]) :- !.
base([_,H|T]) :- H =:= 1, base(T).
现在我们得到:
?- time(base([1])).
% 1 inferences, 0.000 CPU in 0.000 seconds (81% CPU, 49419 Lips)
true.
?- time(base([3,1,3])).
% 3 inferences, 0.000 CPU in 0.000 seconds (83% CPU, 388500 Lips)
true.
我了解这种截断行为是特定于规则位置的,并且可以被视为不良实践。
然而,接下来我们可以将情况重新定位如下:
base([_,H|T]) :- H =:= 1, base(T).
base([_]).
base([]).
这也可以去除冗余的选择点而不使用cut,但是当然,我们只会将选择点转移到带有偶数位数字列表的查询中,例如以下内容:
?- time(base([3,1])).
% 2 inferences, 0.000 CPU in 0.000 seconds (82% CPU, 99157 Lips)
true ;
% 2 inferences, 0.000 CPU in 0.000 seconds (85% CPU, 96632 Lips)
false.
这显然也不是一个解决方案。但我们可以通过以下方式对规则进行调整,使其更具有可行性:
base([_,H|T]) :- H =:= 1, base(T), !.
base([_]).
base([]).
这实际上将不留下任何选择点。看一些查询:
?- time(base([3])).
% 1 inferences, 0.000 CPU in 0.000 seconds (81% CPU, 157679 Lips)
true.
?- time(base([3,1])).
% 3 inferences, 0.000 CPU in 0.000 seconds (83% CPU, 138447 Lips)
true.
?- time(base([3,1,3])).
% 3 inferences, 0.000 CPU in 0.000 seconds (82% CPU, 393649 Lips)
true.
然而,需要再强调一遍,这种切割行为之所以能正确地运作,是因为规则的顺序。如果有人将基本情况重新排列回如下原始形式:
base([]).
base([_]).
base([_,H|T]) :- H =:= 1, base(T), !.
即使这样,我们仍会得到不想要的行为:
?- time(base([1])).
% 0 inferences, 0.000 CPU in 0.000 seconds (83% CPU, 0 Lips)
true ;
% 2 inferences, 0.000 CPU in 0.000 seconds (84% CPU, 119546 Lips)
false.
在这些情况下,我总是在第二个基本情况中使用单个剪切符号,因为只有我一个人经过我的代码,而且我已经习惯了这种用法。然而,在我的另一个SO帖子的回答中,有人告诉我这不是推荐使用剪切符号的方式,我应该尽量避免使用它。
这就引出了我的双重问题:
- 如果一个剪切符号,无论其出现在规则的位置如何,都会改变行为但不改变解决方案(就像上面的例子一样),那么它是否仍被认为是不良实践? - 如果我想要摆脱像上面例子中典型的多余选择点,以使谓词完全确定,除了使用剪切符号之外,还有其他推荐的方法来完成这个目标吗?
提前感谢!
H =:= 1
吗?请注意,如果H
没有被实例化,这个目标会产生一个错误。 - false