這裏的問題很微妙,可能很難在沒有先了解一些宏的情況下進行研究。
宏以與函數操作值相同的方式操作語法。事實上,宏只是帶鉤子的函數,它會在編譯時對它們進行評估。它們傳遞的是您在源代碼中看到的數據文字,並且自上而下進行評估。讓我們做一個函數和宏具有相同的身體,所以你可以看到其中的差別:
(defmacro print-args-m [& args]
(print "Your args:")
(prn args))
(defn print-args-f [& args]
(print "Your args:")
(prn args))
(print-args-m (+ 1 2) (str "hello" " sir!"))
; Your args: ((+ 1 2) (str "hello" " sir!"))
(print-args-f (+ 1 2) (str "hello" " sir!"))
; Your args: (3 "hello sir!")
宏是由它們的返回值替換。你可以用macroexpand
(defmacro defmap [sym & args]
`(def ~sym (hash-map [email protected]))) ; I won't explain these crazy symbols here.
; There are plenty of good tutorials around
(macroexpand
'(defmap people
"Steve" {:age 53, :gender :male}
"Agnes" {:age 7, :gender :female}))
; (def people
; (clojure.core/hash-map
; "Steve" {:age 53, :gender :male}
; "Agnes" {:age 7, :gender :female}))
此時檢查這個過程中,我也許應該解釋'
導致以下表格是quote
d。這意味着編譯器將讀取表單,但不執行它或嘗試解析符號等等。即'conj
評估爲符號,而conj
評估爲函數。 (eval 'conj)
相當於(eval (quote conj))
相當於conj
。
考慮到這一點,知道你不能將一個符號解析爲一個名稱空間,直到它以奇蹟般的方式被導入到你的名字空間中。這是require
函數的作用。它使用符號並查找它們對應的名稱空間,使其在當前名稱空間中可用。
讓我們來看看有什麼ns
宏展開:
(macroexpand
'(ns sample.core
(:require clojure.set clojure.string)))
; (do
; (clojure.core/in-ns 'sample.core)
; (clojure.core/with-loading-context
; (clojure.core/refer 'clojure.core)
; (clojure.core/require 'clojure.set 'clojure.string)))
看看它是如何引用符號clojure.set
和clojure.string
我們呢?多麼方便!但是,當您使用require
而不是:require
時,該協議是什麼?
(macroexpand
'(ns sample.core
(require clojure.set clojure.string)))
; (do
; (clojure.core/in-ns 'sample.core)
; (clojure.core/with-loading-context
; (clojure.core/refer 'clojure.core)
; (clojure.core/require 'clojure.set 'clojure.string)))
看來,誰寫的ns
宏是不夠好,讓我們做左右逢源,因爲這種結果是完全和以前一樣。 NEATO!
編輯:tvachon是正確的大約只有使用:require
,因爲它是唯一正式支持的形式
但是,什麼是用方括號括交易?
(macroexpand
'(ns sample.core
(:require [clojure.set]
[clojure.string])))
; (do
; (clojure.core/in-ns 'sample.core)
; (clojure.core/with-loading-context
; (clojure.core/refer 'clojure.core)
; (clojure.core/require '[clojure.set] '[clojure.string])))
結果,他們得到引述過,就像如果我們寫獨立的呼叫require
我們應該這樣做。
它也證明ns
並不關心我們是否給它列表(parens)或向量(括號)來處理。它只是將觀點看作是一系列事物。例如,這個工程:
(ns sample.core
[:gen-class]
[:require [clojure.set]
[clojure.string]])
require
,如在評論amalloy指出,有載體,並列出不同的語義,所以不要混用這些了!
最後,爲什麼以下不工作?
(ns sample.core
(:require 'clojure.string 'clojure.test))
好吧,既然ns
確實我們的報價對我們來說,這些符號得到引述兩次,這是被引述只有一次語義不同,也是純粹的瘋狂。
conj ; => #<core$conj [email protected]>
'conj ; => conj
''conj ; => (quote conj)
'''conj ; => (quote (quote conj))
我希望這可以幫助,我絕對推薦學習如何編寫宏。他們超級好玩。
可能的重複[爲什麼要求在ns形式表現不同於require函數](http://stackoverflow.com/questions/3719929/why-does-require-in-the-ns-form-behave-與需求函數不同) – 2013-04-09 14:51:29
嗯,並沒有真正詢問[]和repl和clj代碼之間的區別。 – 2013-04-09 15:07:00