【问题标题】:Pattern matching and infinite lists模式匹配和无限列表
【发布时间】:2014-12-29 13:33:55
【问题描述】:

我无法理解这个简单的 sn-p 代码:

-- This works:     foldr go1 [] [1..]
-- This doesn't:   foldr go2 [] [1..]

go1 a b = a : b

go2 a [] = a : []
go2 a b  = a : b

go1 折叠立即开始返回值,但go2 似乎正在等待列表的结尾。

显然,模式匹配导致处理方式不同。有人能解释一下这里到底发生了什么吗?

【问题讨论】:

  • 感谢大家的出色回答。如果我可以选择多种解决方案,我会选择的,因为它们都帮助我理解了这个概念。
  • 除了“选择”一个答案之外,您还可以投票给那些您认为“有帮助”的答案(当您将鼠标悬停在向上箭头上方时会这样说)。 :)

标签: list haskell lazy-evaluation infinite fold


【解决方案1】:

go1 不同,go2 检查其第二个参数是否为空。为此,需要评估第二个参数,至少足以确定它是否为空。

因此,对于您致电foldr,这意味着:

go1go2 首先使用两个参数调用:1 和 foldr go [] [2 ..] 的结果。在go1 的情况下,第二个参数保持不变,因此foldr 的结果只是1 :: foldr go [] [2 ..],无需进一步评估尾部,直到它被访问。

然而,在go2 的情况下,需要评估foldr go [] [2 ..] 以检查它是否为空。为此,foldr go [] [3 ..] 需要出于同样的原因进行评估。以此类推,无止境。

【讨论】:

    【解决方案2】:

    要测试一个表达式是否满足某种模式,您至少需要将其评估为弱头范式。所以模式匹配迫使评估。 一个常见的例子是interleave 函数,它交错两个列表。可以这样定义

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

    但是这个函数在第二个参数中是严格的。而更懒的版本是

    interleave  []    ys = ys
    interleave (x:xs) ys = x : interleave ys xs
    

    您可以在此处阅读更多内容:http://en.wikibooks.org/wiki/Haskell/Laziness

    【讨论】:

    • 如果它“交错两个列表”,不应该叫interleave吗? “合并”更适合例如合并排序...
    • @Will Ness,已修复。我已经多次看到这个函数被称为“合并”。
    【解决方案3】:

    这是因为懒惰....因为 go1go2 在此示例中定义的方式,它们的行为与 b==[] 完全相同,但编译器不知道这一点.

    对于go1,最左边的折叠将使用尾递归立即输出a的值,然后计算b的值。

    go1 a b -> create and return the value of a, then calculate b 
    

    对于go2,编译器甚至不知道要匹配哪种情况,直到计算出b 的值......这永远不会发生。

    go2 a b -> wait for the value of b, pattern match against it, then output a:b
    

    【讨论】:

      【解决方案4】:

      要查看差异,请在 GHCi 中尝试:

      > head (go1 1 (error "urk!"))
      1
      > head (go2 1 (error "urk!"))
      *** Exception: urk!
      

      问题是go2 将在返回列表的头部之前评估它的第二个参数。也就是说,go2 在其第二个参数中是 strict,而 go1lazy

      当您折叠无限列表时,这很重要:

      fold1 go1 [] [1..] =
      go1 1 (go1 2 (go1 3 ( ..... =
      1 : (go1 2 (go1 3 ( ..... =
      1 : 2 : (go1 3 ( ...
      

      工作正常,但是

      fold1 go1 [] [1..] =
      go2 1 (go2 2 (go2 3 ( .....
      

      不能简化为1:...,因为go2 坚持评估它的第二个参数,这是对go2 的另一个调用,而这又需要评估它自己的第二个参数,这是另一个...

      嗯,你明白了。第二个不会停止。

      【讨论】:

        猜你喜欢
        • 2015-01-17
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2016-04-02
        • 1970-01-01
        相关资源
        最近更新 更多