习惯地在Clojure中迭代2(或更高)维序列
在Clojure中,是否有一种“正确”的方法来迭代二维序列? 假设我有一个数字列表,像这样习惯地在Clojure中迭代2(或更高)维序列,clojure,Clojure,在Clojure中,是否有一种“正确”的方法来迭代二维序列? 假设我有一个数字列表,像这样 ((1 2 3) (4 5 6) (7 8 9)) 我想生成一个列表的新列表,每个列表的数字增加一。在Clojure中有没有一种不依赖嵌套映射或循环/递归的简单方法?我能够做到这一点,但我的解决方案很难看,当我重新阅读它们时,我发现它们很难理解 感谢二维案例,您可以执行以下操作: (map #(map inc %) my-two-d-list) 读起来还不错:将函数#(map inc%)应用
((1 2 3)
(4 5 6)
(7 8 9))
我想生成一个列表的新列表,每个列表的数字增加一。在Clojure中有没有一种不依赖嵌套映射或循环/递归的简单方法?我能够做到这一点,但我的解决方案很难看,当我重新阅读它们时,我发现它们很难理解
感谢二维案例,您可以执行以下操作:
(map #(map inc %) my-two-d-list)
读起来还不错:将函数#(map inc%)
应用于列表中的每个元素
对于高阶情况,您基本上讨论的是树遍历。您需要一个包含树和函数的函数,并将该函数应用于树中的每个节点。您可以在中找到用于此的函数。您始终可以使用列表理解。我发现自己经常在命令式背景下使用它们,所以我不知道它有多地道。在您的具体情况下,您可以执行以下操作:
(for [my-list my-matrix] (map inc my-list))
肖恩和马特的其他答案都显示了获得正确结果的简洁有效的方法 但是,您可以对此进行一些重要的扩展:
- 处理更高维度的情况会更好
- 最好将功能包装在高阶函数中
;; 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))
您所描述的正是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]] (def矩阵[[1 2 3] [4 5 6] [7 8 9]]) (使用'clojure.walk:仅限[prewalk]) (预走#(如果(数字%)(inc%)%)矩阵) => [[2 3 4] [5 6 7] [8 9 10]] 注1:对于文字顺序集合,使用向量而不是括号是惯用方法
注2:walk保留类型。自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维数组操作函数-
,转置
,形状
,整形
,切片
等子数组
- 能够插入高性能阵列实现(例如,对于大型数字阵列)
(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)))))
感谢您提到注释1-我仍然有点不确定何时使用其中一个。Clojure.org上的Clojure备忘单引用了这个答案。我将接受这个答案,尽管其他答案肯定是有效的。这让我觉得这是最短、可读性最强的。应该注意的是,
for
生成了一个惰性序列,因此它只在请求值时“迭代”。不过,看看如何从头开始就可以很有价值。另外,prewalk
需要一个区分叶节点和分支节点的函数。例如,你不能仅仅给出inc
。