此Prolog代码如何真正起作用-随机排列两个列表

时间:2018-09-06 19:45:05

标签: list recursion prolog shuffle non-deterministic

我有以下代码,有效,可将两个列表混在一起:

shuffle([], [], []).
shuffle([X|Xs], Ys, [X|Zs]):-
          shuffle(Xs, Ys, Zs).
shuffle(Xs, [Y|Ys], [Y|Zs]):-
          shuffle(Xs, Ys, Zs).

我分别理解每个部分。第一个子句接收两个列表,一个列表的X head Xs tail 。在结果中,我们仅“获取”第一个列表的 head 。与第二个子句相同–我们不取Xs作为结果,只取Y head

Prolog递归地分离列表,然后将它们统一起来。

我在这里不明白它是如何工作的?在结束“取出”所有Xs之后,它只是“移至”第二个子句,取Ys?是什么触发Prolog这么做的?

谢谢。

2 个答案:

答案 0 :(得分:2)

例如,当您尝试在Prolog中证明目标时:shuffle([a],[c],L). Prolog所做的是在数据库中搜索以查找谓词改组的规则。

在这种情况下,第二条规则和第三条规则都会发生,因此您有两个选择-选择Prolog中所提到的点:

第一选择点 :我们检查第二条规则:shuffle([X|Xs],Ys,[X|Zs]):- shuffle(Xs,Ys,Zs).并将其应用到目标中,我们得到[X|Xs] = [a](所以{{1 }},X = a, Xs = []Ys = [c]的格式为L,最后以递归方式调用[a|Zs]。现在,该目标仅匹配第三条规则,我们得到shuffle([],[c],Zs),然后递归地调用Zs = [c|Zs'],现在只有第一条规则匹配,我们得到shuffle([],[],Zs')。因此,从检查的第一个案例中我们得到Zs' = []。现在我们离开了另一个案例:

第二选择点 :我们研究了第三条规则:Zs = [a,c],并将其应用到我们的目标中,我们得到shuffle(Xs,[Y|Ys],[Y|Zs]):- shuffle(Xs,Ys,Zs).(所以{{1 }}),并且Xs = [a], [Y|Ys] = [c]的形式为Y = c, Ys = [],最后递归调用L。现在,该目标仅匹配第二个规则,我们得到[c|Zs],然后递归调用shuffle([a],[],Zs),现在只有第一个规则匹配,我们得到Zs = [a|Zs']。因此,从检查的第二种情况我们得到shuffle([],[],Zs')

所以最后我们得到了两种解决方案。如您所见,Prolog对选择点进行了深度优先分析,因为它找到了第一个选择点并对其进行了检查,然后进行第三个选择,依此类推。这里的明显问题是,您可以想象两个元素列表(例如Zs' = [])的选择点数吗?这将是四个选择点,对于Zs = [c,a]的一般情况而言,选择点太多了。

答案 1 :(得分:1)

避免所有X,Y和Z部分,对于工作代码,我们能说些什么:

  1. 您从类似shuffle([1,2],[a,b],L).的查询开始,然后Prolog尝试通过解决三个shuffle规则来解决该问题。
  2. 一个洗牌规则可以自己解决,但仅对于空列表,其他两个依赖于解决shuffle规则的另一种情况。
  3. 无论找到什么解决方案,必须进行shuffle -> shuffle -> [shuffle....] -> empty lists。它必须。如果根本无法匹配任何改组,它将回答“ false”,并且您的代码将无法工作。如果它在随机播放之间永远反弹,它将无限循环,并且不给出任何答案,您的代码将无法正常工作。它确实有效,因此必须从一开始就通过随机组合将其链接到空列表。

Prolog将从规则顶部尝试解决:

From the top:

A) shuffle([1,2],[a,b],L).  -no->  shuffle([],[],[]).
B) shuffle([1,2],[a,b],L).  -??->  shuffle([X|Xs],Ys,[X|Zs]):- shuffle(Xs,Ys,Zs).
B) shuffle([1,2],[a,b],L).  -??->  shuffle([X=1|Xs=[2]],Ys=[a,b],[X=1|Zs=??]) :- shuffle(Xs=[2],Ys=[a,b],Zs).

