如何展平传递的参数以重复出现?

How do I flatten params passed to recur?

我正在尝试了解 loop/recur。我想将 return 向量从函数传回循环,我试过这样的事情:

(defn foo [x y] 
  [(dec x) y])

(loop [x 3 y 4]
  (if (> x 0)
    (do
      (prn x y)
      (recur (foo x y)))))

这给出:

1. Caused by java.lang.IllegalArgumentException
   Mismatched argument count to recur, expected: 2 args, got: 1

现在我可以将循环参数更改为其他有效的形式:

(defn foo [x y] 
  [(dec x) y])

(loop [[x y] [3 4]]
  (if (> x 0)
    (do
      (prn x y)
      (recur (foo x y)))))

我想知道是否有任何方法可以更改第一个代码以保留 (loop [x 3 y 4] ...) 但以某种方式更改传递给 recur 的参数。我想我需要类似 apply 函数的东西,但我无法让它与 recur 一起使用,因为 recur 不是函数。

没有简单的解决方法。 recur 特辑 形式 和 Clojures 绕过无法轻松访问 TCO 的 JVM。

因此你不能在这里使用 apply(这将是调用的解决方案 带有列表参数的函数)。

所以你必须坚持 foo 的结果,然后 recur 那些。旁注:只有一个分支的 if 就是 when.

(defn foo [x y] 
  [(dec x) y])

(loop [x 3 y 4]
  (when (> x 0)
    (prn x y)
    (let [[x' y'] (foo x y)] 
      (recur x' y'))))

如果你这里的域名真的是[x y](比如坐标)我的建议 将围绕它构建你的功能并且不要在两者之间跳转 有时使用向量,有时传递 x/y.

您也可以为此目的编写一个简单的宏。可能像这样(这基本上是对矢量化参数变体的宏重写):

(defmacro loop1 [bindings & body]
  (let [left-side (vec (take-nth 2 bindings))
        right-side (vec (take-nth 2 (rest bindings)))]
    `(loop [~left-side ~right-side]
       ~@body)))

(loop1 [x 3 y 4]
       (if (> x 0)
         (do
           (prn x y)
           (recur (foo x y)))))

;;=> 3 4
;;   2 4
;;   1 4
nil

所以,如果您有类似的重复使用模式,这个宏可能是一个不错的实用程序。