Skip to content

Instantly share code, notes, and snippets.

@Joakineee
Last active May 16, 2020 09:37
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Joakineee/3fcb9ffb73e8fc790b1c2174b2476460 to your computer and use it in GitHub Desktop.
Save Joakineee/3fcb9ffb73e8fc790b1c2174b2476460 to your computer and use it in GitHub Desktop.
Tail recursive lists
-module(w2lists).
-export([prod/1,mymax/1,test/0]).
%Moddified according Elbrujohalcon:
%Removed the "list is emty" function clause as we will let it crash.
%prod([]) ->
% "list is empty";
prod([H|T]) ->
prod(T,H).
%Tail recursive prod.
prod([],Acc) ->
Acc;
prod([H|T],Acc) ->
prod(T,Acc * H).
%The product of an empty list is usually taken to be 1: why?
%because Acc starts by 1.
%This could be avoided by,for example, verifying that the list is not empty before calling prod/2.
%
%Moddified according Elbrujohalcon:
%Removed the "list is emty" function clause as we will let it crash.
%mymax([]) ->
% "list is empty";
mymax([H|T]) ->
mymax(T,H).
mymax([],Acc) ->
Acc;
mymax([H|T],Acc) ->
mymax(T,max(Acc,H)).
test() ->
3 = w2lists:prod([3]),
90 = w2lists:prod([1,3,5,6]),
54 = w2lists:mymax([1,23,4,54,23]),
ok.
@elbrujohalcon
Copy link

elbrujohalcon commented May 14, 2020

This is good, although I would not recommend returning strings to signal errors.
You have better alternatives…

  1. Return atoms or tuples: error, {error, empty_list}, etc…
  2. Raise errors: error(empty_list)
  3. Let it crash!: Just don't write a function clause to match that case

I generally recommend 3… if that's not possible, my second choice would be 2…
I would reserve option 1 for scenarios where it's logically possible that such a thing happens. In other words, when it's not an error… but a different type of result. For instance…

%% @doc Not matching against [] here since it would be an error if someone calls this function like that
-spec list_head([T,...]) -> T.
list_head([H|_]) -> H.

%% @doc Raising an error here, since I can't pattern-match the issue away easily…
-spec best_team([user:t()]) -> team:t().
best_team(Users) ->
  Teams = lists:map(fun(User) ->
    case user:team(User) of
      not_found -> error({no_team, User});
      Team -> Team
    end, Users),
  most_popular(Teams).

%% @doc Returning something here, since not finding anything is completely possible
-spec get_user(string()) -> user:t() | not_found.
get_user(Username) ->
  case user_repo:find(#{username => Username}) of
    [] -> not_found;
    [User|_] -> User
  end.

@Joakineee
Copy link
Author

Excellent advice! thanks again!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment