Clojure';s eval不“是的;见;地方符号
我正在Clojure试验eval:Clojure';s eval不“是的;见;地方符号,clojure,eval,read-eval-print-loop,Clojure,Eval,Read Eval Print Loop,我正在Clojure试验eval: (let [code_as_data '(if (< sequ) on_true on_false) sequ [1 3 5] on_true "sequence is sorted in ascending order" on_false "sequence is NOT sorted"] (eval code_as_data)) (让[code_作为_数据”(如果( (让[将_编码为_数据](如果(应用
(let [code_as_data '(if (< sequ) on_true on_false)
sequ [1 3 5]
on_true "sequence is sorted in ascending order"
on_false "sequence is NOT sorted"]
(eval code_as_data))
(让[code_作为_数据”(如果(
CompilerException java.lang.RuntimeException:无法解析符号:sequ在此上下文中,正在编译:(/tmp/form-init3253735970468294023.clj:1:25)
如何定义符号以便eval“看到”它们?eval不识别词汇绑定(本地绑定,如
let
),尽管它识别全局/动态绑定。因此,解决方案之一是在动态绑定上下文中预定义动态变量和eval
:
user> (def ^:dynamic sequ)
#'user/sequ
user> (def ^:dynamic on_true)
#'user/on_true
user> (def ^:dynamic on_false)
#'user/on_false
user>
(let [code_as_data '(if (apply < sequ) on_true on_false)]
(binding [sequ [1 3 5]
on_true "sequence is sorted in ascending order"
on_false "sequence is NOT sorted"]
(eval code_as_data)))
"sequence is sorted in ascending order"
另一个选项(对我来说更有用)是使用walker将您需要的所有符号替换为它们的值:
user>
(let [code_as_data '(if (apply < sequ) on_true on_false)
bnd {'sequ [1 3 5]
'on_true "sequence is sorted in ascending order"
'on_false "sequence is NOT sorted"}]
(eval (clojure.walk/postwalk-replace bnd code_as_data)))
"sequence is sorted in ascending order"
user>
(让[将_编码为_数据](如果(应用
为eval在运行时生成的代码提供本地数据的最简单方法是生成一个带有参数的表单
(let [code-as-data '(fn [sequ on-true on-false]
(if (apply < sequ)
on-true
on-false))
f (eval code-as-data)]
(f [1 3 5]
"sequence is sorted in ascending order"
"sequence is NOT sorted"))
在实际代码中,只有在运行时需要生成逻辑时才需要eval
版本(例如,如果用户提供了新算法)。如果期望用户将其代码作为函数编写是一项繁重的任务,您可以采取折衷措施:
(defn code-with-context
[body sq t else]
(let [f (eval (list 'fn '[sequ on-true on-false] body))]
(f sq t else)))
(code-with-context (read-string "(if (apply < sequ) on-true on-false)")
[1 3 5]
"sequence is sorted in ascending order"
"sequence is NOT sorted")
(使用上下文定义代码
[身体面积]
(让[f(eval(列出'fn'[sequ on true on false]body))]
(f平方吨(其他))
(带上下文的代码(读取字符串)(if(apply
通过宏的邪恶魔力,您实际上可以构建一个版本的eval
,它主要完成您想要的功能
(defmacro super-unsafe-eval
"Like `eval`, but also exposes lexically-bound variables to eval. This
is almost certainly a bad idea."
[form]
`(eval (list 'let
~(vec (mapcat #(vector `(quote ~%)
`(list 'quote ~%))
(keys &env)))
~form)))
此宏使用特殊的&env
变量访问本地环境。然后,它构造一个let
表单,该表单绑定宏展开所在环境中当前绑定的所有名称。这使您的代码示例能够正常工作:
(let [code_as_data '(if (< sequ) on_true on_false)
sequ [1 3 5]
on_true "sequence is sorted in ascending order"
on_false "sequence is NOT sorted"]
(super-unsafe-eval code_as_data))
;;=> "sequence is sorted in ascending order"
第三个选项(我更喜欢的一个)是使用eval创建一个函数,并将局部变量作为传递给函数的参数,调用函数((eval)(fn[sequ on_true on_false](if(apply
@noisesmith,这也是一个不错的选项。@noisesmith,我是你吗,我会回答的。也许这更符合op的目的。我对从零件构造表达式的能力感兴趣。这些零件可以来自不同的来源。我看不出在评估预定义的静态表达式时有什么用处,就像在您的示例中一样。我错过什么了吗?我理解用~取消引用可以生成/组装表达式,就像在letwinskis的例子中一样。类似于:(defn无用的(predicate on)生成器[predicate on(predicate on)true on(on)false]`(if~predicate~ on(u true)on)on(u)on)“
@Sharas我不知道这其中的哪个方面需要evalalso,我编辑了一个字符串来显示用法(可能来自用户输入)
(defmacro super-unsafe-eval
"Like `eval`, but also exposes lexically-bound variables to eval. This
is almost certainly a bad idea."
[form]
`(eval (list 'let
~(vec (mapcat #(vector `(quote ~%)
`(list 'quote ~%))
(keys &env)))
~form)))
(let [code_as_data '(if (< sequ) on_true on_false)
sequ [1 3 5]
on_true "sequence is sorted in ascending order"
on_false "sequence is NOT sorted"]
(super-unsafe-eval code_as_data))
;;=> "sequence is sorted in ascending order"
(let [code_as_data '(if (apply < sequ) on_true on_false)
on_true "sequence is sorted in ascending order"
on_false "sequence is NOT sorted"]
[(let [sequ [1 3 5]]
(super-unsafe-eval code_as_data))
(let [sequ [1 3 1]]
(super-unsafe-eval code_as_data))])
;;=> ["sequence is sorted in ascending order" "sequence is NOT sorted"]