【问题标题】:Idiomatic use case for set! in Clojure集合的惯用用例!在 Clojure 中
【发布时间】:2017-11-11 12:02:54
【问题描述】:

Clojure 中 set! 的惯用用例是什么?

找到用途并不容易!我搜索了一些更流行的 Clojure OSS 项目,但几乎没有。我能找到的唯一用途是设置像 warn-on-reflection 这样的全局变量,尽管目前还不清楚为什么这些不只是在本地使用 binding 设置。

有什么建议吗?

【问题讨论】:

    标签: clojure


    【解决方案1】:

    您已经回答了自己的问题。输入时:

    (def           foo 42)
    (def ^:dynamic bar 43)
    

    您正在创建一个全局变量。在大多数编程语言中,我们尽量减少使用全局变量,因为它们会引入更多复杂性和更难理解代码(这会导致更多错误!)。当我们确实需要全局变量时,我们会尽量减少这些全局变量的变异。这就是为什么在 Clojure 代码中很少看到 set!var-setvar-getalter-var-root 的原因。

    当 Clojure 代码确实需要改变全局状态时,binding 通常是首选,因为当当前线程离开 binding 表单的范围时,所讨论的 var 会返回到其先前的状态。另一个好处是其他线程不受当前线程所做的任何binding 突变的影响。

    有几种方法可以在 Clojure 中操作全局变量。与 动态 var 相比,正常 var 的答案略有不同。以下是一些示例:

    (ns tst.clj.core
      (:use clj.core
            tupelo.test)
      (:require
        [tupelo.core :as t] ))
    (t/refer-tupelo)
    
    (def            fred      0)
    (def ^:dynamic *barney*   0) ; normally use "earmuffs" for dynamic vars
    (def ^:dynamic  wilma     0) ; you aren't forced to use use them (but you should!)
    (def            betty    29)
    
    (dotest
      (throws?
        (binding [fred 1] ; can't bind non-dynamic var
          (is= fred 1)
          (throws? (set! fred 2))
          (is= fred 2)))
    
      (is= fred 0)
      (throws? (set! fred 2)) ;  can't set it
      (is= fred 0)
      (throws? (var-set #'fred 3)) ; java.lang.IllegalStateException: Can't change/establish root binding of: fred with set
      (is= fred 0)
      (is= (var-get #'fred) 0)
    
      (binding [*barney* 3]
        (is= *barney* 3)
        (set! *barney* 4) ; works fine
        (is= *barney* 4))
      (is= *barney* 3)  ; original value once leave binding scope
    
      (binding [wilma 6]
        (is= wilma 6)
        (set! wilma 7)  ; this works
        (is= wilma 7)
        (alter-var-root #'wilma inc)  ; this doesn't work!
        (is= wilma 7))
      (is= wilma 1)     ; the `inc` altered the root value, not the dynamic value in the `binding`
      (alter-var-root #'wilma #(+ 23 %)) ; can alter root value outside of binding
      (is= wilma 24)
    
      (is= betty 29)
      (alter-var-root #'betty inc) ; non-dynamic vars can have root value altered
      (is= betty 30)
    
    )
    

    【讨论】:

      【解决方案2】:

      set! 可以与线程本地binding 结合使用,以允许函数调用堆栈进行通信。这通常是一种在相互递归函数之间共享状态的便捷方式。

      Clojure 编译器是一个很好的例子——尽管它是用 Java 实现的——使用 dynamic Vars 来跟踪内部状态。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2012-03-02
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-06-28
        • 1970-01-01
        • 2014-01-21
        • 2010-12-08
        相关资源
        最近更新 更多