Program constantly going to Edge condition no matter the input - Haskell

Viewed 86

I have been working on a problem whereby I have a list of tuples consisting of songName, artistName and sales. I am required to retrieve either the quantity of sales using the songName and artistName, and if the tuple containing this infomation isn't in the list, I must return 0.

I have attempted to solve this problem with the solution below:

testData :: [Sales]
testData = [("No Lie", "Sean Paul feat. Dua Lipa",  100),
        ("Yes Lie", "Sean Paul feat. Dua Lipa",  10),
        ("Fear & Delight", "The Correspondents",  120)]

findSales :: [Sales] -> String -> String -> Int
findSales ((track, artist,qty): xs) trackName artistName
     | track == trackName && artist == artistName = qty
     | otherwise = 0

However, when I enter a legit query like findSales testData "Yes Lie" "Sean Paul feat. Dua Lipa" whereby I expect this to be included in the list of tuples and therefore return 10, the program returns 0, the edge case instead.

Where am I going wrong whereby the edge case is always being returned?

And is there a good tool/Extension that I could use in VS Code, so that I could use to easily debug the program in the future. I have tried to use Haskelly/Heskero, but I wasn't able to get them working when trying to set them up.

1 Answers

You do not recurse on the tail of the list. So your program only checks if the first item matches, and if not, it goes to the otherwise. This thus means that if the query matches the first item, it will return that item, and otherwise return 0.

You thus should recurse in case of the otherwise, and return 0 in case the list is exhausted:

findSales :: [Sales] -> String -> String -> Int
findSales [] _ _ = 0
findSales ((track, artist,qty): xs) trackName artistName
     | track == trackName && artist == artistName = qty
     | otherwise = findSales xs trackName artistName

You can make use of an function to minimize the amount of parameters you pass in each recursive call:

findSales :: [Sales] -> String -> String -> Int
findSales xs trackName artistName = go xs
    where go [] = 0
          go ((track, artist,qty) : xs)
              | track == trackName && artist == artistName = qty
              | otherwise = go xs

or make use of the find :: (a -> Bool) -> [a] -> Maybe a function:

import Data.List(find)
import Data.Maybe(maybe)

findSales :: [Sales] -> String -> String -> Int
findSales xs trackName artistName = maybe 0 (\(_,_,x) -> x) (find p xs)
    where p (track, artist,qty) = track == trackName && artist == artistName
Related