2015-09-21 12 views
9

Come posso piegare una lista pigra usando un'azione monadica nello spazio costante? Il problema che sto cercando di risolvere è l'aggregazione di un file di grandi dimensioni, e credo che per motivi di prestazioni richieda mutabilità. Ho un'implementazione che funziona in ST usando vettori mutabili, ma usa troppa memoria. Di seguito è riportato un esempio di ciò che sto tentando. Ho anche sperimentato brevemente con Conduit, ma questo non sembrava fornire alcun miglioramento.Monadic Fold in Constant Space

ST forM_:

import Control.Monad (forM_) 
import Control.Monad.ST.Trans as STT 
import Control.Monad.Identity as Identity 

testST :: Int 
testST = do 
    Identity.runIdentity $ STT.runST $ do 
    a <- STT.newSTRef 0 
    forM_ [1..10000000] (\x -> do 
     a' <- STT.readSTRef a 
     STT.writeSTRef a (a' + x) 
    ) 
    STT.readSTRef a 

condotto:

import Data.Conduit (($=),(=$),($$)) 
import qualified Data.Conduit as C 
import qualified Data.Conduit.List as CL 

testCL :: IO Int 
testCL = CL.sourceList [1..10000000] $$ CL.foldM (\a x -> return (a + x)) 0 
+0

Per l'ottimizzazione delle prestazioni: sembra che "STT s Identity' abbia un certo overhead di allocazione rispetto al solito' ST s'; se non hai bisogno dei peculiari poteri 'STT', potresti voler usare' ST'. – dfeuer

+0

@dfeuer Anzi, posso rimuoverlo, ma l'ho messo nell'implementazione inizialmente aspettandomi di dover inserire 'Either' e l'ho trasferito. Grazie per il consiglio! – ryachza

risposta

15

Il problema non è con la piega, ma con il corpo piega. Questo programma alloca un sacco:

testST = runST $ do 
    ref <- newSTRef 0 
    forM_ [1..10000000] $ \x -> do 
     val <- readSTRef ref 
     writeSTRef ref (val + x) 
    readSTRef ref 

questo programma, la cui unica differenza è sulla linea writeSTRef, alloca quasi nulla:

testST = runST $ do 
    ref <- newSTRef 0 
    forM_ [1..10000000] $ \x -> do 
     val <- readSTRef ref 
     writeSTRef ref $! val + x 
    readSTRef ref 

La differenza tra i due pezzi di codice è un buon suggerimento per ciò che è continua: nel primo caso, si sta creando un riferimento a un thunk profondamente annidato con 10000000 strati di applicazioni di +; mentre quest'ultimo appiattisce il thunk ad ogni passo.

A proposito, questo errore comune è explicitly called out in the documentation for modifySTRef.

+0

Grazie a questo ha funzionato perfettamente. Ho provato diverse annotazioni di rigore e diversi metodi di piegatura, ma il mio obiettivo era sempre sugli argomenti non sull'applicazione. Il pensiero che fosse il valore di scrittura non mi è mai passato per la testa. – ryachza

+0

@ryachza In effetti, si tratta di un errore sottile, uno che è stato bruciato nel mio cervello dalla disperazione di una sessione di debug di last-night, last-minute ... –