【问题标题】:Having Trouble Understanding Haskell Merging Function无法理解 Haskell 合并功能
【发布时间】:2021-01-04 18:28:27
【问题描述】:

我有一个任务是创建一个 Haskell 函数来将 2 个列表合并在一起,而不使用 ++ 操作。我在网上找到了以下代码,它按预期工作,但我需要帮助理解它的工作原理和原因。如果有人可以逐步指导我了解此功能的工作原理,我将不胜感激。我对 Haskell 很陌生,所以假设您正在向 5 岁的孩子解释这一点。

merge :: [a] -> [a] -> [a]
merge []     ys = ys
merge (x:xs) ys = x : (merge xs ys)

【问题讨论】:

    标签: list function haskell merge


    【解决方案1】:

    Haskell 中的列表定义为链表,[] 为空列表,(:) 为“cons”,其中第一个参数为head(第一个元素),第二个元素是 tail(所以是一个包含剩余元素的列表)。因此,这意味着像[1,4] 这样的列表表示为(1 : (4 : [])),或者更多可以是规范的(:) 1 ((:) 4 [])

    如果我们现在想要合并两个列表[1,4][2,5],例如我们调用合并merge (1 : (4 : [])) (2 : (5 : []))。合并的第一条规则检查第一个参数是否为空列表,事实并非如此,所以我们继续第二条规则。这将检查第一个列表是否使用 cons 数据构造函数,就是这种情况。因此它将x 与列表的头部(此处为1)统一,并将xs 与列表的其余部分(此处为[4])统一起来。因此,这意味着它将其替换为:

    merge (1 : (4 : [])) (2 : (5 : []))  ->  1 : merge (4 : []) (2 : (5 : []))
    

    现在在递归调用中,将再次检查第一个子句,但 (:) 4 [] 再次与空列表 [] 不匹配,因此我们检查第二个子句,再次匹配。所以在这个调用中x4 统一,xs[]

    merge (4 : [])) (2 : (5 : []))  ->  4 : merge [] (2 : (5 : []))
    

    最后一次递归将空列表数据构造函数作为第一个参数。因此,这与第一个子句中的模式相匹配。因此我们返回第二个列表:

    merge [] (2 : (5 : []))  ->  (2 : (5 : []))
    

    因此结果是一个列表:

    (1 : (4 : (2 : (5 : []))))
    

    或者简而言之:

    [1,4,2,5]
    

    【讨论】:

      【解决方案2】:

      列表要么是空的,要么是非空的。如果它不是空的,那么它有一个头(第一个元素)和一个尾(列表的其余部分,减去第一个元素)。如果我们能说出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]

      *实际上,这个参数依赖于第一个列表是有限的。如果它是无限的,则递归永远不会停止 - 但这仍然不是问题,因为该过程仍然允许我们根据需要计算任何有限数量的元素。事实上,对于任何有限数量的元素,结果只是第一个列表。但是,如果您还没有意义,请不要太担心这种情况,无限数据结构不会每天都出现!

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2015-10-29
        • 1970-01-01
        • 1970-01-01
        • 2019-01-02
        • 2023-03-04
        • 1970-01-01
        • 2019-02-22
        相关资源
        最近更新 更多