2015-08-28 9 views
6

Desidero scrivere un risolutore di gradienti coniugati in Haskell e voglio utilizzare liste pigre per disaccoppiare la regola di arresto e l'output di informazioni dalle iterazioni. Il mio codice è essenzialmente simile a questo:Garbage collection di un elenco durante l'esecuzione di un'azione IO su di esso

data CGState = CGState { cgx :: Image 
         , cgp :: Image 
         , cgr :: Image 
         , cgr2 :: Double 
         } 

cg :: Operator -> Image -> [CGState] 
cg = [...] 

runCG :: (CGState -> Bool) -> Operator -> Image -> IO Image 
runCG stoprule op rhs = do 
    let steps = takeWhile (not . stoprule) $ cg op rhs 
    fmap last $ forM (zip [(1::Int)..] steps) $ \(n, cgs) -> do 
     putStrLn $ show n ++ " " ++ show (sqrt $ cgr2 cgs) 
     return $ cgx cgs 

L'idea è quella di iterare l'elenco, uscita qualche informazione, ma conservano solo l'ultima iterazione. Tuttavia, quando si esegue questo codice, non sembra raccogliere i precedenti iterati. La mia ipotesi è che questo è collegato a IO: se riscrivo il codice come

runCG :: (CGState -> Bool) -> Operator -> Image -> IO Image 
runCG stoprule op rhs = do 
    let steps = takeWhile (not . stoprule) $ cg op rhs 
    return $ cgx $ last steps 

il problema non si verifica, vale a dire tutto, ma l'iterata finale ottiene rifiuti raccolti direttamente.

Come posso ottenere lo stesso effetto pur essendo in grado di generare alcune informazioni sulle iterazioni?

risposta

6

destro, penso che il problema è con fmap in IO: perché IO azioni vengono sempre eseguite in stretta sequenza, la fmap applica solo la lastdopo l'intera lista dei risultati da forM è stato costruito.

Invece probabilmente si può utilizzare Control.Monad.foldM, che si piega monadico su una lista (non testata):

runCG stoprule op rhs = do 
    let steps = takeWhile (not . stoprule) $ cg op rhs 
    foldM (\ _ (n, cgs) -> do 
     putStrLn $ show n ++ " " ++ show (sqrt $ cgr2 cgs) 
     return $ cgx cgs) 
    undefined -- initial value for fold is ignored as long as steps is nonempty 
    (zip [(1::Int)..] steps)