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在命名空间中使用的符号quux将clojure.core/identity通过中间步骤解析,其中quux/identity发现了一个条目; 这通常会参考来clojure.core/identity.这是一个在直观编码时没有想到的实现细节,但在尝试解释时我发现这是不可能的.
这已经是合格的命名空间(就像一个符号zip/root命名空间中的其中refers到clojure.zip无use"荷兰国际集团的话)将在适当的命名空间中查找.
宏有一些额外的复杂性(只能出现在操作员位置),但它与符号本身的行为无关.
请注意,在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它时,它会评估为一个.
Stu*_*rra 25
在Common Lisp和Clojure中,术语"符号"的不同用法可能存在一些混淆.
在Common Lisp中,"符号"是存储器中的位置,可以存储数据的位置.符号的"值"是存储在存储器中该位置的数据.
在Clojure中,"符号"只是一个名称.它没有任何价值.
当Clojure编译器遇到符号时,它会尝试将其解析为
Var,正如之前的海报所指出的那样,代表了一个存储位置.
Clojure将Vars与符号分开是有充分理由的.首先,它避免了Common Lisp自动中断符号的烦恼,这些符号可以"污染"具有不需要符号的包.
其次,Clojure Vars在并发方面具有特殊的语义.Var对所有线程都有一个"根绑定"可见.(当您键入"def"时,您正在设置Var的根绑定.)对线程内的Var所做的更改(使用"set!"或"binding")仅对该线程及其子项可见.
| 归档时间: |
|
| 查看次数: |
9845 次 |
| 最近记录: |