posts - 11, comments - 9, trackbacks - 0, articles - 0
            BlogJava :: 首頁 :: 新隨筆 :: 聯系 :: 聚合  :: 管理

          Clojure的變量和全局環境

          Posted on 2012-07-31 01:48 steven.cui 閱讀(1499) 評論(0)  編輯  收藏 所屬分類: clojure

          原文請參考,如有問題和歧義請指正,謝謝:)

          http://clojure.org/vars

          變量和全局環境

          Clojure是個很實用的語言,偶爾需要將維護和改變數據的值。她提供了4種不同的方式來操作變量:Vars, Refs, Agents, 和Atoms。Vars機制是是指向一個可改變的數據的位置,你可以為每個線程動態的綁定(制定一個新的存儲位置)一個新值。Vars可以初始化根綁定(不是必須的),綁定的值對于所有線程都是共享的,但卻別的線程就不能重新綁定。因此,要么Var可以為每個線程綁定值,要么使用根綁定。

          下面的special form def 創建了一個Var,如果Var不存在和沒有給初始化,var就是不綁定的(不允許創建非動態的Var,必須顯式指定根綁定):

          user=> (def x)
          #'user/x
          user=> x
          java.lang.IllegalStateException: Var user/x is unbound.

          為根值初始化(如果存在,就被再次綁定)

          user=> (def x 1)
          #'user/x
          user=> x
          1
           

          默認情況下(定義的時候初始化了根綁定),Vars是靜態的(static),但是,建立動態Var的定義可以通過元數據標記的方式,然后在線程用時通過binding來指定。

          user=> (def ^:dynamic x 1)

          user=> (def ^:dynamic y 1)

          user=> (+ x y)

          2

          user=> (binding [x 2 y 3]

                   (+ x y))

          5

          user=> (+ x y)

          2

           

          binding被創建后其他線程是是不可見的。創建的binding可以被賦值,也就是在沒有離開調用堆棧之前可以被上下文訪問。可以在一塊代碼之前設置matadata標簽:dynamic來指定:

          user=> (def ^:dynamic x 1)

          #'user/x

          user=> (meta #'x)

          {:ns #<Namespace user>, :name x, :dynamic true, :line 30, :file "NO_SOURCE_PATH"}

          user=> (binding [x 2] (println x))

          2

          nil

          user=> x

          1

          user=>

           

          如果你想讓函數編譯為static的,并且指定返回值,可以看下面的例子(速度提升不少,關鍵的調用函數可以采用這種方式加速):

           (defn fib [n]   (if (<= n 1)

              1

              (+ (fib (dec n)) (fib (- n 2)))))

          #'user/fib

           (defn ^:static fib2 ^long [^long n]

            (if (<= n 1)

              1

              (+ (fib2 (dec n)) (fib2 (- n 2)))))

          #'user/fib2

          user=> (time (fib 38))

          "Elapsed time: 1831.113 msecs"

          63245986

          user=> (time (fib2 38))

          "Elapsed time: 328.715 msecs"

          63245986

          user=> (meta (var fib))

          {:arglists ([n]), :ns #<Namespace user>, :name fib, :line 1, :file "NO_SOURCE_PATH"}

          user=> (meta (var fib2))

          {:arglists ([n]), :ns #<Namespace user>, :name fib2, :static true, :line 4, :file "NO_SOURCE_PATH"}

          user=>

           

          在上下文中可能需要重定義靜態變量,從Clojure1.3開始提供with-redefswith-redefs-fn這兩個宏來修改。

          定義函數的defn也是Vars的存儲方式,也可以在運行時被重定義。這也為aop編程帶來很多方便,例如:你可以封裝一個類似logging函數給調用的上下文或者或者線程。

           

          (set! var-symbol expr)

          將Vars指定為special form

          當地一個操作符為symbol的時候,它必須是全局變量。當前線程綁定的值就是后面的expr,也就是說必須是Thread-local的才可以,否則將會拋出一個使用set!來設定根綁定變量的錯誤。變量的表達式expr必須有返回值。

          注意,你不能賦值給一個函數的參數或者本地綁定,只能是java的字段Vars Refs和Agents,因為這些數據在Clojure里可不變的。

          使用set為java字段設置值,可以查看 Java Interop.

           

          Interning

          命名空間維護了每個Var對象的全局符號映射。如果使用def定義變量沒有在當前的命名空間找到該符號,就創建一個,否則使用現有的。創建或者尋找的過程被稱作interning。這就意味著,除非Var對象取消映射,否則Var對象每次被查詢,所以請在循環中千萬不要引用Var的全局變量,否則將非常慢,通過let或者binding讓全局變量取消映射來提高速度。命名空間在Evaluation中構建了全局環境,編譯器也把所有free symbols當做Vars來解析了。

          可以使用閱讀宏(Reader)#’來得到Var對象的內部的值。

           

          Non-interned的類型的變量

          可以通過with-local-vars來創建non-interned類型的變量,在free symbol解析的時候將不會被發現,這些值只能被手工的訪問,但是也可以用作當前線程的變量。

          user=> (defn factorial [x]

                   (with-local-vars [acc 1, cnt x]

                     (while (> @cnt 0)

                       (var-set acc (* @acc @cnt))

                       (var-set cnt (dec @cnt)))

                     @acc))

          #'user/factorial

          user=> (factorial 7)

          5040


          只有注冊用戶登錄后才能發表評論。


          網站導航:
           
          主站蜘蛛池模板: 连山| 印江| 西林县| 宜章县| 松滋市| 宁波市| 庄浪县| 上犹县| 龙川县| 巴南区| 沧源| 惠州市| 长顺县| 永平县| 白河县| 蒲城县| 河曲县| 东台市| 蛟河市| 阜新| 榆中县| 长宁县| 隆安县| 江阴市| 赤峰市| 丁青县| 永福县| 唐海县| 共和县| 兰考县| 萨迦县| 宜黄县| 岗巴县| 安乡县| 三江| 迁西县| 舒城县| 新兴县| 广州市| 凤冈县| 靖边县|