All lazy-seq does accepts its argument and delays its execution. To create a true lazy sequence, each link must be wrapped in a lazy call. The “granularity” of laziness is how much work is done between lazy-seq calls. The only way around this is to use higher level functions that return lazy seq.
In addition, tail recursion and laziness are mutually exclusive. This does not lead to a stack overflow, because at each step the recursive call ends in a lazy segment and returns. If the caller tries to evaluate lazy seq, a recursive call is called, but it is called by the original caller of the sequence function, not the sequence function itself, which means the stack is not growing. This is somewhat similar to the idea of implementing optimized tail recursion through trampolines (see Clojure trampoline Function).
Here is a version that is lazy:
(defn my-red ([f coll] (my-red f (first coll) (rest coll) )) ([f init coll] (let [mr (fn mr [gic] (if (empty? c) nil (let [gi (gi (first c))] (lazy-seq (cons gi (mr g gi (rest c)))))))] (lazy-seq (mr f init coll)))))
Notice how each mr run immediately returns either nil or lazy seq, and the recursive call comes from the lazy-seq call.
source share