如何在多个列表遍历中避免spaceleak?

时间:2012-06-25 09:57:47

标签: haskell

GHC足够聪明,可以在“半并行”的列表上运行多个操作吗?

考虑这个(简化的)代码:

findElements bigList = do
  let special = head . filter isSpecial $ bigList
  let others  = filter isSpecialOrNormal $ bigList

  return (special, others)

Monad由于原始代码

我猜GHC将运行第一个列表操作,并将所有元素保留在内存中,以便第二个操作能够对它们起作用。

我的问题是我在处理更大的文件时遇到了漏洞。但我相信它应该能够在恒定的空间中运行。有没有办法实现这个目标?

更新1

这样写下这个问题的解决办法当然是改变两行的顺序。

但我的问题仍然存在:GHC是否具有足够的智能性,可以在没有monad的情况下找出这种半并行处理?

1 个答案:

答案 0 :(得分:4)

我不认为GHC足够智能合并这两个遍历,或者,通常情况下,GHC可能足够聪明,但有些情况下你不想要这种行为,所以GHC没有做到这一点。

以下是我将如何使用monoids和foldMap

import Data.Monoid
import Data.Foldable

首先,使用special monoid,如何使用foldMap编写First

specialF :: a -> First a
specialF a = First $ if isSpecial a then Just a else Nothing

special :: [a] -> a
special as = let (First (Just s)) = foldMap specialF as in s

与specialOrNormal类似,使用列表monoid。

specialOrNormalF :: a -> [a]
specialOrNormalF a = if isSpecialOrNormal a then [a] else []

specialOrNormal :: [a] -> [a]
specialOrNormal = foldMap specialOrNormalF

关于幺半群的一个巧妙的事情是,幺半群的元组也是一个幺半群,这使得合并这些褶皱变得容易:

findElements :: [a] -> (a, [a])
findElements bigList =
  let (First (Just s), son) = 
    foldMap (\a -> (specialF a, specialOrNormalF a)) bigList
  in (s, son)

如果你喜欢无点代码,你可以写下这样的全部内容:

findElements :: [a] -> (a, [a])
findElements = 
  first (fromJust . getFirst) . 
  foldMap 
    (   First . mfilter isSpecial . return 
    &&& mfilter isSpecialOrNormal . return
    )