【问题标题】:How to define a repeat function with macros如何使用宏定义重复函数
【发布时间】:2016-03-09 00:31:38
【问题描述】:

我想写一个函数(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")))

我该如何解决?

【问题讨论】:

    标签: clojure macros


    【解决方案1】:

    在这种情况下,您正在为副作用做事,您应该改用doseqdotimes

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

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

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

    但是,关于您对rep-n-times 的定义的问题,调用repeat 需要一个参数,即(print "hello") 的评估结果为nil。所以你要 4 个 nil,得到 4 个 nil。当参数被评估为 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#)))))
    

    【讨论】:

    • 嗨蒂莫西!我完全同意你的看法!但是,我很抱歉我没有把我的问题说清楚。我被要求编写一个必须用宏定义的函数,我不会重复相同的字符串。正如我上面的问题,该函数需要一个或两个或多个参数。例如,(rep-n-times 5 (print "hello") (print "good") (print "night")....)。跨度>
    • 嗨秀芬,好的,我在答案中添加了一个如何将其作为宏实现的示例。
    • 嗨蒂莫西!我非常感谢。我查了很多关于 auto-gensym 和 gensym 的资料,但我仍然不知道它们到底是做什么的。 “使用 gensym 获取唯一标识符”是什么意思?谢谢
    • 在宏中使用符号的问题是它可能与另一个符号冲突。例如,如果我们只是使用i 作为我们的符号而不是i#,那么i 的含义将会改变。因此,如果我们写(let [i 1] (rep-n-times 3 (println i))),我们会对结果感到惊讶。 i# 生成一个随机符号名称,以便将其绑定在由具有唯一名称的宏创建的扩展代码内。这就是它真正的意思。
    猜你喜欢
    • 2011-02-22
    • 1970-01-01
    • 1970-01-01
    • 2016-01-08
    • 2013-10-08
    • 2020-11-22
    • 2016-05-07
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多