jamie
jamie

Reputation: 176

recursion apparently has non exhaustive patterns in it

My function rtnDryPlaces is supposed to return a list of all dry places if they were dry on a particular day - with day 1 being yesterday (last element) and day 7 being last week (first element).

type Name = String
type Location = (Float,Float)
type RainfallFigures = [Int]
type Place = (Name,Location,RainfallFigures)

testData=[("London", (51.5, -0.1), [0, 0, 5, 8, 8, 0, 0]),("Cardiff", (51.5 , -3.2),[12, 8, 15, 0, 0, 0, 2])]
rtnDryPlaces :: [Place] -> Int -> [Name]
rtnDryPlaces ((a,(b,c),d):xs) n
 | d == [] = []
 | (reverse d)!!n-1 == 0 = a:rtnDryPlaces xs n

demo 4 = print (rtnDryPlaces testData 2 )

The second guard reverses the list and returns the element at that index (index is day). If it returns 0, then the name a is appended to the list of names which have also returned 0 for that day. When the test data runs out, so does the rainfall data so I've set the stop condition to be when d = []

Upvotes: 1

Views: 60

Answers (1)

willeM_ Van Onsem
willeM_ Van Onsem

Reputation: 476699

One of the problems is that the recursive calls will eventually reach the empty list, and you did not define a result for that:

rtnDryPlaces :: [Place] -> Int -> [Name]
rtnDryPlaces [] _ = []
rtnDryPlaces ((a,(b,c),d):xs) n
    | d == [] = []
    | (reverse d)!!n-1 == 0 = a:rtnDryPlaces xs n

But even then it will not work (yet). For example (reversed d)!!n-1 is interpreted as ((reverse d)!!n)-1, so it first takes the element at index n, and then it will subtract that element with 1. It will not take the n-1 element.

Furthermore if the (reversed d)!!(n-1) is not zero, then that guard will not "fire", and thus we get again an exhaustive pattern failure. We thus should add an otherwise at the end:

rtnDryPlaces :: [Place] -> Int -> [Name]
rtnDryPlaces [] _ = []
rtnDryPlaces ((a,_,d):xs) n
    | d == [] = []
    | (reverse d)!!(n-1) == 0 = a:rtnDryPlaces xs n
    | otherwise = rtnDryPlaces xs n

Now this will give us a result:

Prelude> rtnDryPlaces testData 2
["London","Cardiff"]

But we can still make this more elegant by making use of filter and map:

rtnDryPlaces :: [Place] -> Int -> [Name]
rtnDryPlaces ps n = map (\(x,_,_) -> x) (filter p ps)
    where p (_,_,d) | (0:_) <- drop (n-1) (reverse d) = True
                    | otherwise = False

or as @DanielWagner says, with list comprehension:

rtnDryPlaces :: [Place] -> Int -> [Name]
rtnDryPlaces ps n = [p | (p, _, d) <- ps, 0 <- take 1 . drop (n-1) . reverse $ d]

Here the take 1 . drop (n-1) will make a singleton list (given the list has enough elements) with the amount of rain for that day. If that then pattern matches with 0, we will yield p in the result. If the value for that day does not match with 0, or the list has not enough elements (then take 1 . drop (n-1) (reverse d) will yield an empty list), then that element is not emitted in the result.

Upvotes: 2

Related Questions