(SWI)Prolog:次级目标的顺序

时间:2017-04-21 23:08:12

标签: prolog

我在Prolog中有两个稍微不同的谓词unique_element / 2实现。当给定元素X和列表L时,谓词成功,元素X在列表中仅出现一次。以下是实施和结果:

实施1:

%%% unique_element/2
unique_element(Elem, [Elem|T]) :-
    not(member(Elem, T)). 
unique_element(Elem, [H|T]) :-
    member(Elem, T), 
    H\==Elem, 
    unique_element(Elem, T), 
    !. 

结果:

?- unique_element(X, [a, a, b, c, c, b]). 
false.

?- unique_element(X, [a, b, c, c, b, d]).
X = a ;
X = d.

实施2:

%%% unique_element/2
unique_element(Elem, [Elem|T]) :- 
    not(member(Elem, T)). 
unique_element(Elem, [H|T]) :-
    H\==Elem, 
    member(Elem, T), 
    unique_element(Elem, T), 
    !. 

如果你第一眼就没注意到:“H \ == Elem”和“成员(Elem,T)”在第二个impl,规则2上被翻转。

结果:

?- unique_element(X, [a, a, b, c, c, b]).
X = a.

?- unique_element(X, [a, b, c, c, b, d]).
X = a ;
X = d.

问题:在这种情况下,订单如何影响结果?我意识到规则/事实/等的顺序很重要。然而,翻转的两个特定规则似乎没有“连接”或以某种方式相互影响(例如,在错误的位置/顺序中的“切割”谓词)。

注意:我们在这里谈论SWI-Prolog。

注2:我知道,可能是不同的和更好的实现。我的问题是关于改变子目标的顺序。

4 个答案:

答案 0 :(得分:7)

H\==Elem正在测试目标执行时的句法不平等。但后来的统一可能会使变量相同:

?- H\==Elem, H = Elem.
H = Elem.

?- H\==Elem, H = Elem, H\==Elem.
false.

所以在这里我们测试它们是否(语法上)不同,然后它们是统一的,因此不再是不同的。因此,这只是一个临时的考验。

如果member(Elem, T)实际上是Elem的元素,则目标T为真。考虑:

 ?- member(Elem, [X]).
 Elem = X.

可以理解为

  

(何时)是否认为Elem是列表[X]的元素?

,答案是

  

在某些情况下,即Elem = X

如果您现在在程序中混合使用这些不同类型的目标,您会得到奇怪的结果,只能通过详细检查您的程序来解释。

作为初学者,最好只坚持Prolog的纯粹部分。在你的情况下:

  • 使用dif/2代替\==

  • 不要使用剪切 - 在您的情况下,它将答案数限制为两个。如在 unique_element(X, [a,b,c])

  • 请勿使用not/1(\+)/1。它产生更多的不正确性。在unique_element(a,[a,X]),X=b.正确成功时,请考虑X=b,unique_element(a,[a,X])错误地失败。

这是您程序的直接纯化版本。还有改进的余地!

non_member(_X, []).
non_member(X, [E|Es]) :-
   dif(X, E),
   non_member(X, Es).

unique_element(Elem, [Elem|T]) :- 
    non_member(Elem, T). 
unique_element(Elem, [H|T]) :-
    dif(H,Elem), 
     % member(Elem, T),          % makes unique_element(a,[b,a,a|Xs]) loop
    unique_element(Elem, T).

?- unique_element(a,[a,X]).
   dif(X, a)
;  false.              % superfluous

?- unique_element(X,[E1,E2,E3]).
   X = E1,
   dif(E1, E3),
   dif(E1, E2)
;  X = E2,
   dif(E2, E3),
   dif(E1, E2)
;  X = E3,
   dif(E2, E3),
   dif(E1, E3)
;  false.

注意最后一个查询是如何读取的?

  

X何时是(任意)列表[E1,E2,E3]的唯一元素?

答案是三重的。考虑一个元素接一个:

  

XE1,但仅限于E2E3

答案 1 :(得分:6)

TL; DR :阅读文档并找出原因:

?- X = a, X \== a.
false.

?- X \== a, X = a.
X = a.

我想知道你为什么这么靠近自己搞清楚; - )

有很多方法可以比较Prolog中的东西。至少,你有统一,有时可以比较,有时甚至更多;比你有平等,它的否定,你正在使用的那个。那么它做了什么:

?- a \== b. % two different ground terms
true.

?- a \== a. % the same ground term
false.

现在它变得有趣了:

?- X \== a. % a free variable and a ground term
true.

?- X \== X. % the same free variable
false.

?- X \== Y. % two different free variables
true.

我建议您执行以下操作:弄清楚member/2如何处理它(它是否使用统一?等效?还有其他什么?)然后替换上面所有示例中使用的member/2并查看结果是否有任何不同。

既然你正在努力确保事情有所不同,那么试试dif/2所做的事情。如:

?- dif(a, b).

?- dif(X, X).

?- dif(X, a).

等等。

另请参阅this question and answers:我认为答案与您的问题相关。

希望有所帮助。

答案 2 :(得分:5)

你能否定义像tcount Prolog - count repetitions in list

这样的unique_element

unique_element(X, List):- tcount(=(X),List,1).

答案 3 :(得分:4)

另一种可能性是使用if_/3和maplist / 2来定义unique_element / 2:

:- use_module(library(apply)).

unique_element(Y,[X|Xs]) :-
   if_(Y=X,maplist(dif(Y),Xs),unique_element(Y,Xs)).

与@ user27815非常优雅的解决方案(+ s(0))相比,此版本不构建在clpfd上(由tcount/3使用)。 OP提供的示例查询按预期工作:

   ?- unique_element(a,[a, a, b, c, c, b]).
no
   ?- unique_element(X,[a, b, c, c, b, d]).
X = a ? ;
X = d ? ;
no

@false提供的示例现在可以成功而不会留下多余的选择点:

   ?- unique_element(a,[a,X]).
dif(a,X)

另一个更通用的查询产生相同的结果:

   ?- unique_element(X,[E1,E2,E3]).
E1 = X,
dif(X,E3),
dif(X,E2) ? ;
E2 = X,
dif(X,E3),
dif(X,E1) ? ;
E3 = X,
dif(X,E2),
dif(X,E1) ? ;
no