从Clojure

时间:2018-04-02 03:52:32

标签: matrix clojure

我有一系列整数:

(9 14 21 23 22 25 32 36 38 42)

代表下三角矩阵的值:

|  0|  0|  0|  0|  0|
|  9|  0|  0|  0|  0|
| 14| 21|  0|  0|  0|
| 23| 22| 25|  0|  0|
| 32| 36| 38| 42|  0|

我想基于这个序列创建一个对称矩阵:

(( 0  9 14 23 32)
 ( 9  0 21 22 36)
 (14 21  0 25 38)
 (23 22 25  0 42)
 (32 36 38 42  0))

由于我对Clojure相对较新,这是我到目前为止所能想到的(这看起来非常程序化):

(defn at
  "Equivalent to matrix[i][j]"
  [m i j]
  (nth (nth m i) j))

(defn group-inf-lines
  "Group each values from inferior lines of the matrix"
  [values n]
  (let [create-line
        (fn [line curr i n]
          (if (empty? curr)
               line
               (recur (conj line (take i curr))
                      (drop i curr)
                      (inc i)
                      n)))]
    (create-line [] values 1 n)))

(defn create-symmetrical-matrix
  "Take a sequence of values and create a symmetrical matrix of size n x n"
  [values n]
  (let [inf-lines (group-inf-lines values n)]
    (for [i (range n)]
      (for [j (range n)]
        (cond (> i j) (at inf-lines (dec i) j)
              (< i j) (at inf-lines (dec j) i)
              :else 0)))))

有最多的Clojure-ist方法吗? 也欢迎使用非本机功能的解决方案(例如来自clojure.core.matrix等库)。

2 个答案:

答案 0 :(得分:2)

在你的代码中,对于clojure(我猜任何函数式语言)来说真正单调的主要因素是依赖于索引,而任务可以仅使用集合处理来实现。这是我提出的一个解决方案(显然可能会有更多)。

首先,我会根据数据制作三角形集合:

(def data '(9 14 21 23 22 25 32 36 38 42))

(defn triangle [[x & xs :as data]]
  (when (seq data)
    (butlast
     (map first
          (reductions (fn [[_ snd] i]
                        (if (seq snd)
                          (split-at i snd)
                          (reduced nil)))
                      [[x] xs]
                      (iterate inc 2))))))

user> (triangle data)
;;=> ([9] (14 21) (23 22 25) (32 36 38 42))

第二步是&#34;转置&#34;三角形,制作矩阵的左半部分:

(defn transpose-triangle [triangle]
  (->> triangle
       (iterate #(filter seq (map rest %)))
       (map (partial map first))
       (take-while seq)))

user> (transpose-triangle (triangle data))
;;=> ((9 14 23 32) (21 22 36) (25 38) (42))

最后一部分是&#34;胶水&#34;带有它的三角形转置版本:

user> (let [tri (triangle data)
            ttri (transpose-triangle tri)]
        (map concat
             (cons nil tri)
             (repeat [0])
             (concat ttri [nil])))
;;=> ((0 9 14 23 32) 
;;    (9 0 21 22 36) 
;;    (14 21 0 25 38) 
;;    (23 22 25 0 42) 
;;    (32 36 38 42 0))

答案 1 :(得分:0)

这实际上是一个非常有趣的算法:

(defn make-triangular [coll]
  (let [n-coll   (count coll)
        n-rows   (->> (range) (reductions +) (take-while #(<= % n-coll)) count)

        ; n-rows x n-rows matrix of zeros, needs to be a vector to support assoc:
        zeros    (mapv (comp vec repeat) (repeat n-rows n-rows) (repeat n-rows 0))]
    (->> coll
         (reduce
           (fn [[result [row col]] v]
             [; assoc v to upper and lower triangles:
              (-> result
                  (assoc-in [row col] v)
                  (assoc-in [col row] v))

              ; calculate where the next value should go:
              (if (= col (dec row))
                [(inc row) 0]            ; Approaching the diagonal, drop down by one row and reset to the first col
                [row       (inc col)])]) ; Not at diagonal yet, stay at the same row and move to the next col
           [zeros [1 0]]) ; Starting from a zero matrix, row 1 and col 0
         first))) ; Extract the result

我选择使用reduce和解构来跟踪&#34;州&#34;,即我们正在填写的位置。更多&#34;流导向&#34;可能是更有效的方法,因为这会O(n^2)与持久性向量相关联。

repeatmap的使用感觉有点笨拙,但我发现它在代码高尔夫中很有用。