如何让Clojure的`while`返回一个值

时间:2014-11-15 19:18:40

标签: macros clojure while-loop

有人可以解释为什么Clojure的while宏没有返回值吗?

documentation说“推定 一些副作用会导致测试变为false / nil。“好的,我们使用swap!来修改测试用例中使用的原子。但是我们还不能有一个返回值(可能会重复返回) ,如loop)的情况,的副作用?

看看source,似乎原因可能与递归和宏如何工作有关,但我不太了解推测它的内部结构。

示例

以下代码仅返回nil

(let [a (atom 0)]
  (while (< @a 10)
    (+ 1 @a)
    (swap! a inc)))

也就是说,它不返回(+ 1 @a)的值,也不返回while循环内的任何其他值或函数。如果我们想通过while循环计算一些值,我们可以使用print,但是我们不能在其他一些操作中轻松使用该值。对于返回值,我们必须使用第二个原子,如下所示:

(let [a (atom 0)
      end (atom 0)]
  (while (< @a 10)
    (swap! end #(+ 1 %))
    (swap! a inc))
  @end)

2 个答案:

答案 0 :(得分:3)

在REPL上使用(source while),我们可以看到它是这样实现的:

(defmacro while
  "Repeatedly executes body while test expression is true. Presumes
  some side-effect will cause test to become false/nil. Returns nil"
  {:added "1.0"}
  [test & body]
  `(loop []
     (when ~test
       ~@body
       (recur))))

所以,它执行身体,然后检查条件,冲洗,重复。当条件为假时,身体的最新返回值消失了。

但你究竟想做什么?你真的需要一个原子吗?

此代码最多可计入10并返回10:

(loop [a 0]
  (if (< a 10)
    (recur (inc a))
    a))

答案 1 :(得分:1)

  

有人可以解释为什么Clojure会在宏观没有返回值时解释?

     

但我们仍然可以...... 副作用吗?

当你需要时会产生副作用,但如果你在常规代码中过于自由地混合它们,那么你就会错过函数式编程的巨大好处之一。

在clojure.core中,&#39;功能&#39;用于实现某些副作用的目的总是如此划分,并且在您自己的代码中也遵循这一点是个好主意。