3
\$\begingroup\$

I am trying to solve some exercises from the "Programming Erlang" book.

One of them is "Write a function to determine which module exports the most functions"

This is my solution:

-module(module_info).
-export([modules_ordered_by_amount_of_functions/0]).

modules_ordered_by_amount_of_functions() ->
  Modules = loaded_modules_with_functions(),
  SortedModules = lists:sort(fun compare_functions_length/2, Modules),
  lists:map(fun extract_amount_of_functions/1, SortedModules).

loaded_modules_with_functions() ->
  LoadedModules = code:all_loaded(),
  LoadedModuleNames = lists:map(fun extract_module_name/1, LoadedModules),
  lists:map(fun extract_functions_from_module/1, LoadedModuleNames).

extract_module_name({ModuleName, _}) -> ModuleName.

extract_functions_from_module(ModuleName) ->
  [_, {exports, Functions} | _] = ModuleName:module_info(),
  {ModuleName, Functions}.

compare_functions_length ({_, FunctionsA}, {_, FunctionsB}) ->
  length(FunctionsA) >= length(FunctionsB).

extract_amount_of_functions ({ModuleName, Functions}) ->
  {ModuleName, length(Functions)}.

It doesn't really feel elegant to me.

\$\endgroup\$
2
\$\begingroup\$

Breaking the problem into tiny, useful functions is a very good way to write Erlang code. So your code is easy to read and gives us a great start in understanding and analyzing it.

The one thing I notice is that, in loaded_modules_with_functions, you map a function over a list and then map a function on the resulting list. This creates an intermediate list which will just be thrown away (GCed.) I'd combine the two operations so there's only one pass through the list. Since the result of this function is the resulting list, I'd use a list comprehension:

loaded_modules_with_functions() ->
  [ extract_functions_from_module(ModName) || {ModName, _} <- code:all_loaded() ].

In extract_functions_from_module, you're making an assumption that the export list is always the second element. Maybe this is the case. Or maybe in your OTP release this is always the case. The next release, however, may break your code if module_info adds more information. You can use the proplists module (in the standard library) to generalize the look-up:

extract_functions_from_module(ModuleName) ->
  {ModuleName, proplists:get_value(exports, ModuleName:module_info(), [])}.

Finally, I'd put a list comprehension at the end of modules_ordered_by_amount_of_functions and inline extract_amount_of_functions.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.