Skip to content

Conditions: filter as a List Comprehension

The expression filter pred xs can be written as the list comprehension [x | x <- xs, pred x]. This describes the list of all values x taken from the list xs such that pred x is true, just as what filter pred xs would produce. For example,

GHCi
>>> [x | x <- [1..10], even x]
[2,4,6,8,10]

However, list comprehensions let us conveniently combine filtering and mapping:

GHCi
>>> import Data.Char
>>> [chr x | x <- [65..90], even x]
"BDFHJLNPRTVXZ"

chr is a function that constructs a character from its code point. The code points of all uppercase English letters are 65 through 90. So this generates the list of all uppercase letters with even code points, "BDFHJLNPRTVXZ".

And we can of course combine iterating over nested lists with mapping and filtering:

GHCi
>>> [x * y | x <- [1..10], even x, y <- [x..10], odd (x + y)]
[6,10,14,18,20,28,36,42,54,72]

You should read this as follows: For every value x taken from the list [1..10] that satisfies even x (is even), take every value y from the list [x..10] that satisfies odd (x + y) and add the product x * y to the list produced by this list comprehension. (I'm not claiming that this is the most meaningful example.)

Exercise

See whether you can come up with an expression that generates the same list as the list comprehension

[x * y | x <- [1..10], even x, y <- [x..10], odd (x + y)]

but which uses only standard list functions.

Solution
GHCi
>>> import Data.List
>>> :{
  | [1..10] & filter even
  |         & concatMap ( \x -> [x..10]
  |                           & filter (\y -> odd (x + y))
  |                           & map    (\y -> x * y)
  |                     )
  | :}
[6,10,14,18,20,28,36,42,54,72]

To make this at least a little more readable, I used the operator (&) from Data.List. It's the flipped version of ($): f $ x applies f to x. So does x & f.

Here, we take the list [1..10] and filter it, keeping only the elements that are even. For each such element x, we take the list and filter it using the predicate \y -> odd (x + y). We replace each element y in the resulting list with x * y. We obtain one such list of ys for each x. By using concatMap instead of map, we end up concatenating these lists.

I think we can agree that the list comprehension is the preferable solution.