这是Clojure第8章中的一个例子:
(defn contextual-eval [ctx expr]
(let [new-expr
`(let [~@(mapcat (fn [[k v]]
[k `'~v])
ctx)]
~expr)]
(pprint new-expr)
(eval new-expr)))
(pprint (contextual-eval '{a 1 b 2} '(+ a b)))
Run Code Online (Sandbox Code Playgroud)
我觉得 ``'` 部分很令人困惑,它是做什么用的?
我也尝试稍微修改一下函数:
(defn contextual-eval [ctx expr]
(let [new-expr
`(let [~@(mapcat (fn [[k v]]
[k `~v])
ctx)]
~expr)]
(pprint new-expr)
(eval new-expr)))
(pprint (contextual-eval '{a 1 b 2} '(+ a b)))
(defn contextual-eval [ctx expr]
(let [new-expr
`(let [~@(vec (apply
concat
ctx))]
~expr)]
(pprint new-expr)
(eval new-expr)))
(pprint (contextual-eval '{a 1 b 2} '(+ a b)))
Run Code Online (Sandbox Code Playgroud)
以上所有版本都有类似的效果。那作者为什么选择用`'呢?
更详细的外观:
(use 'clojure.pprint)
(defmacro epprint [expr]
`(do
(print "==>")
(pprint '~expr)
(pprint ~expr)))
(defmacro epprints [& exprs]
(list* 'do (map (fn [x] (list 'epprint x))
exprs)))
(defn contextual-eval [ctx expr]
(let [new-expr
`(let [~@(mapcat (fn [[k v]]
(epprints
(class v)
v
(class '~v)
'~v
(class `'~v)
`'~v
(class ctx)
ctx)
[k `~v])
ctx)]
~expr)]
(pprint new-expr)
(eval new-expr)))
(pprint (contextual-eval '{a (* 2 3) b (inc 11)} '(+ a b)))
Run Code Online (Sandbox Code Playgroud)
这会在 repl 中打印出以下内容:
==>(class v)
clojure.lang.PersistentList
==>v
(* 2 3)
==>(class '~v)
clojure.lang.PersistentList
==>'~v
~v
==>(class
(clojure.core/seq
(clojure.core/concat
(clojure.core/list 'quote)
(clojure.core/list v))))
clojure.lang.Cons
==>(clojure.core/seq
(clojure.core/concat (clojure.core/list 'quote) (clojure.core/list v)))
'(* 2 3)
==>(class ctx)
clojure.lang.PersistentArrayMap
==>ctx
{a (* 2 3), b (inc 11)}
==>(class v)
clojure.lang.PersistentList
==>v
(inc 11)
==>(class '~v)
clojure.lang.PersistentList
==>'~v
~v
==>(class
(clojure.core/seq
(clojure.core/concat
(clojure.core/list 'quote)
(clojure.core/list v))))
clojure.lang.Cons
==>(clojure.core/seq
(clojure.core/concat (clojure.core/list 'quote) (clojure.core/list v)))
'(inc 11)
==>(class ctx)
clojure.lang.PersistentArrayMap
==>ctx
{a (* 2 3), b (inc 11)}
==>new-expr
(clojure.core/let [a (* 2 3) b (inc 11)] (+ a b))
18
Run Code Online (Sandbox Code Playgroud)
同样,对 v 使用单个语法引号似乎可以完成工作。
事实上,使用 `'v 可能会给你带来一些麻烦:
(defn contextual-eval [ctx expr]
(let [new-expr
`(let [~@(mapcat (fn [[k v]]
[k `'~v])
ctx)]
~expr)]
(pprint new-expr)
(eval new-expr)))
(pprint (contextual-eval '{a (inc 3) b (* 3 4)} '(+ a b)))
CompilerException java.lang.ClassCastException: clojure.lang.PersistentList cannot be cast to java.lang.Number, compiling:(/Users/kaiyin/personal_config_bin_files/workspace/typedclj/src/typedclj/macros.clj:14:22)
Run Code Online (Sandbox Code Playgroud)
`'~v 是一种返回方式
(list 'quote v)
Run Code Online (Sandbox Code Playgroud)
在这种情况下,引用表达式中 v 的实际值let,而不是符号本身。
IDK Clojure 的乐趣,但显然作者希望阻止 ctx 中传递的表单在扩展的 let 表单中被求值。例如 (contextual-eval '{a (+ 3 4)} 'a)将返回(+ 3 4),但7在您的版本中,它们的行为相同。
| 归档时间: |
|
| 查看次数: |
228 次 |
| 最近记录: |