使序言谓词具有确定性

问题描述 投票:0回答:3

我编写了一个谓词

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 的新手,我想知道是否有人对我为什么关心决定论有建议。它对于真正的 prolog 程序重要吗?

prolog
3个回答
12
投票

不,没有办法在保持纯代码的同时使这个谓词具有确定性。要看到这一点,请考虑:

?- 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.

所以这可能是一种可能性......我
will

已经为此ASAP下了500的赏金,但没有回应。我再次尝试另一个。


5
投票
det

版本的

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.

但是:

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

此外,正如 [user:
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].

两个 
pull_twice

电话可能是罪魁祸首。不知怎的,一定只有一个,这将决定是否做另一个......

    


0
投票
reif

包含剪切
,因为->本质上是剪切,所以:我们使用“纯粹”的定义是什么,难道你真的不想要一个行为明智的程序吗?
shuffle([], [], []).
shuffle([H|T], L1, L2) :-
    shuffle_l1_(L1, L2, [H|T]).

shuffle_l1_([], L, L).
shuffle_l1_([H1|T1], L2, L) :-
    shuffle_l2_(L2, [H1|T1], L).

shuffle_l2_([], L, L).
shuffle_l2_([H2|T2], [H1|T1], [H|T]) :-
    % Need to choose side 1 or 2 - using reification
    % If both sides are the same, then just choose 1
    (   (H == H1 ; H1 == H2)
    ->  Side = 1
    ;   H == H2
    ->  Side = 2
    ;   true
    ),
    shuffle_side_(Side, [H1|T1], [H2|T2], [H|T]).

shuffle_side_(1, [H|T1], L2, [H|T]) :-
    shuffle(T, T1, L2).
shuffle_side_(2, L1, [H|T2], [H|T]) :-
    shuffle(T, L1, T2).

这很好地减少了选择点 - 导致 swi-prolog:

?- shuffle([1, 2, 3, 4], [1, 2], [3, 4]). true. ?- shuffle([1, 2, 3, 4], [3, 4], [1, 2]). true. ?- shuffle(L, [1], [1]). L = [1, 1]. ?- shuffle(L, [X1, X2], [Y1, Y2]). L = [X1, X2, Y1, Y2] ; L = [X1, Y1, X2, Y2] ; L = [X1, Y1, Y2, X2] ; L = [Y1, X1, X2, Y2] ; L = [Y1, X1, Y2, X2] ; L = [Y1, Y2, X1, X2]. % Improves, but doesn't remove choicepoints completely ?- shuffle(L, [1, 2], [1, 2]). L = [1, 2, 1, 2] ; L = [1, 1, 2, 2] ; false.

为了删除一些不需要的选择点,这种额外的复杂性值得吗?对于大多数用例来说可能不是。

© www.soinside.com 2019 - 2024. All rights reserved.