Clojure中'()和(list)之间有什么区别?

时间:2012-04-24 11:20:37

标签: clojure

列表中的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中的列表有“简写”(例如地图为{},向量为[],那么它是什么?

3 个答案:

答案 0 :(得分:32)

在lisps中,'(如quote)引用其参数,即保留它们与s-exp形式完全一致,包括不评估其中的任何内容。

换句话说'(foo bar zip)创建一个包含符号foobarzip的列表; (list foo bar zip)创建一个包含foobarzip的列表。在第一种情况下,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")

您已定义了三个符号:foobarzip与值相关联:"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解释为映射到函数的符号,并将barzip作为符号映射到要传递到foo函数的值。

在我们的例子中,它会引发异常:

java.lang.ClassCastException: java.lang.String cannot be cast to clojure.lang.IFn

这是因为没有定义函数foofoo"a"相关联,foo是一个字符串,而不是一个IFn(一个Clojure函数)。

如果定义了函数foo,它会调用"b"作为参数"c"list传入。

案例(list foo bar zip)

list

当使用列表符号时,读者实际上以与无指示符大小写相同的方式解释它。也就是说,它正在寻找映射到函数的符号foo,该函数将映射到barziplist的关联值作为参数。 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引用告诉读者,后面的表单应按原样读取。在我们的示例中,barzip(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 }); }); 很相似,因为它没有评估,只是返回代码,除了它通过完全限定操作代码返回一点其中的符号。这会对宏(有时你可能想要一个完全限定的引用)提示从另一个命名空间中获取,有时你需要灵活地说,在当前命名空间中查找这个符号。