Reputation: 43
For an assignment I have been asked to re-write the following function using list comprehension. This function takes in a list of integers and an integer digit a
and returns the number of integers in the list which contain the digit a
.
e.g.
count [102,41,256] 2
Should return 2
count [98,4,376] 2
Should return 0
This is what I have for the non-list comprehension function:
split :: Int -> [Int]
split 0 = []
split x = split (div x 10) ++ [rem x 10]
checkDigits :: [Int] -> Int -> Int
checkDigits [] y = 0
checkDigits (x:xs) y
| x == y = 1 + (checkDigits xs y)
| otherwise = checkDigits xs y
count :: [Int] -> Int -> Int
count [] a = 0
count (x:xs) a
| (checkDigits (split x) a) > 0 = (checkDigits (split x) a) + (count xs a)
| otherwise = count xs a
Split will take in an integer and return the integer in a list
split 321 will return: [3,2,1]
checkDigits
counts the number of times that a digit appears in an integer (this was to solve an error where count [22] 2
would return as 0
when it should return 2
)
e.g. the digit '2' appears 4 times in the list [2,65,42,22]
Finally in count we use checkDigits
to check if any of the digits in x
are equal to a
, if this number is greater than 0 (1 or more are equal to a) then add the number of digits found and call again.
I am new to list comprehensions and have only managed to get it to work if the number in the list is equal to a
:
countD :: [Int] -> Int -> Int
countD intL a = length [y | y <- intL, y == a]
Here countD [2,3,4,22] 2
returns 1
l how do I change what I have so that it will return 3
(as there are 3 2
's in the list).
Upvotes: 1
Views: 1980
Reputation: 154
Best I could do. I'd appreciate if someone showed me how to pass the integer .2
digit as an argument rather than it hard coded
Thanks Erik for your answer.
countDigits :: [Int] -> Int -> Int
countDigits xs d = sum [1 | n <- (concat . map show) xs, n == d']
where d':[] = show d
Upvotes: 5
Reputation: 38247
Your question is ambiguous about what the count
function should do precisely. At the beginning you say
the number of integers in the list which contain the digit
whereas later on you say
count [22] 2
/.../ should return 2
which is not the number of integers that contain the digit 2
in [22]
— there is only one integer in that list, so you should return 1, regardless of the fact that this integer contains the digit 2
twice.
Therefore, I've provided solutions to both possibilities below.
For the first possibility, this solution is the shortest I could come up with:
-- these are optional
type Digit = Int
type Number = Int
type Count = Int
count :: [Number] -> Digit -> Count
count xs d = sum [count' x | x <- xs] -- same as `sum $ map count' xs`
where count' x = if d' `elem` show x then 1 else 0
d':[] = show d
If, however, the 2nd possibility is indeed correct and count [22] 2
should return 2
, then change the count'
helper function accordingly:
count :: [Number] -> Digit -> Count
count xs d = sum [count' x | x <- xs]
where count' i = length $ filter (== d') (show i)
d':[] = show d
Upvotes: 3
Reputation: 91983
Use checkDigits
and split
in countD
. This is how you count the number of integers that contains the digit (two, in your example):
countD intL a = length [y | y <- intL, (checkDigits (split y) a) > 0]
And here is how you count the total times the digit appears (three in your example):
countD intL a = sum [checkDigits (split y) a | y <- intL]
Upvotes: 3