Clojure - 使用变量作为函数调用

时间:2016-11-06 02:46:54

标签: clojure

我目前正在尝试制作一个简单的Clojure宏,它将输入的函数应用两次:(f (f args)),(例如(inc (inc 4)) => 6

问题是,当我使用(reapply-function '(inc 4))运行以下代码时,我得到nil。这对我没有意义,因为我可以打印f的值和得到inc5的结果。我必须要有一件非常简单的事情。有谁能看到这个问题?

(defmacro reapply-function
  [args]
  (list 'let ['f (list 'first (list 'quote args))
              'result args]
        (list 'f 'result)))

2 个答案:

答案 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中找到宏俱乐部的规则。而且,上面的问题还是对问题进行了侧重讨论,而不是回答问题。