Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/haskell/10.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Haskell 使用mapM/序列是否被视为良好实践?_Haskell - Fatal编程技术网

Haskell 使用mapM/序列是否被视为良好实践?

Haskell 使用mapM/序列是否被视为良好实践?,haskell,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 o

考虑以下示例:

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
safeMapM f xs=safeMapM'xs[]
其中safeMapM'[]acc=返回$reverse acc

safeMapM'(x:xs)acc=do y正如Niklas B所说,
mapM
的语义是有效的右折叠语义,并且它在比翻转版本更多的情况下成功终止。一般来说,
mapM
更有意义,因为我们很少希望在大量数据列表上生成结果映射。更常见的情况是,我们需要评估这样一个列表的效果,在这种情况下,通常推荐使用丢弃结果的
mapM\uu
sequence\u

编辑:换句话说,尽管问题中提出了问题,yes
mapM
sequence
通常被认为是良好的实践

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

如果要在生成列表元素时对其进行处理,请使用
管道
导管
。两者都不会建立一个中间列表

我将显示
管道
方式,因为这是我的库。首先,我将从用户输入在
IO
monad中生成的无限数字列表开始:

import Control.Proxy

infiniteInts :: (Proxy p) => () -> Producer p Int IO r
infiniteInts () = runIdentityP $ forever $ do
    n <- lift readLn
    respond n
然后,它将从用户处读取
Int
s,并在生成时将其回显到控制台,而不会在内存中保存超过一个元素

因此,通常,如果您想要生成元素流并立即使用它们的有效计算,您不需要
mapM
。使用适当的流媒体库


如果您想了解有关管道的更多信息,那么我建议。

如果您想留在惰性阵营,该软件包允许您惰性地处理输入列表。而不是

mapM f
你写

import qualified System.IO.Lazy as LazyIO

LazyIO.run . mapM (LazyIO.interleave . f)

不再有堆栈溢出。

如果不溢出,可能
mapM
会更快,因为您不必
反转
?你测量过了吗?你能发布你用来测试的
Main
模块吗?还有一些Monad(比如
Control.Monad.State.Lazy
),比如
take 100 mapM id[1..]
终止
take 100 safeMapM id[1..]
不可能终止,无论monad@jberryman
main=mapmreturn[1..10000000]>>return()
@NiklasB。我并不建议将safeMapM作为更好的解决方案。事实上,你说明了一个理由。但是,虽然mapM似乎很常见,但它有一个潜在的堆栈空间溢出的缺点。我的问题是为什么或者何时(不)应该使用mapM?我希望我的代码在所有情况下都能运行,而不仅仅是在“更多情况下”。很多效果都与返回值一起出现,例如用户输入。Niklas B.我在开发时可以捕捉到的示例是,部署后可能会出现带有大列表的堆栈空间溢出。更难找到imhot的是不同的语义和不同的权衡。所以你需要选择一个适合你的情况。这就是答案!唯一的答案!您提供的其他代码在足够大的列表上也会失败——它只会占用较大的列表。您也可以只向程序添加堆栈空间。只是,你知道,理解折衷,然后做出选择。这个答案有点过时了。您可能需要更新它。
mapM f
import qualified System.IO.Lazy as LazyIO

LazyIO.run . mapM (LazyIO.interleave . f)