不同/ 2 - 是否存在纯粹的,确定的定义?

时间:2015-06-16 09:32:45

标签: prolog prolog-dif logical-purity

different(Xs, Ys) :-
   member(X, Xs),
   non_member(X, Ys).
different(Xs, Ys) :-
   member(Y, Ys),
   non_member(Y, Xs).

虽然这个使用member/2non_member/2的定义几乎从声明的角度来看是 1 ,但它会为某些查询生成冗余解决方案,并留下选择点。

对此进行改进的定义是什么(以纯粹的方式使用if_/3(=)/3),以便different/2描述完全相同的解决方案集,但是在至少对于地面查询(因此不会留下任何无用的选择点)并省略(如果可能)任何多余的答案?

1 <子> 实际上,different([a|nonlist],[]), different([],[b|nonlist])成功了。它同样可能失败。所以两者都失败的解决方案很好(甚至可能更精细)。

8 个答案:

答案 0 :(得分:14)

首先尝试!

以下代码基于元谓词tfilter/3tpartition/4,单调的if-then-else控件构造if_/3,统一的一元逻辑连接词not_t/3 ,以及术语平等谓词(=)/3

different([],[_|_]).
different([X|Xs0],Ys0) :-
   tpartition(=(X),Ys0,Es,Ys1),
   if_(Es=[], true, (tfilter(not_t(=(X)),Xs0,Xs1),different(Xs1,Ys1))).

示例查询:

?- different([A,B],[X,Y]).
                A=Y ,           dif(B,Y),     X=Y
;     A=X           ,     B=X           , dif(X,Y)
;     A=X           , dif(B,X), dif(B,Y), dif(X,Y)
;               A=Y ,               B=Y , dif(X,Y)
;               A=Y , dif(B,X), dif(B,Y), dif(X,Y)
; dif(A,X), dif(A,Y).

让我们在处理地面数据时观察确定性:

?- different([5,4],[1,2]).
true.

上述方法感觉就像是朝着正确方向迈出的一步......但是,按原样,我不会称之为完美。

答案 1 :(得分:14)

这是另一次尝试!我们利用单调if-then-else控件构造if_/3,结合具体化列表成员谓词memberd_t/3,以及第一个参数索引来避免创建无用的选择点。

different(Xs,Ys) :-
   different_aux(Xs,Ys,Xs,Ys).

different_aux([],[_|_],Xs0,Ys0) :-
   different_aux(Ys0,[],Ys0,Xs0).     % swap Xs/Ys pair
different_aux([X|Xs],Ys,Xs0,Ys0) :-
   if_(memberd_t(X,Ys0),
       different_aux(Ys,Xs,Ys0,Xs0),  % variant: different_aux(Xs,Ys,Xs0,Ys0)
       true).

首先,我们运行一个我们期望失败的查询

?- different([1,2,3],[2,3,1]).
false.

以下查询类似于上面给出的失败查询;每个人在第一个x或第二个列表[1,2,3]中的不同索引处放置一个“不同”商品[2,3,1]

?- different([4,2,3],[2,3,1]), different([1,2,3],[4,3,1]),
   different([1,4,3],[2,3,1]), different([1,2,3],[2,4,1]),
   different([1,2,4],[2,3,1]), different([1,2,3],[2,3,4]).
true.                                 % all subgoals succeed deterministically

好的!让我们运行我在我用过的另一个(非常一般的)查询 previous answer

?- different([A,B],[X,Y]).
      A=X ,               B=X , dif(Y,X)
;     A=X ,           dif(B,X), dif(Y,B)
;               A=Y , dif(B,X), dif(Y,X)
; dif(A,X), dif(A,Y).

紧凑!与我提出的相比有了很大改进 earlier

答案 2 :(得分:11)

回到根源!这个变体非常接近到问题中OP给出的代码。

以下内容基于if_/3memberd_t/3

different(Xs,Ys) :-
   if_(some_absent_t(Xs,Ys),
       true,
       some_absent_t(Ys,Xs,true)).

some_absent_t([]    ,_ ,false).
some_absent_t([X|Xs],Ys,Truth) :-
   if_(memberd_t(X,Ys), some_absent_t(Xs,Ys,Truth), Truth=true).

这是一个地面查询:

