`memberd / 2`的更多确定性?

时间:2015-06-18 19:55:20

标签: list prolog

许多系统提供member/2的纯粹而有效的实现。特别是,没有选择点可供选择:

?- member(b,[a,b]).
true.

然而,member/2的天真实现产生了相反的东西:

?- member(b,[a,b]).
true ;
false.

从声明的角度来看肯定是正确的,但效率较低。

另一方面,member/2存在一些技术问题。它允许冗余解决方案,如:

?- member(a,[a,a]).
true ;
true.

memberd/2使用if_/3(=)/3解决了这个问题。

memberd(E, [X|Xs]) :-
   if_(E = X, true, memberd(E, Xs)).

?- memberd(a,[a,a]).
true.

不幸的是,这个定义让选择点再次打开 - 在成员不这样做的情况下产生; false("剩余的选择点")

?- memberd(X,[a,b]).
X = a ;
X = b ;
false.    % BAD - to be avoided!

?- member(X,[a,b]).
X = a ;
X = b.

所以我的问题:是否有memberd/2的定义可以避免选择点如上所述?

3 个答案:

答案 0 :(得分:12)

首先,为了清楚起见,我们将memberd重命名为memberd_old

然后,我们实现memberd_new/2,它使用滞后和第一个参数索引来阻止在列表末尾创建无用的选择点。

memberd_new(E,[X|Xs]) :-
   memberd_new_aux(Xs,X,E).

% auxiliary predicate to enable first argument indexing
memberd_new_aux([],E,E).
memberd_new_aux([X1|Xs],X0,E) :-
   if_(E=X0, true, memberd_new_aux(Xs,X1,E)).

让我们比较member/2(SWI-Prolog内置谓词),memberd_old/2memberd_new/2

首先是地面查询:

?- member(a,[a,a]).
true ;
true.                       % BAD!

?- memberd_old(a,[a,a]).
true.

?- memberd_new(a,[a,a]).
true.

接下来,另一个地面查询:

?- member(a,[a,b]).
true ;                      % BAD!
false.

?- memberd_old(a,[a,b]).
true.

?- memberd_new(a,[a,b]).
true.

现在,查询有多个不同的解决方案:

?- member(X,[a,b]).
X = a ;
X = b.

?- memberd_old(X,[a,b]).
X = a ;
X = b ;                     % BAD!
false.

?- memberd_new(X,[a,b]).
X = a ;
X = b.

修改

此处提供的memberd_new/2的实施已弃用。

我建议使用in this answer显示的较新实现。

答案 1 :(得分:7)

在这个答案中,我们比较了三个不同的列表成员谓词:

  • member/2,内置谓词,在SWI-Prolog中实现。
  • memberd/2,由OP定义:

    memberd(E,[X|Xs]) :-
       if_(E=X, true, memberd(E,Xs)).
    
  • memberd_new/2,提议的替代方案,定义如下:

    memberd_new(E,[X|Xs]) :-
       (  Xs \= [_|_]
       -> E=X
       ;  if_(E=X, true, memberd_new(E,Xs))
       ).
    

我们走了!

首先,一些基本的查询:

?- member(b,[a,b]).
true.
?- memberd(b,[a,b]).
true.
?- memberd_new(b,[a,b]).
true.

?- member(a,[a,a]).
true ; true.                        % BAD
?- memberd(a,[a,a]).
true.
?- memberd_new(a,[a,a]).
true.

?- member(a,[a,b]).
true ; false.                       % BAD 
?- memberd(a,[a,b]).
true.
?- memberd_new(a,[a,b]).
true.

接下来,一些查询有多个不同的解决方案:

?- member(X,[a,b]).
X = a ; X = b.
?- memberd(X,[a,b]).
X = a ; X = b ; false.              % BAD
?- memberd_new(X,[a,b]).
X = a ; X = b.

接下来,在评论中提出一个测试用例 a previous answer打破了之前提出的memberd_new/2版本。

?- member(a,[a|nonlist]).
true.
?- memberd(a,[a|nonlist]).
true.
?- memberd_new(a,[a|nonlist]).
true.                               % IMPROVED

以上测试用例的变体:

?- member(X,[a|nonlist]).
X = a.
?- memberd(X,[a|nonlist]).
X = a ; false.                      % BAD
?- memberd_new(X,[a|nonlist]).
X = a.                              % IMPROVED

最后,一些非终止查询:

?- member(1,Xs).
  Xs =          [1|_A]
; Xs =       [_A,1|_B]
; Xs =    [_A,_B,1|_C]
; Xs = [_A,_B,_C,1|_D]
...

?- memberd(1,Xs).
  Xs =          [1|_A]
; Xs =       [_A,1|_B], dif(_A,1)
; Xs =    [_A,_B,1|_C], dif(_A,1), dif(_B,1)
; Xs = [_A,_B,_C,1|_D], dif(_A,1), dif(_B,1), dif(_C,1) 
...

?- memberd_new(1,Xs).
  Xs =          [1|_A]
; Xs =       [_A,1|_B], dif(_A,1)
; Xs =    [_A,_B,1|_C], dif(_A,1), dif(_B,1)
; Xs = [_A,_B,_C,1|_D], dif(_A,1), dif(_B,1), dif(_C,1)
...

答案 2 :(得分:6)

还有更多......假设我们基于memberD/2实施memberd_t/3

