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遍历这里有一个分析的例子:你可以找到很多教程,都解释了相同的事情(如在链接中),并显示生成的树。谢谢你的解释!太棒了。如果你有时间,请看我的