带有if-the-else vs multiple子句谓词的单子句

时间:2018-01-20 13:11:37

标签: performance prolog

让我们说我想要实现一个“返回”的谓词。由列表列表共享的所有元素的列表。

我可以将它作为一个子句实现(对于逻辑编程看起来有点难看):

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的实施,但可能存在关于这些案例效率的一般立场。

1 个答案:

答案 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失败并且整个子句失败。

在这种情况下,使用-> ;构造会大大降低实现的一般性,并在某些调用方案中产生错误的结果。