使用 doseq/for 无限序列

Using doseq/for with infinite sequences

在 Clojure 中,可以借助函数 for 或类似地使用 doseq 迭代一个序列以获得副作用并获得 nil 作为 return 值:

(doseq [x (range 3)]
    (prn x))

; 0
; 1
; 2

对于序列无限的情况,有一种方法可以引入中断条件:

(doseq [x (range) :while (< x 3)]
    (prn x))

这将产生与上面相同的输出。

作为一项专业,当您使用多个序列时会有一个非常有趣的行为。正如文档所说:"Collections are iterated in a nested fashion, rightmost fastest".

(doseq [x (range 3) y (range 3)]
    (prn x y))

; 0 0
; 0 1
; 0 2
; 1 0
; 1 1
; 1 2
; 2 0
; 2 1
; 2 2

然而,如果序列又是无限的,会发生什么。当最后一个是无限时,它工作得很好。这与之前的示例一样有效:

(doseq [x (range 3) y (range) :while (< y 3)]
    (prn x y))

如果第一个是无限的,结果输出是预期的,但由于某些原因,在打印最后一行后循环没有停止。换句话说:repl 继续工作。

(doseq [x (range) y (range 3) :while (< x 3)]
    (prn x y))

任何人都可以解释这种行为吗?

这没有意义:

(doseq [x (range) 
        y (range 3) 
        :while (< x 3)]
  (prn x y))

应该是:

(doseq [x (range) 
        :while (< x 3)
        y (range 3)]
  (prn x y))

...终止。

这样想:

(doseq [x (range)]
  (doseq [y (range 3) 
          :while (< x 3)]
    (prn x y)))

对比:

(doseq [x (range)
        :while (< x 3)]
  (doseq [y (range 3)]
    (prn x y)))

在原始版本中,外循环是无限的,在内循环中使用 :while 没有什么区别。循环继续,它什么也不做。在固定版本中 :while 终止外循环。

鉴于 muhuk 的出色解释,这似乎是一个很好的论据,可以明确说明而不是过度压缩代码。所以也许是这样的:

(doseq [x (range 3)]
  (doseq [y (range 3)]
    (prn x y)))

如果你能控制输入顺序,或者像这样:

(let [x-vals    (range)
      y-vals    (range) ]
  (doseq [x x-vals :while (< x 3)]
    (doseq [y y-vals :while (< y 3)]
      (prn x y))))

如果输入序列(例如 x-valsy-vals)是由呼叫者提供给您的。