Clojure 如何使用宏定义重复函数

Clojure 如何使用宏定义重复函数,clojure,macros,Clojure,Macros,我想编写一个函数(rep-n乘以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 (

我想编写一个函数(rep-n乘以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")))

我如何修复它?

在这种情况下,如果您正在做一些副作用的事情,您应该使用
doseq
dotimes

(dotimes [i 3]
  (print "hello! bye "))
不需要定义rep-n-times。如果需要具有副作用的函数的结果,请反复使用
。还要注意,
重复
重复
可选地将重复次数作为参数

(repeatedly 3 (fn [] (print "hello! bye ") (rand-int 10)))
但是,关于您定义的
rep-n-times
的问题,调用
repeat
需要一个参数,这是
(打印“hello”)
的计算结果,该值为零。所以你要求4个零,得到4个零。当参数计算为nil时,打印只发生一次。它还生成一个惰性序列,该序列恰好在REPL处进行计算,但这只是因为它正在打印。您应该避免在惰性序列中产生副作用(例如打印),因为除非您明确实现该序列,否则不会对其进行评估

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

(dotimes [i 3] (print "h1") (print "h2") (print "h3"))
dotimes是一个宏定义的

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

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

在这种情况下,如果您正在做一些副作用的事情,您应该使用
doseq
dotimes

(dotimes [i 3]
  (print "hello! bye "))
不需要定义rep-n-times。如果需要具有副作用的函数的结果,请反复使用
。还要注意,
重复
重复
可选地将重复次数作为参数

(repeatedly 3 (fn [] (print "hello! bye ") (rand-int 10)))
但是,关于您定义的
rep-n-times
的问题,调用
repeat
需要一个参数,这是
(打印“hello”)
的计算结果,该值为零。所以你要求4个零,得到4个零。当参数计算为nil时,打印只发生一次。它还生成一个惰性序列,该序列恰好在REPL处进行计算,但这只是因为它正在打印。您应该避免在惰性序列中产生副作用(例如打印),因为除非您明确实现该序列,否则不会对其进行评估

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

(dotimes [i 3] (print "h1") (print "h2") (print "h3"))
dotimes是一个宏定义的

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

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

嗨,蒂莫西!我完全同意你!不过,很抱歉,我没有把问题说清楚。我被要求编写一个必须用宏定义的函数,我不会重复同样的字符串。正如我上面的问题,函数需要一个或两个或多个参数。比如,(rep-n-times 5(print“hello”)(print“good”)(print“night”)…。嗨,修芬,好的,我在答案中添加了一个如何将其实现为宏的示例。嗨,蒂莫西!我真的很感激。我查阅了很多关于汽车gensym和gensym的资料,但我仍然不知道它们到底是做什么的。“使用gensym获取唯一标识符”是什么意思?在宏中使用符号的问题是,它可能与另一个符号冲突。例如,如果我们只是使用
i
作为我们的符号,而不是
i
,那么
i
的含义就会改变。因此,如果我们编写
(让[i1](rep-n乘以3(printlni))
,我们会对结果感到惊讶
i#
生成一个随机符号名,这样它将绑定在宏创建的扩展代码中,并具有唯一的名称。这就是它真正的意义。嗨,蒂莫西!我完全同意你!不过,很抱歉,我没有把问题说清楚。我被要求编写一个必须用宏定义的函数,我不会重复同样的字符串。正如我上面的问题,函数需要一个或两个或多个参数。比如,(rep-n-times 5(print“hello”)(print“good”)(print“night”)…。嗨,修芬,好的,我在答案中添加了一个如何将其实现为宏的示例。嗨,蒂莫西!我真的很感激。我查阅了很多关于汽车gensym和gensym的资料,但我仍然不知道它们到底是做什么的。“使用gensym获取唯一标识符”是什么意思?在宏中使用符号的问题是,它可能与另一个符号冲突。例如,如果我们只是使用
i
作为我们的符号,而不是
i
,那么
i
的含义就会改变。因此,如果我们编写
(让[i1](rep-n乘以3(printlni))
,我们会对结果感到惊讶
i#
生成一个随机符号名,这样它将绑定在宏创建的扩展代码中,并具有唯一的名称。这就是它真正的意义所在。