3

我想从函数内部获取在函数外部定义的 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.

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

谢谢

4

3 回答 3

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。

于 2012-08-08T03:52:43.260 回答
1

您可以使用 reader 宏将实际 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函数来执行此操作。

于 2012-08-08T04:23:31.313 回答
0

为什么不直接传递(get-var-name _symbol-here_)给您想要在正文中使用 var 名称的函数呢?例如,使用与您在上面给出的完全相同的定义get-var-name(但f2略有变化):f3my-varf1

(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)--this 是返回my-var引用的值(反过来又my-var是 引用的值x1)的东西。

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

如果我的建议不能回答您的问题,那么您似乎不想传递(get-var-name _symbol-here_)给最终可能使用 var 名称的函数,而是出于某种原因确实希望能够(get-var-name ...)从内部执行函数体。如果是这种情况,为什么您希望能够那样做?或者,如果您觉得我出于其他原因没有回答您的问题,那是什么原因?

于 2012-08-08T03:35:13.333 回答