使(映射f c1 c2)映射(计数c1)次,即使c2具有较少的元素

时间:2017-01-30 04:48:09

标签: clojure

做的时候

(map f [0 1 2] [:0 :1])

f将被调用两次,参数为

  • 0:0
  • 1:1

是否有一种简单而有效的方法,即不产生更多的中间序列等,以便使用以下参数为第一个集合的每个值调用f

  • 0:0
  • 1:1
  • 2 nil

编辑在评论中通过@ fl00r解决问题。

触发此问题的实际用例需要map始终正好(count first-coll)次,无论第二个(或第三个或......)集合是否更长。

现在游戏有点晚了,在接受了答案之后有些不公平,但是如果添加一个好的答案只能做我特别要求的 - 映射(count first-coll)次 - 我会接受。 / p>

3 个答案:

答案 0 :(得分:5)

你可以这样做:

(map f [0 1 2] (concat [:0 :1] (repeat nil)))

基本上,用无限的nils序列填充第二个coll。 map在到达第一个集合的末尾时停止。

一个(急切的)循环/重复形式,走向最长的一端:

(loop [c1 [0 1 2] c2 [:0 :1] o []]
  (if (or (seq c1) (seq c2))                
    (recur (rest c1) (rest c2) (conj o (f (first c1) (first c2))))
    o))

或者你可以写一个懒惰的地图版本做类似的事情。

答案 1 :(得分:1)

Alex Miller's answer建议的一般懒惰版本是

(defn map-all [f & colls]
  (lazy-seq
    (when-not (not-any? seq colls)
      (cons
        (apply f (map first colls))
        (apply map-all f (map rest colls))))))

例如,

(map-all vector [0 1 2] [:0 :1])
;([0 :0] [1 :1] [2 nil])

您可能希望为一个和两个集合专门化map-all

答案 2 :(得分:0)

只是为了好玩

这可以通过常见的lisp的do宏轻松完成。我们可以用clojure实现它,并用它来做这个(以及更多有趣的事情):

(defmacro cl-do [clauses [end-check result] & body]
  (let [clauses (map #(if (coll? %) % (list %)) clauses)
        bindings (mapcat (juxt first second) clauses)
        nexts (map #(nth % 2 (first %)) clauses)]
    `(loop [~@bindings]
       (if ~end-check
         ~result
         (do
           ~@body
           (recur ~@nexts))))))

然后只使用它进行映射(注意它可以在2个以上的colls上运行):

(defn map-all [f & colls]
  (cl-do ((colls colls (map next colls))
          (res [] (conj res (apply f (map first colls)))))
         ((every? empty? colls) res)))

在repl中:

user> (map-all vector [1 2 3] [:a :s] '[z x c v])
;;=> [[1 :a z] [2 :s x] [3 nil c] [nil nil v]]