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

时间:2015-02-09 17:23:44

标签: recursion clojure lisp

您好我对Clojure / Lisp编程有点新,但我之前在C语言中使用了递归,我编写了以下代码来汇总所有可以在1到100之间除以3的数字。

(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达到sum时结束递归,然后我会在我返回的sum变量中获得所需的所有倍数,但是我的REPL似乎为这两个变量返回nil这里可能有什么问题?

3 个答案:

答案 0 :(得分:4)

if表达式,不是声明if的结果始终是其中一个分支。实际上Clojure没有陈述here

  

Clojure程序由表达式组成。每个未由特殊形式或宏专门处理的表单都被编译器视为表达式,该表达式被计算为产生值。没有声明或陈述,尽管有时可以评估表达式的副作用并忽略它们的值。

有一本适合初学者的好书({3}}

另外,Lisp中的括号不等同于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 +)))

我认为这个代码的意图比递归版更具表现力。唯一的技巧是http://www.braveclojure.com线程最后一个宏。请查看->>以获取有关线程最后一个宏的说明。

答案 1 :(得分:1)

此代码存在一些问题。

1)if中的第一个sum_of_mult3是noop。它返回的任何内容都不会影响函数的执行。

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

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

最后,一些风格问题:

1)总是将关闭的parens与他们关闭的区块放在同一条线上。这使得Lisp样式代码更具可读性,如果没有别的东西我们大多数人也会阅读Algol样式代码,并且将parens放在正确的位置会提醒我们正在阅读哪种语言。

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))

答案 2 :(得分:0)

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

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

这应该是不言自明的。考虑到所有直到N的数字之和为(N *(N + 1))/ 2,这是一种不使用序列的更“数学”方式。

(defn sum-of-mult3* [n]
  (let [x (quot (dec n) 3)]
    (* 3 x (inc x) 1/2)))
像罗德里戈说的那样,递归并不适合这项任务。