Interesting - thank you!

Good to know about "into" as a way to achieve a non-lazy concat - in 
conjunction with a vector.

I like your accumulator version - morphing the structure of the input as 
you go to achieve the desired result.

I've come up with another version, that passes through both a "result so 
far" accumulator and a "still to be computed" stack.  It is very much 
non-lazy, but that's okay in some applications (a performance feature 
even!).  Here it is...

(defn my-accandstackbased-flatten
  [xs]
  (letfn [(flatten-accandstack [xs accvec stackvec]
            (if (empty? xs)
              (if (empty? stackvec)
                accvec
                (recur (peek stackvec) accvec (pop stackvec)))
              (let [x (first xs), ys (next xs)]
                (if (sequential? x)
                  (recur x accvec (conj stackvec ys))
                  (recur ys (conj accvec x) stackvec)))))]
    (seq (flatten-accandstack xs [] []))))

This has nice recur behaviour and doesn't mess too much with the structure 
of the input nested lists.  In the case of flatten, retaining structure is 
not important (as you nicely illustrated), but there are generalizations of 
this kind of function where retaining structure becomes more important.

Now here's the thing...  In the above, my stackvec is sort of like a 
continuation.  It contains a stack of computation to be performed later on. 
 That sounds very much like a continuation to me (though I am still in the 
process of getting my head around this stuff).  So I've thought, surely I 
could write an "acc and cps" version of my-flatten?  Here is what I came up 
with...

(defn my-accandcpsbased-flatten
  [xs]
  (letfn [(flatten-accandcps [xs accvec k]
            (if (empty? xs)
              (k accvec)
              (let [x (first xs), ys (next xs)]
                (if (sequential? x)
                  (recur x accvec (fn [v] (flatten-accandcps ys [] (fn [w] 
(k (into v w))))))
                  (recur ys (conj accvec x) k)))))]
    (seq (flatten-accandcps xs [] identity))))
 
And I could do the trick you showed me with thunks and a trampoline, if I 
wanted to make it completely stack-avoiding.

What does this show?  I'm not sure.

Presumably the acc and stack version is the most efficient as it uses recur 
everywhere?

The structure of the continuation I pass in with the "acc and cps" version 
is very similar in complexity to the continuation I passed as part of my 
original cps implementation.  So where's the gain?  I guess the inclusion 
of an accvec means that continuations are generated less frequently so that 
the overall size and execution time of continuations is smaller?  And it is 
an improvement over my earlier aps version that used only an acc vector - 
because this version has everything in tail-call position and is amenable 
to trampolining.

Is this attempt at analysis reasonable?  Is there anything else worth 
observing etc?

Presumably I could make the "acc and cps" above more lazy using concat, 
cons and lazy-seq.  I'd need to think about how easy/hard it would be to 
make my "acc and stack" version as lazy.

Thanks again for your helpful comments and examples.

-- 
You received this message because you are subscribed to the Google
Groups "Clojure" group.
To post to this group, send email to clojure@googlegroups.com
Note that posts from new members are moderated - please be patient with your 
first post.
To unsubscribe from this group, send email to
clojure+unsubscr...@googlegroups.com
For more options, visit this group at
http://groups.google.com/group/clojure?hl=en
--- 
You received this message because you are subscribed to the Google Groups 
"Clojure" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to clojure+unsubscr...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Reply via email to