使用clojures重复出现令人困惑的参数计数错误

时间:2018-04-11 03:47:06

标签: clojure

我想要一个采用集合并以循环方式返回元素的函数。即:

sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    server_address = ('localhost', 1489)
    sock.connect(server_address)
    sock.setsockopt(socket.SOL_SOCKET, socket.SO_SNDBUF, 8192)  # Buffer size 8192
    sock.setblocking(0) # set non-blocking

    while 1:
        read_ready = select.select([sock], [], [], timeout_in_seconds)  
        if read_ready[0]: # we have what to read
            print('ready')
            data = sock.recv(100)
            if not data:
                break
            print(data)

有效的东西:

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

我对一个更好的成语感兴趣,但这花了我一段时间才想出来,因为我无法理解为什么这不适用于复发。 e.g。

(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

为什么说预期的args是0?

请注意,如果我尝试 (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 ,我会收到同样的错误。可能与变量discusssed in clojuredocs(我不太相信)或懒惰猫是复发目标的复发相关?

只是不明白为什么没有这个版本可以用于复发。

3 个答案:

答案 0 :(得分:3)

另外:你可以使循环延迟只是以功能方式操作集合:

(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 [])
;;=> ()

答案 1 :(得分:2)

这种情况正在发生,因为对lazy-seq的调用扩展为0-arity function.您不会recur唱入round-robin,您将重新加入该函数lazy-seqlazy-cat使用)扩展到的内容。在这里使用宏会使事情复杂化。

如果你写:

(lazy-seq (recur))

这(大致)变成了:

(LazySeq. (fn [] (recur)))

注意现在实际发生的函数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的工作原理,这不会导致Stack Overflow。

答案 2 :(得分:0)

这不是显而易见的,但这就是答案:

(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]