clojure - 自定义 map 函数在惰性场景中表现奇怪

标签 clojure mapping lazy-evaluation

为了好玩,我决定编写自己的 map 版本,但最终学习如何正确使用 lazy-seq 并使用它来使 map 变得惰性:

(defn my-map [f [head & tail]]
  (lazy-seq
    (if head
      (cons (f head) (my-map f tail))
      tail)))

它有效,但是当我针对 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)

现在第一个函数开始运行两次,第二个函数最后连续运行两次,因此,映射不再“同步”。

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 .

关于clojure - 自定义 map 函数在惰性场景中表现奇怪,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/40207024/

相关文章:

clojure - Clojure 中 IFn 和 Fn 的区别

clojure - 如何在 clojure 中参数化对 Java 枚举的访问?

javascript - spring mvc 如何从网页内容加载js和css文件

python - 在 python 中,我可以使用 tee 延迟生成迭代器的副本吗?

haskell - 类型强制 "strict/imperitive"Haskell 子集/版本

clojure - 如何在 Clojure 中的嵌套映射中选择键?

performance - 为什么这个 Clojure Reducers r/fold 没有提供性能优势?

ios - 如何使用 RestKit 映射复杂对象的嵌套数组

c# - Entity Framework : mapping tinyint to boolean

scheme - 通过高阶累积过程展平Scheme中的惰性列表的惰性列表