2 List handling
2.1 Creating a list
Calling
lists:append(List1, List2)
will result in that the whole List1 has to be traversed. When recursively building a list always attach the element to the begining of the list and not to the end. In the case that the order of the elements in the list is important you can always reverse the list when it has been built! It is cheaper to reverse the list in the final step, than to append the element to the end of the list in each recursion. Note that ++ is equivalent to lists:append/2. As an example consider the tail-recursive function fib that creates a list of Fibonacci numbers. (The Fibonacci series is formed by adding the latest two numbers to get the next one, starting from 0 and 1.)> fib(4). [0, 1, 1, 2, 3]DO
fib(N) -> fib(N, 0, 1, [0]). fib(0, Current, Next, Fibs) -> lists:reverse(Fibs); % Reverse the list as the order is important fib(N, Current, Next, Fibs) -> fib(N - 1, Next, Current + Next, [Next | Fibs]).DO NOT
fib(N) -> fib(N, 0, 1, [0]). fib(0, Current, Next, Fibs) -> Fibs; fib(N, Current, Next, Fibs) -> fib(N - 1, Next, Current + Next, Fibs ++ [Next]).2.2 Deleting a list element
When using the function delete in the lists module there is no reason to check that the element is actually part of the list. If the element is not part of the list the delete operation will be considered successful.
DO
... NewList = lists:delete(Element, List), ...DO NOT
... NewList = case lists:member(Element, List) of true -> lists:delete(Element, List); false -> List end, ...2.3 Unnecessary list traversal
Use functions like lists:foldl/3 and lists:mapfoldl/3 to avoid traversing lists more times than necessary. The function mapfold combines the operations of map and foldl into one pass. For example, we could sum the elements in a list and double them at the same time:
> lists:mapfoldl(fun(X, Sum) -> {2*X, X+Sum} end, 0, [1,2,3,4,5]). {[2,4,6,8,10],15}Also consider the function evenMultipleSum below where we make one list traversal in the first version and three in the second version. (This may not be a very useful function, but it serves as a simple example of the principle.)
DO
evenMultipleSum(List, Multiple) -> lists:foldl(fun(X, Sum) when (X rem 2) == 0 -> X * Multiple + Sum; (X, Sum) -> Sum end, 0, List).DO NOT
evenMultipleSum(List, Multiple) -> FilteredList = lists:filter(fun(X) -> (X rem 2) == 0 end, List), MultipleList = lists:map(fun(X) -> X * Multiple end, FilteredList), lists:sum(MultipleList).2.4 Deep and flat lists
lists:flatten/1
is a very general function and because of this it can be quite expensive to use. So do not use it if you do not have to. There are especially two senarios where you do not need to use flatten
- When sending data to a port. Ports understands deep lists so there is no reason to flatten the list before sending it to the port.
- When you have a deep list of depth 1 you can flatten it using
append/1
Port example
DO
... port_command(Port, DeepList) ...DO NOT
... port_command(Port, lists:flatten(DeepList)) ...A common way that people construct a flat list in vain is when they use append to send a 0-terminated string to a port. In the example below please note that "foo" is equivalent to [102, 111, 111].
DO
... TerminatedStr = [String, 0], % String="foo" => [[102, 111, 111], 0] port_command(Port, TerminatedStr) ...DO NOT
... TerminatedStr = String ++ [0], % String="foo" => [102, 111, 111, 0] port_command(Port, TerminatedStr) ...Append example
DO
> lists:append([[1], [2], [3]]). [1,2,3] >DO NOT
> lists:flatten([[1], [2], [3]]). [1,2,3] >
If your deep list is a list of strings you will not get the wanted result using flatten. Remember that strings are lists. See example below:
> lists:append([["foo"], ["bar"]]). ["foo","bar"] > lists:flatten([["foo"], ["bar"]]). "foobar"