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

4
请问为什么Clojure的while宏不返回任何值呢?根据文档,“假设某些副作用将导致测试变为false/nil。” 好吧,所以我们使用swap!来修改用于测试的原子状态。但是难道我们不能有一个返回值(例如像loop中一样重复返回),并带有副作用吗?
查看源代码,似乎原因与递归和宏的工作方式有关,但我对其内部机制了解不够,无法进行推测。
以下代码仅返回nil
```clojure (defn foo [] (let [x (atom 0)] (while (< @x 5) (swap! x inc)))) ```
(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个回答

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

所以,它先执行主体,然后检查条件,反复这样做。当条件为 false 时,来自主体的最新返回值已经不存在了。但是,你到底想要做什么?你真的需要一个原子来实现吗?这段代码从 1 数数到 10,并返回 10:
(loop [a 0]
  (if (< a 10)
    (recur (inc a))
    a))

啊,我明白了。谢谢。我正在比较map1的值和map2的值,以便找到匹配并返回与map2相关键的数组(基本上像一个查找表)。因此,函数体比这个示例更复杂。我肯定可以使用loop,但是我在过去的一天中已经使用了loop四次,而且我从来没有使用过while,所以我想尝试一下。 - Ben

1
请问为什么Clojure的while宏不返回值?
但是我们不能和副作用一起使用吗?
副作用在需要时存在,但是如果您在正常代码中过于自由地混合它们,则会错过函数式编程的巨大好处。
在clojure.core中,用于实现某些副作用的“函数”始终被标记为此类,并且在您自己的代码中遵循此方法是个好主意。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接