元数据
Clojure里面的元数据是附加到一个符号或者集合的一些数据,它们和符号或者集合的逻辑数据没有直接的关系。两个逻辑上一样的方法可以有不同的元数据。 下面是一个有关扑克牌的例子
(defstruct card-struct :rank :suit)
(def card1 (struct card-struct :king :club))
(def card2 (struct card-struct :king :club))
(println (== card1 card2)) ; same identity? -> false
(println (= card1 card2)) ; same value? -> true
(def card2 #^{:bent true} card2) ; adds metadata at read-time
(def card2 (with-meta card2 {:bent true})) ; adds metadata at run-time
(println (meta card1)) ; -> nil
(println (meta card2)) ; -> {:bent true}
(println (= card1 card2)) ; still same value despite metadata diff. -> true
一些元数据是Clojure内部定义的。比如 :private
它表示一个Var是否能被包外的函数访问。 :doc
是一个 Var 的文档字符串。 :test
元数据是一个Boolean值表示这个函数是否是一个测试函数。
:tag
是一个字符串类型的类名或者一个 Class
对象,表示一个Var在Java里面对应的类型,或者一个函数的返回值。这些被称为“类型提示” 。提供这些可以提高代码性能。如果你想查看你的clojure代码里面哪里使用反射来决定类型信息 -- 也就是说这里可能会有性能的问题, 那么你可以设置全局变量 *warn-on-reflection*
为 true
。
一些元数据会由Clojure的编译器自动地绑定到Var对象。 :file
是定义这个 Var的文件的名字。 :line
是定义这个Var的行数。 :name
是一个Var的名字的 Symbol
对象。 :ns
是一个 Namespace
对象描述这个Var所在的名字空间。 :macro
是一个标识符标识这个符号是不是一个宏。 :arglist
是一个装有一堆vector的一个list, 表示一个函数所接受的所有的参数列表(前面在介绍函数的时候说过一个函数可以接受多个参数列表)。
函数以及宏,都是有一个 Var
对象来表示的, 它们都有关联的元数据。比如输入这个在REPL里面: (meta (var reverse))
或者 ^#'reverse
。输出结果应该下面这些类似(为了好看我加了换行缩进)
{
:ns #<Namespace clojure.core>,
:name reverse,
:file "core.clj",
:line 630,
:arglists ([coll]),
:doc "Returns a seq of the items in coll in reverse order. Not lazy."
}
clojure.repl包里面的 source
函数, 利用元数据来获取一个指定函数的源代码,比如:
(source reverse)
上面代码的输出应该是:
(defn reverse
"Returns a seq of the items in coll in reverse order. Not lazy."
[coll]
(reduce conj nil coll))