【问题标题】:Is the Macro argument a function?宏参数是函数吗?
【发布时间】:2012-09-28 07:11:41
【问题描述】:
我正在尝试确定宏中的给定参数是否是函数,例如
(defmacro call-special? [a b]
(if (ifn? a)
`(~a ~b)
`(-> ~b ~a)))
这样下面的两个调用都会生成“Hello World”
(call-special #(println % " World") "Hello")
(call-special (println " World") "Hello")
但是,我不知道如何将“a”转换为 ifn?可以理解。任何帮助表示赞赏。
【问题讨论】:
标签:
function
clojure
macros
【解决方案1】:
您可能想问自己为什么要以这种方式定义call-special?。它似乎不是特别有用,甚至不会为您节省任何输入 - 您真的需要一个宏来执行此操作吗?
话虽如此,如果你决心让它工作,那么一种选择是查看a 内部,看看它是否是一个函数定义:
(defmacro call-special? [a b]
(if (#{'fn 'fn*} (first a))
`(~a ~b)
`(-> ~b ~a)))
这是因为#() 函数字面量扩展为如下形式:
(macroexpand `#(println % " World"))
=> (fn* [p1__2609__2610__auto__]
(clojure.core/println p1__2609__2610__auto__ " World"))
我仍然认为这个解决方案相当丑陋,一旦你开始做更复杂的事情(例如使用嵌套宏来生成你的函数)就容易失败
【解决方案2】:
a 在您的宏中只是一个 clojure 列表数据结构(它还不是一个函数)。所以基本上你需要检查数据结构a在评估时是否是一个函数,可以如下所示:
(defmacro call-special? [a b]
(if (or (= (first a) 'fn) (= (first a) 'fn*))
`(~a ~b)
`(-> ~b ~a)))
通过检查a的第一个元素是符号fn*还是fn
用于创建函数。
此宏仅适用于 2 种情况:您将其传递给匿名函数或表达式。
【解决方案3】:
首先,有几点:
- 宏是简单的函数,接收作为输入[文字、符号或文字和符号的集合],并输出[文字、符号或文字和符号的集合]。参数永远不是函数,因此您永远无法直接检查符号映射到的函数。
-
(call-special #(println % " World") "Hello") 包含阅读器宏代码。由于阅读器宏在常规宏之前执行,因此您应该在进行更多分析之前对其进行扩展。通过应用 (read-string "(call-special #(println % \" World\") \"Hello\")") 来做到这一点,它变成了 (call-special (fn* [p1__417#] (println p1__417# "world")) "Hello")。
虽然一般来说,当您可能应该使用替代方法时,您何时想要使用某些东西并不明显,这就是我将如何处理它。
您需要在a 上致电macroexpand-all。如果代码最终变成(fn*) 形式,那么它就保证是一个函数。然后你可以安全地发出 (~a ~b)。如果它宏扩展为最终成为一个符号,您也可以发出(~a ~b)。如果符号不是函数,则在运行时会抛出错误。最后,如果它宏扩展为一个列表(函数调用或特殊形式调用),例如(println ...),那么您可以发出使用线程宏-> 的代码。
您还可以涵盖以下情况,例如表单宏扩展为数据结构,但您没有指定所需的行为。