如何在Haskell中获得大数字的总和?

时间:2010-05-15 02:51:47

标签: haskell

我是一名C ++程序员,正在尝试自学Haskell,并且证明它正在挑战把握使用函数作为一种循环的基础知识。我有一个很大的数字,50!,我需要添加其数字的总和。这是一个相对简单的C ++循环,但我想学习如何在Haskell中完成它。

我已经阅读了一些介绍性指南,并且能够获得50分!与

sum50fac.hs ::

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

不幸的是,在这一点上我并不完全确定如何处理这个问题。 是否有可能编写一个函数将(mod)x 10添加到一个值,然后在x / 10上再次调用相同的函数,直到x / 10小于10?如果那不可能,我应该如何解决这个问题呢?

谢谢!

7 个答案:

答案 0 :(得分:11)

为什么不

sumd = sum . map Char.digitToInt . show

答案 1 :(得分:10)

sumd 0 = 0
sumd x = (x `mod` 10) + sumd (x `div` 10)

然后运行它:

ghci> sumd 2345
14

更新1:

这个不会生成thunk并使用accumulator

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

测试:

ghci> sumd2 2345 0
14

更新2:

pointfree样式的部分应用版本:

sumd2w = (flip sumd2) 0

测试:

ghci> sumd2w 2345
14

我在这里使用flip因为某些原因(可能是由于GHC设计)的函数不能用累加器作为第一个参数。

答案 2 :(得分:5)

这只是@ ony的变种,但我是怎么写的:

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)

这将产生从低到高的数字,虽然不自然的读数,通常是你想要的涉及数字数字的数学问题。 (Project Euler任何人?)另请注意,0生成[],并且接受负数,但会产生绝对值的数字。 (我不想要部分功能!)

另一方面,如果我需要一个数字的数字,因为它们通常被编写,那么我会使用@ newacct的方法,因为问题 基本上是正字法,而不是数学:

import Data.Char (digitToInt)

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

比较输出:

> 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]

在做Project Euler时,我实际上发现有些问题需要一个,有些则需要另一个问题。

关于.和“无点”风格

为了那些不熟悉Haskell的.运算符和“无点”样式的人,这些可以改写为:

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))

这些与上面完全相同。您应该知道这些是相同的:

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

“无点”意味着它们是相同的:

foo a = bar a
foo   = bar

结合这些想法,这些是相同的:

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

laster是惯用的Haskell,因为一旦你习惯了它,你可以看到它非常简洁。

答案 3 :(得分:5)

总结一个数字的所有数字:

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

show将数字转换为字符串。 map迭代字符串的单个元素(即数字),将它们变成一个字符串(例如字符'1'变成字符串“1”)并读取它们将它们变回整数。总和最后计算总和。

答案 4 :(得分:1)

只是为了让解决方案更加出色:

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)

答案 5 :(得分:0)

好吧,其中一个,你的Haskell函数错过了括号,你需要fac(n - 1)。 (哦,我看到你现在修好了)

二,真正的答案,你想要的是先列出一个清单:

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

这应该只是组成所有数字的列表(类型:Int - &gt; [Int])。

然后我们只是总和(listdigits n)。我们应该完成。

当然,您可以将上面的示例概括为许多不同基数的列表,同样,您也可以轻松地将其转换为产品。

答案 6 :(得分:0)

虽然可能没有其他例子那么有效,但这是一种不同的接近方式:

import Data.Char

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

编辑:newacct的方法非常相似,我更喜欢它: - )