无法弄清楚简单的haskell递归问题

问题描述 投票:-1回答:3

我正在尝试编写一个简单的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)
haskell recursion functional-programming
3个回答
2
投票

你的函数只适用于一次迭代。你只需要递归调用它,直到得到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

3
投票

正数和它的数字总和总是一致的模数9.此外,由于所有非零数字都至少有一个正数而没有负数,因此无法从正数得到0的数字和。因此:

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

在ghci中尝试一下:

> digitSum 888
6

此函数可能无法按照您对负数进行操作 - 但是,原始函数也不会优雅地处理负数,所以...... =)


2
投票

在另一种答案中,特别是因为你正在做递归数字(基数为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 

推荐问答