2013-02-25 3 views
2

다음의 간단한 하스켈 프로그램은 파일의 항목 목록을 세는 것을 목적으로합니다. foldl'을 사용하는 버전은 정상적으로 작동하지만 ST Monad을 사용하는 버전은 스택 공간 오버플로 메시지를 제공합니다. 분명히 여기에 어떤 종류의 공간 누출이 있지만, 나는 그것을 해결할 수 없었습니다. 매우 흥미로운 부분은 ST monad이 현재 위치에서 업데이트를 수행하고 있어야하며 리소스가 이처럼 커지지 ​​않도록해야한다는 것입니다.하지만 주 메모리와 스택 공간에만 관련 될 수 있습니다. 누군가 여기서 무슨 일이 일어 났는지 설명 할 수 있을까요?ST 모나드의 스택 공간 오버 플로우

import Control.Monad 
import Data.List 
import Control.Monad.ST 
import Data.STRef 

--count items using foldl' 
countFold :: Num a => [b] -> a 
countFold = foldl' (\a _ -> a+1) 0 

-- count items using the ST monad 
-- derived fromt the sumST example on http://www.haskell.org/haskellwiki/Monad/ST 
-- only using +1 instead of adding the values 
countST :: Num a => [b] -> a 
countST xs = runST $ do 

    n <- newSTRef 0 

    forM_ xs (\_ -> modifySTRef n (+1)) 

    readSTRef n 



main = do 

    mydata <- readFile "data_files/values_1000000.num" 
    let trainingdata = lines mydata 

    -- this works just fine 
    --(putStrLn (show (countFold trainingdata))) 

    -- This fails with the message: 
    -- Stack space overflow: current size 8388608 bytes. 
    -- Use `+RTS -Ksize -RTS' to increase it. 
    (putStrLn (show (countST trainingdata))) 

UPDATE : 답변 및 의견 감사합니다. 나는 여기서 무슨 일이 일어 났는지 생각해. modifySTRef '는 버전 4.6에서 새로 추가 된 기능으로 문제를 능숙하게 해결하고 누군가 언급 한 설명을 포함합니다. 나는 우분투에서 표준으로 보이는 Data.STRef 버전 4.5를 사용하고 있으며 설명이나 modifySTRef가 포함되어 있지 않습니다. '

4.6 패키지 버전 함수를 찾고, 그 차이는 ('및 X에 저장된)는 함수 f 엄격히 적용 보장하는 서열을 사용하는 것이다 :

modifySTRef :: STRef s a -> (a -> a) -> ST s() 
modifySTRef ref f = writeSTRef ref . f =<< readSTRef ref 

modifySTRef' :: STRef s a -> (a -> a) -> ST s() 
modifySTRef' ref f = do 
    x <- readSTRef ref 
    let x' = f x 
    x' `seq` writeSTRef ref x' 

그래서 다른 방법으로 해결 내 자신의 프로그램 공간에서 함수의 코드를 새로운 이름으로 복사하고 누설 영역에 seq를 적용하는 것이 었습니다. 이것은 앞으로 사용하게 될 훌륭한 범용 트릭입니다. 이 문제를 해결할 수 있도록 도와 주신 모든 분들께 감사드립니다.

+8

[docs] (http://hackage.haskell.org/packages/archive/base/latest/doc/html/Data-STRef.html)를 읽어야합니다. "modifySTRef가 적용되지 않는다는 경고를 받으십시오. 이것은 프로그램이'modifySTRef'를 여러 번 호출하지만 그 값을 거의 사용하지 않는다면 썽크가 메모리에 쌓여 공간 누출이 발생한다는 것을 의미합니다. 이것은 'STREF'를 카운터로 사용할 때 흔히 저지르는 실수입니다. " 실수를 설명하기 위해'countST' 함수의 코드를 거의 닮은 예제도 있습니다. 짧은 이야기 :'modifySTRef '사용 –

답변

8

이것은 a classic space leak입니다.

modifySTRef은 함수 인수의 적용 결과를 강제로 상태로 강제하지 않습니다. 사실, 엄격함을 보장하기 위해 인수 함수를 작성할 수있는 방법이 없습니다.

modifySTRef'을 사용하십시오.

관련 문제