折叠

时间:2015-10-08 04:01:00

标签: haskell fold

考虑Haskell中的以下两个表达式:

foldl' (>>=) Nothing (repeat (\y -> Just (y+1)))
foldM (\x y -> if x==0 then Nothing else Just (x+y)) (-10) (repeat 1)

第一个需要永远,因为它试图评估无限表达

...(((Nothing >>= f) >>= f) >>=f)...

和Haskell将尝试从内到外进行评估。

然而,第二个表达式立即给出了Nothing。我一直以为foldM只是使用(>> =)进行折叠,但是它会遇到同样的问题。所以它在这里做了一些更聪明的事情 - 一旦它命中了它就知道停止了。 foldM实际上如何运作?

2 个答案:

答案 0 :(得分:6)

foldM无法使用foldl实施。它需要foldr的力量来阻止。在我们到达之前,这里有一个没有任何花哨的版本。

foldM f b [] = return b
foldM f b (x : xs) = f b x >>= \q -> foldM f q xs

我们可以将其转换为使用foldr的版本。首先我们翻转它:

foldM f b0 xs = foldM' xs b0 where
  foldM' [] b = return b
  foldM' (x : xs) b = f b x >>= foldM' xs

然后移动最后一个参数:

  foldM' [] = return
  foldM' (x : xs) = \b -> f b x >>= foldM' xs

然后识别foldr模式:

  foldM' = foldr go return where
    go x r = \b -> f b x >>= r

最后,我们可以内联foldM'并将b移回左侧:

foldM f b0 xs = foldr go return xs b0 where
  go x r b = f b x >>= r

这种相同的通用方法适用于您想要在右侧折叠中从左向右传递累加器的各种情况。首先将累加器一直向右移动,这样您就可以使用foldr构建一个带累加器的函数,而不是直接尝试构建最终结果。 Joachim Breitner做了很多工作来为GHC 7.10创建Call Arity编译器分析,帮助GHC优化以这种方式编写的函数。想要这样做的主要原因是它允许他们参与GHC列表库。融合框架。

答案 1 :(得分:1)

根据<div class="closePop"> <p></p> </div> body{background:black;} .closePop { right: -5px; top: -5px; height: 28px; background-color: #f68d1e; color: white; border-radius: 50%; width: 29px; text-align: center; border-style: solid; border-width: 2px; vertical-align: middle; cursor: pointer; transform: rotate(0deg); transition: all .3s ease-out; } .closePop p { margin: 0; margin-top: 4px; } .closePop:hover p:after { content:':('; transition:contentArea 3s ease-out rotate .3s ease-out; -webkit-transform: rotate(0deg); -ms-transform: rotate(0deg); -o-transform: rotate(0deg); transform: translateZ(1px) rotate(0deg); } .closePop p:after { content: 'X'; transition: all .3s ease-out; -webkit-transform: rotate(90deg); -ms-transform: rotate(90deg); -o-transform: rotate(90deg); transform: translateZ(1px) rotate(90deg); } .closePop:hover { border-radius: 0; -webkit-transform: rotate(90deg); -ms-transform: rotate(90deg); -o-transform: rotate(90deg); transform: translateZ(1px) rotate(90deg); } .closePop p { transition: contentArea .3s ease-out rotate .3s ease-out; } 定义foldl的一种方法是:

foldr

这可能是值得解决的原因。它可以使用foldl f z xn = foldr (\ x g y -> g (f y x)) id xn z 中的>>>重写为

Control.Arrow

foldl f z xn = foldr (>>>) id (map (flip f) xn) z 的monadic等价物是

>>>

允许我们猜测f >=> g = \ x -> f x >>= \ y -> g y 可能是

foldM

结果证明是正确的定义。它可以使用foldM f z xn = foldr (>=>) return (map (flip f) xn) z 重写为

foldr/map