在 Clojure 中递归求和所有三的倍数

Summing all the multiples of three recursively in Clojure

嗨,我对 Clojure/Lisp 编程有点陌生,但我以前在 C 之类的语言中使用过递归,我编写了以下代码来对 1 到 100 之间所有可以被三除的数字求和。

(defn is_div_by_3[number]
  (if( = 0 (mod number 3))
    true false) 
 )

(defn sum_of_mult3[step,sum]
  (if (= step 100)
    sum
    )
  (if (is_div_by_3 step)
    (sum_of_mult3 (+ step 1 ) (+ sum step)) 
    )
  )

我的想法是在 step 达到总和时结束递归,然后我将在我 return 的总和变量中得到我需要的所有倍数,但我的 REPL 似乎 returning nil对于这两个变量,这里可能有什么问题?

if is an expression not a statement. The result of the if is always one of the branches. In fact Clojure doesn't have statements has stated here:

Clojure programs are composed of expressions. Every form not handled specially by a special form or macro is considered by the compiler to be an expression, which is evaluated to yield a value. There are no declarations or statements, although sometimes expressions may be evaluated for their side-effects and their values ignored.

有一本适合初学者的不错的在线(免费)书籍:http://www.braveclojure.com

另外,Lisps 中的圆括号不等同于 C 系列语言中的大括号。例如,我会将您的 is_div_by_3 函数写为:

(defn div-by-3? [number]
  (zero? (mod number 3)))

我还会对 sum_of_mult3 函数使用更惯用的方法:

(defn sum-of-mult-3 [max] 
  (->> (range 1 (inc max))
       (filter div-by-3?)
       (apply +)))

我认为这段代码比递归版本更能表达其意图。唯一的技巧是 ->> thread last macro. Take a look at 用于解释线程最后一个宏。

此代码存在一些问题。

1) 您在 sum_of_mult3 中的第一个 if 是空话。 returns 不会影响函数的执行。

2) sum_of_mult3 中的第二个 if 只有一个条件,如果步长是 3 的倍数则直接递归。对于大多数数字,不会采用第一个分支。第二个分支只是一个隐含的 nil。无论输入如何,您的函数都保证return(即使提供的第一个参数是三的倍数,下一个重复值也不会)。

3) 尽可能使用 recur 而不是自我调用,自我调用会消耗堆栈,recur 编译成一个不消耗堆栈的简单循环。

最后,一些样式问题:

1) 始终将右括号与要关闭的块放在同一行。这使得 Lisp 风格的代码更具可读性,如果不出意外的话,我们大多数人也会阅读 Algol 风格的代码,将括号放在正确的位置会提醒我们正在阅读哪种语言。

2) (if (= 0 (mod number 3)) true false)(= 0 (mod number 3) 相同,后者又与 (zero? (mod number 3))

相同

3) 使用 (inc x) 而不是 (+ x 1)

4) 对于两个以上的潜在操作,使用 casecondcondp

(defn sum-of-mult3
  [step sum]
  (cond (= step 100) sum
        (zero? (mod step 3)) (recur (inc step) (+ sum step))
        :else (recur (inc step) sum))

除了罗德里戈的回答之外,这是我想到的第一种解决问题的方法:

(defn sum-of-mult3 [n]
  (->> n
       range
       (take-nth 3)
       (apply +)))

这应该是不言自明的。这里有一个不使用序列的更 "mathematical" 的方法,考虑到所有数字的总和直到 N 包括在内是 (N * (N + 1)) / 2.

(defn sum-of-mult3* [n]
  (let [x (quot (dec n) 3)]
    (* 3 x (inc x) 1/2)))

正如 Rodrigo 所说,递归不是完成此任务的正确工具。