Clojure Interleaving 的惰性重新实现

lazy re-implementation of Clojure Interleaving

我想重写(我不确定原来的实现是否懒惰)一个使用 lazy-seq 的 clojure 交错的懒惰实现,它的工作原理如下:

(take 4 (lazy-interleave ’( 1 2 3) ’( a b c)))
(1 a 2 b)

我想到了这样的方法,但我不确定为什么它不起作用:

 (defn lazy-interleave [v1 v2]
   (lazy-seq (concat (list (first v1) (first v2))) (lazy-interleave (next v1) (next v2)))
   )

编辑:

感谢亚瑟的回答,这是一个修改后的工作解决方案:

(defn lazy-interleave [v1 v2]
    (lazy-seq
      (cons (first v1) (cons (first v2) (lazy-interleave (rest v1) (rest v2))))
      )
   )

稍微重新格式化一下就会发现问题:

(defn lazy-interleave [v1 v2]
  (lazy-seq
   (concat (list (first v1) (first v2)))
   (lazy-interleave (next v1) (next v2))))

换句话说,您正在构造一个惰性序列,实现后将计算 (concat (list (first v1) (first v2))),忽略结果,然后尝试计算 return (lazy-interleave (next v1) (next v2))。对 lazy-interleave 的调用做同样的事情,再次删除 v1v2 的第一个元素,依此类推,无穷无尽。

你永远不会到达底部,因为你没有空检查,所以自从 (next nil) returns nil,即使你用完了两个序列,它也会继续下去。你没有得到 WhosebugError 因为你使用的是惰性序列而不是递归。

正确的实现应该是这样的:

(defn lazy-interleave [v1 v2]
  (when (and (seq v1) (seq v2))
    (lazy-cat [(first v1) (first v2)]
              (lazy-interleave (rest v1) (rest v2)))))

interleave已经懒了:

user> (interleave (take 5 (iterate #(do (println "sequence A:" %) (inc %)) 0))
                  (take 5 (iterate #(do (println "sequence B:" %) (inc %)) 100)))
sequence A: 0
sequence B: 100
sequence A: 1
sequence B: 101
sequence A: 2
sequence B: 102
sequence A: 3
sequence B: 103
(0 100 1 101 2 102 3 103 4 104)


user> (take 4 (interleave (take 5 (iterate #(do (println "sequence A:" %) (inc %)) 0))
                          (take 5 (iterate #(do (println "sequence B:" %) (inc %)) 100))))
sequence A: 0
sequence B: 100
(0 100 1 101)

其实现的核心与您的示例非常相似:

(lazy-seq
      (let [s1 (seq c1) s2 (seq c2)]
        (when (and s1 s2)
          (cons (first s1) (cons (first s2) 
                                 (interleave (rest s1) (rest s2)))))))

除了它也适用于两个以上的序列,所以它有另一个处理这种情况的参数。