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