clojure中的语境评估

时间:2015-05-23 22:35:01

标签: clojure macros

这是一个来自第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)))

我发现`''`部分非常令人困惑,它的用途是什么?

我还尝试稍微修改一下这个函数:

(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)))

以上所有版本都有类似的效果。为什么作者选择使用`'然后?

更详细的外观:

(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)))

这将在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

同样,使用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) 

2 个答案:

答案 0 :(得分:2)

`' ~v是一种返回的方式

setup_custom_target_arduino.m

在这种情况下引用(list 'quote v) 表达式中v的实际值,而不是符号本身。

IDK Clojure的喜悦,但显然作者想要阻止在扩展的let形式中评估ctx中传递的形式。 E. g。 let会在您的版本中返回(contextual-eval '{a (+ 3 4)} 'a)(+ 3 4),但行为相同。

答案 1 :(得分:1)

您修改后的版本只会因为您在非常简单的数据上尝试它们而具有相同的效果。请尝试使用{'a 'x}之类的映射,这是a的绑定为符号x的上下文。

user> (defn contextual-eval [ctx expr]
        (let [new-expr
              `(let [~@(mapcat (fn [[k v]]
                                 [k `'~v])
                               ctx)]
                 ~expr)]
          (eval new-expr)))
#'user/contextual-eval
user> (contextual-eval {'a 'x} '(name a))
"x"
user> (defn contextual-eval [ctx expr]
        (let [new-expr
              `(let [~@(mapcat (fn [[k v]]
                                 [k `~v])
                               ctx)]
                 ~expr)]
          (eval new-expr)))
#'user/contextual-eval
user> (contextual-eval {'a 'x} '(name a))
; Evaluation aborted.

问题在于,在您的版本中,忽略引号,您将双重评估绑定到符号的值:x 不应进行评估,因为值为实际上是符号x。在简单的测试用例中,您可以避免这种双重评估,因为1评估自身:(eval(eval(eval 1)))也可以正常工作。但是对大多数数据结构这样做是错误的,因为它们具有非平凡的评估语义。

另请注意,以下表达式在所有情况下都是相同的,因此除了第一个之外,没有理由写任何一个:

x
`~x
`~`~x
```~`~~`~~x

如果您使用语法引用然后立即取消引用,则表示您尚未完成任何操作。所以,如果你发现自己写了一个引号后跟一个非引号,这应该是一个大红旗,你做错了。