【问题标题】:How to find sum of n number using clojure for loop macro?如何使用clojure for循环宏查找n个数的总和?
【发布时间】:2014-10-28 14:42:21
【问题描述】:

我在 clojure 中使用了 for 循环宏:

 (defmacro for-loop [[sym initial check-exp post-exp] & steps]
 `(loop [~sym ~initial]
    (if ~check-exp
      (do
        ~@steps
        (recur ~post-exp)))))

我想写一个简单的函数来求n个数字的总和,比如:

for(int i=1; i<n; i++)
sum=sum+i;

如何使用 for-loop 宏在 clojure 中执行此操作?

【问题讨论】:

  • 拥有一个变量并更新它的值不是惯用的clojure,你可能想要(reduce + (range (inc n)))这样的东西。
  • 但是,这种情况仅在不使用 for 循环宏的情况下发生。反正有没有使用for循环宏?我正在尝试学习宏,因此问。
  • “学习宏”是指“学习编写宏”吗?还是“学习使用宏”?因为后者不需要太多专业知识(不过需要一些文档),而且它绝对是前者的先决条件。

标签: macros clojure


【解决方案1】:

首先,这里不需要使用宏。其次,您使用do 并最终调用recur 将阻止宏返回除nil 以外的值,如Lee 的回答中所述。当您没有真正提供 else 案例时,您应该使用 when 而不是 if 只是一件小事,但暗示了一个可能更好的解决方案:使用 else 案例返回结果。为此,您需要在方程中引入一个累加器。

(defmacro for-loop [[sym initial result initial-result check-exp post-exp] & steps]
 `(loop [~sym ~initial
         ~result ~initial-result]
    (if ~check-exp
      (do
        ~@steps
        (recur ~@post-exp))
      ~result)))

这仍然很丑陋,但允许您像这样解决求和任务,实际上不需要任何中间步骤:

 (for-loop [i 0 r 0 (< i n) ((inc i) (+ r i))] nil)

如果您对此进行宏扩展,则更容易看到正在发生的事情:

 (pprint (macroexpand-1 '(for-loop [i 0 r 0 (< i n) ((inc i) (+ r i))] nil)))
 ==> (clojure.core/loop [i 0 r 0]
       (if (< i n) 
           (do nil 
               (recur (inc i) (+ r i))) 
           r))

如果不必显式命名和使用累加器,而只需使用来自steps 的结果,那就太好了,但是因为这些步骤可能需要引用到目前为止所建立的任何中间值例如,没有办法绕过它。

【讨论】:

    【解决方案2】:
    (def sum (atom 0))
    (def n 100)
    
    (for-loop [i 0 (< i n) (inc i)] (swap! sum #(+ % i)))
    

    【讨论】:

    • 嗨@Lee,它给出了一个零值。
    • @MarthaPears - 是的,循环计算结果为 nil,但 @sum 包含总和。循环仅针对其效果执行。
    • 使用外部原子确实不习惯。
    • @schaueho - 除非我遗漏了一些东西,否则for-loop 宏的计算结果总是nil,所以如果你想用它来计算一个值,你必须使用外部状态。
    • 是的,我同意如果坚持使用所提供的确切 for-loop 宏,这可能是唯一的方法。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-05-18
    • 2015-12-22
    • 2018-10-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多