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

          Clojure的變量和全局環(huán)境

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

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

          http://clojure.org/vars

          變量和全局環(huán)境

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

          下面的special form def 創(chuàng)建了一個Var,如果Var不存在和沒有給初始化,var就是不綁定的(不允許創(chuàng)建非動態(tài)的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是靜態(tài)的(static),但是,建立動態(tài)Var的定義可以通過元數(shù)據(jù)標記的方式,然后在線程用時通過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被創(chuàng)建后其他線程是是不可見的。創(chuàng)建的binding可以被賦值,也就是在沒有離開調(diào)用堆棧之前可以被上下文訪問。可以在一塊代碼之前設置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=>

           

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

           (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=>

           

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

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

           

          (set! var-symbol expr)

          將Vars指定為special form

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

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

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

           

          Interning

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

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

           

          Non-interned的類型的變量

          可以通過with-local-vars來創(chuàng)建non-interned類型的變量,在free symbol解析的時候?qū)⒉粫话l(fā)現(xiàn),這些值只能被手工的訪問,但是也可以用作當前線程的變量。

          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


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


          網(wǎng)站導航:
           
          主站蜘蛛池模板: 梧州市| 洛扎县| 西乌| 香河县| 密山市| 昌邑市| 南郑县| 武汉市| 澄江县| 穆棱市| 蓬莱市| 新密市| 南漳县| 越西县| 蒙自县| 甘德县| 华亭县| 泾阳县| 白玉县| 商水县| 成武县| 霍邱县| 乐清市| 临汾市| 保山市| 奉化市| 和田市| 平顶山市| 抚顺县| 林芝县| 佳木斯市| 察哈| 舞钢市| 德令哈市| 湖州市| 晋江市| 宁德市| 即墨市| 定日县| 防城港市| 曲麻莱县|