【问题标题】:(SWI)Prolog: Order of sub-goals(SWI)Prolog:子目标的顺序
【发布时间】:2017-04-21 23:08:12
【问题描述】:

我在 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\==Elemmember(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.

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

注意:我们在这里讨论的是 SWI-Prolog。

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

【问题讨论】:

  • 对于unique_element(X,[a,b,c]), X = c.,两个版本都错误地失败了

标签: prolog


【解决方案1】:

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

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

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

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

另一方面,如果Elem 实际上是T 的一个元素,则目标member(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

等等

【讨论】:

    【解决方案2】:

    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:我认为答案与您的问题相关。

    希望对您有所帮助。

    【讨论】:

      【解决方案3】:

      这是另一种可能性,使用 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
      

      【讨论】:

        【解决方案4】:

        不能像tcountProlog - count repetitions in list那样定义unique_element吗

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

        【讨论】:

        • 也许会添加一些有趣的用途?
        猜你喜欢
        • 2016-06-11
        • 1970-01-01
        • 1970-01-01
        • 2016-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2016-01-16
        • 1970-01-01
        相关资源
        最近更新 更多