Clojure:适用于n维度

时间:2016-03-08 17:31:38

标签: clojure clojurescript

在Clojure中,函数for可用于迭代嵌套序列。想象一下具有x轴,y轴和z轴的3D空间:

(for [x (range 10)
      y (range 5)
      z (range 2)]
    [x y z])

上面的代码会生成一系列向量,表示长方体内的所有可能位置。 (当然,限于指数是自然数的位置)

有人知道一个更好的方法吗?意思是,如果你没有3个但是有n个维度,那么它就可以了。

3 个答案:

答案 0 :(得分:5)

假设已知数量的维度,

Most approaches似乎正如您所做的那样使用for。你似乎在寻找的是cartesian product。在clojure.math.combinatorics中有一个计算笛卡尔积的函数。

(cartesian-product (range 10) (range 5) (range 2))
(apply cartesian-product (map range [10 5 2]))
(apply cartesian-product (repeatedly n #(range 3)))

如果您不想包含其他图书馆,this question会提供一些您可以使用和/或学习的有趣答案。

截至2016年3月,这是clojure.math.combinatorics/cartesian-product的来源:

(defn cartesian-product
  "All the ways to take one item from each sequence"
  [& seqs]
  (let [v-original-seqs (vec seqs)
        step
        (fn step [v-seqs]
          (let [increment
                (fn [v-seqs]
                  (loop [i (dec (count v-seqs)), v-seqs v-seqs]
                    (if (= i -1) nil
                      (if-let [rst (next (v-seqs i))]
                        (assoc v-seqs i rst)
                            (recur (dec i) (assoc v-seqs i (v-original-seqs i)))))))]
            (when v-seqs
              (cons (map first v-seqs)
                    (lazy-seq (step (increment v-seqs)))))))]
    (when (every? seq seqs)
      (lazy-seq (step v-original-seqs)))))

答案 1 :(得分:1)

另一种方法(可能比cartesian-product更差,但仍然很好地显示了clojure宏的强大功能):

(defmacro product [& colls]
  (let [names (repeatedly (count colls) #(gensym "var"))]
    `(for ~(vec (interleave names colls))
       ~(vec names))))

它只为任意数量的coll生成此for列表解析。例如:

(product (range 3) [:a :b :c] (range 2))

将扩展为以下内容:

(for [var19715 (range 3) var19716 [:a :b :c] var19717 (range 2)]
  [var19715 var19716 var19717])

答案 2 :(得分:0)

for是一个宏,它的正文表达式可以包含任意代码,例如do块或IO调用:

(for [x (range 3)]
  (do
    (prn x)
    x))

假设所需的身体表情始终采用[x y z ... n]的形式且输入为正范围

如何创建n维矩阵的位置序列:

(defn matrix [h & t]
  (if (some? t)
    (for [d (range h)
          ds (apply matrix t)]
      (into [d] ds))
    (map vector (range h))))

这有点天真,但似乎做了这个工作:

(matrix 3) ;; => (map vector (range 3))
;; => ([0] [1] [2])

(matrix 3 2) ;; => (for [d (range 3) ds (apply matrix '(2))] (into [d] ds))
;; => ([0 0] [0 1] [1 0] [1 1] [2 0] [2 1])

(matrix 3 2 4)
;; => ([0 0 0] [0 0 1] [0 0 2] [0 0 3] [0 1 0] [0 1 1] [0 1 2] [0 1 3] 
;;     [1 0 0] [1 0 1] [1 0 2] [1 0 3] [1 1 0] [1 1 1] [1 1 2] [1 1 3] 
;;     [2 0 0] [2 0 1] [2 0 2] [2 0 3] [2 1 0] [2 1 1] [2 1 2] [2 1 3])

我说天真是因为for很懒,但into很渴望。将fordoall一起包装会强制进行评估,但使用瞬变的loop可能会更好。