这里的问题很微妙,可能很难在没有先了解一些宏的情况下进行研究。
宏以与函数操作值相同的方式操作语法。事实上,宏只是带钩子的函数,它会在编译时对它们进行评估。它们传递的是您在源代码中看到的数据文字,并且自上而下进行评估。让我们做一个函数和宏具有相同的身体,所以你可以看到其中的差别:
(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