嗯,我只能说,解决问题的方式很大程度上取决于问题本身。有一系列问题可以通过使用来解决递归,其中 Prolog 非常适合解决这些问题。
在这类问题中,可以通过将较大问题划分为两个或多个案例类来导出更大问题的解决方案。
在一类中,我们有“基本情况”,当输入无法进一步分为更小的情况时,我们提供问题的解决方案。
另一类是“递归情况”,我们将输入分成几部分,分别求解,然后“连接”结果以给出此较大输入的解决方案。
在 flatten/2 的示例中,我们希望将一个项目列表作为输入,其中每个项目也可以是一个列表,结果应是一个包含输入中所有项目的列表。因此,我们将问题分成不同的情况。
我们将使用辅助参数来保存中间展平列表,这就是我们实现 flatten/3 的原因。
因此,我们的 flatten/2 谓词将仅使用空列表作为起始中间扁平列表来调用 flatten/3:
flatten(List, Flattened):-
flatten(List, [], Flattened).
现在对于 flatten/3 谓词,我们有两个基本情况。第一个处理空列表。请注意,当输入为空列表时,我们无法进一步划分问题。在这种情况下,我们只将中间扁平列表作为我们的结果。
flatten([], Flattened, Flattened).
我们现在采取递归步骤。这涉及获取输入列表并将问题分为两个步骤。第一步是展平此输入列表的第一项。第二步是递归地展平其余部分:
flatten([Item|Tail], L, Flattened):-
flatten(Item, L1, Flattened),
flatten(Tail, L, L1).
好的,所以打电话给展平(项目,L1,展平)展平第一项,但将未绑定变量 L1 作为中间列表传递。这只是一个技巧,因此在谓词返回时,变量 L1 仍然保持无界,并且 Flattened 将采用 [...|L1] 形式,其中 ... 是 Item 的扁平项。
下一步,调用展平(尾部,L,L1)展平输入列表的其余部分,结果以 L1 为界。
我们的最后一个子句实际上是另一个基本情况,处理单个项目(不是列表)。因此我们有:
flatten(Item, Flattened, [Item|Flattened]):-
\+ is_list(Item).
它检查 item 是否是列表,当它不是列表时,它将结果绑定为带有 head=Item 的列表,并将中间扁平列表绑定为 tail。