Erlang Language List


Example

Here the simplest recursive function over list type. This function only navigate into a list from its start to its end and do nothing more.

-spec loop(list()) -> ok.
loop([]) ->
  ok;
loop([H|T]) ->
  loop(T).

You can call it like this:

loop([1,2,3]). % will return ok.

Here the recursive function expansion:

loop([1|2,3]) ->
  loop([2|3]) ->
    loop([3|]) ->
      loop([]) ->
        ok.

Recursive loop with IO actions

Previous code do nothing, and is pretty useless. So, we will create now a recursive function who execute some actions. This code is similar to lists:foreach/2.

-spec loop(list(), fun()) -> ok.
loop([], _) ->
  ok;
loop([H|T], Fun) 
  when is_function(Fun) ->
    Fun(H),
    loop(T, Fun).

You can call it in like this:

Fun = fun(X) -> io:format("~p", [X]) end.
loop([1,2,3]).

Here the recursive function expansion:

loop([1|2,3], Fun(1)) ->
  loop([2|3], Fun(2)) ->
    loop([3|], Fun(3)) ->
      loop([], _) ->
        ok.

You can compare with lists:foreach/2 output:

lists:foreach(Fun, [1,2,3]).

Recursive loop over list returning modified list

Another useful example, similar to lists:map/2. This function will take one list and one anonymous function. Each time a value in list is matched, we apply function on it.

-spec loop(A :: list(), fun()) -> list().
loop(List, Fun) 
  when is_list(List), is_function(Fun) ->
    loop(List, Fun, []).

-spec loop(list(), fun(), list()) -> list() | {error, list()}.
loop([], _, Buffer) 
  when is_list(Buffer) ->
    lists:reverse(Buffer);
loop([H|T], Fun, Buffer) 
  when is_function(Fun), is_list(Buffer) ->
    BufferReturn = [Fun(H)] ++ Buffer,
    loop(T, Fun, BufferReturn).

You can call it like this:

Fun(X) -> X+1 end.
loop([1,2,3], Fun).

Here the recursive function expansion:

loop([1|2,3], Fun(1), [2]) ->
  loop([2|3], Fun(2), [3,2]) ->
    loop([3|], Fun(3), [4,3,2]) ->
      loop([], _, [4,3,2]) ->
        list:reverse([4,3,2]) ->
          [2,3,4].

This function is also called "tail recursive function", because we use a variable like an accumulator to pass modified data over multiple execution context.