自定义地图函数在惰性场景中表现异常
Custom map function behaves oddly in lazy scenarios
为了好玩,我决定自己写一个版本的map
,但终于学会了如何正确使用lazy-seq
并用它来使地图变得懒惰:
(defn my-map [f [head & tail]]
(lazy-seq
(if head
(cons (f head) (my-map f tail))
tail)))
它有效,但是当我针对 map
测试它的惰性行为时,我发现了一些不同之处。我正在使用一个 helper-map 函数,该函数在处理元素时打印:
(defn print-map [description-str f coll mapping-f]
(mapping-f
(fn [x]
(do
(print (str description-str ":" x))
(f x)))
coll))
当我使用标准 map
函数时,一次处理一个元素,在函数之间交替:
(defn -main []
(let [m map
coll (into '() (range 10 0 -1))
coll2 (print-map "A" identity coll m)
coll3 (print-map "B" identity coll2 m)]
(println (doall coll3))))
打印:
A:1 B:1 A:2 B:2 A:3 B:3 A:4 B:4 A:5 B:5 A:6 B:6 A:7 B:7 A:8 B:8 A:9 B:9 A:10 B:10 (1 2 3 4 5 6 7 8 9 10)
请注意每个数字是如何先由两个函数处理的,然后再由任一函数看到其余元素。
但是当我把-main
里面的m
改成my-map
的时候,处理顺序略有变化:
A:1 A:2 B:1 A:3 B:2 A:4 B:3 A:5 B:4 A:6 B:5 A:7 B:6 A:8 B:7 A:9 B:8 A:10 B:9 B:10 (1 2 3 4 5 6 7 8 9 10)
现在第一个函数运行两次开始,第二个函数运行结束连续两次,结果映射不再是"in sync".
导致这种情况发生的 my-map
出了什么问题?
您在 my-map
中进行的破坏将在您的惰性序列上调用 next
。
你可以通过不破坏来避免这种情况:
(defn my-map [f [x :as xs]]
#_(next xs) ;; uncomment to observere similar "broken" behaviour
(lazy-seq
(if x
(cons (f x) (my-map f (rest xs)))
(rest xs))))
;; You can find out what destructing does with this call:
(destructure '[[x & r :as xs] numbers])
和next is not as lazy as rest.
为了好玩,我决定自己写一个版本的map
,但终于学会了如何正确使用lazy-seq
并用它来使地图变得懒惰:
(defn my-map [f [head & tail]]
(lazy-seq
(if head
(cons (f head) (my-map f tail))
tail)))
它有效,但是当我针对 map
测试它的惰性行为时,我发现了一些不同之处。我正在使用一个 helper-map 函数,该函数在处理元素时打印:
(defn print-map [description-str f coll mapping-f]
(mapping-f
(fn [x]
(do
(print (str description-str ":" x))
(f x)))
coll))
当我使用标准 map
函数时,一次处理一个元素,在函数之间交替:
(defn -main []
(let [m map
coll (into '() (range 10 0 -1))
coll2 (print-map "A" identity coll m)
coll3 (print-map "B" identity coll2 m)]
(println (doall coll3))))
打印:
A:1 B:1 A:2 B:2 A:3 B:3 A:4 B:4 A:5 B:5 A:6 B:6 A:7 B:7 A:8 B:8 A:9 B:9 A:10 B:10 (1 2 3 4 5 6 7 8 9 10)
请注意每个数字是如何先由两个函数处理的,然后再由任一函数看到其余元素。
但是当我把-main
里面的m
改成my-map
的时候,处理顺序略有变化:
A:1 A:2 B:1 A:3 B:2 A:4 B:3 A:5 B:4 A:6 B:5 A:7 B:6 A:8 B:7 A:9 B:8 A:10 B:9 B:10 (1 2 3 4 5 6 7 8 9 10)
现在第一个函数运行两次开始,第二个函数运行结束连续两次,结果映射不再是"in sync".
导致这种情况发生的 my-map
出了什么问题?
您在 my-map
中进行的破坏将在您的惰性序列上调用 next
。
你可以通过不破坏来避免这种情况:
(defn my-map [f [x :as xs]]
#_(next xs) ;; uncomment to observere similar "broken" behaviour
(lazy-seq
(if x
(cons (f x) (my-map f (rest xs)))
(rest xs))))
;; You can find out what destructing does with this call:
(destructure '[[x & r :as xs] numbers])
和next is not as lazy as rest.