2

For kicks, I decided to write my own version of map, but finally learn how to use lazy-seq correctly and use it to make the map lazy:

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

It works, but when I tested its lazy behavior against map, I noticed something different. I'm using a helper-map function that prints when an element is processed:

(defn print-map [description-str f coll mapping-f]
  (mapping-f
    (fn [x]
      (do
        (print (str description-str ":" x))
        (f x)))
    coll))

When I use the standard map function, the elements are processed one at a time, alternating between functions:

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

Prints:

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)

Note how each number is processed by both functions first before the rest of the elements are seen by either function.

But when I change m in -main to my-map, the processing order changes slightly:

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)

Now the first function is run twice to start, the second function runs twice in a row in the end, and as a consequence, the mappings are no longer "in sync".

What's wrong with my-map that causes this to happen?

Carcigenicate
  • 43,494
  • 9
  • 68
  • 117

1 Answers1

4

The destructing you do in my-map will call next on your lazy sequence.

You can avoid that by not destructing:

(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])

And next is not as lazy as rest.

Community
  • 1
  • 1
ClojureMostly
  • 4,652
  • 2
  • 22
  • 24