Clojure中的符号

时间:2010-02-23 17:38:53

标签: clojure

Clojure中的符号绑定到底层对象并具有可选的单独值的基本原理是什么?也许是我缺少的基本内容,但如果有人能指出为什么会很好。

2 个答案:

答案 0 :(得分:56)

一般介绍:

任何Lisp中的符号都用作标识符。如果您要引用变量的值,比如说,您需要有一种方法来命名它;这就是符号的用途。请记住,所有Lisp代码都会在读取时转换为Lisp数据结构;标识符也必须由某些数据结构表示,它恰好是符号。遇到符号后,eval将调度到某种“名称查找”操作。

从Lisp通用性转向Clojure细节,Clojure eval /编译器的行为是在遇到符号时,它将它作为let引入的局部变量或函数参数或命名空间中条目的名称。实际上,在第一个容量中只能使用非命名空间限定的符号(意思是foo而不是some-namespace/foo形式的符号。

一个粗略概述的例子:

对于非命名空间限定符号foo,如果找到名称为let的{​​{1}}绑定/函数参数,则符号将计算其值。如果不是,则将符号转换为foo形式(*ns*/foo表示当前名称空间),并尝试在*ns*中查找相应的条目;如果有这样的条目,则返回其值,否则抛出异常。

请注意,*ns*这样的符号在名称空间identity中使用时,将通过中间步骤解析为quux,其中clojure.core/identity下的条目被发现;这通常引用到quux/identity。这是一个在直观编码时没有想到的实现细节,但在尝试解释时我发现这是不可能的。

已经命名空间限定的符号(类似于名称空间clojure.core/identity的{​​{1}} zip/rootrefer而不clojure.zip'的符号将会被查找在适当的命名空间中。

宏增加了一些复杂性(只能出现在操作员位置),但它与符号本身的行为并不相关。

Vars vs Symbols:

请注意,在Clojure中,符号本身不是存储位置 - Vars。所以当我在上面说一个符号在命名空间中查找时,我的意思是use查找由解析为其名称空间限定形式的符号命名的Var,然后获取该值。特殊形式eval(通常缩写为var)修改此行为,以便返回Var对象本身。但是,符号到瓦尔分辨率的机制没有改变。

结束语:

请注意,所有这些意味着符号仅在#'的意义上“绑定”到对象,在评估符号时,继续查找其他对象。符号本身没有“槽”或“字段”用于绑定到它的对象;符号被绑定到某个对象的任何印象都归因于eval的工作原理。这是一个Clojure特性,因为在某些Lisps符号中它们本身就是存储位置。

最后,可以使用通常的引用机制来阻止对符号的评估:在eval中,将不会评估符号'foo(因此不会执行任何类型的名称查找) ;相反,它将保持不变。

回应OP的评论:试试这个很有趣:

foo

最后一个解释:(defmacro symbol?? [x] (if (symbol? x) true false)) (def s 1) (symbol? s) ; => false (symbol?? s) ; => true (symbol? 's) ; => true (symbol?? 's) ; => false 's的简写;这是一个列表结构,而不是符号。一个宏对其直接传入的参数进行操作,而不进行评估;所以在(quote s)中它实际上看到了(symbol?? 's)列表结构,当然这本身并不是一个符号 - 尽管传递给(quote s)时,它会评估为一个。

答案 1 :(得分:23)

在Common Lisp和Clojure中,术语“符号”的不同用法可能存在一些混淆。

在Common Lisp中,“符号”是内存中的位置,可以存储数据的位置。符号的“值”是存储在内存中该位置的数据。

在Clojure中,“符号”只是一个名字。它没有价值。

当Clojure编译器遇到符号时,它会尝试将其解析为

  1. Java类名称(如果符号包含点)
  2. 本地(与“let”或函数参数一样)
  3. 当前命名空间中的Var
  4. 从另一个命名空间引用的Var
  5. Var,正如之前的海报所指出的那样,代表了一个存储位置。

    Clojure将Vars与Symbols分开是有充分理由的。首先,它避免了Common Lisp自动中断符号的烦恼,这些符号可以“污染”具有不需要符号的包。

    其次,Clojure Vars在并发方面具有特殊的语义。 Var对所有线程都有一个“根绑定”可见。 (当您键入“def”时,您正在设置Var的根绑定。)对线程内的Var所做的更改(使用“set!”或“binding”)仅对该线程及其子项可见。