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

Dan*_*igk 6 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
Run Code Online (Sandbox Code Playgroud)

我真的不明白建筑的意义`'~v.有人可以详细说明吗?

在书中,只是说

创建的绑定使用有趣的`'~v模式在运行时获取构建的绑定的值.

例如

(contextual-eval '{a 1, b 2} '(+ a b))
Run Code Online (Sandbox Code Playgroud)

扩大到

(let [a '1 b '2] (+ a b)))
Run Code Online (Sandbox Code Playgroud)

我不明白为什么引用这些报价,它们有什么好处.

此外,我们有以下行为:

(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
Run Code Online (Sandbox Code Playgroud)

Art*_*ldt 8

该表达式使用了几乎所有Clojure中可用的特殊线条噪声符号,因此值得将它分开:

  • `是一个
    "语法引用"的读者宏 - "语法引用"在读者宏中是特殊的,因为你只能通过它的简短形式调用该函数.你不能打电话给(syntax-quote something-here )你写的东西`something-here.它提供了一组丰富的选项,用于指定在应该评估表达式之后的哪些部分,以及应该从字面上理解哪些部分.
  • 'quote特殊表单的reader-macro快捷方式.它导致它包装的表达式不被评估,而是被视为数据.如果你想编写一个文字quote形式而不评估它,你可以写作`'something得到`(quote something)结果.这将导致结果引用表达式不被评估,只是按原样返回而不运行它.

  • ~是语法引用的语法的一部分(它是带语法的"引用")意味着"实际上让这部分运行"所以如果你有一个你想要字面意义的大列表(现在不运行),除了你有你真正想要评估的一个项目,然后你可以写,`(a b c ~d e f g)并且d将是该列表中唯一被评估为当前定义的内容的东西.

所以现在我们可以把它们放在一起:

`'~ 表示"制作一个包含v值的引用表达式,因为它现在是"

user> (def v 4)
#'user/v
user> `'~v
(quote 4)
Run Code Online (Sandbox Code Playgroud)

对于这种幻想的动机:

(contextual-eval '{a 1, b 2} '(+ a b))
Run Code Online (Sandbox Code Playgroud)

似乎只是添加一些额外的思维而没有任何好处,因为它基本上只是引用值1和2.因为这些是正确的"值",它们无论如何都不会改变.

现在,如果表达式是:

(contextual-eval 
  '{a (slurp "https://example.com/launch?getCode")
    b the-big-red-button} 
  '(press b a))
Run Code Online (Sandbox Code Playgroud)

那么,特定的代码运行要小心它会更有意义.因此,这种模式是关于控制程序生命的哪个阶段实际运行代码.当代码可以运行时,Clojure有几个"时间":

  • 在宏观评估时:代码正在形成.(这里的副作用需要很多预见).
  • 加载名称空间时:这是顶级表单运行的时候.当您启动程序时以及之前main调用时,通常会发生这种情况.
  • 因运行而运行的东西 main

ps:以上定义是针对此问题的背景而定制的,并非旨在使用"官方"术语.