/Erlang 21

# 3 List Comprehensions

## 3.1 Simple Examples

This section starts with a simple example, showing a generator and a filter:

```> [X || X <- [1,2,a,3,4,b,5,6], X > 3].
[a,4,b,5,6]```

This is read as follows: The list of X such that X is taken from the list `[1,2,a,...]` and X is greater than 3.

The notation `X <- [1,2,a,...]` is a generator and the expression `X > 3` is a filter.

An additional filter, `integer(X)`, can be added to restrict the result to integers:

```> [X || X <- [1,2,a,3,4,b,5,6], integer(X), X > 3].
[4,5,6]```

Generators can be combined. For example, the Cartesian product of two lists can be written as follows:

```> [{X, Y} || X <- [1,2,3], Y <- [a,b]].
[{1,a},{1,b},{2,a},{2,b},{3,a},{3,b}]```

## 3.2 Quick Sort

The well-known quick sort routine can be written as follows:

```sort([Pivot|T]) ->
sort([ X || X <- T, X < Pivot]) ++
[Pivot] ++
sort([ X || X <- T, X >= Pivot]);
sort([]) -> [].```

The expression `[X || X <- T, X < Pivot]` is the list of all elements in `T` that are less than `Pivot`.

`[X || X <- T, X >= Pivot]` is the list of all elements in `T` that are greater than or equal to `Pivot`.

A list sorted as follows:

• The first element in the list is isolated and the list is split into two sublists.
• The first sublist contains all elements that are smaller than the first element in the list.
• The second sublist contains all elements that are greater than, or equal to, the first element in the list.
• Then the sublists are sorted and the results are combined.

## 3.3 Permutations

The following example generates all permutations of the elements in a list:

```perms([]) -> [[]];
perms(L)  -> [[H|T] || H <- L, T <- perms(L--[H])].```

This takes `H` from `L` in all possible ways. The result is the set of all lists `[H|T]`, where `T` is the set of all possible permutations of `L`, with `H` removed:

```> perms([b,u,g]).
[[b,u,g],[b,g,u],[u,b,g],[u,g,b],[g,b,u],[g,u,b]]```

## 3.4 Pythagorean Triplets

Pythagorean triplets are sets of integers `{A,B,C}` such that `A**2 + B**2 = C**2`.

The function `pyth(N)` generates a list of all integers `{A,B,C}` such that `A**2 + B**2 = C**2` and where the sum of the sides is equal to, or less than, `N`:

```pyth(N) ->
[ {A,B,C} ||
A <- lists:seq(1,N),
B <- lists:seq(1,N),
C <- lists:seq(1,N),
A+B+C =< N,
A*A+B*B == C*C
].```
```> pyth(3).
[].
> pyth(11).
[].
> pyth(12).
[{3,4,5},{4,3,5}]
> pyth(50).
[{3,4,5},
{4,3,5},
{5,12,13},
{6,8,10},
{8,6,10},
{8,15,17},
{9,12,15},
{12,5,13},
{12,9,15},
{12,16,20},
{15,8,17},
{16,12,20}]```

The following code reduces the search space and is more efficient:

```pyth1(N) ->
[{A,B,C} ||
A <- lists:seq(1,N-2),
B <- lists:seq(A+1,N-1),
C <- lists:seq(B+1,N),
A+B+C =< N,
A*A+B*B == C*C ].```

## 3.5 Simplifications With List Comprehensions

As an example, list comprehensions can be used to simplify some of the functions in `lists.erl`:

```append(L)   ->  [X || L1 <- L, X <- L1].
map(Fun, L) -> [Fun(X) || X <- L].
filter(Pred, L) -> [X || X <- L, Pred(X)].```

## 3.6 Variable Bindings in List Comprehensions

The scope rules for variables that occur in list comprehensions are as follows:

• All variables that occur in a generator pattern are assumed to be "fresh" variables.
• Any variables that are defined before the list comprehension, and that are used in filters, have the values they had before the list comprehension.
• Variables cannot be exported from a list comprehension.

As an example of these rules, suppose you want to write the function `select`, which selects certain elements from a list of tuples. Suppose you write `select(X, L) -> [Y || {X, Y} <- L].` with the intention of extracting all tuples from `L`, where the first item is `X`.

Compiling this gives the following diagnostic:

`./FileName.erl:Line: Warning: variable 'X' shadowed in generate`

This diagnostic warns that the variable `X` in the pattern is not the same as the variable `X` that occurs in the function head.

Evaluating `select` gives the following result:

```> select(b,[{a,1},{b,2},{c,3},{b,7}]).
[1,2,3,7]```

This is not the wanted result. To achieve the desired effect, `select` must be written as follows:

`select(X, L) ->  [Y || {X1, Y} <- L, X == X1].`

The generator now contains unbound variables and the test has been moved into the filter.

This now works as expected:

```> select(b,[{a,1},{b,2},{c,3},{b,7}]).
[2,7]```

A consequence of the rules for importing variables into a list comprehensions is that certain pattern matching operations must be moved into the filters and cannot be written directly in the generators.

To illustrate this, do not write as follows:

```f(...) ->
Y = ...
[ Expression || PatternInvolving Y  <- Expr, ...]
...```

Instead, write as follows:

```f(...) ->
Y = ...
[ Expression || PatternInvolving Y1  <- Expr, Y == Y1, ...]
...```

© 2010–2017 Ericsson AB
Licensed under the Apache License, Version 2.0.