让我们说我想要实现一个“返回”的谓词。由列表列表共享的所有元素的列表。
我可以将它作为一个子句实现(对于逻辑编程看起来有点难看):
shared_members(Members, Lists) :-
Lists = [] ->
Members = []
; findall(M, (maplist(member(M), Lists)), Members).
或作为一组条款:
shared_members([], []). % possibly adding cut here to increase effciency
shared_members(Members, Lists) :-
findall(M, (maplist(member(M), Lists)), Members).
哪种实施被认为更有效? 我知道这取决于Prolog的实施,但可能存在关于这些案例效率的一般立场。
答案 0 :(得分:2)
在这种情况下,您甚至不需要第一个子句shared_member([], [])
。如果findall/3
,则会Members = []
调用Lists = []
。
但问题仍然很有趣,所以我们暂时忽略这个问题。您可以运行一些统计数据来确定时间效率。内存效率差异可以忽略不计。然而,第二种方法被认为是Prolog中的规范方法。但他们的行为也不相同。 Prolog中的“if-else”由p1 -> p2 ; p3
剪切表示,在评估p1
后删除了选择点。它相当于p1, !, p2 ; p3
。
这就是为什么这很重要。我将使用一个人为的例子(这也不需要两个条款,但说明了这一点)。如果第一个参数是第二个参数的长度,我将定义一个len/2
谓词为真:
len(0, []).
len(N, L) :- length(L, N).
显然,正如原始问题的情况一样,这里的第一个条款是多余的,但对于这个例子来说这很重要。如果我运行此查询,我会得到以下结果:
| ?- len(N, [a,b,c]).
N = 3
yes
| ?- len(3, L).
L = [_,_,_]
yes
| ?- len(N, L).
L = []
N = 0 ? ;
L = []
N = 0 ? ;
L = [_]
N = 1 ? ;
L = [_,_]
N = 2 ? ;
L = [_,_,_]
N = 3 ?
请注意,如果两个参数都是可变的,则枚举解决方案。 (另外,由于冗余的第一个子句,其中一个解决方案出现了两次。)
让我们使用“if-else”重写这个谓词:
len(N, L) :-
( L = []
-> N = 0
; length(L, N)
).
我们将运行它:
| ?- len(N, [a,b,c]).
N = 3
yes
到目前为止,这么好。但...
| ?- len(3, L).
no
| ?- len(N, L).
L = []
N = 0
yes
| ?-
糟糕!这是完全不同的。发生了什么事?
在第二种方法中,( L = [] -> N = 0 ; length(L, N) )
首先尝试统一L
和[]
。如果L
是变量,则会以L = []
成功。由于它成功了,Prolog然后试图统一N = 0
。但是对于查询len(3, L)
,N
已经绑定到3.所以N = 0
失败并且整个子句失败。
在这种情况下,使用-> ;
构造会大大降低实现的一般性,并在某些调用方案中产生错误的结果。