以下代码来自Fogus,Houser的 Clojure的欢乐的第二版8.1.1:
(defn contextual-eval [ctx expr]
(eval
`(let [~@(mapcat (fn [[k v]] [k `'~v]) ctx)] ; Build let bindings at compile time
~expr)))
(contextual-eval '{a 1, b 2} '(+ a b))
;;=> 3
(contextual-eval '{a 1, b 2} '(let [b 1000] (+ a b)))
;;=> 1001
我真的不明白构造`'~v
的含义。有人可以详细说明吗?
在书中,只是说
创建的绑定使用有趣的`'~v模式来获取构建的值 在运行时绑定。
例如
(contextual-eval '{a 1, b 2} '(+ a b))
扩展为
(let [a '1 b '2] (+ a b)))
我不明白为什么引入了这些引用,它们有什么好处。
此外,我们还有以下行为:
(contextual-eval '{a 1, b (+ a 1)} '(+ a b))
ClassCastException clojure.lang.PersistentList cannot be cast to java.lang.Number
(defn contextual-eval' [ctx expr]
(eval
`(let [~@(mapcat (fn [[k v]] [k v]) ctx)]
~expr)))
(contextual-eval' '{a 1, b (+ a 1)} '(+ a b))
;=> 3
答案 0 :(得分:7)
该表达式几乎使用了Clojure中可用的所有特殊线条噪声符号,因此值得分开:
`
是" syntax-quote"的读者宏
"语法引号"在读者宏中是特殊的,因为你只能通过它的简短形式调用该函数。例如,您不能调用类似(syntax-quote something-here )
的内容而是编写`something-here
。它提供了一组丰富的选项,用于指定在应该评估表达式之后的哪些部分,以及应该从字面上理解哪些部分。 '
是quote
特殊表单的读者宏快捷方式。它导致它包装的表达式不被评估,而是被视为数据。如果您想在不对其进行评估的情况下编写文字quote
表单,则可以编写`'something
以获取`(quote something)
作为结果。这将导致结果引用表达式不被评估,只是按原样返回而不运行它。
~
是syntax-quote语法的一部分(它" s"引用"带语法)意味着"实际让这部分运行& #34;所以,如果你有一个你想要字面意义的大列表(现在不运行),除了你有一个你真正想要现在评估的项目,那么你可以写`(a b c ~d e f g)
而d将是唯一的该列表将被评估为当前定义的内容。
所以现在我们可以把它们放在一起:
`'~
表示"制作一个引用表达式,其中包含v的值,因为它现在是"
user> (def v 4)
#'user/v
user> `'~v
(quote 4)
以及这种幻想的动机:
(contextual-eval '{a 1, b 2} '(+ a b))
似乎只是添加一些额外的想法而没有任何好处,因为它基本上只是引用值1和2.因为这些是正确的"值"他们永远不会改变。
现在,如果表达式是:
(contextual-eval
'{a (slurp "https://example.com/launch?getCode")
b the-big-red-button}
'(press b a))
然后,在 特定位代码运行时要小心。因此,这种模式是关于控制程序生命的哪个阶段实际运行代码。 Clojure有几个"次"代码可以运行时:
main
之前,通常会发生这种情况。main
ps:以上定义是针对此问题的背景而定制的,并非打算使用"官方"术语