为什么此代码使用多种类型的引号
Why is this code using multiple types of quotes
来自:Clojure 的乐趣
(defn contextual-eval [ctx expr]
(eval
`(let [~@(mapcat (fn [[k v]] [k `'~v]) ctx)]
~expr)))
现在如果我打电话给
(contextual-eval '{a 1 b 2} '(+ a b))
符合预期returns3
但为什么要使用 `'~v 而不是 v 呢?
如果我删除 eval.. 使函数看起来像这样
(defn contextual-eval [ctx expr]
`(let [~@(mapcat (fn [[k v]] [k v]) ctx)]
~expr))
然后调用
(eval (contextual-eval '{a 1 b 2} '(+ a b)))
它仍然 returns 3 符合预期。
所以我不确定为什么在 eval 里面时使用 `'~v
函数体。
我认为这是一个打字错误。此代码显示了正在发生的事情以及应该发生的事情:
(defn contextual-eval [ctx expr]
(spy :01 ctx)
(spy :02 (mapcat (fn [[k v]] [k `'~v]) ctx))
(spy :03 `(let [~@(mapcat (fn [[k v]] [k `'~v]) ctx)] ~expr) )
(spy :02a (mapcat (fn [[k v]] [k v]) ctx))
(spy :03a `(let [~@(mapcat (fn [[k v]] [k v]) ctx)] ~expr) )
(eval `(let [~@(mapcat (fn [[k v]] [k v]) ctx)] ~expr) )
)
(dotest
(nl)
(spy :10
(contextual-eval '{a 1 b 2}
'(+ a b)))
(nl)
(spy :20
(contextual-eval '{a 1
b (+ a 3)}
'(+ a b)))
)
我们得到第一个 运行 的结果:
:01 => {a 1, b 2}
:02 => (a (quote 1) b (quote 2))
:03 => (clojure.core/let [a (quote 1) b (quote 2)] (+ a b))
:02a => (a 1 b 2)
:03a => (clojure.core/let [a 1 b 2] (+ a b))
:10 => 3
第二个运行:
:01 => {a 1, b (+ a 3)}
:02 => (a (quote 1) b (quote (+ a 3)))
:03 => (clojure.core/let [a (quote 1) b (quote (+ a 3))] (+ a b))
:02a => (a 1 b (+ a 3))
:03a => (clojure.core/let [a 1 b (+ a 3)] (+ a b))
:20 => 5
通过将“`'~v”转换为 "v" 它可以完成它应该做的事情。
P.S。虽然 JoC 有很多好东西,但它非常先进,应该更像是你的第五本 Clojure 书,而不是第一本(这是我的第一本 Clojure 书,我很困惑)。从以下开始你会得到很好的服务:
- 获取 Clojure
- 生动的 Clojure
- 勇敢而真实的 Clojure
更新:
我会做一些不同的事情。 mapcat
真的没必要。注意 (ns ...)
形式的 (:use ...)
子句:
(ns tst.demo.core
(:use demo.core tupelo.core tupelo.test))
(defn contextual-eval [ctx expr]
(spy :01 ctx)
(spy :02 (seq ctx))
(spy :03 (vec (apply concat (seq ctx))))
(spy :04 `(let ~(vec (apply concat (seq ctx)))
~expr))
(eval `(let ~(vec (apply concat (seq ctx)))
~expr)))
(dotest
(nl)
(spy :100
(contextual-eval '{a 1 b 2}
'(+ a b)))
(nl)
(spy :200
(contextual-eval '{a 1
b (+ a 3)}
'(+ a b)))
)
结果
:01 => {a 1, b 2}
:02 => ([a 1] [b 2])
:03 => [a 1 b 2]
:04 => (clojure.core/let [a 1 b 2] (+ a b))
:100 => 3
:01 => {a 1, b (+ a 3)}
:02 => ([a 1] [b (+ a 3)])
:03 => [a 1 b (+ a 3)]
:04 => (clojure.core/let [a 1 b (+ a 3)] (+ a b))
:200 => 5
并且在 project.clj
中,您需要添加对 the Tupelo library
的依赖
[tupelo "0.9.138"]
更新#2:
@amalloy 是正确的,他的例子有效。但是,原始示例失败了:
(ns tst.demo.core
(:use demo.core tupelo.core tupelo.test))
(defn contextual-eval [ctx expr]
(println :01 ctx)
(println :02 (seq ctx))
(println :04a (map (fn [[k v]] [k `'~v]) ctx))
(println :04b (mapcat (fn [[k v]] [k `'~v]) ctx))
(println :05 `(let [~@(mapcat (fn [[k v]]
[k `'~v]) ctx)]
~expr))
(spy :99 (eval
`(let [~@(mapcat (fn [[k v]]
[k `'~v]) ctx)]
~expr))))
(dotest
(newline)
(throws?
(println :100
(contextual-eval '{a 2, b 5}
'(- (first a) b))))
(newline)
(is= -4
(let [a (range 5)
b (last a)]
(spy :300
(contextual-eval {'a a, 'b b}
'(- (first a) b))))) )
结果:
:01 {a 2, b 5}
:02 ([a 2] [b 5])
:04a ([a (quote 2)] [b (quote 5)])
:04b (a (quote 2) b (quote 5))
:05 (clojure.core/let [a (quote 2) b (quote 5)] (- (first a) b))
:01 {a (0 1 2 3 4), b 4}
:02 ([a (0 1 2 3 4)] [b 4])
:04a ([a (quote (0 1 2 3 4))] [b (quote 4)])
:04b (a (quote (0 1 2 3 4)) b (quote 4))
:05 (clojure.core/let [a (quote (0 1 2 3 4)) b (quote 4)] (- (first a) b))
:99 => -4
:300 => -4
第一次尝试失败,异常:
actual: java.lang.IllegalArgumentException: Don't know how to create ISeq from: java.lang.Long
所以,我想说这种脆弱的东西可能不是在 Clojure 中教授 quoting/unquoting 的最佳例子。此外,除非您正在编写编译器或类似程序,否则 很少 需要这样的代码。而且,如果您确实需要这种复杂且容易出错的东西,我建议添加更多检查,分散步骤并显示中间结果以帮助 reader 理解并由维护者进行调试。
来自:Clojure 的乐趣
(defn contextual-eval [ctx expr]
(eval
`(let [~@(mapcat (fn [[k v]] [k `'~v]) ctx)]
~expr)))
现在如果我打电话给
(contextual-eval '{a 1 b 2} '(+ a b))
符合预期returns3
但为什么要使用 `'~v 而不是 v 呢?
如果我删除 eval.. 使函数看起来像这样
(defn contextual-eval [ctx expr]
`(let [~@(mapcat (fn [[k v]] [k v]) ctx)]
~expr))
然后调用
(eval (contextual-eval '{a 1 b 2} '(+ a b)))
它仍然 returns 3 符合预期。
所以我不确定为什么在 eval 里面时使用 `'~v 函数体。
我认为这是一个打字错误。此代码显示了正在发生的事情以及应该发生的事情:
(defn contextual-eval [ctx expr]
(spy :01 ctx)
(spy :02 (mapcat (fn [[k v]] [k `'~v]) ctx))
(spy :03 `(let [~@(mapcat (fn [[k v]] [k `'~v]) ctx)] ~expr) )
(spy :02a (mapcat (fn [[k v]] [k v]) ctx))
(spy :03a `(let [~@(mapcat (fn [[k v]] [k v]) ctx)] ~expr) )
(eval `(let [~@(mapcat (fn [[k v]] [k v]) ctx)] ~expr) )
)
(dotest
(nl)
(spy :10
(contextual-eval '{a 1 b 2}
'(+ a b)))
(nl)
(spy :20
(contextual-eval '{a 1
b (+ a 3)}
'(+ a b)))
)
我们得到第一个 运行 的结果:
:01 => {a 1, b 2}
:02 => (a (quote 1) b (quote 2))
:03 => (clojure.core/let [a (quote 1) b (quote 2)] (+ a b))
:02a => (a 1 b 2)
:03a => (clojure.core/let [a 1 b 2] (+ a b))
:10 => 3
第二个运行:
:01 => {a 1, b (+ a 3)}
:02 => (a (quote 1) b (quote (+ a 3)))
:03 => (clojure.core/let [a (quote 1) b (quote (+ a 3))] (+ a b))
:02a => (a 1 b (+ a 3))
:03a => (clojure.core/let [a 1 b (+ a 3)] (+ a b))
:20 => 5
通过将“`'~v”转换为 "v" 它可以完成它应该做的事情。
P.S。虽然 JoC 有很多好东西,但它非常先进,应该更像是你的第五本 Clojure 书,而不是第一本(这是我的第一本 Clojure 书,我很困惑)。从以下开始你会得到很好的服务:
- 获取 Clojure
- 生动的 Clojure
- 勇敢而真实的 Clojure
更新:
我会做一些不同的事情。 mapcat
真的没必要。注意 (ns ...)
形式的 (:use ...)
子句:
(ns tst.demo.core
(:use demo.core tupelo.core tupelo.test))
(defn contextual-eval [ctx expr]
(spy :01 ctx)
(spy :02 (seq ctx))
(spy :03 (vec (apply concat (seq ctx))))
(spy :04 `(let ~(vec (apply concat (seq ctx)))
~expr))
(eval `(let ~(vec (apply concat (seq ctx)))
~expr)))
(dotest
(nl)
(spy :100
(contextual-eval '{a 1 b 2}
'(+ a b)))
(nl)
(spy :200
(contextual-eval '{a 1
b (+ a 3)}
'(+ a b)))
)
结果
:01 => {a 1, b 2}
:02 => ([a 1] [b 2])
:03 => [a 1 b 2]
:04 => (clojure.core/let [a 1 b 2] (+ a b))
:100 => 3
:01 => {a 1, b (+ a 3)}
:02 => ([a 1] [b (+ a 3)])
:03 => [a 1 b (+ a 3)]
:04 => (clojure.core/let [a 1 b (+ a 3)] (+ a b))
:200 => 5
并且在 project.clj
中,您需要添加对 the Tupelo library
[tupelo "0.9.138"]
更新#2:
@amalloy 是正确的,他的例子有效。但是,原始示例失败了:
(ns tst.demo.core
(:use demo.core tupelo.core tupelo.test))
(defn contextual-eval [ctx expr]
(println :01 ctx)
(println :02 (seq ctx))
(println :04a (map (fn [[k v]] [k `'~v]) ctx))
(println :04b (mapcat (fn [[k v]] [k `'~v]) ctx))
(println :05 `(let [~@(mapcat (fn [[k v]]
[k `'~v]) ctx)]
~expr))
(spy :99 (eval
`(let [~@(mapcat (fn [[k v]]
[k `'~v]) ctx)]
~expr))))
(dotest
(newline)
(throws?
(println :100
(contextual-eval '{a 2, b 5}
'(- (first a) b))))
(newline)
(is= -4
(let [a (range 5)
b (last a)]
(spy :300
(contextual-eval {'a a, 'b b}
'(- (first a) b))))) )
结果:
:01 {a 2, b 5}
:02 ([a 2] [b 5])
:04a ([a (quote 2)] [b (quote 5)])
:04b (a (quote 2) b (quote 5))
:05 (clojure.core/let [a (quote 2) b (quote 5)] (- (first a) b))
:01 {a (0 1 2 3 4), b 4}
:02 ([a (0 1 2 3 4)] [b 4])
:04a ([a (quote (0 1 2 3 4))] [b (quote 4)])
:04b (a (quote (0 1 2 3 4)) b (quote 4))
:05 (clojure.core/let [a (quote (0 1 2 3 4)) b (quote 4)] (- (first a) b))
:99 => -4
:300 => -4
第一次尝试失败,异常:
actual: java.lang.IllegalArgumentException: Don't know how to create ISeq from: java.lang.Long
所以,我想说这种脆弱的东西可能不是在 Clojure 中教授 quoting/unquoting 的最佳例子。此外,除非您正在编写编译器或类似程序,否则 很少 需要这样的代码。而且,如果您确实需要这种复杂且容易出错的东西,我建议添加更多检查,分散步骤并显示中间结果以帮助 reader 理解并由维护者进行调试。