Подтвердить что ты не робот

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

Я программист на С++, который пытается научить себя Haskell, и это доказывает сложность понимания основ использования функций как типа цикла. У меня большое число, 50!, и мне нужно добавить сумму его цифр. Это относительно легкий цикл в С++, но я хочу узнать, как это сделать в 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? Если это невозможно, как я должен подходить к этой проблеме?

Спасибо!

4b9b3361

Ответ 1

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

sumd = sum . map Char.digitToInt . show

Ответ 2

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

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

ghci> sumd 2345
14

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

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

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

Тест:

ghci> sumd2 2345 0
14

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

Частично примененная версия в pointfree style:

sumd2w = (flip sumd2) 0

Тест:

ghci> sumd2w 2345
14

Я использовал flip здесь, потому что функция по какой-либо причине (вероятно, из-за конструкции GHC) не работала с аккумулятором в качестве первого параметра.

Ответ 3

Это просто вариант @ony's, но как я его напишу:

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

И "point-free" означает, что они одинаковы:

foo a = bar a
foo   = bar

Объединяя эти идеи, они одинаковы:

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

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

Ответ 4

Подводя итог всем цифрам числа:

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

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

Ответ 5

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

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)

Ответ 6

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

Два, реальный ответ, что вы хотите, сначала сделайте список:

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

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

Затем мы просто делаем сумму, как в sum (listdigits n). И мы должны это сделать.

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

Ответ 7

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

import Data.Char

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

Изменить: метод newacct очень похож, и мне он немного лучше: -)