考虑以下示例:
safeMapM f xs = safeMapM' xs []
where safeMapM' [] acc = return $ reverse acc
safeMapM' (x:xs) acc = do y <- f x
safeMapM' xs (y:acc)
mapM return largelist -- Causes stack space overflow on large lists
safeMapM return largelist -- Seems to work fine
Run Code Online (Sandbox Code Playgroud)
mapM在大型列表上使用会导致堆栈空间溢出,而safeMapM似乎工作正常(使用GHC 7.6.1 -O2).但是我无法找到类似于safeMapMHaskell标准库的函数.
是否仍然被认为是使用mapM(或sequence就此而言)的良好做法?
如果是这样,尽管存在堆栈空间溢出的危险,为什么它被认为是一种好的做法?
如果不是您建议使用哪种替代方案?
正如Niklas B.所说,语义mapM是有效的右折叠,并且它在更多情况下比翻转版本成功终止.一般来说,mapM更有意义,因为我们很少想要在庞大的数据列表上做一个结果映射.更常见的是,我们要评估这样的列表的效果,而且在这种情况下mapM_和sequence_,其扔掉的结果,通常建议是什么.
编辑:换句话说,尽管在问题中提出了问题,是的,mapM并且sequence是常用的,通常被认为是良好的做法.
如果是这样,尽管存在堆栈空间溢出的危险,为什么它被认为是一种好的做法?如果不是您建议使用哪种替代方案?
如果要在生成列表元素时处理它们,请使用pipes或conduit.两者都不会建立一个中间列表.
我会说明pipes道路,因为那是我的图书馆.我首先IO从用户输入的monad中生成的无限数字列表开始:
import Control.Proxy
infiniteInts :: (Proxy p) => () -> Producer p Int IO r
infiniteInts () = runIdentityP $ forever $ do
n <- lift readLn
respond n
Run Code Online (Sandbox Code Playgroud)
现在,我想在生成它们时打印它们.这需要定义下游处理程序:
printer :: (Proxy p) => () -> Consumer p Int IO r
printer () = runIdentityP $ forever $ do
n <- request ()
lift $ print n
Run Code Online (Sandbox Code Playgroud)
现在我可以连接Producer和Consumer使用(>->),并使用runProxy以下命令运行结果:
>>> runProxy $ infiniteInts >-> printer
4<Enter>
4
7<Enter>
7
...
Run Code Online (Sandbox Code Playgroud)
然后,它将Int从用户读取s并在生成它们时将它们回送到控制台,而不会在内存中保存多个元素.
因此,通常如果您想要一个有效的计算来生成元素流并立即使用它们,那么您通常不需要mapM.使用正确的流媒体库.
如果您想了解更多信息pipes,我建议您阅读本教程.
| 归档时间: |
|
| 查看次数: |
1116 次 |
| 最近记录: |