我写了一个谓词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的新手,我想知道是否有人对我为什么关心确定性有任何建议。对于真正的序言程序来说重要吗?
答案 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).
我不知道这是否真的很重要,但只是为了记录。