tags:

views:

201

answers:

7

I'm a C++ Programmer trying to teach myself Haskell and it's proving to be challenging grasping the basics of using functions as a type of loop. I have a large number, 50!, and I need to add the sum of its digits. It's a relatively easy loop in C++ but I want to learn how to do it in Haskell.

I've read some introductory guides and am able to get 50! with

sum50fac.hs::

fac 0 = 1
fac n = n * fac (n-1)
x = fac 50
main = print x

Unfortunately at this point I'm not entirely sure how to approach the problem. Is it possible to write a function that adds (mod) x 10 to a value and then calls the same function again on x / 10 until x / 10 is less than 10? If that's not possible how should I approach this problem?

Thanks!

+4  A: 
sumd 0 = 0
sumd x = (x `mod` 10) + sumd (x `div` 10)

Then run it:

ghci> sumd 2345
14

UPDATE 1:

This one doesn't generate thunks and uses accumulator:

sumd2 0 acc = acc
sumd2 x acc = sumd2 (x `div` 10) (acc + (x `mod` 10))

Test:

ghci> sumd2 2345 0
14

UPDATE 2:

Partially applied version in pointfree style:

sumd2w = (flip sumd2) 0

Test:

ghci> sumd2w 2345
14

I used flip here because function for some reason (probably due to GHC design) didn't work with accumulator as a first parameter.

Yasir Arsanukaev
Writing your recursive functions in this manner is going to use unbounded stack. Much better to recurse in terms with an accumulator, so no thunks are generated.
jrockway
Best to wrap sumd2 so the user doesn't have to put in the initial accumulation value.
trinithis
A: 

Well, one, your Haskell function misses brackets, you need fac (n - 1). (oh, I see you fixed that now)

Two, the real answer, what you want is first make a list:

listdigits n = if n < 10 then [n] else (listdigits (n `div` 10)) ++ (listdigits (n `mod` 10))

This should just compose a list of all the digits (type: Int -> [Int]).

Then we just make a sum as in sum (listdigits n). And we should be done.

Naturally, you can generalize the example above for the list for many different radices, also, you can easily translate this to products too.

Lajla
`(++)` is slow however, I wouldn't recommend to use it in other algorithms.
Yasir Arsanukaev
+7  A: 

Why not just

sumd = sum . map Char.digitToInt . show
newacct
I find going via strings and characters is really unnatural since there is nothing in the problem that is concerned with strings. It's just numbers and the solution should reflect that.
svenningsson
A: 

Although maybe not as efficient as the other examples, here is a different way of approaching it:

import Data.Char

sumDigits :: Integer -> Int
sumDigits = foldr ((+) . digitToInt) 0 . show

Edit: newacct's method is very similar, and I like it a bit better :-)

Zach
+1  A: 

Just to make pool of solutions greater:

miterate :: (a -> Maybe (a, b)) -> a -> [b]
miterate f = go . f where
    go Nothing = []
    go (Just (x, y)) = y : (go (f x))

sumd = sum . miterate f where
    f 0 = Nothing
    f x = Just (x `divMod` 10)
ony
The 'miterate' function is really just 'unfoldr'.
svenningsson
Yep. I haven't looked good enough (i.e. in `Data.List`). Why it wasn't imported in `Prelude`?...
ony
+3  A: 

This is just a variant of @ony's, but how I'd write it:

import Data.List (unfoldr)

digits :: (Integral a) => a -> [a]
digits = unfoldr step . abs
    where step n = if n==0 then Nothing else let (q,r)=n`divMod`10 in Just (r,q)

This will product the digits from low to high, which while unnatural for reading, is generally what you want for mathematical problems involving the digits of a number. (Project Euler anyone?) Also note that 0 produces [], and negative numbers are accepted, but produce the digits of the absolute value. (I don't want partial functions!)

If, on the other hand, I need the digits of a number as they are commonly written, then I would use @newacct's method, since the problem is one of essentially orthography, not math:

import Data.Char (digitToInt)

writtenDigits :: (Integral a) => a -> [a]
writtenDigits = map (fromIntegral.digitToInt) . show . abs

Compare output:

> digits 123
[3,2,1]
> writtenDigits 123
[1,2,3]

> digits 12300
[0,0,3,2,1]
> writtenDigits 12300
[1,2,3,0,0]

> digits 0
[]
> writtenDigits 0
[0]

In doing Project Euler, I've actually found that some problems call for one, and some call for the other.

About . and "point-free" style

To make this clear for those not familiar with Haskell's . operator, and "point-free" style, these could be rewritten as:

import Data.Char (digitToInt)
import Data.List (unfoldr)

digits :: (Integral a) => a -> [a]
digits i = unfoldr step (abs i)
    where step n = if n==0 then Nothing else let (q,r)=n`divMod`10 in Just (r,q)

writtenDigits :: (Integral a) => a -> [a]
writtenDigits i = map (fromIntegral.digitToInt) (show (abs i))

These are exactly the same as the above. You should learn that these are the same:

f . g
(\a -> f (g a))

And "point-free" means that these are the same:

foo a = bar a
foo   = bar

Combining these ideas, these are the same:

foo a = bar (baz a)
foo a = (bar . baz) a
foo   = bar . baz 

The laster is idiomatic Haskell, since once you get used to reading it, you can see that it is very concise.

MtnViewMark
I am unable to find what what the .show and .abs are doing in the writtenDigits function. Could you explain what are they doing? thanks
Tim
".show"? `.` doesn't work like it does in C/C++/Java/Python/PHP, see my expanded answer for details. The reason for `abs` is that I want the functions to be defined for negative numbers, rather than crash or loop (which they will without the abs). That makes them total functions, not partial functions, which in Haskell (or any language really) good.
MtnViewMark
A: 

To sum up all digits of a number:

digitSum = sum . map (read . return) . show

show transforms a number to a string. map iterates over the single elements of the string (i.e. the digits), turns them into a string (e.g. character '1' becomes the string "1") and read turns them back to an integer. sum finally calculates the sum.

jkramer