Clojure中是否存在惰性变量?

时间:2012-06-14 03:38:49

标签: clojure lazy-evaluation variables

我有一些计算有些昂贵(启动数据库),我只想创建数据库,如果我真的要使用它。我正在寻找一个引用变量(或只是一个普通的变量,如果可能的话),它只会在它被使用(或解除引用)的情况下评估它的值。在概念上类似于以下内容。

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

并且在将来,当我或者只是使用var v,或者调用@v时,我得到它以打印出“真正昂贵的功能”,并且从那里v具有值true。这里重要的是fn直到变量被引用(de)才被评估。在需要时,该函数仅被评估一次以计算变量的值。在clojure中这可能吗?

2 个答案:

答案 0 :(得分:28)

delay非常适合这个应用程序:

  

<强> delay - (delay & body)

     

获取一个表达式体并产生一个Delay对象,该对象仅在第一次被强制时使用forcederef / @)来调用正文,并将缓存结果并在随后的force次调用中返回。

放置代码以在delay调用的主体内构造数据库句柄,存储为Var。然后在需要使用数据库句柄时取消引用此Var - 在第一次取消引用时将运行正文,并在随后的解引用中返回缓存的句柄。

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

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

答案 1 :(得分:6)

Clojure 1.3为此目的引入了memoize功能:

  

(memoize f)

     

返回referentially透明函数的memoized版本。   该函数的memoized版本保留了映射的缓存   结果的参数,以及具有相同参数的调用时   经常重复,以更高的内存为代价具有更高的性能   使用

在您的示例中,使用memoize替换不存在的lazy-var:

(def v (memoize (fn [] (do (println "REALLY EXPENSIVE FUNCTION") true))))
(v)
=>REALLY EXPENSIVE FUNCTION
=>true
(v)
=>true
另一个答案解释说,

(延迟expr)也可以完成工作。关于解除引用延迟的额外注释 - force和deref / @之间的差异是如果在非延迟变量上使用强制不会抛出异常,而deref / @可能抛出ClassCastException“无法强制转换为clojure.lang.IDeref”。 / p>