2012-06-14 38 views
11

我的计算有点贵(启动数据库),我只想创建数据库,如果我真的要使用它。我正在寻找一个引用变量(或者只是一个普通变量,如果可能的话),它只会在使用(或取消引用)的情况下评估其值。概念上如下所示。Clojure中是否存在惰性变量?

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

,并在将来,当我要么只使用VAR V,或致电@v,然后我把它打印出“真贵FUNCTION”,并从上面V具有的真正价值。这里最重要的是fn在变量被(de)引用之前不被评估。当需要时,该函数被评估一次并且只计算一次以计算变量的值。这是可能的clojure?

回答

25

delay会为这个应用程序是完美的:

delay- (delay & body)

注意到表达式的身体,并产生一个延迟对象,将调用体仅在第一次是被迫(与forcederef/@),并将结果缓存并在所有后续force调用中返回。

将代码构建在delay调用的主体内的数据库句柄存储为Var。然后,当需要使用DB句柄时,取消引用此Var - 在第一次解引用时,主体将运行,并且在随后的解除引用中,缓存的句柄将被返回。用于此目的的

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

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

geez,为什么我没有想到这个词,当试图查看这个? –

6

的Clojure 1.3引入memoize的功能:

(memoize的F)

返回引用透明功能的memoized版本。 该函数的记忆版本保留从 参数到结果的映射的缓存,并且当具有相同参数的调用是 时经常重复,具有更高的性能,代价是使用更高的内存。

在你的榜样与memoize的替代不存在懒-VAR:

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

(延迟表达式)还做这项工作作为另一个答案解释。对解除引用延迟的额外评论 - force和deref/@之间的区别在于,如果在非延迟变量上使用force,则不会抛出异常,而deref/@可能会抛出ClassCastException“无法转换为clojure.lang.IDeref”。