Haskell Basics #4

List comprehension

The powers of list comprehension in a functional programming language are best illustrated when compared alongside their imperative language equivalents

If I wish to create a list of all the odd numbers between 1 and 100

In C++, I could do:

int len = 50;
int[len] m;
counter = 0;
for (int i = 1; i <= 100; ++i)
{
  if (i % 2 == 1)
  {
    m[counter] = i;
    ++counter;
  }
}

In Haskell I could do:

m = [ x | x <- [1..100], x `mod` 2 == 1]
main = print $ m

The C++ was a lot of code, compared to [ x | x <- [1..100], x mod 2 == 1]!

Also, notice that I did not have to predict the number of elements that there would be in the final array, as I had to do in C++.

We can even replace the modulo arithmetic with a the built in odd.

m = [ x | x <- [1..100], odd x]
main = print $ m

The way this works is everything before the pipe, |, is what is getting added to the list. Everything after the pipe describes how that gets added.

One could read that in prose as, "Let the list m be contain elements, where each element is and integer between 1 and 100, and is odd."

The power, and beauty, of this, is that using list comprehensions, one feels more like they are expressing what they want, instead of expressing how to get what they want.

FizzBuzz

A classic beginners programming exercise is FizzBuzz: "Write a program that prints out all of the numbers between 1 and 100. If a number is divisble by 5 print FIZZ instead of that number. If a number is divisble by 3 print BUZZ instead of that number. If a number is divisble by both 3 and 5 print FIZZBUZZ instead of that number."

Try writing this in C++, or some other imperative language.

Now how would you do the same in Haskell, using list comprehensions?

Firstly, let us just take the list comprehension above, [ x | x <- [1..100], odd x] and add an if .. then .. else block to it just for FIZZBUZZ.

m = [ if (x `mod` 15 == 0) then "FIZZBUZZ" else (show x) | x <- [1..100]]
main = print $ m

Note that show as used here, converts the integer to a string. This is necessary because, remember that a list is homogenous - all of its elements must be of the same type.

Now try adding FIZZ into the mix. Hint: Nest the new if .. then .. else block within the else block of the existing else.

Now add BUZZ into the mix.

Now that is starting to a look a little unwieldy, isn't it? All we need to do now is to make it look neater, and we will do that by splitting into multiple lines.

Exercise:

Define a function fizzbuzz which you will use as the filter. Define this function using guards, as we have previously in our recursive list functions.

fizzbuzz x
  -- your code here

m = [ fizzbuzz x | x <- [1..100] ]
main = print $ m