我正在学习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).
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
是正确的”输出结果?但是两个Results
怎么可能是相同的呢?而且我也没有得到哪个规则被激活。它什么时候在我的条款列表中继续?它什么时候回来?
对不起所有问题。我真的很想了解一切。
答案 0 :(得分:0)
任何递归实现都至少有两个子句 - 基本子句,以及一个或多个递归子句。
Base子句处理退化情况:空列表,零等。它们给出了简单的答案 - 例如,在您的情况下,base子句指出空列表的答案是空列表。
递归子句单独处理项目在列表中的情况(第二个子句),以及项目不在列表中的情况(第三个子句)。第二个条款说当在列表的尾部(T的成员)上找到一个项目时,它现在不应该添加到结果中;它将在稍后添加。另一方面,如果这是最后一项,则第三项将其添加到输出列表中。
但是,最后,成员检查将失败(
b
不是[c,a]
的成员),但它是如何工作的呢?
一旦成员检查失败,Prolog将转到第三个子句,它将H
添加到带有[H|Result]
的输出列表,然后从尾部开始计算Result
的其余部分T
列表的redu(T, Result)
。
注1:但是程序中有一个错误:最后一个句子需要以列表尾部的项目而不是为条件:
redu([H|T], [H|Result]):-
\+ member(H,T),
redu(T, Result).
如果第二个子句已经执行,这可以防止Prolog进入该子句。
注2:另一个选项是在第二个子句中使用cut,但强烈建议不要使用此选项。
答案 1 :(得分:0)
所以你有
redu([], []).
redu([H|T], R ):- member(H, T), redu(T, R).
redu([H|T], [H|R]):- redu(T, R).
==
redu([], []).
redu([H|T], X ):- member(H, T), X = R , redu(T, R).
redu([H|T], X):- X = [H|R], redu(T, R).
==
redu([], []).
redu([H|T], X ):- ( member(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):- member(H, T).
disj(H,_T, [H|R], R).
这两个新的redu/2
子句是互斥的,因此这种形式的代码更容易理解。 disj/4
是否H
包含X
正在构建的列表disj/4
(以自上而下的方式) - 无论多少次成功(*) - redu/2
之后它的问题,就是对redu(L,X)
的递归调用。
因此,如果 还有一些H
s,我们会在L=[H|T]
中将H
读为“”作为头元素T
在H
中, 在“输出”列表X
中不包含H
,或者包含T
,并且对于唯一的X
- 这样在H
中不会出现 - 在L
总是; 然后中包含它,处理{{1}的头元素L
继续以相同的方式处理列表中的其余元素。“换句话说,对列表[H|T]
中的每个元素执行 this 。
这种递归定义自然遵循列表的归纳定义为 []
或 member
结构。
(*)(请注意 A。 disj/4
可能会多次成功, B。 redu([a,b,b,c,a], X)
==
disj( a, [b,b,c,a], X,R), % AND redu([b,b,c,a], R) i.e.
disj( b, [b,c,a], R,R2), % AND redu([b,c,a], R2) i.e.
disj( b, [c,a], R2,R3), % AND redu([c,a], R3) i.e.
disj( c, [a], R3,R4), % AND redu([a], R4) i.e.
disj( a, [], R4,R5), % AND the final clause,
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].
是
/etc/nginx/sites-available