Thanks for explaining how one creates a trampolined version from the cps 
version!  This makes sense.

Thanks also for alerting me to potential issues with the concat laziness. 
 From a code efficiency point of view, producing a lazy something and then 
immediately evaluating it is inefficient.  It is better to use a strict 
version of the something in the first place.  Does Clojure provide strict 
versions of things like concat, or would I need to roll-my-own?

Thinking again of efficiency... I had a go at doing an 
accumulator-passing-style (APS) version of flatten.  Here it is...

(defn my-apsbased-flatten
  [xs]
  (letfn [(my-flatten-aps [xs avec]
            (if (nil? xs)
              avec
              (let [x (first xs), ys (next xs)]
                (if (sequential? x)
                  (recur ys (into avec (my-flatten-aps (seq x) '[])))
                  (recur ys (conj avec x))))))]
    (seq (my-flatten-aps (seq xs) '[]))))

This is more efficient (I think) than my earlier cps-based flatten.  But it 
has the problem again of using the stack via the non-recur call to 
my-flatten-aps.

I'm wondering if it's somehow possible to modify this aps implementation to 
also use trampolining?

Alternatively, maybe there's a combination aps-cps variation which can be 
trampolined??  I've had a bit of a go at this and so far can't see how to 
do it.  Perhaps not, as APS seems to do pre-calculation whereas CPS seems 
to do post-calculation?

But I can't help thinking that there must be a more efficient trampolined 
version of flatten that utilizes the structure of the "flatten problem" 
more, like APS does?

Cheers,

Mark.

On Tuesday, 15 July 2014 19:25:26 UTC+9:30, puzzler wrote:
>
> Yes, here's the trampolined version which won't stack overflow:
>
> (defn my-flatten
>   [xs]
>   (letfn [(my-flatten-cps [xs k]
>             (if (nil? xs)
>               (k '())
>               (let [x (first xs), ys (next xs)]
>                 (if (sequential? x)
>                   (recur ys (fn [v] (fn [] (my-flatten-cps (seq x) (fn [w] 
> (fn [] (k (doall (concat w v)))))))))
>                   (recur ys (fn [v] #(k (conj v x))))))))]        
>     (trampoline my-flatten-cps (seq xs) identity)))
>
> Basically, you just wrap the body of each continuation in a function of no 
> arguments, and call the recursive function with trampoline.
>
> Another thing you have to do is wrap the concat in a doall, otherwise 
> you'll get a stack overflow when the deeply nested lazy concatenation is 
> realized.
>
>
> On Tue, Jul 15, 2014 at 12:13 AM, Mark P <pier...@gmail.com <javascript:>> 
> wrote:
>
>> I'm very new to continuation passing style (CPS), and as part of the 
>> learning process I've done a CPS version of a "flatten" function.  Ie, it 
>> does the same thing as the standard clojure "flatten", but is implemented 
>> using a recursive local CPS helper function.  I'm interested in comments / 
>> critiques on what I've done.
>>
>> Here it is...
>>
>> (defn my-flatten
>>   [xs]
>>   (letfn [(my-flatten-cps [xs k]
>>             (if (nil? xs)
>>               (k '())
>>               (let [x (first xs), ys (next xs)]
>>                 (if (sequential? x)
>>                   (recur ys (fn [v] (my-flatten-cps (seq x) (fn [w] (k 
>> (concat w v))))))
>>                   (recur ys (fn [v] (k (conj v x))))))))]
>>     (my-flatten-cps (seq xs) identity)))
>>
>> I'm relatively inexperienced with clojure, so please feel free to suggest 
>> improvements to my clojure code.
>>
>> But what I'm most interested in is understanding CPS better and about how 
>> it interacts with Clojure and with "recur".  As you can see, my-flatten-cps 
>> uses recur nicely to traverse at a breadth level.  But as we sink down into 
>> depth, I've done an actual non-recur call to my-flatten-cps.  I presume I 
>> can't do a recur here instead (because it would attempt to jump to the most 
>> immediate fn??) - is this correct?
>>
>> Is there any way around this?  (As I write this, the word "trampoline" 
>> comes to mind - some videos I've watched speak of this - but not sure how 
>> this would work and what efficiency trade-offs would be involved.)
>>
>> The other things is... is it so bad that it is not fully using recur - 
>> maybe using a bit of stack is okay??
>>
>> Thanks,
>>
>> Mark.
>>
>>  -- 
>> You received this message because you are subscribed to the Google
>> Groups "Clojure" group.
>> To post to this group, send email to clo...@googlegroups.com 
>> <javascript:>
>> Note that posts from new members are moderated - please be patient with 
>> your first post.
>> To unsubscribe from this group, send email to
>> clojure+u...@googlegroups.com <javascript:>
>> 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+u...@googlegroups.com <javascript:>.
>> For more options, visit https://groups.google.com/d/optout.
>>
>
>

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