将元组作为参数解构时,无限列表上的 Haskell 元组解构与使用 let 解构时的行为不同

Pro*_*het 5 haskell tuples list destructuring lazy-evaluation

当尝试使用 foldr 实现 dropWhile 时,我想出的第一个算法是

dropWhile' :: (a -> Bool) -> [a] -> [a]
dropWhile' pred = fst . foldr (\cur (acc, xs) ->
    if pred cur
    then (acc, cur:xs)
    else (cur:xs, cur:xs)) ([], [])

Run Code Online (Sandbox Code Playgroud)

虽然这有效,但它会导致无限列表上的堆栈溢出而没有给出任何值。因为我不确定,为什么这不起作用,我只是玩弄这个函数,直到我想出这个:

dropWhile' :: (a -> Bool) -> [a] -> [a]
dropWhile' pred = fst . foldr (\cur t -> let (acc, xs) = t in
    if pred cur
    then (acc, cur:xs)
    else (cur:xs, cur:xs)) ([], [])
Run Code Online (Sandbox Code Playgroud)

正如您所看到的,这个和第一个之间的唯一区别是,这里我在(acc, xs)let 绑定中解构元组,而不是直接在函数参数内部解构。出于某种奇怪的原因,此代码适用于无限列表。如果有人知道为什么会这样,请告诉我。

Li-*_*Xia 7

Haskell 中的 Let 表达式是惰性的:

Let 表达式

(...) 模式绑定是惰性匹配的;隐含的 ~ 使这些模式无可辩驳。

相比之下, lambda 抽象 desugar 到case,从而使构造函数模式严格:

Curried 应用程序和 lambda 抽象

翻译:以下身份成立:

\ p1 … pn -> e = \ x1 … xn -> case (x1, …, xn) of (p1, …, pn) -> e

  • ...并且您可以使用暗示的“~”使 lambda 模式显式惰性化并获得两全其美的效果(“let”在无限列表上的良好行为和 lambda 更好的美学)。`\cur ~(acc, xs) -> 如果 ...` (2认同)