只是通过一些教程,我发现了一些高级递归过程,比如 flatten。我试图在谷歌上找到涉及多个递归(头尾)但无法得到我需要的结果的类似示例。
您能否推断出一些涵盖高级列表回避(头部,尾部)的谓词或教程?
只是通过一些教程,我发现了一些高级递归过程,比如 flatten。我试图在谷歌上找到涉及多个递归(头尾)但无法得到我需要的结果的类似示例。
您能否推断出一些涵盖高级列表回避(头部,尾部)的谓词或教程?
只是为了扩展@hardmath 的意思,让我们看一下列表的定义:
[]
[Head|Tail]
使它成为递归数据结构的原因在于Tail
它也是一个列表。所以当你看到时[1,2,3]
,你也在看到[1|[2|[3|[]]]]
。让我们证明一下:
?- X = [1|[2|[3|[]]]].
X = [1, 2, 3].
因此,更“高级”的递归形式是涉及更复杂的递归数据类型或更复杂的计算的形式。大多数人接触到的下一个递归数据类型是二叉树,二叉树有一个很好的特性,即每个节点有两个分支,所以让我们先看看树。
首先,我们需要一个很好的定义,比如列表中的定义。我提出以下建议:
empty
tree(LeftBranch, Value, RightBranch)
现在让我们创建一些示例树来感受一下它们的外观:
% this is like the empty list: no data
empty
% this is your basic tree of one node
tree(empty, 1, empty)
% this is a tree with two nodes
tree(tree(empty, 1, empty), 2, empty).
在结构上,最后一个例子可能看起来像这样:
2
/
1
现在让我们用几个层次来做一个更完整的例子。让我们构建这棵树:
10
/ \
5 9
/ \ / \
4 6 7 14
在我们的 Prolog 语法中,它看起来像这样:
tree(tree(tree(empty, 4, empty), 5, tree(empty, 6, empty)),
10,
tree(tree(empty, 7, empty), 9, tree(empty, 14, empty)))
我们首先需要的是一种将树的大小相加的方法。与列表一样,我们需要考虑基本案例,然后是归纳案例。
% base case
tree_size(empty, 0).
% inductive case
tree_size(tree(Left, _, Right), Size) :-
tree_size(Left, LeftSize),
tree_size(Right, RightSize),
Size is LeftSize + RightSize + 1.
为了比较,让我们看一下列表长度:
% base case
length([], 0).
% inductive case
length([_|Rest], Length) :-
length(Rest, LengthOfRest),
Length is LengthOfRest + 1.
编辑:@false 指出,尽管上述内容很直观,但可以通过将归纳案例更改为:
length([_|Rest], Length) :-
length(Rest, LengthOfRest),
succ(LengthOfRest, Length).
因此,通过比较这两者,您可以清楚地看到递归处理数据结构的标志:
您编写规则的基础来处理基本情况。
这一步通常很明显;在长度或大小的情况下,您的数据结构将有一个空的基本情况,因此您只需将零与该情况相关联。
你写下你的规则的归纳步骤。
归纳步骤采用数据结构的递归案例并处理该案例添加的任何内容,并将其与递归调用您的规则以处理数据结构的“其余部分”的结果相结合。
因为列表只在一个方向上递归,所以在大多数列表处理规则中只有一次递归调用。因为树有两个分支,所以可以有一个或两个,这取决于您是需要处理整棵树还是只走一条路径。列表和树实际上都有两个“构造函数”,因此大多数规则都有两个主体,一个处理空案例,一个处理归纳案例。更复杂的结构,例如语言语法,可以有两个以上的基本模式,通常你要么分别处理它们,要么只寻找一种特别的模式。
作为练习,您可能想尝试编写search
、insert
、height
或balance
以及is_balanced
各种其他树查询来更熟悉该过程。