您好我对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这里可能有什么问题?
答案 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)对于两个以上的潜在操作,请使用case
,cond
或condp
(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)))
像罗德里戈说的那样,递归并不适合这项任务。