Haskell List Comprehension: Boolean Item in List used as predicate

Viewed 144

I am writing a function majority.

The function returns True if at least two of the parameters are True.

The function returns False if at least two of the parameters are False.

The function can be written like this:

majority :: (Bool,Bool,Bool) -> Bool
majority (True,True,_)=True
majority (True,_,True) = True
majority (_,True,True)=True
majority _ = False

Alternatively, a list comprehension can be used:

majority' (x,y,z) = length [b | b <- [x,y,z] , b] >= 2

I don't fully understand why this second solution works.

I know that this means we will take b such that it is one of [x,y,z] and b applied to b must be True. The length of this list must be greater than or equal to 2.

Does the b in the predicate (the third b) change its value for that it is equal to x, then y and then z. Or, does b always have the value of x?

I tried:

majority'' (x,y,z) = length [b | b <- [x,y,z] , True] >= 2

I found that this always returned True,even when the function should return False.

Also, I noticed that

[b | b <- [False,True,True] , True] would return [False,True,True]

And in general,

[b | b <- [x,y,z] , True] would return [x,y,z]

However,

[b | b <- [False,True,True] , b] would return [True,True]

And in general,

[b | b <- [x,y,z] , b] would return a list of Trues which is as long as the number of Trues in [x,y,z]

This explains why

majority'' (x,y,z) = length [b | b <- [x,y,z] , True] >= 2

always returns True. The inequality is always True, because the list [b | b <- [x,y,z],True] always has a length of 3.

Why does using b as the predicate work in?

majority' (x,y,z) = length [b | b <- [x,y,z] , b] >= 2
1 Answers

I don't fully understand why this second solution works.

Let us first focus on the list comprehension:

[b | b <- [x,y,z] , b]

Here we create a list [x, y, z] and we will enumerate over it, with b as enumerator. It thus looks to some extend like a for loop in Python for example.

The next part, the b at the right of the list comprehension is a filter. This means that it will only yield a result if b is True. This thus means that if [x, y, z] is False, False, True, then the list comprehension will yield one True, since the filter b will reject the first two items, since then b is False.

We thus construct a list of Trues: all items in the list will be True, but we are not that interested in what the items are in the list. We are interested in the number of items the list comprehension produces. We thus make use of length :: [a] -> Int to determine the number of times x, y, and z are True. This thus means that:

length [b | b <- [x, y, z], b]  -- number of True's in [x, y, z]

If that number is greater than or equal to two, then we thus know that there are at least two Trues.

Related