我正在读<<现实世界haskell>>第8章想看看SumFile.hs程序如何处理100万个数字:
main :: IO ()
main = do
contents <- getContents
print (sumFile contents)
where sumFile = sum . map read . words
当我通过以下方式向程序提供100万个整数时:
runhaskell SumFile.hs&lt; data.txt,程序给出了正确的结果。
然而,当我使用GHC编译它时:
ghc SumFile.hs
二进制文件出现“堆栈空间溢出”错误:
./SumFile < data.txt
Stack space overflow: current size 8388608 bytes.
Use `+RTS -Ksize -RTS' to increase it.
我有两个问题:
谢谢!
编辑:
好的原因是map,但这是一个使用lazy bytestring的修改版本:
import qualified Data.ByteString.Lazy as L
import qualified Data.ByteString.Lazy.Char8 as LCHAR
import Data.Monoid
import Data.List
main :: IO ()
main = do
contents <- L.getContents
case sumFile contents of
Nothing -> print "Invalid input"
Just s -> print $ getSum s
where sumFile = foldl' mappend (Just (Sum 0)) . map ((fmap Sum) . (fmap fst) . LCHAR.readInt) . (LCHAR.words)
结果是一样的:即使我没有使用sum,二进制版本也会占用堆栈空间。
答案 0 :(得分:1)
我与#haskell上的人讨论过,ByteString版本给出堆栈溢出错误的原因是嵌套的Just(Sum Num),其中内部部分未经过严格评估。
基本上,当我们使用两个Maybe(Just Num),比如Just(Sum 2)和Just(Sum 3)时,foldl'使用seq生成Just((Sum 2)mappend(Sum 3)),即。 seq严格评估最外面的构造函数(mappend two Just(Monoid)生成一个Just(Monoid))。在这种情况下,内部Monoid不会被严格评估,因此它们保持为mappend连接(Sum Num)。这导致100万mappend连接(Sum Num)包裹在Just。
所以#haskell上的Saizan给出了这个严格评估Maybe(Sum Num)内部部分的版本
import qualified Data.ByteString.Lazy as L
import qualified Data.ByteString.Lazy.Char8 as LCHAR
import Data.Monoid
import Data.List
forceMaybe Nothing = Nothing
forceMaybe (Just x) = x `seq` (Just x)
main :: IO ()
main = do
contents <- L.getContents
case sumFile contents of
Nothing -> print "Invalid input"
Just s -> print $ getSum s
where sumFile = foldl' (\ x y -> forceMaybe (x `mappend` y)) (Just (Sum 0)) . map ((fmap Sum) . (fmap fst) . LCHAR.readInt) . (LCHAR.words)
答案 1 :(得分:0)
我查看了本书的在线版本,在该程序下有一些讨论,它使用堆栈空间的原因是由于地图,用foldl替换地图'解决了问题。
答案 2 :(得分:0)
首先,简单说明:ghc运行时堆栈没有处理堆栈段,它是运行时的内部结构,这不是缓冲区溢出类型攻击的来源。
二。哈斯克尔很懒。 Lazy io(getContents)生成懒惰列表。总和懒得产生结果。但是,一旦请求求和的结果,它必须递归地挖掘列表,快速耗尽堆栈空间(如果希望你可以查看源代码)
为了避免它,你必须使用严格版本的和,它应该消除问题。标准库对这种情况有特殊功能,foldl' - foldl的严格版本。使用foldl' (+) 0
代替总和应该可以消除问题
第三。当使用惰性IO时,堆栈空间泄漏是非常常见的问题。如果切换到基于迭代的IO,则可以解决该问题。否则,应该学会在需要的地方添加严格注释。
阿。顺便说一下。 GHC正在优化编译器。这种情况并不常见,但仍有可能在编译程序中出现内存泄漏问题,并且不能使用ghci,反之亦然。