我编写了一个对列表成员进行求和的函数。非常简单,新手的东西,但由于某种原因,它返回零,我不知道为什么:这应该是一个明智的选择。实现和函数调用如下。
实施:
(defn add-list-members
([x] (add-list-members x 0))
([x total]
(if (= (count x) 2)
(+ (first x) (first (rest x)) total))
(if (= (count x) 1)
(+ (first x) total))
(if (> (count x) 2)
(recur (rest (rest x)) (+ (+ (first x) (first (rest x))) total)))))
对函数的调用:
(println "our total is: " (add-list-members '(2 23 33 14 33 134 9000 98993)))
与此同时,我有一个添加了print语句的调试版本,并且我已经能够验证实现是否正常工作。
调试版本:
(defn add-list-members-debug
([x] (add-list-members-debug x 0))
([x total]
(println ">>> our list now: " x)
(println ">>> our total now: " total)
(if (= (count x) 2)
(println "outcome 2 : " (+ (first x) (first (rest x)) total)))
(if (= (count x) 1)
(println "outcome 1 : " (+ (first x) total)))
(if (> (count x) 2)
(recur (rest (rest x)) (+ (+ (first x) (first (rest x))) total)))))
调用调试版:
(add-list-members-debug '(2 23 33 14 33 134 9000 98993))
我使用的开发工具是Vim插入Leiningen REPL。关于我在这里做错的任何提示都将非常感激。
答案 0 :(得分:6)
您以错误的方式使用了if
。请考虑以下代码:我有意简化代码以明确发生的情况。
(defn xxx [v]
(if (= (count v) 1) "one")
(if (= (count v) 2) "two")
(if (> (count v) 2) "more"))
如果您运行(xxx [1])
会怎样?也许您希望代码返回"one"
,但是,它会返回nil
。为什么呢?
函数返回其最后一个表达式的值,在本例中为最后一行(if (> (count v) 2) "more")
。如果您传递[1]
,则第一个if
表达式将返回"one"
,这将被忽略。第二个if
将返回nil
,也会被忽略。第三个和最后一个将返回nil
,这是函数的返回值。
计算元素总和的最简单方法可能是:
(defn add-elements [xs] (apply + xs))
或
(defn add-elements [xs] (reduce + xs))
您的代码可以更简单的方式重新实现:
(defn add-elements
([xs] (add-elements xs 0))
([xs total] (if (empty? xs) total
(recur (rest xs) (+ total (first xs))))))
答案 1 :(得分:2)
括号是错误的,或者至少是次优的,并且您没有返回total
的基本情况。 (关于括号:您的缩进在括号中是正确的,显示了这一点:每个if
都在同一级别;其中没有一个位于" else"位置。)这里&#39 ; s正确版本:
(defn add-list-members
([x] (add-list-members x 0))
([x total]
(if (= (count x) 0)
total
(if (= (count x) 2)
(+ (first x) (first (rest x)) total)
(if (= (count x) 1)
(+ (first x) total)
(if (> (count x) 2)
(recur (rest (rest x)) (+ (+ (first x) (first (rest x))) total))))))))
您获得nil
作为返回值,因为当最后一个if
测试失败时,它将转到else
子句,在这种情况下,该子句是列表的空端,即nil
。
但是,多个if
可以替换为一个cond
。您还可以ffirst
使用(first (first
,fnext
代替(first (rest
,nnext
代替(rest (rest
。使用诸如empty?
之类的谓词而不是检查等于0的count
也是更惯用的;我使用count
作为基本情况,只是为了保持与现有代码的并行。最简单的版本可能是:
(defn add-list-members [x] (apply + x))
您还可以将reduce
用于非常简单的版本。不过,您可能只想尝试recur
。
(嗯,你可以在没有使用嵌入式else
条款的情况下进行操作,就像你原来的那样,但我不会推荐它,你仍然需要空序列基本情况。当你在一个函数定义中放入多个单独的表达式时,它们中的每一个总是被执行。这是低效的,我觉得,容易出错。在函数定义中使用多个单独的表达式是有意义的。创建副作用 - 例如添加打印语句以进行调试。)