Clojure - 递归地使用地图

Clojure - using map recursively

如果我有一个列表,我可以使用 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)))

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

函数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 通常 returns 一个惰性序列。

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

(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 and function 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))

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

要完成这项工作,您可以使用 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))

@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]]]