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

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(使用前将#替换为@)

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

  • 最有用的用户制作的 C 宏(在 GCC 中,还有 C99)? [关闭]

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

    我在阅读时在 IntelliJ IDEA 中运行 Clojure 1 3 0 和 La ClojureClojure 的乐趣 在第 4 1 3 节 第 64 页 中 作者使用以下代码演示了整数溢出 Integer MAX VALUE Int
  • 为什么我的字符串函数返回 clojure.lang.LazySeq@xxxxxx?

    我使用 leiningen REPL 定义了以下 3 个函数 defn rand int range floor ceiling floor rand int ceiling floor defn mutate index mutates
  • 如何检查spec/col-of中的不同ID

    s def users s coll of user distinct true 上面的规范要求每个用户映射都是不同的 但是我如何指定它来检查不同的 user ids only 不应允许以下集合 id 10 name Jessica id
  • Clojure 的映射和减少 Monad...Juxt Monad 怎么样?

    在学习 Clojure 的过程中 我花了很长时间试图理解 monad 它们是什么以及我们如何使用它们 但没有取得太大成功 然而 我发现了一个很棒的 Monads for Dummies 视频系列 http vimeo com 2071730
  • 如何解构向量以用作函数参数

    在 Python 中 您可以将列表或元组传递给函数并让函数解压参数 我怎样才能在 Clojure 中做到这一点 下面是一些 Python 代码示例 def f a b c d print a a print b b print c c pr
  • clojure.spec 人类可读的形状?

    使用 clojure spec 有没有办法为嵌套映射定义更 人类可读 的规范 以下内容读起来不太好 s def my domain entity s keys req un a b s def a s keys req un c d s d
  • C 相当于 C++ decltype

    在我的 C 项目中 有一个由另一位同事创建的结构体 其中包含一些函数指针 struct tools int tool a int int int 我无权更改此结构和相关文件 现在我正在使用该结构进行编码 我必须定义一个函数 其返回类型和参数
  • 如何创建一个扩展为“(x+y*240)*2”这样的表达式的 GNU GAS 宏?

    我正在使用 GAS 为 ARM Linux 构建一个程序 但我想做一些宏以使我的开发更加智能 然后我想知道 我怎样才能为此做一个宏 x y 240 2 were x and y are int 将像这样使用 mov r0 MACRO SHO
  • 在 Android 上运行 Java 字节码 - DalvikVM 之上的 Sun JVM

    由于 java 实现 OpenJDK 和Android的虚拟机DalvikVM是开源的 因此必须可以在Google的DalvikVM之上实现Sun的JavaVM 这将使运行基于 JVM 的应用程序和语言成为可能 Clojure Jython
  • 使用命令行界面构建 Clojure 应用程序?

    我刚刚开始使用 Clojure 来自 Ruby 我想构建一个带有命令行界面的小型应用程序 如何处理 CL 的输入 输出 我注意到有一个 clojure contrib command line 但文档很少 http github com r
  • 用于嵌套表达式的方案宏

    可以在Scheme中编写宏吗 用define syntax 例如 将采用如下表达式 op a b c d e f g h i j 并将像这样的yield表达式作为输出 op op op op op op op op op a b c d e
  • 如何将 clojure Web 应用程序部署到 Amazon EC2(AWS Elastic Beanstalk + Leiningen + Compojure + Ring + Tomcat)

    如题 我的IDE是intellij idea 12 1 4 我需要什么工具包或插件才能 将 Clojure Web 应用程序部署到 Amazon EC2 有任何链接或参考或分步解决方案吗 谢谢 如果您只是部署一个 war 文件 没有其他自定
  • 调用 Clojure 高阶函数

    如果我定义一个返回如下函数的函数 defn add n n fn x x n 然后我可以将结果分配给一个符号 def add 1 add n 1 并称其为 add 1 41 gt 42 我如何调用结果 add n 1 而不将其分配给新符号
  • C - 可变参数宏,它扩展为每个参数的一组宏调用

    我想要一个接受多个函数指针的宏调用 并且每个函数指针由第二个宏调用 该宏是函数声明 我想要表单上的两个宏 define FUNCTION DEF func extern int func void define FUNCTION DEFS
  • 对C宏扩展和整数运算感到困惑[重复]

    这个问题在这里已经有答案了 可能的重复 一个谜语 C https stackoverflow com questions 5611724 a riddle in c 我对以下代码片段有几个问题 include
  • 为什么 CouchDB 使用仅追加 B+ 树而不是 HAMT

    我正在阅读数据结构 尤其是不可变的数据结构 例如仅追加 B 树 http guide couchdb org draft btree html用于 CouchDB 和哈希数组映射 trie http en wikipedia org wik
  • 如何在 REPL 中显示 Clojure 中函数的定义?

    我正在寻找让 REPL 打印函数当前定义的能力 有什么办法可以做到这一点吗 例如 给定 defn foo if true true 我想说类似的话 print definition foo 并得到一些类似的东西 foo if true tr
  • 如何在 Access 2010 中创建自定义 ID

    这个问题的标题可能不准确 因为我不确定如何提出这个问题 有没有办法让 AC 2010 中的 ID 字段具有常量部分 然后是用户将输入的 ID 的一部分 示例 EMP9066 我希望 ID 的 EMP 部分始终保持不变 用户不应更改它 并且
  • defn 和 defmacro 和有什么不一样?

    defn 和 defmacro 和有什么不一样 函数和宏有什么区别 defn定义一个函数 defmacro定义一个宏 函数和宏之间的区别在于 在函数调用时 首先评估函数的参数 然后使用参数评估函数体 另一方面 宏描述了从一段代码到另一段代码

随机推荐