2012-06-14 8 views
11

Tengo un poco de computación que es algo costosa (comenzar una base de datos), y solo quiero crear la base de datos si realmente voy a usarla. Estoy buscando una variable de referencia (o simplemente una variable simple, si es posible) que solo evaluaría su valor en caso de que se use (o se elimine la referencia). Algo conceptualmente como el siguiente.¿Existen variables diferidas en Clojure?

(def v (lazy-var (fn [] (do (println "REALLY EXPENSIVE FUNCTION") true)))) 

y en el futuro, cuando sea sólo tiene que utilizar var v, o llame @v, que luego conseguirlo para imprimir "FUNCIÓN muy caro", y desde el mismo v tiene un valor de verdad. Lo importante aquí es que el fn no se evaluó hasta que la variable fue (de) referenciada. Cuando sea necesario, la función se evalúa una sola vez para calcular el valor de la variable. ¿Es esto posible en Clojure?

Respuesta

25

delay sería perfecto para esta aplicación:

delay- (delay & body)

Toma un conjunto de expresiones y produce un objeto de retardo que invocará el cuerpo sólo la primera vez que se ve obligado (con force o deref/@), y guardará en caché el resultado y lo devolverá en todas las llamadas posteriores force.

Coloque el código para construir el identificador de la base de datos dentro del cuerpo de una invocación delay, almacenada como Var. A continuación, elimine la referencia de este Var cuando necesite usar el identificador de base de datos: en la primera desreferencia se ejecutará el cuerpo y, en las desreferencias posteriores, se devolverá el identificador de memoria caché.

(def db (delay (println "DB stuff") x)) 

(select @db ...) ; "DB stuff" printed, x returned 
(insert @db ...) ; x returned (cached) 
+1

caray, ¿por qué no pensé en esa palabra al tratar de buscar esto? –

6

Clojure 1.3 función introducido memoize para este propósito:

(memoize f)

Devuelve una versión memoized de una función referencialmente transparente. La versión memorada de la función mantiene un caché de la asignación de argumentos a los resultados y, cuando las llamadas con los mismos argumentos son se repiten a menudo, tiene un mayor rendimiento a expensas de una mayor memoria uso.

En su ejemplo reemplazar inexistente perezoso-var con memoize:

(def v (memoize (fn [] (do (println "REALLY EXPENSIVE FUNCTION") true)))) 
(v) 
=>REALLY EXPENSIVE FUNCTION 
=>true 
(v) 
=>true 

(expr retraso) también hace el trabajo como otra respuesta explica. Un comentario adicional sobre la desreferenciación del retraso: la diferencia entre force y deref/@ es que force no arroja excepción si se usa en variables sin retardo, mientras que deref/@ puede arrojar ClassCastException "no se puede convertir a clojure.lang.IDeref".

Cuestiones relacionadas