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

Clojure : function returns nil rather an evaluated expression

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

实施:

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

与此同时,我有一个添加了打印语句的调试版本,我已经能够验证该实现是否按预期工作。

调试版本:

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

括号是错误的,或者至少是次优的,并且您没有 returns total 的基本情况。 (关于括号:你的缩进,在给定括号的情况下是正确的,表明:每个 if 都在同一水平上;其中 none 在 "else" 位置。)这是一个正确版本:

(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 作为 return 值,因为当最后一个 if 测试失败时,它会转到 else 子句,在本例中它是一个空的列表的末尾,即 nil

不过,多个if可以用一个cond代替。您还可以使用 ffirst 代替 (first (first,使用 fnext 代替 (first (rest,使用 nnext 代替 (rest (rest。使用诸如 empty? 之类的谓词而不是检查 count 是否等于 0 也更符合习惯;我使用 count 作为基本案例,只是为了保持与现有代码的并行性。最简单的版本可能是:

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

您也可以使用 reduce 作为一个非常简单的版本。不过,您可能只是想尝试一下 recur

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

你用错了if。考虑以下代码:我有意简化了代码以清楚说明发生了什么。

(defn xxx [v]
  (if (= (count v) 1) "one")
  (if (= (count v) 2) "two")
  (if (> (count v) 2) "more"))

如果你 运行 (xxx [1]) 会怎样?也许您期望代码会 return "one",但是,它 returns nil。为什么?

函数 return 其最后一个表达式的值,在本例中为最后一行 (if (> (count v) 2) "more")。如果传递 [1],第一个 if 表达式 returns "one" 将被忽略。第二个 if 将 return nil,这也被忽略了。第三个也是最后一个会returnnil,也就是函数的return值

也许计算元素总和的最简单方法是:

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