2011-02-06 7 views
5

Tengo un procedimiento que (a) hace algunos IO, (b) construye una tabla de búsqueda y (c) devuelve una acción IO que usa la tabla de búsqueda. Pero cuando se compila con -O, GHC (versión 6.12.1) detalla la construcción de la tabla de búsqueda, de modo que se reevalúa para cada llamada de la acción IO.Evaluación repetida de la expresión pura en la acción IO

Ejemplo:

module Main where 
import Data.Array 
import Data.IORef 
import Control.Monad 

makeAction getX getY sumRef = do 
    x <- getX 
    let a = listArray (0, 1000) [x ..] 
    return $ do 
     y <- getY 
     modifyIORef sumRef (\sum -> sum + a ! y) 

main = do 
    sumRef <- newIORef 0 
    action <- makeAction getX getY sumRef 
    replicateM_ 100000 action 
    n <- readIORef sumRef 
    putStrLn (show n) 
    where 
    getX = return (1 :: Int) 
    getY = return 0 

¿Es este problema bien conocido lo suficiente como para tener una solución GHC-toda prueba estándar - o cómo habría que ajustar el programa para que a en repetidas ocasiones no está siendo asignado?

+0

¿Has probado el pragma '{- # NOINLINE # -}'? – fuz

+0

@FUZxxl Sí, '{- # NOINLINE a # -}' en el 'a' local no lo hace. – antonakos

+0

Si quiere una tabla de búsqueda y desea compartirla entre las acciones repetidas, seguramente debería separarla de la función @ makeAction @? Parece que makeAction hará una matriz cada vez independientemente de la alineación. Tal vez deberías estar usando un IOArray extraído de la función makeAction. –

Respuesta

4

La solución más sencilla es forzar la evaluación mediante el uso de anotaciones rigurosidad.

{-# LANGUAGE BangPatterns #-} 

A continuación, forzar la asignación simplemente haciendo a estricta el uso de un ! ("bang").

let !a = listArray (0, 1000) [x ..] 

Alternativamente, si se está trabajando en el IO mónada, anotaciones rigurosidad no siempre pueden ayudar. Para forzar la evaluación de una expresión antes de ejecutar alguna acción IO, puede usar evaluate. Por ejemplo:

let a = listArray (0, 1000) [x ..] 
    evaluate a 
2

intentar forzar a al calcular el valor monádico para volver:

makeAction getX getY sumRef = do 
    x <- getX 
    let a = listArray (0, 1000) [x ..] 
    return $ a `seq` do 
     y <- getY 
     modifyIORef sumRef (\sum -> sum + a ! y) 
Cuestiones relacionadas