Skip to content

Nested Iteration

We can also generate a list that contains one entry for every pair of values taken from a pair of lists:

GHCi
>>> [(x, y) | x <- [1..3], y <- [10..12]]
[(1,10),(1,11),(1,12),(2,10),(2,11),(2,12),(3,10),(3,11),(3,12)]

As this example shows, we can think about this as two nested loops where the outer loop iterates over the x values and the inner loop iterates over the y values. So we first have all the pairs with the first component equal to 1, and the second components of these pairs iterate from 10 to 12, then we have all the pairs with the first component equal to 2, and the second components of these pairs iterate from 10 to 12 again, and so on. But we don't have to combine x and y into a pair. We can once again combine them using whatever function we want:

GHCi
>>> import Data.Char
>>> [a * ord b | a <- [1..3], b <- "Hello"]
[72,101,108,108,111,144,202,216,216,222,216,303,324,324,333]

And we can also nest more than two "loops":

GHCi
>>> [a + b + c | a <- [0,100], b <- [0,10], c <- [0,1]]
[0,1,10,11,100,101,110,111]

In the examples so far, the "inner loops" were completely independent of the "outer loops". When implementing nested loops in imperative languages, the range of values that the inner loops iterate over can depend on the loop variables of the surrounding loops. We can do the same with list comprehensions. For example, here is how we enumerate all pairs \((x,y)\) with \(1 \le x < y \le 5\):

GHCi
>>> [(x,y) | x <- [1..4], y <- [x+1..5]]
[(1,2),(1,3),(1,4),(1,5),(2,3),(2,4),(2,5),(3,4),(3,5),(4,5)]

You can read this as: For every x taken from the list [1..4], take y from the list [x+1..5] and add the pair (x,y) to the list produced by this list comprehension.

Exercise

Remember good old concat. It takes a list of lists and concatenates them:

GHCi
>>> concat [[1,2],[],[3,4],[5]]
[1,2,3,4,5]

The standard implementation is

concat = foldr (++) []

Use a list comprehension to implement concat instead.

Solution
concat xss = [x | xs <- xss, x <- xs]

This says: Build the list containing every x in xs, for every xs in xss. That's exactly what the concatenation of a list of lists is.