Как я получаю суммы цифр большого количества в Haskell?

Я - Программист на C++, пытающийся преподавать мне Haskell, и это оказывается оспариванием, схватывая основы использования функций как тип цикла. У меня есть большое количество, 50!, и я должен добавить сумму его цифр. Это - относительно легкий цикл в C++, но я хочу изучить, как сделать это в Haskell.

Я прочитал некоторые вводные руководства и могу добраться 50! с

sum50fac.hs::

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

К сожалению, в этой точке я не совсем уверен, как приблизиться к проблеме. Действительно ли возможно записать функцию, которая добавляет (модификация) x 10 к значению и затем вызывает ту же функцию снова на x / 10, пока x / 10 не меньше чем 10? Если это не возможно, как я должен приблизиться к этой проблеме?

Спасибо!

13
задан jrockway 15 May 2010 в 03:08
поделиться

7 ответов

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

Затем запустите:

ghci> sumd 2345
14

ОБНОВЛЕНИЕ 1:

Это не генерирует преобразователи и использует аккумулятор :

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) не работала с аккумулятором в качестве первого параметра .

11
ответ дан 1 December 2019 в 19:49
поделиться

Во-первых, ваша функция Haskell пропускает скобки, вам нужен fac (n - 1). (о, я вижу, вы исправили это сейчас)

Два, настоящий ответ, вам нужно сначала составить список:

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

Это должно просто составить список всех цифр (введите: Int -> [Int] ).

Затем мы просто вычисляем сумму как сумму (listdigits n). И мы должны закончить.

Естественно, вы можете обобщить приведенный выше пример для списка для множества различных оснований, кроме того, вы также можете легко перевести его на продукты.

0
ответ дан 1 December 2019 в 19:49
поделиться

Это просто вариант от @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)

Это будет производить цифры от младших к старшим, что, хотя и неестественно для чтения, обычно то, что вы хотите для математических задач, связанных с цифрами числа. (Проект Эйлера кто нибудь?) Также обратите внимание, что 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]

Делая Проект Эйлера, я обнаружил, что некоторые проблемы требуют одного, а некоторые - другого.

О . и "бессмысленном" стиле

Чтобы было понятно тем, кто не знаком с Хаскеля. и стилем "point-free", их можно переписать так:

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 

Ластер является идиоматическим Haskell, поскольку, привыкнув к его чтению, вы увидите, что он очень краток.

5
ответ дан 1 December 2019 в 19:49
поделиться

Почему бы просто не

sumd = sum . map Char.digitToInt . show
11
ответ дан 1 December 2019 в 19:49
поделиться

Хотя, возможно, это не так эффективно, как другие примеры, вот другой способ подхода:

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

0
ответ дан 1 December 2019 в 19:49
поделиться

Просто чтобы расширить пул решений:

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)
1
ответ дан 1 December 2019 в 19:49
поделиться

Чтобы сложить все цифры числа:

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

show преобразует число в строку. map перебирает отдельные элементы строки (т.е. цифры), превращает их в строку (например, символ '1' становится строкой "1") и read превращает их обратно в целое число. sum окончательно вычисляет сумму.

5
ответ дан 1 December 2019 в 19:49
поделиться
Другие вопросы по тегам:

Похожие вопросы: