使用clojures重复出现混淆参数计数错误

使用clojures重复出现混淆参数计数错误,clojure,Clojure,我想要一个函数,它接受集合并以循环方式返回元素。i、 e: (round-robin [[:a1 :a2 :a3] [:b1] [:c1 :c2][) ;; => (:a1 :b1 :c1 :a2 :c2 :a3) 一些有用的东西: (defn round-robin [all-colls] (let [colls (filter seq all-colls)] (if (seq colls) (lazy-cat

我想要一个函数,它接受集合并以循环方式返回元素。i、 e:

 (round-robin [[:a1 :a2 :a3] [:b1] [:c1 :c2][)
 ;; => (:a1 :b1 :c1 :a2 :c2 :a3)
一些有用的东西:

(defn round-robin [all-colls]
    (let [colls (filter seq all-colls)]
         (if (seq colls)
             (lazy-cat
                (map first colls)
                (round-robin (map next colls))))))  ;; recursive call
我对一个更好的习惯用法很感兴趣,但我花了一段时间才想出这个习惯用法,因为我不明白为什么这个习惯用法不适用于recur。e、 g

 (defn round-robin [all-colls]
    (let [colls (filter seq all-colls)]
         (if (seq colls)
             (lazy-cat
                (map first colls)
                (recur (map next colls))))))  ;; same with recur
 java.lang.IllegalArgumentException: Mismatched argument count to recur, expected: 0 args, got: 1
为什么会说预期参数为0?

请注意,如果我尝试使用
循环,则会出现相同的错误。。。重复出现
。可能与反复发作的变量(我不太清楚)或懒猫是反复发作的目标有关


只是不明白为什么没有一个版本能与recur一起工作。

之所以会发生这种情况,是因为对
lazy seq
的调用扩展为一个函数,而不是
recur
唱成
循环
,而是递归到
lazy seq
使用的函数中。这里宏的使用使事情变得复杂

如果你写:

(lazy-seq (recur))
这(大致)变成:

(LazySeq. (fn [] (recur)))
注意现在实际发生的函数是什么

此外,正如@leetwinski指出的,
recur
不在尾部位置,因此无论如何都无法优化此递归。如果查看
lazy cat
的文档,您将看到以下等式描述宏如何展开:

(lazy-cat xs ys zs) === (concat (lazy-seq xs) (lazy-seq ys) (lazy-seq zs))
在您的情况下,
recur
基本上是
zs
。它在形状内部,处于尾部位置,但不在位置本身


只需直接调用该函数,而不用
recur
。由于
lazy seq
的工作方式,这不会导致堆栈溢出

这并不明显,但这是答案:

(def data [[:a1 :a2 :a3] [:b1] [:c1 :c2]])
(defn round-robin [data-seqs]
  (let
    [lengths   (for [s data-seqs] (count s))
     limit     (apply max lengths)
     coll-seqs (for [s data-seqs]
                 (take limit (concat s (repeat nil))))
     matrix    (apply map vector coll-seqs)
     keepers   (filter not-nil? (flatten matrix))
     ]
    (vec keepers)))
结果如下:

lengths => (3 1 2)
limit => 3
coll-seqs => ((:a1 :a2 :a3) (:b1 nil nil) (:c1 :c2 nil))
matrix => ([:a1 :b1 :c1] [:a2 nil :c2] [:a3 nil nil])
keepers => (:a1 :b1 :c1 :a2 :c2 :a3)
(round-robin data) => [:a1 :b1 :c1 :a2 :c2 :a3]

此外:您可以通过功能性方式操作集合,使循环变得懒惰:

(defn round-robin [data]
  (->> data
       (filter seq)
       (iterate (partial keep next))
       (take-while seq)
       (mapcat (partial map first))))
#'user/round-robin

user> (round-robin [[:a1 :a2 :a3] [:b1] [:c1 :c2]])
;;=> (:a1 :b1 :c1 :a2 :c2 :a3)

user> (round-robin [[] []])
;;=> ()

user> (round-robin [])
;;=> ()

我要补充的是,即使这里没有棘手的宏问题,在那个例子中,
recur
的用法也是错误的,因为编译器会注意到它不是尾部递归。@leetwinski谢谢。我加了一个广告。尽管如此,我仍然认为这是一个“宏观混水”问题。如果懒猫是一个普通的函数,
recur
就我所见,将处于尾部位置。是的,我的第一条评论部分错误。Recur处于尾部位置,但这就是clojure中的
循环/Recur
设计的问题:
(循环[x1](如果(>=x10)[])(cons x(Recur(inc x'))
=>“CompilerException java.lang.UnsupportedOperationException:只能从尾部位置恢复”不是对您问题的直接回答,这已经给出了,但更确切地说是一个评论:如果你需要多线程的方式,你可以看看[)(请注意,这是一个自我广告)通常不需要使用低级的懒惰原语。如此多的库函数,如您在这里展示的,已经使用了
lazy seq
,很容易意外地以懒惰代码告终。在挖掘之前,一定要检查一下,确保自己没有因为尝试重新发明轮子而使生活变得困难,并用
lazy seq
。虽然并不比我的解决方案短,+1是我希望想到的一个非常酷的解决方案。接受的ans转到@Carcigenicate,因为它回答了我的直接问题:为什么我会遇到让我沮丧了这么久的错误。