我在(似乎是)不同的上下文中遇到了本地变量的上下文问题。
我想创建一个特殊的fn
- alternave,我可以存储未评估的代码。 (在以下代码my-fn
)
对于我的用例,我必须变形这段代码。我会使用morph
- 宏来解决此问题,但即使没有这个问题,我的问题也会出现。
; Should work like fn, but save the code of args and body
(defmacro my-fn
[args body]
(with-meta (list 'fn args body)
{:args (list 'quote args)
:body (list 'quote body) }))
; in this macro, the predicate should be morphed later.
; Both args and body will be needed.
; To show my problem, i will skip this morphing and return just the body
(defmacro morph
[args body]
body)
现在我选择一些示例数据并使用my-fn
创建谓词。
在变形和评估新函数时,会发生一些事情,它无法解析谓词内部的局部变量。
; example data for this case
(def people #{{:id "S1" :name "Smith" :status 20 }
{:id "S2" :name "Jones" :status 10 }
{:id "S3" :name "Blake" :status 30 }})
(let [a 20
f (my-fn [t] (<= a (:status t)))] ; pred should be used like fn
(let [pred (eval (list 'fn ; later this second let code comes into a macro or somehting
(:args (meta f))
(list 'morph
(:args (meta f))
(:body (meta f)))))]
(filter pred people))) ; => clojure.lang.Compiler$CompilerException: java.lang.RuntimeException: Unable to resolve symbol: a in this context
有人可以解释一下,为什么会发生这种情况,我该如何避免这种情况?当我在a
的定义中使用常数而不是f
时,一切正常。但这对我的用例没有帮助......
答案 0 :(得分:2)
我不认为eval
捕获上下文,docs状态也没有。
; eval the context too
(eval '(let [a 3] a))
; => 3
; outer scope
(let [a 3] (eval 'a))
; => CompilerException .. Unable to resolve symbol: a in this context ..
编辑:
当我在f的定义中使用常数而不是a时, 一切正常。
那是因为(= '3 3)
为真 - 引用的数字文字是它的值。甚至对a
s值进行任何操作都需要绑定/解析它(这是a
根本不存在/绑定的点,但是你尝试使用它。)
答案 1 :(得分:0)
好的,谢谢你对本地绑定的解释。
对于我的用例,非常重要的是,我可以像my-fn
一样使用fn
(包括使用本地绑定)。
我发现这个thread用于访问宏中的本地绑定。
所以我将本地环境存储在my-fn
中,并在变形谓词周围放置一个let
,其中包含上面的所有本地绑定(在以后的版本中只是需要的绑定)和谓词单词。
所以我的新版本的函数和宏看起来像这样:
(defmacro my-fn
[args body]
(with-meta (list 'fn args body)
{:args (list 'quote args)
:body (list 'quote body)
:env (list 'get-env)}))
(defmacro morph
[args body]
body)
; example data for this case
(def people #{{:id "S1" :name "Smith" :status 20 }
{:id "S2" :name "Jones" :status 10 }
{:id "S3" :name "Blake" :status 30 }})
(let[a 20
f (fn [t] (<= a (:status t)))]
(filter f people))
(defmacro get-env
[]
(into {} (for [k (keys &env)]
[(name k) k])))
(let [a 20
f (my-fn [t] (<= a (:status t)))] ; pred should be used like fn
(let [env (get-env)
pred (eval (list 'fn ; later this second let code for comes into a macro or somehting
(:args (meta f))
(list 'let
(reduce (fn[v [x y]] (conj v (read-string x) y)) [] (:env (meta f)))
(list 'morph
(:args (meta f))
(:body (meta f))))))]
(filter pred people))) ; => ({:id "S3", :name "Blake", :status 30} {:id "S1", :name "Smith", :status 20})
THX!