0

I'm a completely new to erlang. As an exercise to learn the language, I'm trying to implement the function sublist using tail recursion and without using reverse. Here's the function that I took from this site http://learnyousomeerlang.com/recursion:

tail_sublist(L, N) -> reverse(tail_sublist(L, N, [])).

tail_sublist(_, 0, SubList) -> SubList;
tail_sublist([], _, SubList) -> SubList;
tail_sublist([H|T], N, SubList) when N > 0 ->
tail_sublist(T, N-1, [H|SubList]).

It seems the use of reverse in erlang is very frequent.

In Mozart/Oz, it's very easy to create such the function using unbound variables:

proc {Sublist Xs N R}
   if N>0 then
      case Xs
      of nil then
         R = nil
      [] X|Xr then
         Unbound
      in
         R = X|Unbound
         {Sublist Xr N-1 Unbound}
      end
   else
      R=nil
   end
end

Is it possible to create a similar code in erlang? If not, why?

Edit:

I want to clarify something about the question. The function in Oz doesn't use any auxiliary function (no append, no reverse, no anything external or BIF). It's also built using tail recursion.

When I ask if it's possible to create something similar in erlang, I'm asking if it's possible to implement a function or set of functions in erlang using tail recursion, and iterating over the initial list only once.

At this point, after reading your comments and answers, I'm doubtful that it can be done, because erlang doesn't seem to support unbound variables. It seems that all variables need to be assigned to value.

Javier
  • 376
  • 2
  • 12
  • 1
    See [this anwser](http://stackoverflow.com/a/6179634/727663) addressing why is a good idea to use reverse in tail recursive functions. – Nuno Freitas Sep 16 '13 at 18:00
  • Ok. I don't think it's a good idea to use `reverse` if you can achieve the same result efficiently and without using it. – Javier Sep 16 '13 at 18:56
  • 2
    using lists:reverse is definitively a good idea if you need to use list where the order is significant. It seems that you didn't spend enough time to understand the topic pointed by Nuno. Anyway, for didactic purpose it is a very good idea to do it yourself. – Pascal Sep 16 '13 at 19:50
  • If the point is to be efficient, use lists:sublist/2 ... – Berzemus Sep 17 '13 at 06:56
  • using `reverse` is not only a good idea. It seems to be the only way to implement the function efficiently. It seems it's not possible to do it anything better in erlang using lists. Although I spent time reading the answer to the other question, it doesn't answer my own question. I'm not asking if `reverse` is efficient nor I'm asking why `reverse` is a good idea. I'm asking if it's possible to implement the code, efficiently, without using `reverse`. In Oz, I didn't need to use any auxiliary function, and the implementation uses tail recursion. Is it possible to do the same in erlang? – Javier Sep 17 '13 at 14:13
  • @Berzemus lists:sublist might be efficient, however it doesn't seem to use tail recursion. See the [source code here](https://github.com/erlang/otp/blob/maint/lib/stdlib/src/lists.erl). – Javier Sep 17 '13 at 14:44

2 Answers2

1

Here's a version that uses appends along the way instead of a reverse at the end.

subl(L, N) -> subl(L, N, []).

subl(_, 0, Accumulator) ->
    Accumulator;
subl([], _, Accumulator) ->
    Accumulator;
subl([H|T], N, Accumulator) ->
    subl(T, N-1, Accumulator ++ [H]).

I would not say that "the use of reverse in Erlang is very frequent". I would say that the use of reverse is very common in toy problems in functional languages where lists are a significant data type.

I'm not sure how close to your Oz code you're trying to get with your "is it possible to create a similar code in Erlang? If not, why?" They are two different languages and have made many different syntax choices.

Nathaniel Waisbrot
  • 23,261
  • 7
  • 71
  • 99
  • I'd like to avoid the use of the concatenation, for a very long list, it would be better to use `reverse`. In the Oz code, you don't need to use the append to achieve the same result. Thanks for taking the time to read the question and even writing code :) – Javier Sep 16 '13 at 18:48
  • OK, I don't have tons of experience in Erlang, but I think the answer is that Erlang is not optimized for manipulation of massive linked lists. Erlang feels best running small processes that pass messages between each other frequently. Building a giant list and grinding over it probably means that either Erlang's the wrong choice or your design is bad. (Or, as a last resort, you need to extend the language.) – Nathaniel Waisbrot Sep 16 '13 at 19:35
  • Your answer makes sense. Actually, it seems it's not possible to create unbound variables in erlang and, as consequence, it's not possible to create a solution to the problem without using `reverse`. Probably, it's a bad idea to have unbound variables that can be passed to other processes, because synchronizing those values would be very difficult. Also, since erlang doesn't support dataflow variables neither, it makes the code easier to read, given the language limitations. Thanks for taking the time to answer. – Javier Sep 17 '13 at 14:05
1

Short Version

No, you can't have a similar code in Erlang. The reason is because in Erlang variables are Single assignment variables. Unbound Variables are simply not allowed in Erlang.

Long Version

I can't imagine a tail recursive function similar to the one you presenting above due to differences at paradigm level of the two languages you are trying to compare.

But nevertheless it also depends of what you mean by similar code. So, correct me if I am wrong, the following

R = X|Unbound
{Sublist Xr N-1 Unbound}

Means that the attribution (R=X|Unbound) will not be executed until the recursive call returns the value of Unbound. This to me looks a lot like the following:

sublist(_,0) -> [];
sublist([],_) -> [];
sublist([H|T],N) 
    when is_integer(N) ->
         NewTail = sublist(T,N-1), 
         [H|NewTail].
%% or 
%%sublist([H|T],N) 
%%    when is_integer(N) -> [H|sublist(T,N-1)].

But this code isn't tail recursive.

Nuno Freitas
  • 983
  • 8
  • 23
  • Thanks for your answer. In Oz, the variables are single assignment too, however, when variables are created, they have no default value (i.e. the variables are created unbounded). When you execute this code `R = X|Unbound`, it means that `R` is bounded to a *pair* of `X` and `Unbound`. Your code seems to be correct, however that is what I was trying to avoid. This is the key point: Unbound Variables are simply not allowed in Erlang. – Javier Sep 17 '13 at 19:13
  • I know little to none of Oz, What I've retained is that you can create a Unbounded Var, and when this Var is needed the thread is "halted". Then another thread can assign the value of Var and the previous thread will proceed. But main point if you leave a Unbouded Variable in a erlnag module the erlc will not compile the code. – Nuno Freitas Sep 17 '13 at 19:32