List is the the most important data type in Erlang, as in every (?) functional programming language. We have seen and used lists in almost every previous post. In this post, I will briefly present the “theory” of lists and then present the Erlang’s lists module and its most important functions.
Definition
A list can be recursively defined as the construct that
either is the empty list (denoted as [] in Erlang),
or is a cosntruct that its first element is a term (called head) and what remains if the head is removed is a list (called tail)
In Erlang a list of N elements has the [Element1, Element2, ..., ElementN] format (N is called the length of the list). So, [] is the empty list, [1], [{a}] are 1-element lists, [1, 2], [a, {b, c}] are 2-elements lists, etc. As we will see in a while, the format [Element1, Element2, ..., ElementN] is a shorthand for the [Element1 | [Element2 | ... | [ElementN | []] ... ] representation.
Decomposing & Pattern Matching
An empty list pattern matches with [].
1>Empty=[].[]2>Empty==[].
true
3>Empty=[].[]4>NotEmpty=[1].[1]5>Empty==NotEmpty.
false
6>Empty=NotEmpty.** exception error: no match of right hand side value [1]
1> Empty = [].
[]
2> Empty == [].
true
3> Empty = [].
[]
4> NotEmpty = [1].
[1]
5> Empty == NotEmpty.
false
6> Empty = NotEmpty.
** exception error: no match of right hand side value [1]
A non-empty list pattern matches with [Head | Tail].
1>[Head | Tail]=[1,2,3,4].[1,2,3,4]2>Head.13>Tail.[2,3,4]4>[Head1 | Tail1]=Tail.[2,3,4]5>Head1.26>Tail1.[3,4]7>[Head2 | Tail2]=[].** exception error: no match of right hand side value []
1> [Head | Tail] = [1, 2, 3, 4].
[1,2,3,4]
2> Head.
1
3> Tail.
[2,3,4]
4> [Head1 | Tail1] = Tail.
[2,3,4]
5> Head1.
2
6> Tail1.
[3,4]
7> [Head2 | Tail2] = [].
** exception error: no match of right hand side value []
Normal Representation
As mentioned before, the format [Element1, Element2, ..., ElementN] is a shorthand for the [Element1 | [Element2 | ... | [ElementN | []] ... ] representation.
For example the list [1, 2, 3] is a shorthand for the [1 | [2 | 3 | []]], that is the normal representation of a list.
1> [A | [B |[C | D]]] = [1, 2, 3].
[1,2,3]
2> A.
1
3> B.
2
4> C.
3
5> D.
[]
and composing
1>List=[2,3].[2,3]2>List1=[1 | List].[1,2,3]3>List2=[-1 | [0 | List1]].[-1,0,1,2,3]4>List3=[[-3,-2] | List2].[[-3,-2],-1,0,1,2,3]% the head is just 1 element
1> List = [2,3].
[2,3]
2> List1 = [1 | List].
[1,2,3]
3> List2 = [-1 | [0 | List1]].
[-1,0,1,2,3]
4> List3 = [[-3, -2] | List2].
[[-3,-2],-1,0,1,2,3] % the head is just 1 element
lists. Of course, since it is more readable and easier to write, the shorthand representation is usually used:
1>[A,B,C]=[1,2,3].[1,2,3]2>[A,B,C,D]=[1,2,3].% does not match cause% the left-hand side matches a 4-elements list** exception error: no match of right hand side value [1,2,3]3>[A,B,C | D]=[1,2,3].[1,2,3]4>D.[]5>[-3,-2 | [-1,0 | [1 | [2,3]]]].[-3,-2,-1,0,1,2,3]
1> [A, B, C] = [1, 2, 3].
[1,2,3]
2> [A, B, C, D] = [1, 2, 3]. % does not match cause
% the left-hand side matches a 4-elements list
** exception error: no match of right hand side value [1,2,3]
3> [A, B, C | D] = [1, 2, 3].
[1,2,3]
4> D.
[]
5> [-3, -2 | [-1, 0 | [1 | [2,3]]]].
[-3,-2,-1,0,1,2,3]
List Parsing
The pattern matching we saw before can be used in a function in order to parse the list
parse([])->% parsed the whole list
parsed.parse([Head | Tail])->% do whatever with the current element Headparse(Tail).
parse([]) ->
% parsed the whole list
parsed.
parse([Head | Tail]) ->
% do whatever with the current element Head
parse(Tail).
The lists module defines some commonly used list processing functions. This module is extremely useful, so it is a good idea to “remember” what functions it provides. You can find lists‘s module documentation here.
all/2
Called as all(Pred, List). Returns true if Pred(Element) returns true for all lists’ elements.
1>L=[2,4,6,8],1>F=fun(X)->Xrem2==0end,1>lists:all(F,L).
true
2>f().
ok
3>L=[2,4,5,8],3>F=fun(X)->Xrem2==0end,3>lists:all(F,L).
false
1> L = [2, 4, 6, 8],
1> F = fun(X) -> X rem 2 == 0 end,
1> lists:all(F, L).
true
2> f().
ok
3> L = [2, 4, 5, 8],
3> F = fun(X) -> X rem 2 == 0 end,
3> lists:all(F, L).
false
any/2
Like all/2 but returns true if any of the elements returns true for the predicate function.
Notice that the operator ++ and the function append/2 are the same.
concat/1
Accepts a list of items (atom, integer, float, string) and returns the concatenation of their textual representation as a list.
9>lists:concat(["ab",'.',1])."ab.1"
9> lists:concat(["ab", '.', 1]).
"ab.1"
delete/2
Deletes an element from the list (first occurrence, if any).
10>lists:delete(a,[d, a, d, a, d]).[d,d,a,d]
10> lists:delete(a, [d, a, d, a, d]).
[d,d,a,d]
filter/2
Called as filter(Pred, List). Returns a list containing only the elements that return true for the Pred.
11>Gt10=fun(X)->X>10end,lists:filter(Gt10,[1,2,22,3,44,5,66]).[22,44,66]12>L=[1, a, b,2, c,3.0, d,{4}].[1,a,b,2,c,3.0,d,{4}]13>lists:filter(fun(X)->is_number(X)end,L).[1,2,3.0]
Called as nth(N, List). Return the Nth element of the List. The head of the list is element 1.
24>lists:nth(3,[{1},{2},{three},{},{s, i, x}]).{three}
24> lists:nth(3, [{1}, {2}, {three}, {}, {s, i, x}]).
{three}
partition/2
Partitions a list to two according to if the elements satisfy or not a given predicate.
3>L=[1, a, b,2, c,3.0, d,{4}].[1,a,b,2,c,3.0,d,{4}]4>{Satisfy,Not}=lists:partition(fun(X)->is_number(X)end,L).{[1,2,3.0],[a,b,c,d,{4}]}5>Satisfy.[1,2,3.0]
Called as seq(From, To [, Increment]). Returns the sequence of integers starting from From and containing the integers From + 1, From + 2, ... up to To or the integers From + 1*Increment, From + 2*Increment, ... up to To if it is called as seq/3.