List 这个Prolog代码到底是如何工作的-洗牌两个列表

List 这个Prolog代码到底是如何工作的-洗牌两个列表,list,recursion,prolog,shuffle,non-deterministic,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的列表是头,Xs是尾。结果我们只“拿走”了第一个列表的标题。与第二个子句相同–我们不使用Xs作为结果,只使用Y的头

我有下面的代码,正在工作,它洗牌两个列表:

shuffle([], [], []).
shuffle([X|Xs], Ys, [X|Zs]):-
          shuffle(Xs, Ys, Zs).
shuffle(Xs, [Y|Ys], [Y|Zs]):-
          shuffle(Xs, Ys, Zs).
我分别理解每一部分。第一个子句接收两个列表,一个带有
X
的列表是头,
Xs
是尾。结果我们只“拿走”了第一个列表的标题。与第二个子句相同–我们不使用
Xs
作为结果,只使用
Y
的头

Prolog递归地分离列表,然后统一它们

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


谢谢。

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

在这种情况下,第二条和第三条规则都会出现,因此您有两个选项选择点,如Prolog中所述:

第一选择点:我们检查第二条规则:
shuffle([X | Xs],Ys,[X | Zs]):-shuffle(Xs,Ys,Zs)。
在我们的目标中,我们得到
[X | Xs]=[a]
(因此
X=a,Xs=[]
Ys=[c]
,而
L
的形式是
[a,最后是递归的(
[c],
被调用。这个目标现在只匹配第三条规则,我们得到
Zs=[c | Zs']
,然后再次递归地调用
shuffle([],[],Zs')
,现在只有第一条规则匹配,我们得到
Zs'=[]
。因此,从第一个案例中,我们得到了
Zs=[a,c]
。现在我们留下了另一个案例:

第二选择点:我们检查第三条规则:
shuffle(Xs,[Y | Ys],[Y | Zs]):-shuffle(Xs,Ys,Zs)。
在我们的目标中,我们得到
Xs=[a],[Y | Ys]=[c]
(因此
Y=c,Ys=[/code>),而
L
的形式是
[c | Zs]
,最后递归地
洗牌([zsa])
被调用。这个目标现在只匹配第二条规则,我们得到
Zs=[a | Zs']
,然后再次递归地调用
shuffle([],[],Zs')
,现在只有第一条规则匹配,我们得到
Zs'=[]
。因此,从第二个案例中,我们得到了
Zs=[c,a]


最后我们得到了两个解。正如您所看到的,Prolog对选择点进行了深度优先分析,因为它找到了第一个选择点并对其进行了检查,然后继续到第三个选择点,依此类推。这里明显的问题是,你能想象两个元素列表的选择点数吗?例如
shuffle([a,b],[c,d],L)
??这将是四个选择点,对于一般的
Xs,Ys
情况,选择点太多了

避免所有的X、Y和Z部分,我们可以对工作代码说些什么:

  • 您可以从一个查询开始,比如
    shuffle([1,2],[a,b],L)。
    Prolog试图通过解决三个
    shuffle
    规则来解决它
  • 一个洗牌规则可以自行解决,但仅适用于空列表,其他两个规则依赖于解决另一种情况的
    shuffle
    规则
  • 无论找到什么解决方案,都必须转到
    shuffle->shuffle->[shuffle….]->空列表
    。这是必须的。如果它不能匹配任何洗牌,它将回答“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 []
    
    这是一条从原点到空列表的完整链,经过四次洗牌。在这个链中,Zs被构造为
    [1 |?]
    然后
    [1 |[2 |?]
    然后
    [1 |[2 |[a |?]]
    然后
    [1 |[2 |[a |[b |?]]]]
    然后
    [1 |[2 |[a |[b |[b]这是完整的,没有任何遗漏。绑定到第一个结果的
    L
    输入

    它经历了洗牌
    bc


    但是搜索空间还没有用尽,可能会有更多的答案。如果你要求它们,它会把链条放回一个可能会走另一条路的地方。它不需要求解洗牌([X | Xs]…
    问题,而是可以跳过这一问题,向下跳转
    洗牌(Xs

    两个具有大量值的
    shuffle
    谓词一起构成一个反弹模式,该模式以三个空列表案例终止:

    [1,2],[a,b],Unknown
            \
             \
              \ ? shuffle shuffle shuffle
              /
             /
             \
          [],[],[]
    
    一个逻辑连接链是
    bca
    。另一个链是
    bca
    ,这将导致下一个答案
    L=[1,A,2,B]

    [1,2],[a,b],Unknown
           /   \       
          /     \
          \      \ B C B A
    B B C C\     /
           |    /
           |    \
          [],[],[]
    
    一旦它一路返回,在每一个选择中,将洗牌换成另一个,并沿着链一直到空列表,它将找到6条路径,6种方式在洗牌中反弹


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

    感谢您的解释。我确实理解,这里的解决方案需要很多时间选项(这就是我要问的)。让我明白一点-在任何程序中,prolog都会首先全面检查第一个选项,然后再检查第二个选项?是的,prolog会生成选项点,并对每个选项点进行深度优先检查。您可能已经看到,此过程正在生成选项点树,而找到解决方案的路径正在进行dfs遍历这里有一个分析的例子:你可以找到很多教程,都解释了相同的事情(如在链接中),并显示生成的树。谢谢你的解释!太棒了。如果你有时间,请看我的