Clojure

變數和全域環境

Clojure 是一種實用的語言,它承認偶爾需要維護對變動值的持續參考,並提供 4 種不同的機制以受控的方式執行此操作 - Vars、RefsAgentsAtoms。Vars 提供一種機制來參考可變儲存位置,該位置可以在每個執行緒的基礎上動態重新繫結(到新的儲存位置)。每個 Var 都可以(但不必)具有根繫結,這是一個由所有沒有每個執行緒繫結的執行緒共用的繫結。因此,Var 的值是其每個執行緒繫結的值,或者,如果它未繫結在請求值的執行緒中,則為根繫結的值(如果有的話)。

特殊形式 def 會建立(並 實習)一個 Var。如果 Var 尚未存在且未提供初始值,則 var 未繫結

user=> (def x)
#'user/x
user=> x
#object[clojure.lang.Var$Unbound 0x14008db3 "Unbound: #'user/x"]

提供初始值會繫結根(即使它已經繫結)。

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

user=> x
1

Vars 預設為靜態,但 Vars 可以標記為動態,以允許透過巨集 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 建立的繫結可以被指定,這提供了一種讓巢狀內容在放置在呼叫堆疊之前與程式碼通訊的方法。此功能僅透過設定元資料標籤:將 ^:dynamic 設定為 true(如上方的程式碼區塊所示)來選擇加入。在某些情況下,您可能希望在內容中重新定義靜態 Vars,而 Clojure(自 1.3 版以來)提供函式 with-redefswith-redefs-fn 來執行此類目的。

使用 defn 定義的函式會儲存在 Vars 中,允許在執行中的程式中重新定義函式。這也啟用了面向切面或面向內容程式設計的許多可能性。例如,您可以在某些呼叫內容或執行緒中僅使用記錄行為來包裝函式。

繫結傳送

有些 Clojure 並行函數(未來、代理)提供「繫結傳送」,允許將目前的動態繫結設定傳送至另一個執行緒,以便在相同的環境中非同步地繼續工作。此功能由 futuresendsend-offpmap 提供。

(def ^:dynamic *num* 1)
(binding [*num* 2] (future (println *num*)))
;; prints "2", not "1"

(set! var-symbol expr)

指定特殊形式。

當第一個運算元是符號時,它必須解析為一個全域變數。變數的目前執行緒繫結值會設定為 expr 的值。目前,嘗試使用 set! 設定變數的根繫結會產生錯誤,也就是說,變數指定是執行緒本地的。在所有情況下,都會傳回 expr 的值。

注意 - 您無法指定函數參數或本地繫結。在 Clojure 中,只有 Java 欄位、變數、參照和代理是可變的

Java 互操作 中記錄了將 set! 用於 Java 欄位。

實例化

命名空間系統維護符號至變數物件的全局對應表(請參閱 命名空間。如果 def 表達式在目前命名空間中找不到正在定義符號的實例化項目,它會建立一個項目,否則它會使用現有的變數。這個尋找或建立的程序稱為實例化。這表示,除非它們已解除對應,否則變數物件是穩定的參考,不需要每次都查詢。這也表示命名空間構成一個全局環境,正如在 評估 中所述,編譯器會嘗試將所有自由符號解析為變數。

可以使用 var 特殊形式或 #' 讀取巨集(請參閱 讀取器來取得實例化變數物件,而不是它的目前值。

未實例化的變數

可以使用 with-local-vars 建立未實例化的變數。在自由符號解析期間找不到這些變數,而且必須手動存取它們的值。但是,它們可以用作有用的執行緒本地可變儲存格。

變數元資料

建立變數 defdefndefmacro 等的表單使用一組標準的變數 元資料 來描述變數。其中一些表單使用明確的語法來接受儲存在元資料中的值,但通常您也可以在變數符號上提供該元資料作為一個映射。

常見的變數元資料金鑰(在變數定義中都是選用的)

  • :doc - 記錄變數的字串,通常由文件字串參數設定

  • :added - 記錄新增此變數的版本的字串

  • :private - 布林旗標,通常由 defn- 設定,由作者用來宣告此變數是實作細節的意圖。私人變數是全域可存取的,但不會在過濾到非私人變數的 ns-…​ 函式中被參照或列出。

  • :arglists - arglist 的集合,如果未提供,將自動產生,最常被用來記錄巨集語法

  • :macro - defmacro 自動新增的布林旗標(通常不直接使用)

  • :tag - 變數中值的類型識別碼(通常是類別)或變數中所含函式的傳回類型。請注意,變數元資料會被評估,因此變數上的類型提示(例如 ^long)會評估為 long 函式,而不是 long 基本類型提示。一般來說,建議在 defn 變數的 arglist 上使用類型提示。

  • :test - clojure.test 架構使用此金鑰將單元測試附加到變數(通常不直接使用)

  • :dynamic - 表示變數可以在執行緒內容中動態重新繫結(請參閱上方)。使用直接連結編譯時,動態變數不會直接連結。

  • :redef - 表示使用直接連結編譯時,不應直接連結變數(因此允許重新定義變數)

  • :static - 不再使用(原本變數預設為動態,現在預設為靜態)

  • :const - 表示變數為編譯時期常數,編譯器可以將值內聯到使用該值的程式碼中。注意:這很少需要,而且僅適用於編譯時期的常數(讀取,但未評估),例如數字、字串等(非類別、函式、參考類型等)。重新定義或動態繫結常數變數不會影響已編譯並載入執行時期中使用該變數的程式碼。

另請參閱 編譯器選項,以進一步瞭解編譯期間的直接連結和元資料消除。