2012-10-08 61 views
2

我试图确定一个宏在给定的参数是否是一个函数,像宏参数是函数吗?

(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

你可能想问自己为什么你想用这种方式定义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

a在你的宏只是一个clojure列表数据结构(它不是一个函数呢)。所以基本上你需要检查的数据结构a将导致无论是功能还是不是当其被评估,它可以像下面显示来完成:

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

通过检查a的第一个元素是符号fn*或用于创建功能的fn

此宏只适用于2种情况:要么将其传递给匿名函数或表达式。

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")

虽然一般来说,当你想使用某些东西时,你应该使用替代方法并不明显,下面是我如何处理它。

您需要致电macroexpand-alla。如果代码最终变成(fn*)表单,那么它肯定是一个函数。然后你可以安全地发射(〜a〜b)。如果它最终扩展为符号,则也可以发出(~a ~b)。如果符号不是函数,那么在运行时会出现错误。最后,如果宏扩展到列表(函数调用或特殊窗体调用),例如(println ...),则可以发出使用线程宏->的代码。

您还可以覆盖这种情况,例如表单宏展开到数据结构中但尚未指定所需行为的情况。