[erlang-questions] list:join() for erlang?

Ben Munat bent@REDACTED
Fri Sep 14 02:32:40 CEST 2007


Thanks for the very thorough reply... Yeah, "stack-recursive" came out cuz I 
couldn't think of the term for "not tail recursive"... so, it's "body 
recursive". Thanks for setting me straight.

b

ok wrote:
> I wrote:
>>> join0(X, [Y|Ys]) -> X ++ join0(Y, Ys);
>>> join0(X, [])     -> X.
>>>
> [join/2, join1/1, join2/3 deleted]
> 
> On 13 Sep 2007, at 10:34 pm, Ben Munat wrote:
> 
>> Out of curiosity -- still feeling my way around erlang and functional
>> programming -- aren't the "helper" functions here stack-recursive 
>> (i.e. *not* tail-recursive)?
> 
> I've never heard the term "stack-recursive" before; the normal term
> for calls that are not tail calls is "body" calls (in this case,
> body recursion).  Yes, they are body calls.  What of that?  They are
> what they need to be.  As a matter of fact, the Prolog equivalents
> of these *would* be tail recursive:
> 
>     join0([], X, X).
>     join0([Y|Ys], X, Ans) :-
>         append(X, R, Ans),
>         join0(Ys, Y, R).
> 
> It would be possible to implement Erlang in such a way that these
> Erlang functions were also tail recursive.  It wouldn't even be hard.
> In effect, each function would receive a hidden parameter that was
> the address of where to put the result.  This would require a
> different kind of garbage collector, though; the current one can and
> does exploit the fact that there cannot be pointers from "older"
> cells to "newer" ones.
> 
> Tail recursion is nice when you can use it, but in the current
> version of Erlang it would be necessary to build up a result in
> reverse and then reverse it at the end.  There are times when that's
> a good idea, but this isn't one of them.  Another idea would be not
> to build up the result but to build a stack of lists to be appended.
> Let's see what happens.
> 
> join([], _) -> [];
> join(Xs, [])  -> [Last|Stack] = reverse(Xs), join0(Stack, Last);
> join(Xs, [C]) -> [Last|Stack] = reverse(Xs), join1(Stack, Last, C);
> join(Xs, Sep) -> [Last|Stack] = reverse(Xs), join2(Stack, Last, Sep).
> 
> join0([Ys|Yss], Xs) -> join0(Yss, Ys++Xs);
> join0([],       Xs) -> Xs.
> 
> join1([Ys|Yss], Xs, C) -> join1(Yss, Ys++[C|Xs], C);
> join1([],      Xs, _) -> Xs.
> 
> join2([Ys|Yss], Xs, Sep) -> join2(Yss, Ys++Sep++Xs, Sep);
> join2([],      Xs, _  ) -> Xs.
> 
> In effect, this also uses a stack, but it builds the stack as a list
> in the heap.  Rather to my surprise, this second version turned out
> to be faster than the previous version.  Mind you, you have to make
> the inputs so large, and repeat the operation so many times, to get
> a measurable time, that it's not really a problem.
> 
> I wondered whether unrolling the loops would be worth while, but
> the measurements fluctuated so much it was hard to tell.  What
> I'd really like is a high resolution cpu time BIF, which is the
> subject of another thread...
> 
> 
> 




More information about the erlang-questions mailing list