如何用宏定义重复函数

时间:2016-03-09 00:31:38

标签: clojure macros

我想写一个函数(rep-n-times n& args),它应该像:

user=>(rep-n-times 3 (print "hello!") (print "bye"))
hello! bye hello! bye hello! bye nil

我的代码是

(defmacro ntimes [n & body]
      `(take ~n (repeat ~@body)))

测试:

#'user/rep-n-times
user=> (rep-n-ntimes 5 (print "hah"))
hah(nil nil nil nil nil)
user=> (macroexpand '(rep-n-ntimes 4 (print "hello")))
(clojure.core/take 4 (clojure.core/repeat (print "hello")))

我该如何解决?

1 个答案:

答案 0 :(得分:3)

在这种情况下,您正在为副作用做事,您应该使用doseqdotimes代替:

(dotimes [i 3]
  (print "hello! bye "))

无需定义rep-n次。如果您需要具有副作用的函数的结果,请使用repeatedly。另请注意,repeatedlyrepeat可选择将重复次数作为参数。

(repeatedly 3 (fn [] (print "hello! bye ") (rand-int 10)))

但是对于rep-n-times定义的问题,调用repeat只需要一个参数,这是(print "hello")的评估结果,即nil。所以你要求4个nils,得到4个nils。当参数被评估为nil时,打印发生一次。它也会产生一个懒惰的序列,恰好在REPL上进行评估,但这只是因为它正在被打印。你应该避免在延迟序列中产生副作用(例如打印),因为除非你明确地意识到序列,否则它们不会被评估。

请注意,dotimes可以采用多种形式:

(dotimes [i 3] (print "h1") (print "h2") (print "h3"))

而dotimes是一个定义为宏的here

您可以使用该代码作为起点编写自己的版本:

(defmacro rep-n-times [n & body]
  `(loop [i# ~n]
     (when (pos? i#)
       ~@body
       (recur (dec i#)))))