for(Max, Max, F) -gt; [F(Max)];
for(I, Max, F) -gt; [F(I)|for(I+1, Max, F)].
My I suggest instead either:
lists:each(lists:seq(Min, Max), F).
or:
for(I, Max, F) when I <= Max ->
for_impl(I, Max, F, []).
for_impl(Max, Max, F, Acc) ->
lists:reverse(Acc);
for_impl(I, Max, F, Acc) ->
for_impl(I+1, Max, F, [F(I)|Acc].
The original version of this code will blow out the stack with non-trivial length loops.
Hey, thanks.
I hadn’t really meant for my little post to be published on Planet Erlang as a definitive example… I’ve just been noodling with the language for the past week or so. The code actually came from an early chapters of Programming Erlang, and I’d posted it as an example of how functional languages are a different kind of beast than I’m used to.
Your example of how to do it properly is interesting.
So, just so I understand: While the loop is incrementing, the results of each iteration go into Acc, and then when I =:= Max, (handled by the first clause of for_impl), Acc is reversed and passed back?
Yep. You always want to add stuff to (or operate on) the head of the list, because that’s the fastest operation to perform on a list, then reverse it when you’re done.
Even reversal is easy to do in a tail-recursive method.
list_reverse([], Acc) -> Acc;
list_reverse([H|T], Acc) -> list_reverse(T, [H|Acc]).
The other thing is that any recursive call should always be the last call in a function. There’s a standard optimization for that in languages that encourage recursion to prevent the stack from growing. But when you need to perform an operation on the return of a recursive call, that optimization can’t be performed.