?- different([4,2,3],[2,3,1]), different([1,2,3],[4,3,1]),
   different([1,4,3],[2,3,1]), different([1,2,3],[2,4,1]),
   different([1,2,4],[2,3,1]), different([1,2,3],[2,3,4]).
true.                                 % all subgoals succeed deterministically

这是我在之前的答案中使用的(更一般的)查询:

?- different([A,B],[X,Y]).
      A=X ,               B=X ,           dif(Y,X)
;     A=X ,           dif(B,X), dif(B,Y)
;               A=Y ,               B=Y , dif(Y,X), dif(Y,X)
;               A=Y , dif(B,X), dif(B,Y), dif(Y,X)
; dif(A,X), dif(A,Y).

答案 3 :(得分:11)

(很多受@ repeat&#39; s last answer的启发,名字仍然太笨拙了)

different(Xs, Ys) :-
   if_(tnotexists_inlist_t(list_memberd_t(Ys), Xs),
      true,
      tnotexists_inlist_t(list_memberd_t(Xs), Ys)).

tnotexists_inlist_t(_P_2, [], false).
tnotexists_inlist_t(P_2, [E|Es], T) :-
   if_(call(P_2, E),
      tnotexists_inlist_t(P_2, Es, T),
      T = true).

答案 4 :(得分:8)

下一位参赛选手参加选美比赛! - )

这个答案显示了重构的代码变体 a previous answer。它使用具体的连接和分离:

and_(P_1,Q_1) :-
   and_t(P_1,Q_1,true).

or_(P_1,Q_1) :-
   or_t(P_1,Q_1,true).

and_t(P_1,Q_1,Truth) :-
   if_(P_1, call(Q_1,Truth), Truth=false).

or_t(P_1,Q_1,Truth) :-
   if_(P_1, Truth=true, call(Q_1,Truth)).

注意“and”和“or”两个版本;带有后缀_t的那些对于真值具有额外的参数,没有后缀的那些没有并假设Truth=true应该成立。

基于and_t/3和有效术语不等式谓词dif/3,我们定义nonmember_t/3

nonmember_t(X,Ys,Truth) :-
   list_nonmember_t(Ys,X,Truth).

list_nonmember_t([]    ,_, true).
list_nonmember_t([Y|Ys],X,Truth) :-
   and_t(dif(X,Y), list_nonmember_t(Ys,X), Truth).

现在,让我们定义some_absent_t/3different_t/3different/2,如下所示:

some_absent_t([]    ,_ ,false).
some_absent_t([X|Xs],Ys,Truth) :-
   or_t(nonmember_t(X,Ys), some_absent_t(Xs,Ys), Truth).

different_t(Xs,Ys,Truth) :-
   or_t(some_absent_t(Xs,Ys),
        some_absent_t(Ys,Xs),
        Truth).

different(Xs,Ys) :-
   different_t(Xs,Ys,true).

它仍然运行吗?

?- different([A,B],[X,Y]).
      A=X ,               B=X ,           dif(Y,X)
;     A=X ,           dif(B,X), dif(B,Y)
;               A=Y ,               B=Y , dif(Y,X), dif(Y,X)
;               A=Y , dif(B,X), dif(B,Y), dif(Y,X)
; dif(A,X), dif(A,Y).                     % same result as before

?- different([4,2,3],[2,3,1]), different([1,2,3],[4,3,1]),
   different([1,4,3],[2,3,1]), different([1,2,3],[2,4,1]),
   different([1,2,4],[2,3,1]), different([1,2,3],[2,3,4]).
true.                                    % same result as before

看起来好的

总而言之,不是对现有答案的巨大改进,而是IMO稍微更具可读性的代码,以及different/2的具体版本作为额外奖励!

答案 5 :(得分:6)

list_nonmember_t/3exists_in_t/3的帮助下,让它达到极限 or_/2

some_absent_t(Xs,Ys,Truth) :-
   exists_in_t(list_nonmember_t(Ys),Xs,Truth).

different(Xs,Ys) :-
   or_(some_absent_t(Xs,Ys),
       some_absent_t(Ys,Xs)).

答案 6 :(得分:6)

不久前提供了以下大胆赏金(+500)

  

这里仍然缺少一个惯用的答案。例如,or_t / 3应该是(;)/ 3。还有更多内容。

