2011-08-26 10 views
6

tengo la siguiente función en Haskell:¿Hay alguna forma de memorizar un valor en Haskell?

memdb = -- load the contents of a database into memory as a Map 

Y luego tengo la siguiente línea:

map (\x -> memdb ! x) values 

me gustaría MemDB para generar el mapa sólo una vez, en lugar de en cada iteración de map . Podría hacerlo usando algo como esto:

make_memdb = -- equivalent to memdb in previous example 
memdb <- make_memdb 
map (\x -> memdb ! x) values 

Pero esto significaría que tendría que pasar en memdb a todas las funciones que lo utiliza. ¿Hay alguna manera que pueda:

a. evitar volver a calcular memdb cada vez que se llama O

b. Guarde el valor producido en make_memdb como constante, así que puedo evitar pasarlo a cada función que lo use.

+0

posible duplicado de [¿Cómo se crea una función memoize genérico Haskell] (http://stackoverflow.com/questions/141650/how-do-you-make-a-generic-memoize-function-in-haskell) –

+0

@Craig Stuntz:? había leído a través de esa cuestión antes de publicar el mío ¿Cómo me ayudarían esas respuestas a lograr a. o b. ? –

+0

@Craig: A pesar del título, esta cuestión no es realmente acerca de memoization en el sentido habitual del término. No es un duplicado de esa pregunta. – hammar

Respuesta

13

Como su mapa proviene de una base de datos, eso significa que no puede ser una constante, ya que puede ser diferente entre las corridas de su aplicación.

Pero esto significa que tendré que pasar el memdb a cada función que lo use.

Sí, pero hay herramientas para que esto no sea tan malo como parece. ¡En particular, esto suena como un caso de uso perfecto para el the reader monad!

Las mónadas Reader se usan comúnmente cuando tiene algún valor, como una configuración, que quiera cargar al inicio de su programa y luego poder acceder a él sin tener que pasarlo explícitamente todo el tiempo .He aquí un breve ejemplo de cómo se usa:

main = do 
    memdb <- make_memdb -- Get the memdb from the database once and for all 
    runReaderT foo memdb 

foo = do 
    memdb <- ask -- Grab the memdb. Will not reload from the database 
    liftIO $ putStrLn "Hello, world" -- IO actions have to be lifted 
    -- [...] 

Ver también:

+0

¡Muchas gracias! –

+0

Es casi como si esta pregunta se hizo * * evangelizar la mónada lector. Buena respuesta. –

1

Parece que desea obtener el memdb a través de IO como una forma de evitar pasar más parámetros, ¿correcto? Luego puede preguntar si puede (A) definir memdb, lo que implica que será una función de nivel superior, sin la sobrecarga de cargar datos desde el DB o (B) si puede guardar la estructura de datos cargada con alcance global.

Ambos son factibles con IORef y unsafePerformIO para definir una variable global variable de alto nivel. NO TE SUGERGO QUE HAGAS ESTO. Es torpe y molesto refactorizar. Dicho esto, te voy a mostrar cómo todos modos:

Asumiendo que tiene una función:

make_memdb :: IO (Map K V) 

Se puede declarar una variable mutable de nivel superior:

import Data.Map as M 
import Data.IORef 

mRef :: IORef (Map K V) 
mRef = unsafePerformIO $ newIORef M.empty 
{-# NOINLINE mRef #-} 

main = do 
    m <- make_memdb 
    writeIORef mRef m 
    ... do stuff using mRef ... 

stuffUsingMRef ... = do 
    memdb <- readIORef 
    let vs = map (memdb !) values 
    return vs 

en cuenta que sus funciones se vivir para siempre en IO. Esto se debe a que necesita IO para leer la variable mutable global en la que colocó memdb. Si no te gusta esto, y no te gusta pasar parámetros, ¡aprende la mónada de estado! Estoy seguro de que otra respuesta discutirá eso, que es la solución correcta.

+0

Podría darme un ejemplo de cómo la mónada Estado ayudaría? Ver respuesta –

+0

de martillo usando la mónada Reader, que es lo mismo que lo que yo le he mostrado (usando la mónada Estado sin ninguna modificación - por lo que debe haber pensado en 'Reader' también). –

Cuestiones relacionadas