【问题标题】:Do lazy variables exist in Clojure?Clojure 中是否存在惰性变量?
【发布时间】:2012-06-17 02:05:16
【问题描述】:
我有一些有点昂贵的计算(启动数据库),我只想在我真的要使用它时创建数据库。我正在寻找一个参考变量(或者只是一个普通变量,如果可能的话),它只会在它被使用(或取消引用)的情况下评估它的值。概念上类似于以下内容。
(def v (lazy-var (fn [] (do (println "REALLY EXPENSIVE FUNCTION") true))))
以后,当我要么只使用 var v 或调用 @v 时,我就会让它打印出“真正昂贵的功能”,从那时起 v 的值为 true。这里重要的是 fn 直到变量被(取消)引用时才被评估。需要时,对函数进行一次且仅计算一次以计算变量的值。这在clojure中可能吗?
【问题讨论】:
标签:
clojure
lazy-evaluation
variables
【解决方案1】:
delay 非常适合此应用程序:
delay- (delay & body)
获取一个表达式主体并生成一个延迟对象,该对象仅在第一次被强制时调用主体(使用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)
【解决方案2】:
Clojure 1.3 为此引入了 memoize 函数:
(记住 f)
返回引用透明函数的记忆版本。
该函数的记忆版本保留了映射的缓存
结果的参数,以及当调用具有相同参数时
经常重复,以更高的内存为代价获得更高的性能
使用。
在您的示例中,将不存在的惰性变量替换为 memoize:
(def v (memoize (fn [] (do (println "REALLY EXPENSIVE FUNCTION") true))))
(v)
=>REALLY EXPENSIVE FUNCTION
=>true
(v)
=>true
(delay expr) 正如另一个答案所解释的那样,它也可以完成这项工作。关于取消引用延迟的额外评论 - force 和 deref/@ 之间的区别在于,如果在非延迟变量上使用 force 不会抛出异常,而 deref/@ 可能会抛出 ClassCastException“无法转换为 clojure.lang.IDeref”。