使用mapM/sequence被认为是好的做法吗?

jon*_*dee 16 haskell

考虑以下示例:

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就此而言)的良好做法?
如果是这样,尽管存在堆栈空间溢出的危险,为什么它被认为是一种好的做法?
如果不是您建议使用哪种替代方案?

scl*_*clv 9

正如Niklas B.所说,语义mapM是有效的右折叠,并且它在更多情况下比翻转版本成功终止.一般来说,mapM更有意义,因为我们很少想要在庞大的数据列表上做一个结果映射.更常见的是,我们要评估这样的列表的效果,而且在这种情况下mapM_sequence_,其扔掉的结果,通常建议是什么.

编辑:换句话说,尽管在问题中提出了问题,是的,mapM并且sequence是常用的,通常被认为是良好的做法.

  • 有不同的语义和不同的权衡.所以你需要选择一个适合你情况的那个.这就是答案!唯一的答案!您提供的其他代码也将在足够大的列表上失败 - 它只会占用更大的列表.你也可以只为程序添加堆栈空间.只是,你知道,了解权衡,然后做出选择. (2认同)

Gab*_*lez 7

如果是这样,尽管存在堆栈空间溢出的危险,为什么它被认为是一种好的做法?如果不是您建议使用哪种替代方案?

如果要在生成列表元素时处理它们,请使用pipesconduit.两者都不会建立一个中间列表.

我会说明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)

现在我可以连接ProducerConsumer使用(>->),并使用runProxy以下命令运行结果:

>>> runProxy $ infiniteInts >-> printer
4<Enter>
4
7<Enter>
7
...
Run Code Online (Sandbox Code Playgroud)

然后,它将Int从用户读取s并在生成它们时将它们回送到控制台,而不会在内存中保存多个元素.

因此,通常如果您想要一个有效的计算来生成元素流并立即使用它们,那么您通常不需要mapM.使用正确的流媒体库.

如果您想了解更多信息pipes,我建议您阅读本教程.