首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >Clojure: for for n Dimensions

Clojure: for for n Dimensions
EN

Stack Overflow用户
提问于 2016-03-09 01:31:38
回答 3查看 279关注 0票数 4

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

代码语言:javascript
复制
(for [x (range 10)
      y (range 5)
      z (range 2)]
    [x y z])

上面的代码将产生一个代表长方体内部所有可能位置的向量序列。(仅限于索引为自然数的位置)

有没有人知道让它更通用的好方法?这意味着,如果你不是有3个维度,而是n个维度,就能让它工作。

EN

回答 3

Stack Overflow用户

发布于 2016-03-09 01:45:12

Most approaches似乎和您一样使用for,假设维度数已知。你似乎要找的是cartesian product。在clojure.math.combinatorics中有一个计算笛卡尔乘积的函数。

代码语言:javascript
复制
(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的来源

代码语言:javascript
复制
(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)))))
票数 5
EN

Stack Overflow用户

发布于 2016-03-09 04:47:49

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

代码语言:javascript
复制
(defmacro product [& colls]
  (let [names (repeatedly (count colls) #(gensym "var"))]
    `(for ~(vec (interleave names colls))
       ~(vec names))))

它只是为任意数量的colls生成这个for列表理解。例如:

代码语言:javascript
复制
(product (range 3) [:a :b :c] (range 2))

将扩展为以下内容:

代码语言:javascript
复制
(for [var19715 (range 3) var19716 [:a :b :c] var19717 (range 2)]
  [var19715 var19716 var19717])
票数 1
EN

Stack Overflow用户

发布于 2016-03-09 05:08:17

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

代码语言:javascript
复制
(for [x (range 3)]
  (do
    (prn x)
    x))

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

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

代码语言:javascript
复制
(defn matrix [h & t]
  (if (some? t)
    (for [d (range h)
          ds (apply matrix t)]
      (into [d] ds))
    (map vector (range h))))

这有点天真,但似乎能做好工作:

代码语言:javascript
复制
(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却很渴望。使用doall包装for将强制执行计算,但使用瞬态的loop可能会执行得更好。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/35874072

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档