Clojure:函数返回nil而不是求值表达式

时间:2015-02-02 03:39:55

标签: clojure functional-programming

我编写了一个对列表成员进行求和的函数。非常简单,新手的东西,但由于某种原因,它返回零,我不知道为什么:这应该是一个明智的选择。实现和函数调用如下。

实施:

(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。关于我在这里做错的任何提示都将非常感激。

2 个答案:

答案 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 (firstfnext代替(first (restnnext代替(rest (rest。使用诸如empty?之类的谓词而不是检查等于0的count也是更惯用的;我使用count作为基本情况,只是为了保持与现有代码的并行。最简单的版本可能是:

(defn add-list-members [x] (apply + x))

您还可以将reduce用于非常简单的版本。不过,您可能只想尝试recur

(嗯,你可以在没有使用嵌入式else条款的情况下进行操作,就像你原来的那样,但我不会推荐它,你仍然需要空序列基本情况。当你在一个函数定义中放入多个单独的表达式时,它们中的每一个总是被执行。这是低效的,我觉得,容易出错。在函数定义中使用多个单独的表达式是有意义的。创建副作用 - 例如添加打印语句以进行调试。)