【问题标题】:Clojure - using map recursivelyClojure - 递归使用地图
【发布时间】:2021-11-04 18:13:15
【问题描述】:

如果我有一个列表,我可以使用map 对列表的每个项目应用一个函数。

(map sqrt (list 1 4 9))

(1 2 3)

我也可以在列表列表前面使用map

(map count (list (list 1 2 3) (list 4 5)))

(4 5)

现在有没有办法将sqrt 应用于列表列表中的每个数字?我想从

(list (list 1 4 9) (list 16 25))

获得

((1 2 3)(4 5))

但是,以下似乎不起作用,

(map (map sqrt) (list (list 1 4 9) (list 16 25)))

也不是以下内容。

(map (fn [x] (map sqrt x)) (list (list 1 4 9) (list 16 25)))

为什么? (我该如何解决这个问题?)

【问题讨论】:

  • 您的最后一个 sn-p 确实有效。您收到什么错误消息?

标签: clojure nested-lists


【解决方案1】:

您的倒数第二个版本“几乎”有效。 Clojure 没有自动 柯里化,所以(map sqrt) 不是 部分应用,而是(map sqrt) 返回一个转换器,它接受一个参数并返回一个函数 使用三种不同的参数 - 所以在那里运行你的代码会给你 为每个数字列表返回一个函数。

为此,您可以使用partial

user=> (map (partial map sqrt) (list (list 1 4 9) (list 16 25)))
((1 2 3) (4 5))

当然还有必填的specter 回答:

user=> (transform [ALL ALL] sqrt '((1 4 9)(16 25)))
((1 2 3) (4 5))

【讨论】:

    【解决方案2】:

    map 函数与for 函数密切相关,我认为它有时更易于使用。以下是我将如何解决这个问题:

      (let [matrix [[1 4 9]
                    [16 25]]
            result (vec (for [row matrix]
                          (vec (for [num row]
                                 (Math/sqrt num)))))]
        result)
    

    结果:

    result => 
        [[1.0 2.0 3.0] 
         [4.0 5.0]]
    

    如果您删除两个 (vec ...) 位,您将看到相同的结果,但 for 通常会返回一个惰性序列。

    【讨论】:

      【解决方案3】:

      您可以为此任务编写递归函数:

      (defn deep-map [f seq1]
        (cond (empty? seq1) nil
              (sequential? (first seq1))
              (cons (deep-map f (first seq1))
                    (deep-map f (rest seq1)))
              :else (cons (f (first seq1))
                          (deep-map f (rest seq1)))))
      

      例子:

      (deep-map #(Math/sqrt %) '((1 4 9) (16 25 36)))
      => ((1.0 2.0 3.0) (4.0 5.0 6.0))
      

      或者你可以使用clojure.walk和函数postwalk

      (clojure.walk/postwalk
        #(if (number? %) (Math/sqrt %) %)
        '((1 4 9) (16 25 36)))
      
      => ((1.0 2.0 3.0) (4.0 5.0 6.0))
      

      【讨论】:

        【解决方案4】:

        @MartinPuda 的回答是对的。

        尾调用递归版本在这里:

        (defn map* [f sq & {:keys [acc] :or {acc '()}}]
          (cond (empty? sq) (vec (reverse acc))
                (sequential? (first sq)) (map* f 
                                               (rest sq)
                                               :acc (cons (map* f (first sq)) acc))
                :else (map* f (rest sq) :acc (cons (f (first sq)) acc))))
        

        按照 lisp 的传统,这种递归到嵌套结构中的函数是 fnname*(最后用星号标记)。 acc 累加由cons 构造的结果嵌套树。

        在你的情况下,这将是:

        (map* Math/sqrt (list (list 1 4 9) (list 16 25)))
        

        测试:

        (map* (partial + 1) '[1 2 [3 4 [5] 6] 7 [8 [9]]])
        
        ;; => [2 3 [4 5 [6] 7] 8 [9 [10]]]
        

        【讨论】:

          猜你喜欢
          • 2015-01-28
          • 2023-04-03
          • 2019-03-31
          • 1970-01-01
          • 2020-06-06
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2012-01-13
          相关资源
          最近更新 更多