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



当做

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

f将被调用两次,参数是

    0
  • :0
  • 1:
  • 1

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

    0
  • :0
  • 1:
  • 1
  • 2 无

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

触发此问题的实际用例需要映射始终精确地工作(count first-coll)次,无论第二个(或第三个或...)集合是否更长。

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

你可以做:

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

基本上,用无限序列 nil 填充第二个 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))

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

一般的懒惰版本,正如 Alex Miller 的回答所建议的那样,是

(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

只是为了好玩

这可以通过Common 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)))

在回复中:

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

最新更新