STArray和堆栈溢出

时间:2013-01-23 02:10:19

标签: haskell stack-overflow starray

我很难理解为什么以下尝试在STArray中找到最小元素导致堆栈空间溢出时使用ghc编译(7.4.1,无论-O级别),但工作正常<{1}}中的

ghci

注意:切换到import Control.Monad import Control.Monad.ST import Control.Applicative import Data.Array.ST n = 1000 :: Int minElem = runST $ do arr <- newArray ((1,1),(n,n)) 0 :: ST s (STArray s (Int,Int) Int) let ixs = [(i,j) | i <- [1..n], j <- [1..n]] forM_ ixs $ \(i,j) -> writeArray arr (i,j) (i*j `mod` 7927) -- readArray arr (34,56) -- this works OK -- findMin1 arr -- stackoverflows when compiled findMin2 arr -- stackoverflows when compiled findMin1 arr = do es <- getElems arr return $ minimum es findMin2 arr = do e11 <- readArray arr (1,1) foldM (\m ij -> min m <$> readArray arr ij) e11 ixs where ixs = [(i,j) | i <- [1..n], j <- [1..n]] main = print minElem STUArray似乎没有任何效果。

主要问题是:在ST.Lazy内部对STArray进行此类“类似折叠”操作的正确方法是什么?

3 个答案:

答案 0 :(得分:5)

这可能是getElems一个坏主意的结果。在这种情况下,数组完全是一​​个坏主意:

minimum (zipWith (\x y -> (x, y, mod (x*y) 7927)) [1..1000] [1..1000])

这个给你答案:(1,1,1)。

如果您还想使用数组我建议先将数组转换为ArrayUArray,然后再使用elemsassocs。如果您使用runSTArrayrunSTUArray进行此操作,则无需额外费用。

答案 1 :(得分:3)

findMin1中的一个大问题是getElems

getElems :: (MArray a e m, Ix i) => a i e -> m [e]
getElems marr = do
  (_l, _u) <- getBounds marr      -- Hmm, why is that there?
  n <- getNumElements marr
  sequence [unsafeRead marr i | i <- [0 .. n - 1]]

在长列表中使用sequence(>>=)不够懒的monad中堆栈溢出的常见原因,因为

sequence ms = foldr k (return []) ms
        where
          k m m' = do { x <- m; xs <- m'; return (x:xs) }

然后它必须构建一个与列表长度成比例的大小。 getElems可以使用Control.Monad.ST.Lazy,但随后使用

填充数组
forM_ ixs $ \(i,j) -> writeArray arr (i,j) (i*j `mod` 7927)

创建一个溢出堆栈的巨大thunk。对于严格的ST变体,您需要将getElems替换为不使用sequence构建列表的内容,或者更好 - 在不创建元素列表的情况下计算最小值。对于惰性ST变体,您需要确保数组的填充不会构建一个巨大的thunk,例如通过强制writeArray调用的结果

let fill i j
      | i > n     = return ()
      | j > n     = fill (i+1) 1
      | otherwise = do
          () <- writeArray arr (i,j) $ (i*j `mod` 7927)
          fill i (j+1)
() <- fill 1 1

findMin2中的问题是

foldM (\m ij -> min m <$> readArray arr ij) e11 ixs

m中是懒惰的,所以它构建了一个巨大的thunk来计算最小值。您可以使用seq(或爆炸模式)轻松解决这个问题,以便在m中严格限制。

  

主要问题是:在STArray内部对ST进行此类“类似折叠”操作的正确方法是什么?

通常,您将使用严格的ST变体(对于Int等类型,您几乎肯定会使用STUArray而不是STArray s)。那么最重要的规则是你的功能足够严格。 findMin2的结构没问题,实现太懒了。

如果性能很重要,您通常必须避免使用foldM这样的通用高阶函数,并编写自己的循环以避免分配列表​​并严格控制严格性,因为手头的问题需要。

答案 2 :(得分:0)

问题是最小值是非严格折叠,因此导致thunk积累。使用(foldl'min)。

现在我们添加一堆词汇来忽略,因为stackoverflow变得毫无价值,不再允许发布直截了当的答案。