我目前正在尝试制作一个简单的Clojure宏,它将输入的函数应用两次:(f (f args))
,(例如(inc (inc 4)) => 6
)
问题是,当我使用(reapply-function '(inc 4))
运行以下代码时,我得到nil
。这对我没有意义,因为我可以打印f
的值和得到inc
和5
的结果。我必须要有一件非常简单的事情。有谁能看到这个问题?
(defmacro reapply-function
[args]
(list 'let ['f (list 'first (list 'quote args))
'result args]
(list 'f 'result)))
答案 0 :(得分:4)
假设您正在尝试学习使用宏本身而提供此答案。我完全赞同@Thumbnail's answer:不要使用宏,除非你绝对,积极地无法避免它 - 这不是那个时代之一。
考虑:
(defmacro reapply-function [[func & rest]]
`(~func (~func ~@rest)))
macroexpand
演示了它的工作原理:
user=> (macroexpand '(reapply-function (inc 4)))
(inc (inc 4))
...它在repl中起作用:
user=> (reapply-function (inc 4))
6
通过原始实现,macroexpand-1
为我们提供了这个:
(let [f (first (quote (inc 4)))
result (inc 4)]
(f result))
......确实评估为nil
。
但为什么呢?简而言之: f
在此代码中是符号,而不是符号指向的函数。
因此,要做出使原始代码起作用的最短可能的更改:
(defmacro reapply-function
[args]
(list 'let ['f (list 'first (list 'quote args))
'result args]
(list '(resolve f) 'result)))
答案 1 :(得分:3)
宏俱乐部的第一条规则是......除非必须,否则不要使用宏。
在这种情况下,
(defn twice [f]
(fn [& args] (f (apply f args))))
((twice inc) 4)
;6
或者,如果您希望立即吞下该函数及其参数,
(defn reapply-function [f & args]
(f (apply f args)))
(reapply-function inc 4)
;6
您可以在Programming Clojure by Halloway & Bedra中找到宏俱乐部的规则。而且,上面的问题还是对问题进行了侧重讨论,而不是回答问题。