Я - Программист на 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? Если это не возможно, как я должен приблизиться к этой проблеме?
Спасибо!
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) не работала с аккумулятором в качестве первого параметра .
Во-первых, ваша функция Haskell пропускает скобки, вам нужен fac (n - 1). (о, я вижу, вы исправили это сейчас)
Два, настоящий ответ, вам нужно сначала составить список:
listdigits n = if n < 10 then [n] else (listdigits (n `div` 10)) ++ (listdigits (n `mod` 10))
Это должно просто составить список всех цифр (введите: Int -> [Int] ).
Затем мы просто вычисляем сумму как сумму (listdigits n). И мы должны закончить.
Естественно, вы можете обобщить приведенный выше пример для списка для множества различных оснований, кроме того, вы также можете легко перевести его на продукты.
Это просто вариант от @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, поскольку, привыкнув к его чтению, вы увидите, что он очень краток.
Почему бы просто не
sumd = sum . map Char.digitToInt . show
Хотя, возможно, это не так эффективно, как другие примеры, вот другой способ подхода:
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 :-)
Просто чтобы расширить пул решений:
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)
Чтобы сложить все цифры числа:
digitSum = sum . map (read . return) . show
show преобразует число в строку. map перебирает отдельные элементы строки (т.е. цифры), превращает их в строку (например, символ '1' становится строкой "1") и read превращает их обратно в целое число. sum окончательно вычисляет сумму.