Не могу понять простую проблему рекурсии на Haskell - PullRequest
0 голосов
/ 29 апреля 2019

Я пытаюсь написать простую программу на Haskell, которая суммирует цифры целого числа, например, мое целое число 888, поэтому сумма должна быть 8 + 8 + 8 = 24.Я запустил эту часть, но я хочу, чтобы моя программа продолжала работать, пока нечего добавить, например, после добавления 8 + 8 + 8 = 24 следует добавить 2 + 4 = 6, а затем вернуть 6. Спасибо за помощь!

import System.IO
import Data.List

integer = 888

todigits :: Integral x => x -> [x]
todigits 0 = []
todigits x = todigits (x `div` 10) ++ [x `mod` 10]

add::[Int]->Int
add (x:xs) = sum(x:xs)

added = add (todigits integer)

main = do
    print(added)

Ответы [ 3 ]

3 голосов
/ 29 апреля 2019

Положительное число и его сумма цифр всегда совпадают по модулю 9. Более того, поскольку все ненулевые числа имеют хотя бы одну положительную цифру и не имеют отрицательных цифр, невозможно получить сумму цифр 0 из положительного числа. Поэтому:

digitSum x = case (x, x `mod` 9) of
    (0, _) -> 0
    (_, 0) -> 9
    (_, v) -> v

Попробуйте в ghci:

> digitSum 888
6

Эта функция может не выполнять то, что вы ожидаете от отрицательных чисел, но оригинал также не обрабатывает отрицательные числа изящно, поэтому ... =)

2 голосов
/ 29 апреля 2019

При альтернативном ответе, в частности, потому что вы делаете рекурсивную сумму (основание 10), а по умолчанию show экземпляр - это основание 10, вы можете совершать обходы через строки и красиво писать с шаблоном представления,

{-# LANGUAGE ViewPatterns #-}

digitSum :: Int -> Int
digitSum x@(show -> (_:"")) = x
digitSum (show -> cs) = digitSum $ sum . map ( read . (:[]) ) $ cs

Если строковое представление представляет собой любой отдельный символ (т. Е. 0 <= x <= 9), тогда просто верните x, в противном случае выполните рекурсивное вычисление суммы целых чисел в строковом представлении.

Вы все еще можете красиво использовать шаблоны представления (imo) без округления, но для этого требуется вспомогательная функция для выражения целого числа в виде списка его цифр,

import Data.List (unfoldr)
import Data.Tuple (swap)

digitList :: (Integral a) => a -> [a]
digitList 0 = [0]
digitList n = unfoldr f n
    where f 0 = Nothing
          f i = Just . swap $ i `divMod` 10

digitSum' :: (Integral a) => a -> a
digitSum' (digitList -> x:[]) = x
digitSum' (digitList -> xs) = digitSum' $ sum xs 
2 голосов
/ 29 апреля 2019

Ваша функция хороша только для одной итерации.Вам просто нужно вызывать его рекурсивно, пока вы не получите 1-значный результат (или пустой список, конечно).

Начнем с вашей существующей функции, которую я переименовал и переписал так, чтобы она возвращала список в обратном порядке (с цифрой единиц первой).В этом нет необходимости, и вы можете использовать именно ваше предыдущее определение (поскольку единственное, что нас интересует на данный момент, это сумма списка, для которой порядок не имеет значения), но это будет болееУдобно, если вам нужно восстановить число из списка цифр (и я думаю, что я тоже немного лучше):

todigitsOnce :: Integral x => x -> [x]
todigitsOnce 0 = []
todigitsOnce x = x `mod` 10 : todigitsOnce (x `div` 10)

А вот рекурсивная функция toDigit:

toDigit :: Integral x => x -> [x]
toDigit x
    | length firstResult < 2 = firstResult
    | otherwise = toDigit . sum $ firstResult
    where firstResult = todigitsOnce x
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...