接受挑战!此答案是对this previous answer的跟进。

  1. 我们使用具体的逻辑连接词(;)/3,可以这样定义:

    ';'(P_1,Q_1,T) :- if_(P_1, T=true, call(Q_1,T)).
    
  2. 接下来,我们定义 call_/1。它对于本答案中使用的具体谓词很有用。凭借其名称和语义,call_/1跟在if_/3and_/2or_/2之后!

    call_(P_1) :- call(P_1,true).
    
  3. 使用(;)/3call_/1some_absent_t/3我们实施different/2

    different(As,Bs) :- call_((some_absent_t(As,Bs) ; some_absent_t(Bs,As))).
    
  4. 完成!那就是它。

    让我们重新运行我们在之前的答案中使用的查询!

    ?- different([5,4],[1,2]).
    true.
    
    ?- different([1,2,3],[2,3,1]).
    false.
    
    ?- different([4,2,3],[2,3,1]), different([1,4,3],[2,3,1]), different([1,2,4],[2,3,1]),
       different([1,2,3],[4,3,1]), different([1,2,3],[2,4,1]), different([1,2,3],[2,3,4]).
    true.
    

    相同的疑问,相同的答案......看起来好的给我!

答案 7 :(得分:3)

使用if_的Conerning解决方案,我想说另一种方法是从一开始就使用建设性的否定。 Constructive negation已经在80年代进行了研究,先驱者是David Chan,并且不时出现。

假设我们有一个Prolog解释器,它具有建设性的否定(~)/2。这个建设性的否定(~)/2可用于定义声明if-then-else如下,让我们调用此运算符(~->)/2

  (A ~-> B; C) :- (A, B); (~A, C).

如果Prolog解释器除了建设性否定之外还嵌入了隐含(=>)/2,那么可以选择定义声明if-then-else如下,让我们调用此运算符(~=>)/2

  (A ~=> B; C) :- (A => B), (~A => C).

请注意从分离(;)/2切换到联合(,)/2。询问二元决策图(BDD)人员为什么它们在逻辑上是等价的。程序上存在细微差别,但是通过某些A的嵌入式暗示的后门,第二个if-then-else变体也将引入非确定性。

但是我们如何在Prolog解释器中进行并引入例如建设性否定。一种直接的方法是将建设性否定委托给约束求解器。如果约束求解器具有确定的否定,则可以按如下方式进行:

 ~ A :- #\ A.

但是没有那么多的约束求解器可以允许合理地使用member/2等示例。因为它们通常仅为有限整数,有理数等域提供具体的否定。但是对于诸如member/2之类的谓词,我们需要对Herbrand宇宙进行具体否定。

另请注意,建设性否定的常用方法也假设普通规则含义得到另一种解读。这意味着通常在建设性否定下,我们可以选择普通的member/2定义,并获得查询结果,例如:

?- ~ member(X, [a,b,c]).
dif(X, a),
dif(X, b),
dif(X, c).

但是再次使用具体化的否定很难使用已定义的谓词,因此以下查询可能无效:

?- #\ member(X, [a,b,c]).
/* typically won't work */

如果上述成功,则任何声明性if-then-else如(~->)/2(~=>)/2的使用频率较低,因为普通谓词定义已经由Prolog解释器提供声明性解释。但为什么建设性否定没有广泛传播?一个原因可能是这种Prolog解释器的大型设计空间。我注意到我们有以下选择:

向后链接:我们基本上将香草翻译solve/1吐入两个谓词solvep/1solven/1solvep/1负责解决积极目标,solven/1负责解决消极目标。当我们尝试这个时,我们迟早会看到我们需要对量词进行更仔细的处理,并最终可能会为Herbrand域提供量化消除方法。

正向链接:我们还会注意到,在反向链接中,我们将遇到问题,即在头部建立具有析取或存在量词的子句。这与适用于Prolog的后续微积分在右侧只有一个公式的事实有关。因此,我们要么在右侧使用多个公式并且会松散不一致,要么我们使用正向链接。

Magic Sets:但是正向链接会以不受控制的方式对解决方案空间进行处理。所以我们可能需要某种形式的前向和后向链接组合。我并不仅仅意味着,我们应该能够在解决过程中在两者之间动态切换,但我的意思是我们还有一种方法可以生成指导正向链接过程的集合。

在这个答案here中也提到了更多问题。这并不意味着迟早会找到一个公式,以便将所有这些问题和解决方案对组合在一起,但可能还需要一些时间。

再见