【问题标题】:Idiomatically iterating over a 2 (or higher) dimensional sequence in Clojure在 Clojure 中惯用地迭代 2(或更高)维序列
【发布时间】:2015-07-21 22:49:34
【问题描述】:

在 Clojure 中是否有一种“正确”的方式来迭代二维序列? 假设我有一个数字列表,像这样

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

我想生成一个新的列表列表,每个数字加一。在 Clojure 中是否有一种简单的方法可以在不依赖嵌套映射或循环/递归的情况下做到这一点?我已经做到了,但是我的解决方案很丑陋,当我重新阅读它们时,我发现它们很难理解。

谢谢

【问题讨论】:

    标签: clojure


    【解决方案1】:

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

    (定义矩阵 [[1 2 3] [4 5 6] [7 8 9]]) (使用 'clojure.walk :only [prewalk]) (prewalk #(if (number?%) (inc %) %) 矩阵) => [[2 3 4] [5 6 7] [8 9 10]]

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

    注 2:walk 保留类型。

    【讨论】:

    • 感谢您提及注 1 - 我对何时使用其中一个还是有点不确定。
    • 这个答案被 clojure.org 上的 Clojure 备忘单引用。
    【解决方案2】:

    您总是可以只使用列表推导式。我发现自己经常使用它们来自命令式背景,所以我不知道它是多么地道。在您的具体情况下,您可以这样做:

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

    【讨论】:

    • 我会接受这个,虽然其他的肯定是有效的回应。这只是让我觉得最短、最易读。
    • 需要注意的是for产生了一个惰性序列,所以它只在请求值时“迭代”。
    【解决方案3】:

    对于二维情况,您可以执行以下操作:

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

    读起来还不错:将函数#(map inc %) 应用于列表中的每个元素。

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

    【讨论】:

      【解决方案4】:

      肖恩和马特的其他答案都展示了获得正确结果的简洁有效的方法。

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

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

      示例代码:

      ;; 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
      【解决方案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 等。
      • 能够插入高性能数组实现(例如,用于大型数值数组)

      【讨论】:

        【解决方案6】:

        一个迟来的答案,也许不完全是需要的:你可以试试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)))))
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2011-03-15
          • 2015-12-15
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2015-12-19
          • 2018-11-21
          • 2013-10-29
          相关资源
          最近更新 更多