我有一个"a"绑定到函数的符号:
(defn a []
(println "Hello, World"))
user=> a
#<user$a__292 user$a__292@97eded>
user=> (a)
Hello, World
nil
Run Code Online (Sandbox Code Playgroud)
然后我使用语法引用,它"解析当前上下文中的符号,产生一个完全限定的符号",根据Clojure文档.但为什么我不能像不合格的符号一样使用它?
user=> `a
user/a
user=> (`a)
java.lang.IllegalArgumentException: Wrong number of args passed to: Symbol (NO_SOURCE_FILE:0)
Run Code Online (Sandbox Code Playgroud)
第二个问题:如果我在列表中有符号,为什么我不能像直接评估符号一样评估它?
user=> (def l '(a 1 2))
#'user/l
user=> 'l
l
user=> (first l)
a
user=> ((first l))
java.lang.IllegalArgumentException: Wrong number of args passed to: Symbol (NO_SOURCE_FILE:0)
Run Code Online (Sandbox Code Playgroud)
我怀疑在某些地方对于符号如何在这里工作有一个致命的缺陷.上面的代码有什么问题?
Bri*_*per 36
REPL =读取eval打印循环.逐步完成read-eval过程.
阅读:Clojure看到字符串"(`a)",解析它并最终得到一个数据结构.在读取时,读取器宏被扩展,并且没有太多其他事情发生.在这种情况下,读者扩展反引用并最终得到:
user> (read-string "(`a)")
((quote user/a))
Run Code Online (Sandbox Code Playgroud)
EVAL:Clojure试图评估这个对象.评估规则取决于您正在查看的对象类型.
请参阅clojure.lang.Compiler/analyzeSeqClojure源以查看列表或clojure.lang.Compiler/analyzeSymbol符号的评估规则.那里有很多其他的评估规则.
假设你这样做:
user> (user/a)
Run Code Online (Sandbox Code Playgroud)
REPL最终在内部执行此操作:
user> (eval '(user/a))
Run Code Online (Sandbox Code Playgroud)
Clojure看到您正在评估列表,因此它会评估列表中的所有项目.第一个(也是唯一的)项目:
user> (eval 'user/a)
#<user$a__1811 user$a__1811@82c23d>
Run Code Online (Sandbox Code Playgroud)
a不是一个特殊的形式,这个列表不需要进行宏扩展,因此a在命名空间中查找符号user,这里得到的值是一个fn.所以这fn就是所谓的.
但相反,你有这个:
user> (eval '((quote user/a)))
Run Code Online (Sandbox Code Playgroud)
Clojure评估列表中的第一项,它本身就是一个列表.
user> (eval '(quote user/a))
user/a
Run Code Online (Sandbox Code Playgroud)
它评估了此子列表中的第一个项目quote,这是一个特殊形式,因此应用了特殊规则并返回其a未评估的参数(符号).
符号a是值在这种情况下为fn是值向上的上方.所以Clojure将Symbol本身视为一个函数并将其调用.在Clojure中,任何实现Ifn接口的东西都可以调用fn.恰巧,clojure.lang.Symbol农具Ifn.称为函数的符号需要一个参数,一个集合,并且它在该集合中查找自己.这意味着像这样使用:
user> ('a {'a :foo})
:foo
Run Code Online (Sandbox Code Playgroud)
这是它试图在这里做的.但是你没有传递任何参数,所以你得到错误"传递给:符号的args数量错误"(它需要一个集合).
为了使您的代码工作,您需要两个级别eval.这有效,希望您能看到原因:
user> (eval '((eval (quote user/a))))
Hello, world
user> ((eval (first l)))
Hello, world
Run Code Online (Sandbox Code Playgroud)
请注意,在实际代码中,eval直接使用通常是一个非常糟糕的主意.到目前为止,宏是一个更好的主意.我只是在这里用它来演示.
查看Compiler.javaClojure源代码,了解这一切是如何发挥作用的.这并不难理解.
Chr*_*est 15
使用符号作为函数与评估函数不同.符号作为函数的作用与关键字作为函数的作用相同.像这样:
user=> (declare a)
#'user/a
user=> (def a-map {'a "value"})
#'user/a-map
user=> ('a a-map)
"value"
user=>
Run Code Online (Sandbox Code Playgroud)
这不是您通常使用符号的方式.它们更常用于在命名空间中查找变量,以及在宏中生成代码时.
为了打破间接层,让我们将"x"定义为1,看看会发生什么:
user=> (def x 1)
#'user/x
Run Code Online (Sandbox Code Playgroud)
使用def,我们创建了一个"var." var的名称是符号user/x.该def特殊形式返回VAR本身的REPL,而这正是我们可以看到打印出来.让我们试着抓住那个var:
user=> #'x
#'user/x
Run Code Online (Sandbox Code Playgroud)
该#'语法是阅读器宏说:"给我用下面的符号代表的变种." 在我们的例子中,该符号是"x".我们和以前一样恢复了相同的var.Vars是指向值的指针,可以解除引用:
user=> (deref #'x)
1
Run Code Online (Sandbox Code Playgroud)
但是在可以取消引用之前需要找到var.这就是符号的可调性发挥作用的地方.命名空间就像一个映射,其中符号是键,vars是值,当我们明确地命名符号时,我们隐式地在我们的命名空间中查找它的var.像这样:
user=> ('x (.getMappings *ns*))
#'user/x
Run Code Online (Sandbox Code Playgroud)
虽然,实际上,它可能更像是这样的:
user=> (.findInternedVar *ns* 'x)
#'user/x
Run Code Online (Sandbox Code Playgroud)
现在我们已经完全围绕着未加引号的符号的旅程:
user=> (deref (.findInternedVar *ns* 'x))
1
user=> x
1
Run Code Online (Sandbox Code Playgroud)
但两者并不完全相同.因为求值程序对所有符号执行此操作,包括deref和*ns*.
关于引用的事情是你基本上绕过了整个机制,只是得到了简单的符号.就像#'阅读器宏得到普通的vars一样,`和'阅读器宏将分别返回普通符号,分别有或没有命名空间限定:
user=> 'x
x
user=> `x
user/x
Run Code Online (Sandbox Code Playgroud)
user =>(def l'(a 1 2))user =>((first l))
把它变成:
user =>(def l`(~a 1 2))
〜这里将符号a解析为其对应的var,并且反引号使得取消引用.
通常,您必须了解vars(绑定到某些东西)和符号(从不绑定到任何东西)之间的区别.
我会尝试解释它(希望我的出版物不会让你更进一步混淆):
user=> (def v "content")
#'user/content
Run Code Online (Sandbox Code Playgroud)
- >在符号'v(完全限定'user/v,假设这是当前命名空间)下的当前命名空间中定义var,并将它(var,而不是符号)绑定到对象"content".
user=> v
"content"
Run Code Online (Sandbox Code Playgroud)
- >将v解析为var,并获取绑定值
user=> #'v
#'user/v
Run Code Online (Sandbox Code Playgroud)
- >解析为var本身
user=> 'v
v
Run Code Online (Sandbox Code Playgroud)
- >不解决任何问题,只是一个简单的符号(不幸的是,REPL没有表明这一点,打印'v as v)
user=> `v
user/v
Run Code Online (Sandbox Code Playgroud)
- >正如您已经引用的那样,解析为当前上下文(命名空间)中的符号,但结果仍然是符号(完全限定),而不是var user/v
user=> '(v)
(v)
Run Code Online (Sandbox Code Playgroud)
- >简单引用,不解决任何问题
user=> `(v)
(user/v)
Run Code Online (Sandbox Code Playgroud)
- > syntax-quote,与引用相同,但将符号解析为名称空间限定符号
user=> `(~v)
("content")
Run Code Online (Sandbox Code Playgroud)
- >将符号解析为其var(隐含地取消引用),生成其绑定对象
| 归档时间: |
|
| 查看次数: |
8659 次 |
| 最近记录: |