【问题标题】:Why is foldr' not as strict as foldl'?为什么 foldr' 不如 foldl' 严格?
【发布时间】:2020-11-26 20:48:57
【问题描述】:

考虑一下这些不同的尝试,比如last

Prelude> import Data.Foldable
Prelude Data.Foldable> foldr const undefined (reverse [1,2,3])
3
Prelude Data.Foldable> foldr' const undefined (reverse [1,2,3])
3
Prelude Data.Foldable> foldl (flip const) undefined [1,2,3]
3
Prelude Data.Foldable> foldl' (flip const) undefined [1,2,3]
*** Exception: Prelude.undefined
CallStack (from HasCallStack):
  error, called at libraries/base/GHC/Err.hs:79:14 in base:GHC.Err
  undefined, called at <interactive>:5:21 in interactive:Ghci4

foldlfoldr 都可以工作,因为它们的累加器并不严格,对我来说很有意义,foldl' 没有,因为确实如此。但是为什么foldr' 有效?它的累加器不应该也很严格吗?

【问题讨论】:

    标签: haskell lazy-evaluation fold strictness


    【解决方案1】:

    作为参考,实例Foldable [] 覆盖foldrfoldlfoldl',但不是foldr' (source):

    instance Foldable [] where
        elem    = List.elem
        foldl   = List.foldl
        foldl'  = List.foldl'
        foldl1  = List.foldl1
        foldr   = List.foldr
        {- ... -}
    

    foldr' 默认定义为 (source):

    foldr' :: (a -> b -> b) -> b -> t a -> b
    foldr' f z0 xs = foldl f' id xs z0
      where f' k x z = k $! f x z
    

    注意f的结果只有一个严格的注解。所以初始累加器不是强制的。

    这表明了一个不同的实现,它确实强制累加器:

    foldr'' :: Foldable t => (a -> b -> b) -> b -> t a -> b
    foldr'' f = foldr (\x z -> f x $! z)
    

    (已编辑:以前的版本专门用于列表。)

    我不知道为什么选择一个而不是另一个。大概是疏忽, 并且foldr' 不使用Foldable [] 实例中的默认实现会更加一致。


    顺便说一句,foldl' 的默认定义也与列表中的定义相同:

    -- Default (class Foldable t where ...)
    foldl' :: (b -> a -> b) -> b -> t a -> b
    foldl' f z0 xs = foldr f' id xs z0
      where f' x k z = k $! f z x
    
    -- List implementation
    foldl'           :: forall a b . (b -> a -> b) -> b -> [a] -> b
    foldl' k z0 xs =
      foldr (\(v::a) (fn::b->b) -> oneShot (\(z::b) -> z `seq` fn (k z v))) (id :: b -> b) xs z0
    

    【讨论】:

    • 您的定义专门针对列表,原始定义是针对通用可折叠的(例如,仅定义了惰性 foldr)。我猜可以有foldl f' id xs $! z0。 (如果它使事情“正确”更严格,我还没有尝试过)。
    • 这是一个公平的观察。实际上,在四个 fold[lr]'? 变体中,foldr' 是唯一一个 Foldable [] 没有覆盖,导致这种不一致的行为。
    • 哦,这确实很奇怪。 (考虑到 Foldable 是一个庞大的字典,多使用一种方法是有意义的,尤其是在这种情况下,对于 Seq 或 SnocList)。
    • 对于泛型Foldables 上foldl'foldr' 的定义,将foldl' 定义为foldrfoldr' 更有意义定义为foldl。这是因为您通常希望 foldl' 工作类似于尾递归严格函数(例如求和或其他),并且您不能根据 foldl 定义它(在 cons-lists 上)。 foldr' 对 snoc 列表的作用相同。列表上的通用定义也受益于专业化。 foldr' 没有“好的”版本,这可能是为什么不包含特殊版本的原因。
    • 歇斯底里的葡萄干。这种不一致是约阿希姆·布莱特纳 (Joachim Breitner) 的少数错误之一。当他想出如何让foldl 很好地融合的绝妙方法时,他将同样的技巧应用于foldl'。不幸的是,当他这样做时,他不小心把函数变得更严格了。
    猜你喜欢
    • 1970-01-01
    • 2011-08-28
    • 2015-01-08
    • 1970-01-01
    • 2012-11-12
    • 2010-09-27
    • 2011-05-13
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多