Recursion 请解释这个Prolog递归的例子
我正在学习Prolog,我在递归方面遇到了困难。对于数据库的简单情况,我可以理解,但我无法遵循此练习,其中实现了Recursion 请解释这个Prolog递归的例子,recursion,prolog,Recursion,Prolog,我正在学习Prolog,我在递归方面遇到了困难。对于数据库的简单情况,我可以理解,但我无法遵循此练习,其中实现了redu/2,它将删除给定列表的重复项,并将新列表作为第二个参数: redu([],[]). redu([H|T], Result):- member(H,T), redu(T,Result). redu([H|T], [H|Result]):- redu(T, Result). 一条线索告诉我: [trace] ?- redu([a,b,b,c,a], X).
redu/2
,它将删除给定列表的重复项,并将新列表作为第二个参数:
redu([],[]).
redu([H|T], Result):-
member(H,T),
redu(T,Result).
redu([H|T], [H|Result]):-
redu(T, Result).
一条线索告诉我:
[trace] ?- redu([a,b,b,c,a], X).
Call: (8) redu([a, b, b, c, a], _35630) ? creep
Call: (9) lists:member(a, [b, b, c, a]) ? creep
Exit: (9) lists:member(a, [b, b, c, a]) ? creep
Call: (9) redu([b, b, c, a], _35630) ? creep
Call: (10) lists:member(b, [b, c, a]) ? creep
Exit: (10) lists:member(b, [b, c, a]) ? creep
Call: (10) redu([b, c, a], _35630) ? creep
Call: (11) lists:member(b, [c, a]) ? creep
Fail: (11) lists:member(b, [c, a]) ? creep
Redo: (10) redu([b, c, a], _35630) ? creep
Call: (11) redu([c, a], _35900) ? creep
Call: (12) lists:member(c, [a]) ? creep
Fail: (12) lists:member(c, [a]) ? creep
Redo: (11) redu([c, a], _35900) ? creep
Call: (12) redu([a], _35906) ? creep
Call: (13) lists:member(a, []) ? creep
Fail: (13) lists:member(a, []) ? creep
Redo: (12) redu([a], _35906) ? creep
Call: (13) redu([], _35912) ? creep
Exit: (13) redu([], []) ? creep
Exit: (12) redu([a], [a]) ? creep
Exit: (11) redu([c, a], [c, a]) ? creep
Exit: (10) redu([b, c, a], [b, c, a]) ? creep
Exit: (9) redu([b, b, c, a], [b, c, a]) ? creep
Exit: (8) redu([a, b, b, c, a], [b, c, a]) ? creep
X = [b, c, a]
如果有人能用自然语言向我解释递归的作用以及如何阅读子句,我将不胜感激。就像第二条一样,它的意思是“从列表中删除重复项H | T
并输出Result
(如果该列表的头部是尾部的成员),然后从尾部删除重复项并输出结果?”?但是这两个结果怎么可能相同呢?我也不知道什么时候激活了哪个规则。什么时候在我的条款清单中继续?什么时候回去
很抱歉问了这么多问题。我真的想了解一切。任何递归实现都至少有两个子句-基本子句和一个或多个递归子句
基本子句处理退化情况:空列表、零等。它们给出了简单的答案——例如,在您的例子中,base子句说明空列表的答案是空列表
递归子句分别处理项目在列表上(第二子句)和项目不在列表上(第三子句)的情况。第二条规定,当在列表的尾部(T的成员)发现一个项目时,现在不应将其添加到结果中;它将在以后添加。另一方面,如果这是最后一项,则第三个子句将其添加到输出列表中
但是,最终,成员检查将失败(b
不是[c,a]
的成员),但是它是如何工作的呢
一旦成员检查失败,Prolog将转到第三个子句,该子句使用[H | Result]
将H
添加到输出列表中,并继续使用redu(T,Result)
从列表的尾部T
计算剩余的结果
注1:程序中有一个错误:最后一个子句的条件是该项不在列表的尾部:
redu([H|T], [H|Result]):-
\+ member(H,T),
redu(T, Result).
如果第二个子句已经执行,这应该可以防止Prolog进入该子句
注2:另一个选项是在第二个子句中使用cut,但是这个选项是非常不推荐的。所以你有
redu([],[])。
redu([H | T],R):-成员(H,T),redu(T,R)。
redu([H | T],[H | R]):-redu(T,R)。
==
redu([],[])。
redu([H | T],X):-成员(H,T),X=R,redu(T,R)。
redu([H | T],X):-X=[H | R],redu(T,R)。
==
redu([],[])。
redu([H | T],X):-(成员(H,T),X=R
;X=[H | R]),redu(T,R)。
==
redu([],[])。
redu([H | T],X):-disj(H,T,X,R),redu(T,R)。
disj(H,T,R,R):-成员(H,T)。
disj(H,| T,[H | R],R)。
这两个新的redu/2
子句是互斥的,因此代码在这种形式下更容易理解。无论disj/4
是否将H
包括在正在构建的X
列表中(以自顶向下的方式),无论它成功了多少次(*),在disj/4
完成它的工作之后,对redu/2
的递归调用都是简单的
因此我们将redu(L,X)
as解读为L=[H | T]中的头元素H
,如果在T
中有更多的H
,要么不将H
包括在“输出”列表X
中,要么就这样做;对于唯一的H
,例如在T
中不会出现的H
,总是将其包括在X
中;然后,处理完的这个头元素H
>L
,继续以相同的方式处理列表中的其余元素。”换句话说,对列表中的每个元素执行此操作
这种递归定义自然遵循列表的归纳定义,即[H | T]
或[]
结构
(*)(请注意,A.成员
可能多次成功,B.disj/4
的两个子句并不相互排斥)
以你为例,
redu([a,b,b,c,a],X)
==
disj(a[b,b,c,a],X,R),%和redu([b,b,c,a],R)即。
disj(b[b,c,a],R,R2),%和redu([b,c,a],R2)即。
disj(b,[c,a],R2,R3),%和redu([c,a],R3)即。
disj(c[a],R3,R4),%和redu([a],R4)即。
disj(a,[],R4,R5),%和最终条款,
redu([],R5)。
现在,您可以尝试每个disj/4
调用,看看那里发生了什么,比如
33?-disj(a[b,b,c,a],X,R)。
X=R;
X=[a | R]。
34?-disj(b[c,a],R2,R3)。
R2=[b | R3]。
这样,整个例子就变成了一个例子
(X=R;X=[a | R]),%[a
(R=R2;R=[b | R2]),%b
R2=[b | R3],%b
R3=[c | R4],%c
R4=[a | R5],%a
R5=[].%]
或
ex(X):-
(X=R;X=[a | R]),
(R=R2;R=[b | R2]),
R2=[b,c,a]。
那是
42?-ex(X)。
X=[b,c,a];
X=[b,b,c,a];
X=[a,b,c,a];
X=[a,b,b,c,a]。
好的,当存在重复项时,我会得到它:如果membercheck成功,则会激活redu(T,Result)规则,然后prolog会重新滚动到开头。新列表(即尾部)首先与第一个子句匹配,但没有匹配,它继续到第二个,有一个匹配,membercheck被激活