列表要么是空的,要么是非空的。如果它不是空的,那么它有一个头(第一个元素)和一个尾(列表的其余部分,减去第一个元素)。如果我们能说出merge 在它的第一个参数满足这两种情况时做了什么,那么我们将处理所有可能的情况 - 因为第一个列表,作为一个列表,要么为空,要么不为空。
所以假设首先我们将空列表与另一个列表合并。在这种情况下,显然结果只是另一个列表 - 没有什么可以合并的。这是简单的情况,在函数的第一行中介绍:merge [] ys = ys。
所以我们只剩下第一个列表非空的情况。如前所述,这意味着它有一个头和一个尾。应该很容易看出,在将这个列表与另一个列表合并时,结果就像我们将尾部与另一个列表合并,然后将头部添加到前面一样。这就是函数的第二行所说的,在模式匹配语言中(特别是使用“cons”构造函数(:) 将列表分成头部和尾部):merge (x:xs) ys = x : (merge xs ys)。
一旦我们有了这些,我们就可以为任意两个列表计算merge。这两种情况不仅涵盖了所有可能的情况,而且至关重要的是,它们允许我们“向后工作”以始终得到答案——因为在递归情况下,我们用merge xs 表示merge (x:xs),而xs 是比(x:xs) 短,所以通过重复这个过程,我们最终总是会回到空列表的情况*
一个简单的例子应该演示:merge [1, 2, 3] [4, 5, 6]依次变成:
1 : (merge [2, 3] [4, 5, 6])
1 : (2 : (merge [3] [4, 5, 6]))
1 : (2 : (3 : (merge [] [4, 5, 6])))
1 : (2 : (3 : [4, 5, 6]))
我们通常会写成[1, 2, 3, 4, 5, 6]。
*实际上,这个参数依赖于第一个列表是有限的。如果它是无限的,则递归永远不会停止 - 但这仍然不是问题,因为该过程仍然允许我们根据需要计算任何有限数量的元素。事实上,对于任何有限数量的元素,结果只是第一个列表。但是,如果您还没有意义,请不要太担心这种情况,无限数据结构不会每天都出现!