【发布时间】:2012-10-14 09:38:51
【问题描述】:
我想制作一个通用函数,可以折叠各种输入(请参阅Making a single function work on lists, ByteStrings and Texts (and perhaps other similar representations))。作为one answer suggested,ListLike 就是为了这个。它的FoldableLL 类为任何可折叠的东西定义了一个抽象。但是,我需要一个单子折叠。所以我需要根据foldl/foldr来定义foldM。
到目前为止,我的尝试都失败了。我试图定义
foldM'' :: (Monad m, LL.FoldableLL full a) => (b -> a -> m b) -> b -> full -> m b
foldM'' f z = LL.foldl (\acc x -> acc >>= (`f` x)) (return z)
但它在大量输入时会耗尽内存 - 它会构建一个大型未评估的计算树。例如,如果我将一个大文本文件传递给
main :: IO ()
main = getContents >>= foldM'' idx 0 >> return ()
where
-- print the current index if 'a' is found
idx !i 'a' = print i >> return (i + 1)
idx !i _ = return (i + 1)
它会耗尽所有内存并失败。
我感觉问题在于一元计算的组合顺序错误——比如((... >>= ...) >>= ...) 而不是(... >>= (... >>= ...)),但到目前为止我还没有找到解决方法。
解决方法:由于ListLike 暴露了mapM_,我通过将累加器包装到状态单子中,在ListLikes 上构造了foldM:
modifyT :: (Monad m) => (s -> m s) -> StateT s m ()
modifyT f = get >>= \x -> lift (f x) >>= put
foldLLM :: (LL.ListLike full a, Monad m) => (b -> a -> m b) -> b -> full -> m b
foldLLM f z c = execStateT (LL.mapM_ (\x -> modifyT (\b -> f b x)) c) z
虽然这适用于大型数据集,但它不是很好。如果可以在仅 FoldableLL(没有 mapM_)的数据上定义它,它并没有回答最初的问题。
【问题讨论】:
-
为什么不使用
foldMfromControl.Monad?还要使用严格版本foldl'而不是foldl。 -
@Satvik 因为我不仅需要它来定义列表,还需要定义任何FoldableLL,它只暴露不同种类的纯折叠,而不是单子折叠。我的错,我没有在帖子中说清楚 - 我已经更正了类型签名。
-
要以相反的顺序组合计算,请使用
foldr而不是foldl。 -
@dave4420 我也试过了,但是没用。
标签: haskell functional-programming monads fold