% A) fails as [1,2] does not match with []
% B) partially binds but is missing Zs. Solving to try and find the Zs is now:

shuffle(Xs=[2],Ys=[a,b],Zs).



From the top:

A) shuffle([2],[a,b],Zs).  -no->  shuffle([],[],[]).
B) shuffle([2],[a,b],Zs).  -??->  shuffle([X|Xs],Ys,[X|Zs]):- shuffle(Xs,Ys,Zs).
B) shuffle([2],[a,b],Zs).  -??->  shuffle([X=2|Xs=[]],Ys=[a,b],[X=2|Zs=??]):- shuffle(Xs,Ys,Zs).

% A) fails as [2] does not match with []
% B) partially binds but is missing Zs. Solving to try and find the Zs is now:

shuffle(Xs=[],Ys=[a,b],Zs).



From the top:

A) shuffle([],[a,b],Zs).  -no->  shuffle([],[],[]).
B) shuffle([],[a,b],Zs).  -no->  shuffle([X|Xs],Ys,[X|Zs]):- shuffle(Xs,Ys,Zs).
C) shuffle([],[a,b],Zs).  -??->  shuffle(Xs,[Y|Ys],[Y|Zs]):- shuffle(Xs,Ys,Zs).
C) shuffle([],[a,b],Zs).  -??->  shuffle(Xs=[],[Y=a|Ys=[b]],[Y=a|Zs=??]):- shuffle(Xs,Ys,Zs).

% A) fails as [a,b] does not match with the second []
% B) fails as [] does not match with [X|Xs]
% C) partially binds but is missing Zs. Solving to try and find the Zs is now:

shuffle([],[b],Zs).



From the top:

A) shuffle([],[b],Zs).  -no->  shuffle([],[],[]).
B) shuffle([],[b],Zs).  -no->  shuffle([X|Xs],Ys,[X|Zs]):- shuffle(Xs,Ys,Zs).
C) shuffle([],[b],Zs).  -??->  shuffle(Xs,[Y|Ys],[Y|Zs]):- shuffle(Xs,Ys,Zs).
C) shuffle([],[b],Zs).  -??->  shuffle(Xs=[],[Y=b|Ys=[]],[Y=b|Zs=??]):- shuffle(Xs,Ys,Zs).

% A) fails as [b] does not match with the second []
% B) fails as [] does not match with [X|Xs]
% C) partially binds but is missing Zs. Solving to try and find the Zs is now:

shuffle([],[],Zs).



From the top:

A) shuffle([],[],Zs).  -no->  shuffle([],[],[]).

% A) succeeds. Zs can be []

这是一条完成的链,从原点到四个随机排列,再到空列表。在此链中,Z分别被构造为[1|?]然后[1|[2|?]]然后[1|[2|[a|?]]]然后[1|[2|[a|[b|?]]]]然后[1|[2|[a|[b|[]]]]]完成,没有丢失任何东西。这将绑定到您的L输入的第一个结果。

它经过B B C C的洗牌。


但是搜索空间没有用尽,可能会有更多答案。如果您要求他们,它将使链条解开,回到可能采取不同路径的地方。除了解决shuffle([X|Xs]..之外,它还可以跳过该问题而向下跳至shuffle(Xs

两个具有大量值的shuffle谓词一起构成一个跳动模式,该跳动模式以三个空列表例结束:

[1,2],[a,b],Unknown
        \
         \
          \ ? shuffle shuffle shuffle
          /
         /
         \
      [],[],[]

一连串的逻辑连接是B B C C A。另一个链是B C B C A,它会产生下一个答案L=[1,a,2,b]

[1,2],[a,b],Unknown
       /   \       
      /     \
      \      \ B C B A
B B C C\     /
       |    /
       |    \
      [],[],[]

一路回溯,每次选择都将随机播放换为另一随机播放,然后沿着链到空列表,它将找到6条路径,通过随机播放弹跳的6种方式。

随着列表的增加,链条也会变长。当它开始回溯链条时,取消了寻找其他方法的步骤时,就会发现更多的方法。更多选择点,因此它将找到更多解决方案-与输入长度成比例。