列表中的API Cheatsheet部分似乎表明'()
是一个列表构造函数,就像(list)
一样,但我发现在实践中它们并不完全相同。例如,给定:
(def foo "a")
(def bar "b")
(def zip "c")
以下声明:
(apply str '(foo bar zip))
产生输出“foobarzip”,我不指望。
但是应该是等价的:
(apply str (list foo bar zip))
正如我所料,产生“abc”。
这里发生了什么?如果Clojure中的列表有“简写”(例如地图为{}
,向量为[]
,那么它是什么?
答案 0 :(得分:32)
在lisps中,'
(如quote
)引用其参数,即保留它们与s-exp形式完全一致,包括不评估其中的任何内容。
换句话说'(foo bar zip)
创建一个包含符号foo
,bar
,zip
的列表; (list foo bar zip)
创建一个包含foo
,bar
,zip
的值的列表。在第一种情况下,str
将符号本身转换为字符串,然后将它们连接起来。
作为对此的证明:
=> (def foo "a")
=> (type (first '(foo)))
clojure.lang.Symbol
=> (type (first (list foo)))
java.lang.String
答案 1 :(得分:10)
不同之处在于,正如您所看到的,文字语法'()
是引用的。这意味着不评估内部符号。要在评估元素时使用列表文字,可以使用syntax-quote
阅读器宏:
user=> (apply str `(~foo ~bar ~zip))
"abc"
答案 2 :(得分:4)
当你写:
(def foo "a")
(def bar "b")
(def zip "c")
您已定义了三个符号:foo
,bar
和zip
与值相关联:"a"
,"b"
和"c"
关联存储在namsepace表中,因此如果使用REPL,默认情况下命名空间为user
,因此用户命名空间表现在将包含:
{foo
#'user/foo
bar
#'user/bar,
zip
#'user/zip}
您可以通过执行以下操作来查看命名空间表:(ns-interns *ns*)
现在如果你在Clojure中写道:(foo bar zip)
,读者可以通过四种不同的方式阅读。您需要向读者指定应该以哪种方式阅读。那就是`
,'
和list
发挥作用的地方。
没有指标的情况:
(foo bar zip)
如果只是在没有任何指示符的情况下编写,读者会将其解释为S表达式,并将foo
解释为映射到函数的符号,并将bar
和zip
作为符号映射到要传递到foo
函数的值。
在我们的例子中,它会引发异常:
java.lang.ClassCastException: java.lang.String cannot be cast to clojure.lang.IFn
这是因为没有定义函数foo
,foo
与"a"
相关联,foo
是一个字符串,而不是一个IFn(一个Clojure函数)。
如果定义了函数foo
,它会调用"b"
作为参数"c"
和list
传入。
案例(list foo bar zip)
:
list
当使用列表符号时,读者实际上以与无指示符大小写相同的方式解释它。也就是说,它正在寻找映射到函数的符号foo
,该函数将映射到bar
,zip
和list
的关联值作为参数。 list
函数由clojure.core命名空间内的Clojure预定义;它返回一个它的参数列表。
因此,当读者查找foo
时,它会找到clojure.core函数,然后查找"a"
符号,并发现它映射到list
,依此类推。找到符号的所有映射后,它会调用foo bar zip
并向其传递"a" "b" "c"
的关联值,即(list foo bar zip)
。因此(list "a" "b" "c")
与'
相同。
案例'(foo bar zip)
:
'
foo
引用告诉读者,后面的表单应按原样读取。在我们的示例中,bar
,zip
和(foo bar zip)
是符号,(apply str '(foo bar zip))
是符号列表。因此读者会将此解释为符号列表。
这就是为什么当你运行str 'foo 'bar 'zip
时,它会打电话给foobarzip
,这会给你(eval '(foo bar zip))
。也就是说,它会将符号列表中的每个符号转换为String表示形式,然后将它们连接成一个String。
通过采用原样的形式,它将符号列表作为参数传递,而不评估符号,即不查找它们与之相关联的内容。如果您运行eval
,则会将符号列表传递给eval
,'
会将符号计算为值,并返回符号映射到的值列表。因此,您可以将引用`
视为将代码作为代码传递。
案例`(foo bar zip)
:
`
这个有点复杂。读者将看到反引号`(foo bar zip)
并将递归地解析符号列表中的符号,以获得完全限定符号的列表。
基本上,当读者在符号表中查找符号时,它会从当前命名空间的符号表中查找符号。完全限定符号是包含命名空间信息的符号。因此,当运行(user.foo user.bar user.zip)
时,读者会将这些符号替换为完全符合条件的符号,并将其转换为~
。
可以告诉读者评估列表中的元素,同时将其他元素更改为完全限定符号。要执行此操作,请使用`(foo ~bar zip)
为要评估的符号添加前缀,如下所示:
(clojure.foo "b" clojure.zip)
会给你
`
实际上,反引号'
与引用$(function() {
var data = ["1200-1399", "1100-1199", "3400-3409", "5000-5999", "6000-6999", "7000-7120", "9050-9059", "9550"];
$( "#inputBox" ).autocomplete({
source: data
});
});
很相似,因为它没有评估,只是返回代码,除了它通过完全限定操作代码返回一点其中的符号。这会对宏(有时你可能想要一个完全限定的引用)提示从另一个命名空间中获取,有时你需要灵活地说,在当前命名空间中查找这个符号。