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