在“Clojure的喜悦”中的语境中的无条件构造

时间:2016-01-27 14:23:41

标签: clojure

以下代码来自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

1 个答案:

答案 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:以上定义是针对此问题的背景而定制的,并非打算使用"官方"术语