我想写一个函数(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")))
我该如何解决?
答案 0 :(得分:3)
在这种情况下,您正在为副作用做事,您应该使用doseq
或dotimes
代替:
(dotimes [i 3]
(print "hello! bye "))
无需定义rep-n次。如果您需要具有副作用的函数的结果,请使用repeatedly
。另请注意,repeatedly
和repeat
可选择将重复次数作为参数。
(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#)))))