在Clojure中习惯性地迭代2(或更高)维度序列

时间:2011-11-11 02:44:18

标签: clojure

在Clojure中是否存在迭代二维序列的“正确”方法? 假设我有一个数字列表列表,比如这个

 ((1 2 3)
  (4 5 6)
  (7 8 9))

我希望生成一个新的列表列表,每个数字加1。有没有一种简单的方法在Clojure中执行此操作而不依赖于嵌套映射或循环/重复?我已经能够做到了,但我的解决方案很丑陋,当我重新阅读它们时,我发现它们很难理解。

由于

6 个答案:

答案 0 :(得分:18)

您所描述的正是clojure.walk的用途:

(def matrix [[1 2 3]
             [4 5 6]
             [7 8 9]])
(use 'clojure.walk :only [prewalk])
(prewalk #(if (number? %) (inc %) %) matrix)
=> [[2 3 4] [5 6 7] [8 9 10]]

注1:对于文字顺序集合,使用向量而不是括号是惯用的。

注2:步行保留类型。

答案 1 :(得分:14)

您始终可以使用列表理解。我发现自己经常使用它们来自命令背景,所以我不知道它是多么惯用。在您的具体情况下,您可以这样做:

(for [my-list my-matrix] (map inc my-list))

答案 2 :(得分:10)

对于二维案例,您可以执行以下操作:

(map #(map inc %) my-two-d-list)

阅读并不算太糟糕:将函数#(map inc %)应用于列表中的每个元素。

对于高阶情况,你基本上是在谈论树遍历。您需要一个接受树和函数的函数,并将该函数应用于树中的每个节点。您可以在clojure.walk中找到相应的功能。

答案 3 :(得分:5)

Sean和Matt的其他答案都显示了获得正确结果的简明而有效的方法。

但是,您可以对此进行一些重要的扩展:

  • 处理更高维度的情况
  • 会更好
  • 最好将功能包装在更高阶的功能中

示例代码:

;; general higher order function
(defn map-dimensions [n f coll] 
  (if (= n 1)
    (map f coll)
    (map #(map-dimensions (dec n) f %) coll)))

;; use partial application to specialise to 2 dimensions
(def map-2d (partial map-dimensions 2))

(map-2d inc  
    '((1 2 3)
      (4 5 6)
      (7 8 9)))
=> ((2 3 4) (5 6 7) (8 9 10))

答案 4 :(得分:5)

自2013年引入core.matrix以来,现在这是处理多维数组操作的更好方法:

(use 'clojure.core.matrix)

(def M  [[1 2 3]
         [4 5 6]
         [7 8 9]])

(emap inc M)

=> [[2 3 4 ]
    [5 6 7 ]
    [8 9 10]]

使用core.matrix

的优点
  • 干净,惯用的Clojure代码
  • 许多通用的n维数组操作函数 - transposeshapereshapeslicesubarray等。
  • 能够插入高性能阵列实现(例如,用于大数值阵列)

答案 5 :(得分:0)

迟来的回答,也许不完全是需要的:你可以尝试flatten。它将返回一个可以迭代的seq:

(flatten  '((1 2 3)
            (4 5 6)
            (7 8 9)))

user=> (1 2 3 4 5 6 7 8 9)

为了增加矩阵元素并重新组合矩阵:

(partition 3 (map inc (flatten  '((1 2 3)
                                  (4 5 6)
                                  (7 8 9)))))