Clojure中的符号

Aru*_*n R 30 clojure

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

Mic*_*zyk 56

一般介绍:

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

从Lisp通用性转向Clojure细节,Clojure eval/compiler的行为是在遇到符号时,它将其作为let引入的局部变量或函数参数的名称命名空间中条目的名称.实际上,只有非名称空间限定的符号可以用在第一个容量中(意味着表单符号foo而不是some-namespace/foo).

一个大致草拟的例子:

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

请注意,identity在命名空间中使用的符号quuxclojure.core/identity通过中间步骤解析,其中quux/identity发现了一个条目; 这通常会参考clojure.core/identity.这是一个在直观编码时没有想到的实现细节,但在尝试解释时我发现这是不可能的.

这已经是合格的命名空间(就像一个符号zip/root命名空间中的其中refers到clojure.zipuse"荷兰国际集团的话)将在适当的命名空间中查找.

宏有一些额外的复杂性(只能出现在操作员位置),但它与符号本身的行为无关.

Vars vs Symbols:

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

闭幕致辞:

请注意,所有这些意味着符号仅仅"绑定"到对象eval,在评估符号时,继续寻找其他对象.符号本身没有"槽"或"字段"用于绑定到它的对象; 任何一个符号被绑定到某个对象的印象都是由于它eval的运作.这是一个Clojure特性,因为在某些Lisps符号中它们本身充当存储位置.

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

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

(defmacro symbol?? [x]
  (if (symbol? x)
    true
    false))

(def s 1)
(symbol? s)
; => false
(symbol?? s)
; => true
(symbol? 's)
; => true
(symbol?? 's)
; => false
Run Code Online (Sandbox Code Playgroud)

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

  • @Icarus:是的,确切地说!当你说'(symbol?s)`时,`s`在`符号?`函数看到它之前被评估(在这种情况下为'1`),所以你基本上都在询问`1`是否是一个符号(它不是).如果引用符号,它将被传递给`symbol?`不变.(`eval`对数据结构进行操作,因此符号内存表示在两种情况下都已经由读者构造;引用使得'eval`使其未被评估).Clojure的函数调用语义是按值调用的; 你必须使用宏来改变它.事实上,我可能会在一秒钟内编辑一个.:-) (2认同)

Stu*_*rra 25

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

在Common Lisp中,"符号"是存储器中的位置,可以存储数据的位置.符号的"值"是存储在存储器中该位置的数据.

在Clojure中,"符号"只是一个名称.它没有任何价值.

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

  1. Java类名(如果符号包含点)
  2. 本地(与"let"或函数参数一样)
  3. 当前命名空间中的Var
  4. 从另一个命名空间引用的Var

Var,正如之前的海报所指出的那样,代表了一个存储位置.

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

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