如何在 clojure 中转置嵌套向量
How to transpose a nested vector in clojure
我有以下变量
(def a [[1 2] [3 4] [5 6]])
并且想要 return
[[1 3 5][2 4 6]]
如果输入是
[[1 2] [3 4] [5 6] [7 8 9]]
那么要求的结果是
[[1 3 5 7] [2 4 6 8] [9]]
如何在 clojure 中实现?
(persistent!
(reduce
(fn [acc e]
(reduce-kv
(fn [acc2 i2 e2]
(assoc! acc2 i2 ((fnil conj []) (get acc2 i2) e2)))
acc
e))
(transient [])
[[1 2 3] [:a :b] [\a] [111] [200 300 400 500]]))
;;=> [[1 :a \a 111 200] [2 :b 300] [3 400] [500]]
可以通过第 0 个索引处的更新输入 fn 更新空向量,另外,可以在紧跟在最后一个值之后的索引处更新非空向量。
这里的reduce是将外层的累加器传递给内层的reducing函数,进行相应的更新,然后返回给外层的reducing函数,再传给内层的rf处理下一个元素.
编辑:更新到最快的版本。
(map
(partial filter identity) ;;remove nil in each sub-list
(take-while
#(some identity %) ;;stop on all nil sub-list
(for [i (range)]
(map #(get % i) a)))) ;; get returns nil on missing values
使用 get 在缺失值上有 nil,在无限范围内迭代(for),在所有 nil 子列表上停止,从子列表中删除 nil。如果您真的需要向量,请在第一个映射之前和它的函数(第一个参数)中添加向量构造函数。
编辑:如果您认为这没有用,请发表评论。我们都可以从错误中学习。
我喜欢, though it seems weird to use reduce-kv to build a vector that could be easily build with map/mapv。
所以,我会这样做:
(defn transpose [v]
(mapv (fn [ind]
(mapv #(get % ind)
(filter #(contains? % ind) v)))
(->> (map count v)
(apply max)
range)))
(->> (range)
(map (fn [i]
(->> a
(filter #(contains? % i))
(map #(nth % i)))))
(take-while seq))
请注意,此算法创建了一个惰性序列的惰性序列,因此您只需为真正使用的转换付费。如果您坚持创建向量,请在必要的地方将表格包装在 vec
中 - 或者如果您使用的是 Clojurescript 或者不介意 Clojure 1.7 alpha,请使用 transducers 来急切地创建向量而不用为懒惰或不变性付出代价:
(into []
(comp
(map (fn [i]
(into [] (comp (filter #(contains? % i))
(map #(nth % i)))
a)))
(take-while seq))
(range))
我觉得这很容易理解:
(defn nth-column [matrix n]
(for [row matrix] (nth row n)))
(defn transpose [matrix]
(for [column (range (count (first matrix)))]
(nth-column matrix column)))
(transpose a)
=> ((1 3 5) (2 4 6))
nth-column
是一个列表理解,从每个序列(行)的第 n 个元素生成一个序列。
然后 transpose-matrix
只是简单地遍历列,为每个列创建一个序列元素,由 (nth-column matrix column)
组成,即该列的元素序列。
我有以下变量
(def a [[1 2] [3 4] [5 6]])
并且想要 return
[[1 3 5][2 4 6]]
如果输入是
[[1 2] [3 4] [5 6] [7 8 9]]
那么要求的结果是
[[1 3 5 7] [2 4 6 8] [9]]
如何在 clojure 中实现?
(persistent!
(reduce
(fn [acc e]
(reduce-kv
(fn [acc2 i2 e2]
(assoc! acc2 i2 ((fnil conj []) (get acc2 i2) e2)))
acc
e))
(transient [])
[[1 2 3] [:a :b] [\a] [111] [200 300 400 500]]))
;;=> [[1 :a \a 111 200] [2 :b 300] [3 400] [500]]
可以通过第 0 个索引处的更新输入 fn 更新空向量,另外,可以在紧跟在最后一个值之后的索引处更新非空向量。
这里的reduce是将外层的累加器传递给内层的reducing函数,进行相应的更新,然后返回给外层的reducing函数,再传给内层的rf处理下一个元素.
编辑:更新到最快的版本。
(map
(partial filter identity) ;;remove nil in each sub-list
(take-while
#(some identity %) ;;stop on all nil sub-list
(for [i (range)]
(map #(get % i) a)))) ;; get returns nil on missing values
使用 get 在缺失值上有 nil,在无限范围内迭代(for),在所有 nil 子列表上停止,从子列表中删除 nil。如果您真的需要向量,请在第一个映射之前和它的函数(第一个参数)中添加向量构造函数。
编辑:如果您认为这没有用,请发表评论。我们都可以从错误中学习。
我喜欢
所以,我会这样做:
(defn transpose [v]
(mapv (fn [ind]
(mapv #(get % ind)
(filter #(contains? % ind) v)))
(->> (map count v)
(apply max)
range)))
(->> (range)
(map (fn [i]
(->> a
(filter #(contains? % i))
(map #(nth % i)))))
(take-while seq))
请注意,此算法创建了一个惰性序列的惰性序列,因此您只需为真正使用的转换付费。如果您坚持创建向量,请在必要的地方将表格包装在 vec
中 - 或者如果您使用的是 Clojurescript 或者不介意 Clojure 1.7 alpha,请使用 transducers 来急切地创建向量而不用为懒惰或不变性付出代价:
(into []
(comp
(map (fn [i]
(into [] (comp (filter #(contains? % i))
(map #(nth % i)))
a)))
(take-while seq))
(range))
我觉得这很容易理解:
(defn nth-column [matrix n]
(for [row matrix] (nth row n)))
(defn transpose [matrix]
(for [column (range (count (first matrix)))]
(nth-column matrix column)))
(transpose a)
=> ((1 3 5) (2 4 6))
nth-column
是一个列表理解,从每个序列(行)的第 n 个元素生成一个序列。
然后 transpose-matrix
只是简单地遍历列,为每个列创建一个序列元素,由 (nth-column matrix column)
组成,即该列的元素序列。