给出一个矩阵作为clojure中矢量的矢量:
(def A [[1 2 3 4]
[5 6 7 8]
[9 10 11 12]
[13 14 15 16]])
我想把它分成四个季度:
'([[1 2]
[5 6]] ; tl
[[3 4]
[7 8]] ; tr
[[9 10]
[13 14]] ; bl
[[11 12]
[15 16]]) ;br
愚蠢的方法是这样的(灵感来自haskells submatrix):
(defn submatrix [A slice-col slice-row]
(let [tl (map (fn [col] (take slice-col col)) (take slice-row A))
tr (map (fn [col] (take slice-col col)) (drop slice-row A))
bl (map (fn [col] (drop slice-col col)) (take slice-row A))
br (map (fn [col] (drop slice-col col)) (drop slice-row A))]
(vector tl tr bl br))
)
这是有效的(除了它基于列表而不是向量)但在函数式编程方面不是很漂亮。
另一种让clojure感觉更自然的方法让我陷入困境:
;; just a helper, simmilar to split-at but returns vectors instead
(defn split-at' [idx v] [(subvec v 0 idx) (subvec v idx)])
(defn mquarter [A]
(let [cuts (map (fn [col] (split-at' 1 col)) A)] ; 1 is just for testing and will be a parameter later ...
(prn cuts) ; do something clever here like (map (fn [row] .. take .. drop ..) cuts)
))
如果我将其称为(mquarter [[1 2 3 4] [5 6 7 8] [9 10 11 12] [13 14 15 16]])
,则会给我([[1] [2 3 4]] [[5] [6 7 8]] [[9] [10 11 12]] [[13] [14 15 16]])
。这很好,但我不知道如何将 cut 合并到一个具有预期结构的新向量中。
是否有一种聪明的 clojure方式将剪切合并到包含我四个季度的预期结构中?所需的集合与输入向量A
具有完全不同的结构,所以我想知道它是否甚至可以像这样转换 A
。
注意:我知道有些图书馆已经这样做了,我想这样做是为了学习clojure。
更新:我对所提出的方法http://git.io/lAZ5OA进行了快速的性能比较,结果如下:
Elapsed time: 9.618612 msecs
Elapsed time: 8294.234684 msecs (is update-in that slow?)
Elapsed time: 4.223093 msecs by a-webb
Elapsed time: 8.166612 msecs
Elapsed time: 0.046654 msecs by andrew-myers (it is executed lazy right!?)
答案 0 :(得分:7)
使用core.matrix
,submatrix
功能将完全符合您的要求:
(submatrix A 0 2 0 2)
=> #<NDWrapper [[1 2] [5 6]]>
注意:NDWrapper
只是一个轻量级视图对象,可以索引到原始数组中。之所以这样做是因为它的内存效率更高,尽管对于像这样的小矩阵来说,它不太重要。
如果您希望将四个季度作为序列,请执行以下操作:
(for [[i j] [[0 0] [0 2] [2 0] [2 2]]]
(submatrix A i 2 j 2))
答案 1 :(得分:5)
您的助手已重命名:
(defn vec-split-at [idx v] [(subvec v 0 idx) (subvec v idx)])
使用助手对矩阵进行分区:
(defn partition-matrix-at [m row col]
(mapcat (partial apply map vector)
(vec-split-at row (mapv (partial vec-split-at col) m))))
上述A
示例:
(partition-matrix-at A 2 2)
=> ([[1 2]
[5 6]]
[[3 4]
[7 8]]
[[9 10]
[13 14]]
[[11 12]
[15 16]])
答案 2 :(得分:1)
这是使用reduce
的可能实现。
缩减函数f
检查它当前正在处理的行,以便根据其索引和矩阵的高度,它可以决定该行是在顶部还是在下半部分。行本身总是分成两半,对应于左半球和右半球,因此部分很容易确定。基于这些信息(顶部/底部和左/右),您知道quarter
累加器在哪里需要添加到行的一半。
(def A [[1 2 3 4]
[5 6 7 8]
[9 10 11 12]
[13 14 15 16]])
(defn mquarter [A]
(let [[h w] ((juxt count (comp count first)) A)
f (fn [quarters [i row]]
(let [[l r] (map vec (split-at (/ w 2) row))
[hl hr] (if (< i (/ h 2)) [0 1] [2 3])]
(-> quarters
(update-in [hl] conj l)
(update-in [hr] conj r))))
quarters [[][][][]]]
(reduce f quarters (map-indexed vector A))))
(mquarter A)
;=> [[[1 2] [5 6]]
;=> [[3 4] [7 8]]
;=> [[9 10] [13 14]]
;=> [[11 12] [15 16]]]
答案 3 :(得分:1)
以下是受子矩阵
启发的版本的简化(def A [[1 2 3 4]
[5 6 7 8]
[9 10 11 12]
[13 14 15 16]])
(let [inner [take take drop drop]
outer [take drop take drop]]
(vector (map (fn [i o]
(map (fn [sub] (i 2 sub)) (o 2 A)));; Repetition from submatrix
inner outer)))
这只会将对take
和drop
的调用分解为一对向量。然后,我们按顺序将这些向量映射到矩阵中。
答案 4 :(得分:1)
所以,我们有一个矩阵,它是向量的向量:
(def A [[1 2 3 4]
[5 6 7 8]
[9 10 11 12]
[13 14 15 16]])
我们知道如何在给定位置拆分矢量:
(defn vec-split [idx v] ; split vector
[(subvec v 0 idx) (subvec v idx)])
现在我们要拆分矩阵。但是,不是在水平和垂直方向上进行分割,而是首先尝试将其沿着一个维度进行分割。
Horisontal分裂是小菜一碟:
(defn mat-splith [idx m] ; split matrix horisontally
(vec-split idx m))
垂直分割应该看起来有些相似。但它不足以分割矩阵的每一行,所以我们需要一些神奇的功能来将拆分的字符串重新排序为新的矩阵:
(defn transpose [m]
(mapv (fn [i] (mapv #(nth % i) m))
(range (count (first m)))))
现在我们可以垂直分割矩阵了:
(defn mat-splitv [idx m] ; split matrix vertically
(transpose (mapv (partial vec-split idx) m)))
现在最后一点:
(defn mat-split [vidx hidx m] ; split matrix along both dimensions
(vec (mapcat (partial mat-splitv vidx)
(mat-splith hidx m))))
它就像一个魅力:
=> (mat-split 2 2 A)
[[[1 2] [5 6]]
[[3 4] [7 8]]
[[9 10] [13 14]]
[[11 12] [15 16]]]