Macro参数是函数吗?

时间:2012-10-08 03:18:14

标签: function clojure macros

我试图确定宏中的给定参数是否是函数,如

(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的东西?可以理解。任何帮助表示赞赏。

3 个答案:

答案 0 :(得分:4)

您可能想要问自己为什么要以这种方式定义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"))

我仍然认为这个解决方案相当丑陋并且一旦你开始做更复杂的事情就容易失败(例如使用嵌套宏来生成你的函数)

答案 1 :(得分:2)

首先,有几点:

  1. 宏只是作为输入接收的函数[文字,符号或文字和符号的集合],并输出[文字,符号或文字和符号的集合]。参数永远不是函数,因此您永远无法直接检查符号映射到的函数。
  2. (call-special #(println % " World") "Hello")包含读者宏代码。由于读取器宏是在常规宏之前执行的,因此您应该在进行任何更多分析之前对其进行扩展。通过应用(read-string "(call-special #(println % \" World\") \"Hello\")")成为(call-special (fn* [p1__417#] (println p1__417# "world")) "Hello")
  3. 来执行此操作

    虽然一般来说,当你应该使用替代方法时,你想要使用的东西并不明显,这就是我接近它的方法。

    您需要在macroexpand-all上致电a。如果代码最终成为(fn*)形式,则保证它是一个函数。然后你可以安全地发射(~a~b)。如果宏扩展为最终成为符号,您还可以发出(~a ~b)。如果符号不是函数,则会在运行时抛出错误。最后,如果它宏扩展到列表(函数调用或特殊形式调用),如(println ...),那么您可以发出使用线程宏->的代码。

    您还可以涵盖诸如表单宏扩展到数据结构中但未指定所需行为的情况。

答案 2 :(得分:1)

宏中的

a只是一个clojure列表数据结构(它还不是一个函数)。所以基本上你需要检查数据结构a在评估时是否会产生函数,这可以像下面的节目那样完成:

(defmacro call-special? [a b]
  (if (or (= (first a) 'fn) (= (first a) 'fn*)) 
     `(~a ~b)
     `(-> ~b ~a)))

通过检查a的第一个元素是符号fn*还是fn  用于创建函数。

此宏仅适用于2种情况:要么传递匿名函数,要么传递表达式。