当谓词回溯到同一个解决方案时,使用cut有意义吗



我正在编写一个简单的矩阵库,我偶然发现了一个解决方案,它提供了一个了解切割目的的机会。

boolean_mult(1,1,1).
boolean_mult(1,0,0).
boolean_mult(0,1,0).
boolean_mult(0,0,0).
binary_dot_product([B1],[B2],Solution) :-
boolean_mult(B1,B2,Solution).
binary_dot_product([1|_],[1|_],1). % computation can end here.
binary_dot_product([_|B1s],[_|B2s],Solution) :-
binary_dot_product(B1s,B2s,Solution).

从声明的角度来看,这个解决方案是有意义的。给定两个1和0的列表,B1和B2,如果它们的头的布尔积是1,那么列表的点积是1。否则取其尾部的点积。单例列表的点积是这两个元素的布尔积。

不过,我遇到的问题显示在这个查询中:

?- binary_dot_product([1,0,1],[1,1,1],X).
X = 1 ;
X = 1 ;
X = 1.

我能够回溯三次,从声明的角度来看,这是没有意义的。只能有一个点积!不是3!

我可以很容易地解决这个问题使用切割:

binary_dot_product([B1],[B2],Solution) :-
boolean_mult(B1,B2,Solution).
binary_dot_product([1|_],[1|_],1) :- !. 
binary_dot_product([_|B1s],[_|B2s],Solution) :-
binary_dot_product(B1s,B2s,Solution).

现在:

?- binary_dot_product([1,0,1],[1,1,1],X).
X = 1.

到目前为止,在我的Prolog职业生涯中,我已经避免了像瘟疫一样的裁员,因为我已经被警告过他们的危险。在这种情况下,虽然我觉得削减是很有意义的。

在上面的代码中使用一个切口会给我带来什么收获和损失?

EDIT(由于OP坚持;-(

我在回答这个问题:

在上面的代码中使用剪切,我得到了什么和失去了什么?

我不确定你得到了什么。如果你不在代码中使用剪切,你就失去了问免费的更一般问题的能力。

在一般情况下,多次获得相同的正确解决方案强烈表明,无论是否进行切割,您的代码要么是多余的,要么是完全错误的。当然,这并不是普遍正确的,但从经验来看,这往往是正确的。


你不应该"避免像瘟疫一样的割伤";。你应该在需要的时候使用切口,而不是在不需要的时候。

这是一个太大的话题,无法在SO上回答。每一本像样的Prolog教科书都会解释削减,并讨论何时需要以及为什么需要;并运用你的判断,这将随着你的编程而提高。

但是:如果您的程序在没有剪切的情况下不正确,剪切将无法修复


考虑一下这些问题。

  1. 给定两个布尔向量,它们的点积是多少?

  2. 用三个参数实现一个谓词。前两个参数是布尔向量,第三个参数是点积。

在Prolog的土地上,第二个问题假设你可以问这样的问题:;给定一个向量和一个点积,另一个向量是什么"您当前的任何一种实现都能做到这一点吗?你会问这样的问题吗?即使你不认为你会问这样的问题,考虑到你可以做到,你能想出它的用途吗?你看到了吗?


现在,您的代码。它有一些问题(例如,请参阅Isabelle Newbie刚才的评论(。

这不会给出错误的结果:

:- module(boolean, [add/3, mult/3, dot_product/3]).
add(1,1,1).
add(1,0,1).
add(0,1,1).
add(0,0,0).
mult(1,1,1).
mult(1,0,0).
mult(0,1,0).
mult(0,0,0).
dot_product(V1, V2, P) :-
same_length(V1, V2),
maplist(mult, V1, V2, [H|T]),
foldl(add, T, H, P).

使用它:

?- use_module(boolean).
true.
?- dot_product([1,0,1],[1,1,1],P).
P = 1 ;
false.
?- dot_product([1, 1], [1, 0], Product).
Product = 1 ;
false.
?- dot_product([1, 1], V2, 1).
V2 = [1, 1] ;
V2 = [1, 0] ;
V2 = [0, 1] ;
false.
?- dot_product(V1, V2, 1).
V1 = V2, V2 = [1] ;
V1 = V2, V2 = [1, 1] ;
V1 = [1, 1],
V2 = [1, 0] ;
V1 = [1, 0],
V2 = [1, 1] ;
V1 = V2, V2 = [1, 0] ;
V1 = [1, 1],
V2 = [0, 1] ;
V1 = [0, 1],
V2 = [1, 1] ;
V1 = V2, V2 = [0, 1] ;
V1 = V2, V2 = [1, 1, 1] ;
V1 = [1, 1, 1],
V2 = [1, 1, 0] .
?- dot_product(V1, V2, P).
V1 = V2, V2 = [1],
P = 1 ;
V1 = [1],
V2 = [0],
P = 0 ;
V1 = [0],
V2 = [1],
P = 0 ;
V1 = V2, V2 = [0],
P = 0 ;
V1 = V2, V2 = [1, 1],
P = 1 ;
V1 = [1, 1],
V2 = [1, 0],
P = 1 ;
V1 = [1, 0],
V2 = [1, 1],
P = 1 .

编辑2:在最后一个正确答案之后还有一个选择点。要消除它,您可以使用tabling(例如,SWI-Prolog中提供的(。制作add/3mult/3表,如下所示(在定义它们之前(:

:- table add/3, mult/3.

不再";false";s:

?- dot_product([1,0,1],[1,1,1],P).
P = 1.
?- dot_product(V1, [1, 0], 0).
V1 = [0, 1] ;
V1 = [0, 0].

;"相同长度";似乎足够重要;由于不存在剪切,因此可以使用谓词";生成和测试";。不管怎样,点积只是为相同长度的向量定义的,不是吗?

我没有尝试优化代码。正如你在问题中所观察到的,如果论点是有根据的,你可以抄近路。这将是一个很好的切割(或if-then-else(的用法:如果论点是有根据的,则寻找任何"如果";1〃;在任何矢量中;否则,请使用通用算法。

为了检查";1〃;在基本列表中,可以使用memberchk(1, List)

最新更新