Fork me on GitHub
#code-reviews
<
2022-01-07
>
paulocuneo16:01:13

Hi friends! For writing a lazy-seq which of this implementations is preferred?

;; wrap the tail
(defn drop-after
  [p? s]
  (when (seq s)
    (let [head (first s)]
      (if (p? head)
        [head]
        (cons head (lazy-seq (drop-after p? (rest s))))))))

;; wrap everything
(defn drop-after
  [p? s]
  (lazy-seq
   (when (seq s)
     (let [head (first s)]
       (if (p? head)
         [head]
         (cons head (drop-after p? (rest s))))))))

paulocuneo23:02:59

howdy! on the subject of lazy-seq "wrap everything vs wrap tail" I recently find out that wrapping everything may cause stackoverflow when interleaving and concating lazy sequences, while wrapping the tail does not. if everything is wrap when doing a (seq some-lazy-seq) causes the whole seq to be realized combining lazy-sequences, while if only the tail is wrap (seq (cons h some-lazy-seq)) wont realized the whole seq. Also destructuring (let [[h & t] some-lazy-seq] ...) causes the seq to be realized, I think the macro expansion introduces a call to seq .

paulocuneo15:02:35

Sorry, I was wrong, but I was almost right. I've read this article https://stuartsierra.com/2015/04/26/clojure-donts-concat then I asked my self what if only wrap the tail. I didn't realized, that the head is always realized, but the tail will blowup anyway.

(defn concat-tail [x y]
  (if (seq x)
    (cons (first x)
          (lazy-seq (concat-tail (rest x) y)))
    y))

(defn concat-all [x y]
  (lazy-seq
   (if (seq x)
     (cons (first x)
           (concat-all (rest x) y))
     y)))

(defn build-result [n cat]
  (loop [counter 1
         results []]
    (if (< counter n)
      (recur (inc counter)
             (cat results (range 1 counter)))
      results)))

(do (seq (build-result 4000 concat-all))
    nil) ;; stackoverflow

(do (seq (build-result 4000 concat-tail))
    nil) ;; it's ok

(->> (build-result 4000 concat-all)
     (take 1)) ;; stackoverflow

(->> (build-result 4000 concat-tail)
     (take 1)) ;; it's ok

(->> (build-result 4000 concat-all)
     (take 2)) ;; stackoverflow

(->> (build-result 4000 concat-tail)
     (take 2)) ;; stackoverflow blows up anyway

paulocuneo23:02:59

howdy! on the subject of lazy-seq "wrap everything vs wrap tail" I recently find out that wrapping everything may cause stackoverflow when interleaving and concating lazy sequences, while wrapping the tail does not. if everything is wrap when doing a (seq some-lazy-seq) causes the whole seq to be realized combining lazy-sequences, while if only the tail is wrap (seq (cons h some-lazy-seq)) wont realized the whole seq. Also destructuring (let [[h & t] some-lazy-seq] ...) causes the seq to be realized, I think the macro expansion introduces a call to seq .