在Clojure中使用非命中板的符号

发布于 2025-01-21 02:50:33 字数 1366 浏览 1 评论 0原文

这是一个最小的工作示例,展示了 Clojure 如何处理非命名空间符号:

(defmacro simple-macro [s]
  (name `~s))

(str "And the answer is "
     (simple-macro v1))

现在我想做一些更复杂的事情。受到这个例子的启发:

(defn typical-closure []
  (let [names (atom [])]
    (fn [arg] (swap! names conj arg) @names)))

(def Q (typical-closure))
(Q 1)
(Q 2)
;; [1 2]

我现在想定义一个类似的闭包来获取未定义变量的名称。

(defn take-names-fun []
  (let [names (atom [])]
    #((swap! names conj (simple-macro %)) (deref names))))

(def P (take-names-fun))
(P v1)

但这并没有达到预期效果;我收到错误:

在此上下文中无法解析符号:v1

解决此问题,以便我们可以将名称“v1”添加到上面定义的名称列表中?

我尝试使用宏(受到“掌握 Clojure 宏”第 21 页上的语法技巧的启发)...但是 ask.clojure.org 上的这个答案 表示在宏中的原子上定义闭包是没有意义的。

(defmacro take-names-macro []
  (let [names (atom [])]
    `(fn [~'x] (swap! ~names conj (simple-macro ~'x)) (deref ~names))))

(def R (take-names-macro))

事实上,我在这里遇到另一个错误:

无法在代码中嵌入对象,可能 print-dup 未定义:

但是,在 defn 中使用原子没有这样的限制。也许最终我需要将符号放入命名空间中......?

Here's a working minimal example showing how Clojure can handle non-namespaced symbols:

(defmacro simple-macro [s]
  (name `~s))

(str "And the answer is "
     (simple-macro v1))

Now I'd like to do something more complicated. Inspired by this example:

(defn typical-closure []
  (let [names (atom [])]
    (fn [arg] (swap! names conj arg) @names)))

(def Q (typical-closure))
(Q 1)
(Q 2)
;; [1 2]

I now want to define a similar closure to take the names of undefined variables.

(defn take-names-fun []
  (let [names (atom [])]
    #((swap! names conj (simple-macro %)) (deref names))))

(def P (take-names-fun))
(P v1)

But this doesn't work as hoped; I get the error:

Unable to resolve symbol: v1 in this context

Is there a way to fix this so that we can add the name "v1" to the list of names defined above?

I tried using a macro instead (inspired by a syntax trick on page 21 of "Mastering Clojure Macros")... but this answer on ask.clojure.org says it doesn't make sense to define a closure over an atom in a macro.

(defmacro take-names-macro []
  (let [names (atom [])]
    `(fn [~'x] (swap! ~names conj (simple-macro ~'x)) (deref ~names))))

(def R (take-names-macro))

And indeed, I get another error here:

Can't embed object in code, maybe print-dup not defined:

However, there is no such restriction for using atoms inside defn. Maybe at the end of the day I need to put my symbols in a namespace...?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

相守太难 2025-01-28 02:50:33

不太确定您最终试图完成的是什么。

但是,由于p是一个函数,因此它将始终评估其参数。因此,如果将其传递给未定义的符号,您将获得错误的错误。相反,您必须创建一个宏,以便可以引用未定义的符号(以停止对参数的评估),然后将其传递给p。这是这样做的示例。

user> (defn take-names-fun []
        (let [names (atom [])]
          (fn [arg] (swap! names conj (name  arg)))))
#'user/take-names-fun
user> (def P (take-names-fun))
#'user/P
user> (defmacro PM [s] `(P (quote ~s)))
#'user/PM
user> (PM v1)
["v1"]
user> (PM v2)
["v1" "v2"]
user> 

您可能会在

Not quite sure what it is that you're ultimately trying to accomplish.

But, since P is a function, it will always evaluate its arguments. So, if you pass it an undefined symbol, you'll get the error you got. Instead, you have to create a macro so that you can quote the undefined symbol (to stop the evaluation of the argument) and then pass that to P. Here is an example that does that.

user> (defn take-names-fun []
        (let [names (atom [])]
          (fn [arg] (swap! names conj (name  arg)))))
#'user/take-names-fun
user> (def P (take-names-fun))
#'user/P
user> (defmacro PM [s] `(P (quote ~s)))
#'user/PM
user> (PM v1)
["v1"]
user> (PM v2)
["v1" "v2"]
user> 

You might find the article on Evaluation in Clojure helpful.

花心好男孩 2025-01-28 02:50:33

@Dorab的答案很好。
但是您也可以告诉自己:“将未定义的变量输入函数时,我必须引用它们以避免对它们进行评估!”

因此,之后:

(defn typical-closure []
  (let [names (atom [])]
    (fn [arg] (swap! names conj arg) @names)))

(def Q (typical-closure))

做:

user=> (Q 'v1)
[v1]
user=> (Q 'v2)
[v1 v2]
user=> (Q 3)
[v1 v2 3]
user=> (Q 'v4)
[v1 v2 3 v4]
user=> 

这样您就不需要宏,可以在评估和未评估的参数(未定义的符号)之间交替。

@dorab's answer is nice.
But you could also tell yourself: "When entering undefined variables into a function, I have to quote them to avoid evaluation of them!"

So, after:

(defn typical-closure []
  (let [names (atom [])]
    (fn [arg] (swap! names conj arg) @names)))

(def Q (typical-closure))

Do:

user=> (Q 'v1)
[v1]
user=> (Q 'v2)
[v1 v2]
user=> (Q 3)
[v1 v2 3]
user=> (Q 'v4)
[v1 v2 3 v4]
user=> 

In this way you don't need the macro and you can alternate between evaluated and not-evaluated arguments (undefined symbols).

白昼 2025-01-28 02:50:33

因此,不幸的是,按照 fn 在 clojure 中编写的方式,无法从 fn 主体中获取作为参数传递的 var 的名称。凭借对 clojure src 的更多经验,也许能够更好地解释为什么会这样,我最初的猜测是它与保持线程本地作用域的隔离和惰性有关。

但是绝对没有什么可以阻止您使用闭包想法编写一个包装其他宏的宏!

下面是如何编写类似内容的示例:
https://stackoverflow.com/a/11857444

So with the way fn's are written in clojure there is unfortunately no way to get the name of the var being passed as a param from within the fn body.. Someone with more experience with the clojure src may be able to explain better why that is, my initial guess would be that it has something to do with keeping thread local scopes isolated and lazy.

But there's absolutely nothing stopping you from writing a macro that wraps other macros using your closure idea!

Here's an example of how something like that may be written:
https://stackoverflow.com/a/11857444

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文