Clojure中是否存在惰性变量?

Ste*_*gle 13 variables clojure lazy-evaluation

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

(def v (lazy-var (fn [] (do (println "REALLY EXPENSIVE FUNCTION") true))))
Run Code Online (Sandbox Code Playgroud)

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

Jon*_*ier 29

delay 对于这个应用程序将是完美的:

delay- (delay & body)

获取一个表达式体并生成一个Delay对象,该对象仅在第一次强制(使用force或deref/ @)时调用正文,并将缓存结果并在所有后续force调用中返回它.

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

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

(select @db ...) ; "DB stuff" printed, x returned
(insert @db ...) ; x returned (cached)
Run Code Online (Sandbox Code Playgroud)


Grz*_*ywo 6

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

(记忆f)

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

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

(def v (memoize (fn [] (do (println "REALLY EXPENSIVE FUNCTION") true))))
(v)
=>REALLY EXPENSIVE FUNCTION
=>true
(v)
=>true
Run Code Online (Sandbox Code Playgroud)

(延迟expr)也做了另一个答案解释的工作.关于解除引用延迟的额外注释 - force和deref/@之间的差异是如果在非延迟变量上使用force,则不会抛出异常,而deref/@可能抛出ClassCastException"无法转换为clojure.lang.IDeref".