【问题标题】: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)

    获取一个表达式主体并生成一个延迟对象,该对象仅在第一次被强制时调用主体(使用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)
    

    【讨论】:

    • 天哪,为什么我在查找时没有想到这个词?
    【解决方案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”。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-09-06
      • 2020-01-01
      • 1970-01-01
      相关资源
      最近更新 更多