在 Clojure 循环中附加到数组

Appending to arrays in a Clojure loop

我正在尝试编写一个函数来将 0 到 50 之间的所有 3 和 5 的倍数相加,但 Clojure 似乎决定在我告诉它时不将正确的值添加到我的列表中。

(conj toSum counter) 形式应该将当前数字附加到 toSum 数组,当循环退出时 (reduce + toSum) 形式应该将数组中的所有内容加在一起。

就目前而言,当 reduce 函数被调用时,toSum 始终为空,因为 conj 函数没有执行它应该执行的操作。我一定是在某个地方搞砸了我的逻辑,但我似乎无法弄明白。

(defn calculate [target]
  (loop [counter target
         toSum []]
        (if (= 0 counter)
            (reduce + toSum)
          (if (or (= 0 (mod counter 3)) (= 0 (mod counter 5)))
              (do (conj toSum counter)
                  (println toSum)
                  (recur (dec counter) toSum))    
            (recur (dec counter) toSum)))))

conj returns 一个新的集合,它不会改变当前的集合。您需要分配新的集合并重复使用:

(let [nextSum (conj toSum counter)]
   (println nextSum)
   (recur (dec counter) nextSum))

一种更实用和惯用的方法:

(defn multiple-of-3-or-5? [n]
  (or (zero? (mod n 3)) (zero? (mod n 5))))

(defn calculate-functional [target]
  (->> (range 1 (inc target))
       (filter multiple-of-3-or-5?)
       (apply +)))

->> 是线程最后一个宏,这个宏采用第一个传递的形式(range 形式)并将其作为最后一项插入下一个形式(filter form) 而不是采用这个新形式并将其作为最后一项插入 apply 形式中。所以这个宏把calculate-functional函数转换成这样:

(apply + (filter is-multiple-3-or-5?
                 (range 1 (inc target))))

在这种情况下,thread last macro 并不是一个巨大的改进,但是当您的 "pipeline" 中有更多步骤时,使用 thread macros 可以明显更容易阅读。

(range 1 (inc target)) 表单在 REPL 中创建一个从 1 开始到 target 结束的 seq

(range 1 10)
=> (1 2 3 4 5 6 7 8 9)

下一个表达式是 (filter multiple-of-3-or-5?)filter keeps the elements of the sequence for which the predicate is true. And finally (apply +) uses apply+ 应用于序列的所有元素。你也可以在这里使用(reduce +),我只是想展示非常有用的apply