memberD(X,Xs) :- memberd_t(X,Xs,true).

如何与问题中的OP定义的memberd/2进行比较?

让我们重新运行一些查询!

?- memberd(a,[a,a]), memberd(a,[a,b]), memberd(b,[a,b]), 
   memberD(a,[a,a]), memberD(a,[a,b]), memberD(b,[a,b]).
true.                             % all of these succeed deterministiaclly

?- memberd(X,[a,b]).
X = a ; X = b ; false.            % could be better
?- memberD(X,[a,b]).
X = a ; X = b ; false.            % could be better

?- memberd(a,[a|nonlist]), memberD(a,[a|nonlist]).
true.                             % both succeed deterministically

?- memberd(X,[a|nonlist]).
X = a ; false.                    % could be better
?- memberD(X,[a|nonlist]).
X = a ; false.                    % could be better

在上述查询中,memberd/2memberD/2会给出相同的答案,并在相同的实例中留下多余的选择点。

让我们深入挖掘一下!使用带有角落案例的memberd_t/3考虑以下查询:

?- memberd_t(a,[a|nonlist],T).
T = true.                         % OK
?- memberd_t(b,[a|nonlist],T).
false.                            % missing: `T = false`
?- memberd_t(X,[a|nonlist],T).
  T = true, X = a 
; false.                          % missing: `T = false, dif(X,a)`

这不是我期望/想要得到的。我们能做什么?基本上,我看到两个选项:

  1. 接受这种差异并宣称:“这些查询是不重要的角落案例。”

  2. 构建可以处理这些案例的memberd_t/3实现。

  3. 两种选择都有优点和缺点。 在下文中,我们实现了memberd_new_t/3,它们都处理了极端情况并减少了多余选择点的创建。

    警告:未来的丑陋代码!

    memberd_new_t(E,Xs0,Truth) :-
       (  Xs0 \= [_|_]
       -> Truth = false
       ;  Truth = false,
          freeze(Xs0, Xs0\=[_|_])
       ;  Xs0 = [X|Xs],
          (  Xs \= [_|_]
          -> =(E,X,Truth)
          ;  if_(E=X,Truth=true,memberd_new_t(E,Xs,Truth))
          )
       ).
    

    让我们检查一下memberd_new_t/3生成的无用选择点是否更少!

    ?- memberd_t(X,[a,b],true).
    X = a ; X = b ; false.              % suboptimal
    ?- memberd_new_t(X,[a,b],true).
    X = a ; X = b.                      % BETTER
    
    ?- memberd_t(X,[a|nonlist],true).
    X = a ; false.                      % suboptimal
    ?- memberd_new_t(X,[a|nonlist],true).
    X = a.                              % BETTER
    

    好吧!那么上面的角落呢?

    ?- memberd_t(a,[a|nonlist],T).
    T = true.                           % OK
    ?- memberd_new_t(a,[a|nonlist],T).
    T = true.                           % OK
    
    ?- memberd_t(b,[a|nonlist],T).
    false.                              % BAD
    ?- memberd_new_t(b,[a|nonlist],T).
    T = false.                          % OK
    
    ?- memberd_t(X,[a|nonlist],T).
      T = true, X = a 
    ; false.                            % BAD
    ?- memberd_new_t(X,[a|nonlist],T).
      T =  true,     X=a
    ; T = false, dif(X,a).              % OK
    

    有效!最后,考虑最常见的查询:

    ?- memberd_t(X,Xs,T).          
      T=false, Xs = []            
    ; T=true , Xs = [X       |_A]        
    ; T=false, Xs = [_A         ], dif(X,_A)
    ; T=true , Xs = [_A, X   |_B], dif(X,_A)
    ; T=false, Xs = [_A,_B      ], dif(X,_A), dif(X,_B)
    ; T=true , Xs = [_A,_B, X|_C], dif(X,_A), dif(X,_B)
    ; T=false, Xs = [_A,_B,_C   ], dif(X,_A), dif(X,_B), dif(X,_C)
    ...
    
    ?- memberd_new_t(X,Xs,T).
      T=false, freeze(Xs,Xs\=[_|_])
    ; T=true ,                       Xs = [ X      |_A]
    ; T=false, freeze(_B,_B\=[_|_]), Xs = [_A      |_B], dif(X,_A)
    ; T=true ,                       Xs = [_A, X   |_B], dif(X,_A)
    ; T=false, freeze(_C,_C\=[_|_]), Xs = [_A,_B   |_C], dif(X,_A), dif(X,_B)
    ; T=true ,                       Xs = [_A,_B, X|_C], dif(X,_A), dif(X,_B)
    ; T=false, freeze(_D,_D\=[_|_]), Xs = [_A,_B,_C|_D], dif(X,_A), dif(X,_B), dif(X,_C)
    ...       
    

    对于这些极端情况,memberd_new_t/3更像memberd/3而不是memberd_t/3

    ?- memberd(X,Xs).
      Xs = [ X            |_A]
    ; Xs = [_A, X         |_B], dif(X,_A)
    ; Xs = [_A,_B, X      |_C], dif(X,_A), dif(X,_B)
    ; Xs = [_A,_B,_C, X   |_D], dif(X,_A), dif(X,_B), dif(X,_C)
    ; Xs = [_A,_B,_C,_D, X|_E], dif(X,_A), dif(X,_B), dif(X,_C), dif(X,_D)
    ...
    
相关问题