【发布时间】:2015-07-21 22:49:34
【问题描述】:
在 Clojure 中是否有一种“正确”的方式来迭代二维序列? 假设我有一个数字列表,像这样
((1 2 3)
(4 5 6)
(7 8 9))
我想生成一个新的列表列表,每个数字加一。在 Clojure 中是否有一种简单的方法可以在不依赖嵌套映射或循环/递归的情况下做到这一点?我已经做到了,但是我的解决方案很丑陋,当我重新阅读它们时,我发现它们很难理解。
谢谢
【问题讨论】:
标签: clojure
在 Clojure 中是否有一种“正确”的方式来迭代二维序列? 假设我有一个数字列表,像这样
((1 2 3)
(4 5 6)
(7 8 9))
我想生成一个新的列表列表,每个数字加一。在 Clojure 中是否有一种简单的方法可以在不依赖嵌套映射或循环/递归的情况下做到这一点?我已经做到了,但是我的解决方案很丑陋,当我重新阅读它们时,我发现它们很难理解。
谢谢
【问题讨论】:
标签: clojure
您所描述的正是clojure.walk 的用途:
注意 1:对于文字顺序集合,使用向量而不是括号是惯用的。
注 2:walk 保留类型。
【讨论】:
您总是可以只使用列表推导式。我发现自己经常使用它们来自命令式背景,所以我不知道它是多么地道。在您的具体情况下,您可以这样做:
(for [my-list my-matrix] (map inc my-list))
【讨论】:
for产生了一个惰性序列,所以它只在请求值时“迭代”。
对于二维情况,您可以执行以下操作:
(map #(map inc %) my-two-d-list)
读起来还不错:将函数#(map inc %) 应用于列表中的每个元素。
对于高阶情况,您基本上是在谈论树遍历。您需要一个接收树和函数的函数,并将该函数应用于树中的每个节点。您可以在 clojure.walk 中找到这方面的功能。
【讨论】:
肖恩和马特的其他答案都展示了获得正确结果的简洁有效的方法。
但是,您可以对此进行一些重要的扩展:
示例代码:
;; 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))
【讨论】:
prewalk 需要一个区分叶节点和分支节点的函数。例如,你不能只给inc。
自 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的优势:
transpose、shape、reshape、slice、subarray 等。【讨论】:
一个迟来的答案,也许不完全是需要的:你可以试试flatten。它将返回一个可以迭代的序列:
(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)))))
【讨论】: