如何在Clojure中获得参数名称?

时间:2012-08-08 01:40:45

标签: clojure macros var

我想从函数中获取函数外部定义的var的名称。该名称应该是我在原始定义范围内使用的名称,而不是我实际上尝试使用该名称的任何嵌套绑定。

所以我想做(学术范例):

(defn f1 [x1] (println "hello, you passed var name >>" (get-var-name x1) "<<")
(defn f2 [x2] (f1 x2))
(defn f3 [x3] (let [zzz x3] (f2 zzz))
(def my-var 3.1414926)
(f3 my-var)
user> hello, you passed var name >>my-var<<

我能够根据我找到的一些东西来做这个宏:

(defmacro get-var-name [x]
  `(:name (meta (var ~x))))

这在从REPL调用时有效,但编译器在从“内部”范围调用时会阻塞,例如

(defn another-func [y]
  (get-var-name y))

编译器说“说无法解决var y”。 (macroexpand...)显示它正在尝试在当前命名空间中找到局部变量y,而不是当前命名空间中的原始变量。我认为(var...)仅查找名称空间变量,因此这可以防止宏在函数或其他绑定(如let)中工作。

认为我不得不从我定义变量的同一范围手动获取变量名称并将其作为额外参数传递。有没有更优雅的方法通过一系列绑定将var名称信息传递到它所使用的点?那将是坏事。

感谢

3 个答案:

答案 0 :(得分:8)

在函数中获取外部作用域中使用的var的名称是不可能的 - 该函数仅接收在运行时作为参数传递的,不是var本身。

您唯一可能做的就是在每个级别使用宏而不是函数。这允许您在编译时通过不同的宏传递var本身:

(defmacro f1 [x1] `(println "hello, you passed var name >>" ~(str x1) "<<"))
(defmacro f2 [x2] `(f1 ~x2))
(defmacro f3 [x3] (let [zzz x3] `(f2 ~zzz)))

(f3 my-var)
=> hello, you passed var name >> my-var <<

这非常难看 - 您当然不希望用宏来编写所有代码来获得此功能!尽管在某些特殊情况下,例如,如果你正在创建某种基于宏的DSL。

答案 1 :(得分:1)

您可以使用#'阅读器宏将实际的var传递给函数而不是var已解析的值,如下所示:

user=> (defn f1 [x1] (println "hello, you passed var name >>" (:name (meta x1)) "<<"))
#'user/f1
user=> (defn f2 [x2] (f1 x2))
#'user/f2
user=> (defn f3 [x3] (let [zzz x3] (f2 zzz)))  
#'user/f3
user=> (def my-var 3.1414926)
#'user/my-var
user=> (f3 #'my-var)
hello, you passed var name >> my-var <<

如果您想要将值绑定到var,可以使用var-get函数来执行此操作。

答案 2 :(得分:0)

为什么不将(get-var-name _symbol-here_)传递给你想要在体内使用var名称的函数?例如,使用上面给出的get-var-namef2f3my-var完全相同的定义(但略微更改f1):< / p>

(defmacro get-var-name [x]
  `(:name (meta (var ~x))))
(defn f1 [x1] (println "hello, you passed var name >>" x1 "<<"))
(defn f2 [x2] (f1 x2))
(defn f3 [x3] (let [zzz x3] (f2 zzz)))
(def my-var 3.1414926)
=> (f3 (get-var-name my-var))
hello, you passed var name >> my-var <<
=> nil

有时候,你想要引用符号在函数体中引用的值。例如,假设您要在f1中打印出该var名称引用的值。这是你如何做到的:

(defn f1 [x1] (println "hello, you passed var name >>" x1 "<<"
                       "\nwhich refers to value >>" @(resolve x1) "<<"))
=> (f3 (get-var-name my-var))
hello, you passed var name >> my-var << 
which refers to value >> 3.1414926 <<
=> nil

注意@(resolve x1) - 这是返回my-var引用的值的事物(反过来my-varx1引用的值)。< / p>

另外,我想提一下,当传递给它的参数不是符号,或者是符号,但当前没有绑定到某个值时,你当前的get-var-name实现会抛出异常。这是你想要的行为吗?

如果我的建议没有回答你的问题,那么你似乎不希望(get-var-name _symbol-here_)传递给可能最终使用var名称的函数,但出于某种原因,我真的希望能够在函数体内完成(get-var-name ...)。如果是这种情况,为什么你希望能够这样做呢?或者,如果您因为其他原因感觉我没有回答您的问题,那是什么原因?