宏内调用函数和宏的区别?

2024-05-20

我的难题是以下示例:

(defmacro macro1 [x]
  (println x))

(defn func1 [x]
  (println x))

(defmacro macro2 [x]
  `(macro1 ~x)
  (func1 x))

(defmacro macro3 [x]
  (func1 x)
  `(macro1 ~x))

(println "macro2")
(macro2 hello)

(println "macro3")
(macro3 hello)

令人惊讶的是,输出是:

macro2
hello
macro3
hello
hello

为什么macro2和macro3的输出不同?在我的理解中,所有宏内部宏的调用都可以用函数代替(除了重用的原因)。我的理解有什么问题吗?


感谢迈克尔的澄清。我的一般问题是如何选择使用函数或宏内宏来操作 s 表达式。我想知道它们是否可以互换使用,除了它们在不同阶段进行评估之外。另一个例子:

(defn manipulate-func [x]
  (list + x 1))

(defmacro manipulate-macro [x]
  (list + x 1))

(defmacro macro1 [x y]
  [(manipulate-func x) `(manipulate-macro ~y)])

(println (clojure.walk/macroexpand-all '(macro1 (+ 1 2) (+ 3 4))))
;; [(#<core$_PLUS_ clojure.core$_PLUS_@332b9f79> (+ 1 2) 1) (#<core$_PLUS_ clojure.core$_PLUS_@332b9f79> (+ 3 4) 1)]

macro2不打电话macro1。看看它的身体:

`(macro1 ~x)
(func1 x)

第一行是语法引用的;它的值是表单的列表结构(user/macro1 x-value)(假设macro1定义在user命名空间;x-value这是提供给的字面参数macro2)并且没有副作用。因为没有副作用并且该值被丢弃,所以该行没有任何效果。


回应编辑:

首先,区分很重要calling宏体内的另一个宏发出呼叫到另一个宏:

(defmacro some-macro []
  ...)

;; calls some-macro:
(defmacro example-1 []
  (some-macro))

;; emits a call to some-macro:
(defmacro example-2 []
  `(some-macro))

其次,在宏体内调用函数和宏的情况下,必须记住运行时和编译时的相关概念是什么:

  • 宏调用的函数将在宏扩展器的运行时调用,从用户代码的角度来看,这是编译时;

  • 宏调用的宏将在宏体编译时展开。

如果一个宏发出对另一个宏的调用,则与发出的宏调用相关的运行时和编译时间的概念将与与原始宏调用相关的相同。如果一个宏调用另一个宏,它们就会向后移动一步,就像以前那样。

为了说明这一点,让我们考虑一个将其所有工作委托给辅助函数的宏:

(defn emit-abc [abc-name [a b c]]
  `(def ~abc-name {:a ~a :b ~b :c ~c}))

(defmacro defabc [abc-name abc-vals]
  (emit-abc abc-name abc-vals))

来自 REPL:

user> (defabc foo [1 2 3])
#'user/foo
user> foo
{:a 1, :c 3, :b 2}

If emit-abc本身就是一个宏,上面的定义defabc甚至无法编译,因为emit-abc会尝试解构文字符号abc-vals,抛出一个UnsupportedOperationException.

这是另一个例子,可以更容易地解释正在发生的事情:

(let [[a b c] [1 2 3]]
  (defabc foo [a b c]))

defabc接收三个文字符号的向量a, b and c作为第二个参数;它无法访问运行时值1, 2 and 3。它将这个精确的符号向量传递给函数emit-abc,然后能够进入该向量并提取符号以生成地图{:a a :b b :c c}。这张地图成为了defabc称呼。运行时a, b and c结果与值绑定1, 2 and three,所以地图{:a 1 :b 2 :c 3}被生产。

假设我们尝试写emit-abc作为具有相同主体的宏(只是改变defn to defmacro在其定义中)。然后我们就无法有效地从defabc,因为我们没有任何方式向它传递参数的实际值defabc。我们可以写

(emit-abc abc-name [(abc-vals 0) (abc-vals 1) (abc-vals 2)])

to make defabc编译,但这最终会发出abc-name作为正在定义的 Var 的名称并包含向量文字的代码[a b c]在生成的代码中三次。然而我们可以向它发出一个调用:

`(emit-abc ~abc-name ~abc-vals)

这按预期工作。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

宏内调用函数和宏的区别? 的相关文章

  • Clojure Web 应用程序 - 我从哪里开始?

    最近我一直在研究 Clojure 我喜欢这门语言 我想看看我是否可以在其中制作一个小型网络应用程序 只是为了挑战自己 但是 我完全没有设置任何与 Java 相关的 Web 应用程序的经验 事实上 我对 Java 并没有太多的经验 我从哪说起
  • 获取调用表单的名称空间

    我想要一个宏this ns这样它就会返回调用它的位置的名称空间 例如 如果我有这段代码 ns nstest main require nstest core as nstest defn ns str x gt x getName name
  • Scala - Java = ? (或者 Clojure - Java = ?)

    开发人员可以在不懂 Java 的情况下使用 Scala 吗 开发人员可以在不懂 Java 的情况下使用 Clojure 吗 注意 例如 我是一名 C 开发人员 我在不了解任何 VB 的情况下使用 NET 当然 WF 4 0 使用 VB 进行
  • F# 类型提供程序与 Lisp 宏

    我一直在阅读有关 F 3 0 类型提供程序的内容 例如here http msdn microsoft com en us library hh156509 aspx 并且它们似乎基于一种编译时代码生成 在这方面我想知道它们与 Lisp 宏
  • Clojure:让作用域和函数返回值

    我在弄清楚如何使用 let 形式时遇到了一些麻烦 在下面的示例中 我想在本地绑定值 cols 以便稍后在函数中处理它 然而 我注意到 如果我使用 let 函数 sel opt tmp 将返回 nil 值而不是列表 defn sel opt
  • 在抛出异常之前重试某件事 3 次 - 在 clojure 中

    我不知道如何在Clojure中实现这段Python代码 for i in range 3 try except e if i 2 raise e else continue else break 我想知道为什么在 Python 中如此简单的
  • 无法删除 IntelliJ/Cursive 中的括号

    我正在使用 IntelliJ Cursive 编写 Clojure 我发现 删除括号的唯一方法就是将其中的内容完全删除 然后才能将括号删除 例如 假设我有以下代码 list 我只想删除左括号 一旦我在左括号上按退格键 IDE 就会忽略此行为
  • 为什么我收到无法动态绑定非动态变量?

    我正在尝试使用 clojure tools trace 命名空间的 dotrace 函数 dotrace my function my function 5 但我收到了这个错误 IllegalStateException 无法动态绑定非动态
  • 实现只有一个居民的类型的价值

    感谢 MilesSabin 的answer https stackoverflow com a 32157259 867671我可以编写类型级别的斐波那契序列 sealed trait Digit case object Zero exte
  • Clojure/Ring:使用环码头适配器,大请求会给我一个 413: FULL HEAD 错误。

    使用 Ring 的 Jetty 适配器 如果我的请求太大 我会收到 413 FULL HEAD 错误 我追踪到一个名为 headerbuffersize 的属性 但是当我尝试在 run jetty 调用中设置它时 我仍然得到 413 有没有
  • 什么是 fn* 以及 Clojure 如何引导?

    The Clojure core 的来源 https github com clojure clojure blob master src clj clojure core clj似乎假设预先存在fn 这是在哪里定义的 引导程序还需要什么
  • 使用 Scala 宏或反射实例化类

    在我的 scala 代码中 我希望能够实例化一个新类 例如 假设我有以下代码 class Foo def foo 10 trait Bar val bar 20 理想情况下 我希望能够做类似的事情 def newInstance A lt
  • 如何使用语法检查变量的度量(名义/序数/规模)?

    我想使用语法找到变量的度量 然后在 If 语句中使用它 使用语法可以吗 例如 如果我有两个变量a 标称 和b 序数 DO IF a is nominal END IF 您可以创建数据中所有名义变量的列表 在以下示例中 列表将存储在宏调用下
  • __FUNCTION__ 宏的 C# 版本

    有人对 C FUNCTION 宏的 C 版本有好的解决方案吗 编译器似乎不喜欢它 尝试使用这个代替 System Reflection MethodBase GetCurrentMethod Name C 没有 LINE or FUNCTI
  • 从不带破折号的字符串创建 UUID

    如何从不带破折号的字符串创建 java util UUID 5231b533ba17478798a3f2df37de2aD7 gt uuid 5231b533 ba17 4787 98a3 f2df37de2aD7 tl dr java u
  • 你能在 Clojure 中获取加载函数的“代码即数据”吗?

    换一种方式 好吧 代码就是数据 http groups google com group clojure browse thread thread 554cdc59d8a46f01 该线程解决了如何从源文件中读取的问题 但我想知道如何将已加
  • 如何安装 leiningen 插件?

    如何安装 leiningen 插件 例如 leiningen run 我看到这个叫做 clojars org 的东西 以及如何 推 它 但我没有看到任何关于从中 拉 的东西 如果 Clojars 上有可用的插件 例如 lein run 只需
  • 我可以让 lein cloverage 跳过特定测试吗?

    我正在进行一个 Leiningen 项目 其集成测试注释如下 deftest manual test v3 preview preview client http localhost 10313 v3 preview 当我这样做时 这些测试
  • 最有用的用户制作的 C 宏(在 GCC 中,还有 C99)? [关闭]

    就目前情况而言 这个问题不太适合我们的问答形式 我们希望答案得到事实 参考资料或专业知识的支持 但这个问题可能会引发辩论 争论 民意调查或扩展讨论 如果您觉得这个问题可以改进并可能重新开放 访问帮助中心 help reopen questi
  • webjure 与 compojure?

    我听说过两个基于 Clojure 的 Web 应用程序框架 Webjure 和 Compojure 有人可以告诉我哪个更好吗 现在您可以添加Ring http groups google com group clojure browse t

随机推荐