使序言谓语具有确定性

时间:2018-06-21 01:39:59

标签: prolog

我写了一个谓词shuffle/3,它生成两个列表的“混洗”。实例化第二个和第三个参数后,第一个参数将成为一个列表,其中包含Left和Right的所有元素,其顺序与出现在Left和Right中的顺序相同。

例如:

?- shuffle(X, [1, 2], [3, 4]).
X = [1, 3, 2, 4] ;
X = [1, 3, 4, 2] ;
X = [1, 2, 3, 4] ;
X = [3, 4, 1, 2] ;
X = [3, 1, 2, 4] ;
X = [3, 1, 4, 2] ;
false.

这是我想实现它的代码:

shuffle([], [], []).
shuffle([H|R], [H|Left], Right) :- shuffle(R, Right, Left).
shuffle([H|R], Left, [H|Right]) :- shuffle(R, Right, Left).

这很好用,甚至可以为“最一般的查询”生成合理的结果,但是对于任何查询,甚至所有参数都已完全实例化的查询,shuffle([1, 2, 3, 4], [1, 2], [3, 4])都无法确定。

我真正的问题是:在保持纯净(因此不削减)的同时,我能做些什么,使所有谓词都完全实例化时就使谓词具有确定性吗?

当我在这里时,我还是Prolog的新手,我想知道是否有人对我为什么关心确定性有任何建议。对于真正的序言程序来说重要吗?

3 个答案:

答案 0 :(得分:11)

不,在保持纯代码的同时,没有办法使该谓词具有确定性。要查看此内容,请考虑:

?- shuffle([1, 1], [1], [1]).
      true
   ;  true.

对此有两个答案。为什么?最好不要使用调试器来理解这一点,而要使用通用查询

?- shuffle([X1, X2], [Y1], [Y2]).
      X1 = Y1, X2 = Y2
   ;  X1 = Y2, X2 = Y1.

因此,在这里您可以看到参数之间的“真”连接!现在,我们的特定查询是该更通用查询的实例。因此,无法删除这两个答案。

但是,您可以以纯净的方式使用cut,前提是要对其进行保护以使结果始终是纯净的。就像测试ground(shuffe(Xs, Ys, Zs))一样,但这都是临时的。


再三考虑,可能有一个纯粹的,确定的答案,但前提是shuffle([X1, X2], [Y1], [Y2]).的答案有所更改。答案实际上应该是:

?- shuffledet([X1, X2], [Y1], [Y2]).
      X1 = X2, X2 = Y1, Y1 = Y2      % all equal
   ;  dif(X1, X2), X1 = Y1, X2 = Y2
   ;  dif(X1, X2), X1 = Y2, X2 = Y1.

所以这可能是一种可能性...我​​将对此ASAP 悬赏500英镑,但没有回应。再试一次。

答案 1 :(得分:4)

制作det的{​​{1}}版本的方法是使用库模块shuffle中的if_/3

reif

按位置进行工作就可以了,并且确实消除了一些(大多数?)虚假的选择点:

shuffle_det1( A,B,C):-
  if_( B=[], A=C,
   if_( C=[], A=B, 
        ( B=[BH|BT], C=[CH|CT], A=[AH|AT], (
                 AH=BH, shuffle_det1( AT, BT, C)
                 ;
                 AH=CH, shuffle_det1( AT, B, CT) ) ))).

但是:

40 ?- shuffle_det1(X, [1, 2], [3, 4]).
X = [1, 2, 3, 4] ;
X = [1, 3, 2, 4] ;
X = [1, 3, 4, 2] ;
X = [3, 1, 2, 4] ;
X = [3, 1, 4, 2] ;
X = [3, 4, 1, 2].

41 ?- shuffle_det1(X, [11,12], [11,22]).
X = [11, 12, 11, 22] ;
X = [11, 11, 12, 22] ;
X = [11, 11, 22, 12] ;
X = [11, 11, 12, 22] ;
X = [11, 11, 22, 12] ;
X = [11, 22, 11, 12].

81 ?- shuffle_det1([1,2,3,4], [3, 4], [1, 2]).
true.

另外,正如[user:false]指出的那样,如果两个列表的head元素相等,则答案中会有一些冗余:

82 ?- shuffle_det1([1,2,3,4], [1, 2], [3, 4]).
true ;
false.

这里,标记为 11 12 13 .. B 21 22 23 .. C 11 (12.. + 21..) | 21 (11.. + 22..) 12 (13.. + 21..) 11 (12.. + 22..) * | 21 (12.. + 22..) * | 22 (11.. + 23..) 的两种情况在*时实际上是混合在一起的。为了解决这个问题,在这种情况下,我们通过连续两次来“展开”选择:

11 == 21

测试:

shuffle_det( A,B,C):-
  if_( B=[], A=C,
   if_( C=[], A=B, 
        ( B=[BH|BT], C=[CH|CT], A=[AH|AT],
          if_( \X^(dif(BH,CH),X=true ; BH=CH,X=false),
               (
                 AH=BH, shuffle_det( AT, BT, C)
                 ;
                 AH=CH, shuffle_det( AT, B, CT) ),
               (
                 AH=BH, AT=[CH|A2], shuffle_det( A2, BT, CT)     % **
                 ;
                 pull_twice( A,B,C)
                 ;
                 pull_twice( A,C,B)
                ))))).

pull_twice([BH|AT],[BH|BT],C):- % B,C guaranteed to be non-empty
    if_( BT=[], AT=C,
         ( BT=[BH2|B2], AT=[BH2|A2], shuffle_det(A2,B2,C) )).

这已经比35 ?- shuffle_det(A, [11,12], [11,22]). A = [11, 11, 12, 22] ; A = [11, 11, 22, 12] ; A = [11, 12, 11, 22] ; A = [11, 22, 11, 12]. 好得多。但这还不完全正确:

shuffle_det1

这两个38 ?- shuffle_det(A, [1], [1]). A = [1, 1] ; A = [1, 1] ; A = [1, 1]. 电话可能是罪魁祸首。以某种方式必须只有一个,这将决定是否执行另一个...

答案 2 :(得分:-1)

我只想说 OPs shuffle/3 是一个奇怪的 shuffle:

shuffle([], [], []).
shuffle([H|R], [H|Left], Right) :- shuffle(R, Right, Left).
shuffle([H|R], Left, [H|Right]) :- shuffle(R, Right, Left).

logic programming wiki 页面的洗牌是将最后一个参数移到前面。所以Left和Right在尾部没有切换:

shuffle([], [], []).
shuffle([First | ShortMerge], [First | Rest], Right) :-
    shuffle(ShortMerge, Rest, Right).
shuffle( [First | ShortMerge], Left, [First | Rest]) :-
    shuffle(ShortMerge, Left, Rest).

我不知道这是否真的很重要,但只是为了记录。