无法在上下文

时间:2016-02-25 11:10:20

标签: clojure

我在(似乎是)不同的上下文中遇到了本地变量的上下文问题。

我想创建一个特殊的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时,一切正常。但这对我的用例没有帮助......

2 个答案:

答案 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!