Reputation: 100
I need a function that sum all the digits as follow: sumDigits [16,7,12,5] = 1+6+7+1+2+5 = 22
I have defined toDigits function as follow:
toDigits :: Integer -> [Integer]
toDigits n
| n <= 0 = []
| otherwise = toDigits(n `div` 10) ++ [n `mod` 10]
The sumDigits function is defined as follow:
sumDigits :: [Integer] -> Integer
sumDigits [] = 0
sumDigits (x:[]) = x
sumDigits (x:zs) = (sumDigits . toDigits x) + sumDigits zs
The compiler shows the following errors:
Couldn't match expected type ‘a0 -> [Integer]’
with actual type ‘[Integer]’
Possible cause: ‘toDigits’ is applied to too many arguments
In the second argument of ‘(.)’, namely ‘toDigits x’
In the first argument of ‘(+)’, namely ‘(sumDigits . toDigits x)’
In the expression: (sumDigits . toDigits x) + sumDigitsX zs
Couldn't match expected type ‘a0 -> Integer’
with actual type ‘Integer’
Possible cause: ‘sumDigitsX’ is applied to too many arguments
In the second argument of ‘(+)’, namely ‘sumDigits zs’
In the expression: (sumDigits . toDigits x) + sumDigits zs
In an equation for ‘sumDigits’:
sumDigitsX (x : zs) = (sumDigits . toDigits x) + sumDigits zs
I expect that toDigits will split each number to a singleton array and sumDigits will convert it to Integer. Can you explain what is wrong with this one?
Upvotes: 1
Views: 90
Reputation: 2645
The .
operator is function composition, and expects the expressions on each side to be functions. When you write (sumDigits . toDigits x)
, the left side is sumDigits
, which is a function. But the right side is toDigits x
, which is not a function. (toDigits
is a function, but toDigits x
is the result after that function has been applied.)
It seems that you meant (sumDigits . toDigits) x
. That composes the functions first, and then applies the resulting function to x, computing the sum of its digits. In Haskell, function application has a higher precedence than anything else, so you need the parentheses to group the function composition first, and then apply it.
Upvotes: 4
Reputation: 477616
The expression sumDigits . toDigits x
is interpreted as sumDigits . (toDigits x)
. Since the signature of (.)
is (.) :: (b -> c) -> (a -> b) -> a -> c
, the result thus should be a function, not a value.
That being said, it does not look a very good approach here:
toDigits
function runs in O(n2) with n the number of digits, since you each time append, instead of prepending.Since the order of the digits does not matter, we can implement toDigits
as:
toDigits :: Integral i => i -> [i]
toDigits n | n <= 0 = []
| otherwise = r : toDigits q
where (q,r) = quotRem n 10
and we can then calculate the sum of the digits as:
sumDigits :: Integral i => [i] -> i
sumDigits = sum . concatMap toDigits
we here thus concatenate a mapping with toDigits
, and then calculate the sum
over that result.
This gives us the expected:
Prelude> sumDigits [16,7,12,5]
22
Upvotes: 4