FP101x实验2 sumDigits

时间:2016-04-10 15:28:07

标签: haskell recursion

我被Erik Meijer教授的edX功能编程课程的实验2所困。我将复制粘贴作业:

  

在本实验中,您将实施信用卡验证算法。该算法遵循以下步骤:

   Double the value of every second digit beginning with the rightmost.
   Add the digits of the doubled values and the undoubled digits from the original number.
   Calculate the modulus of the sum divided by 10.
     

如果结果等于0,则该数字有效。以下是数字4012888888881881上每个步骤的结果示例。

   In order to start with the rightmost digit, we produce a reversed list of digits. Then, we double every second digit.
     

结果:[1,16,8,2,8,16,8,16,8,16,8,16,2,2,0,8]。

     

我们将上面结果列表的所有数字相加。请注意,我们必须再次将列表中的元素拆分为数字(例如16变为[1,6])。

     

结果:90。

     

最后,我们计算模数为90,超过10。

     

结果:0。

由于最终值为0,我们知道上述号码是有效的信用卡号码。如果我们在输入信用卡号码时出错,而是提供4012888888881891,那么最后一步的结果为2,证明该号码无效。

我的代码:

toDigits   :: Integer -> [Integer]
toDigits n = if 0 <= n && n <= 10 then [n] else toDigits ((n - n `mod` 10) `quot` 10) ++ [n `mod` 10]

toDigitsRev :: Integer -> [Integer]
toDigitsRev n = reverse (toDigits n)


doubleSecond :: [Integer] -> [Integer]
doubleSecond xs | length xs <= 1                   = xs
                | 1 < length xs  && length xs < 4  = [fst (splitAt 2 xs) !! 0 ,(*2) (fst (splitAt 2 xs) !! 1 )] ++ snd (splitAt 2 xs)
                | otherwise                        = doubleSecond (fst (splitAt 2 xs)) ++ doubleSecond (snd (splitAt 2 xs))


sumDigits :: [Integer] -> Integer
sumDigits xs | xs == []  = 0
             | otherwise = sum (toDigits (head xs)) + sumDigits (tail xs)

isValid :: Integer -> Bool
isValid n | sumDigits (doubleSecond (toDigitsRev n)) `mod` 10 == 0 = True
          | otherwise                                              = False

接下来,他们会给你这段代码:

numValid :: [Integer] -> Integer
numValid xs = sum . map (\_ -> 1) $ filter isValid xs


creditcards :: [Integer]
creditcards = [ 4716347184862961,
                4532899082537349,
                4485429517622493,
                4320635998241421,
                4929778869082405,
                5256283618614517,
                5507514403575522,
                5191806267524120,
                5396452857080331,
                5567798501168013,
                6011798764103720,
                6011970953092861,
                6011486447384806,
                6011337752144550,
                6011442159205994,
                4916188093226163,
                4916699537435624,
                4024607115319476,
                4556945538735693,
                4532818294886666,
                5349308918130507,
                5156469512589415,
                5210896944802939,
                5442782486960998,
                5385907818416901,
                6011920409800508,
                6011978316213975,
                6011221666280064,
                6011285399268094,
                6011111757787451,
                4024007106747875,
                4916148692391990,
                4916918116659358,
                4024007109091313,
                4716815014741522,
                5370975221279675,
                5586822747605880,
                5446122675080587,
                5361718970369004,
                5543878863367027,
                6011996932510178,
                6011475323876084,
                6011358905586117,
                6011672107152563,
                6011660634944997,
                4532917110736356,
                4485548499291791,
                4532098581822262,
                4018626753711468,
                4454290525773941,
                5593710059099297,
                5275213041261476,
                5244162726358685,
                5583726743957726,
                5108718020905086,
                6011887079002610,
                6011119104045333,
                6011296087222376,
                6011183539053619,
                6011067418196187,
                4532462702719400,
                4420029044272063,
                4716494048062261,
                4916853817750471,
                4327554795485824,
                5138477489321723,
                5452898762612993,
                5246310677063212,
                5211257116158320,
                5230793016257272,
                6011265295282522,
                6011034443437754,
                6011582769987164,
                6011821695998586,
                6011420220198992,
                4716625186530516,
                4485290399115271,
                4556449305907296,
                4532036228186543,
                4916950537496300,
                5188481717181072,
                5535021441100707,
                5331217916806887,
                5212754109160056,
                5580039541241472,
                6011450326200252,
                6011141461689343,
                6011886911067144,
                6011835735645726,
                6011063209139742,
                379517444387209,
                377250784667541,
                347171902952673,
                379852678889749,
                345449316207827,
                349968440887576,
                347727987370269,
                370147776002793,
                374465794689268,
                340860752032008,
                349569393937707,
                379610201376008,
                346590844560212,
                376638943222680,
                378753384029375,
                348159548355291,
                345714137642682,
                347556554119626,
                370919740116903,
                375059255910682,
                373129538038460,
                346734548488728,
                370697814213115,
                377968192654740,
                379127496780069,
                375213257576161,
                379055805946370,
                345835454524671,
                377851536227201,
                345763240913232
              ]

你应该跑numValid creditcards并获得94,我得到39。 我怀疑我的错误在sumDigits,但无法真正找到它,我们非常感谢任何帮助。

1 个答案:

答案 0 :(得分:2)

让我们尝试找出这些错误。我们将使用QuickCheck来测试多个属性。让我们从toDigits的一些属性开始:

toDigits_prop n = n >= 0 ==> length (toDigit n) === length (show n)

经过一些测试后,它会失败:

*Main> quickCheck toDigits_prop
*** Failed! Falsifiable (after 24 tests):
10
1 /= 2

这意味着我们在10上只有一位数,我们原先预计会有两位数。让我们检查toDigits10的结果:

*Main> toDigits 10
[10]

啊哈。 toDigits上存在逻辑错误,边界上的行为是错误的,10不是数字。因此,将其更改为<= 9而不是<= 10。虽然我们正在使用它,但我们可以简化该功能,因为quotRem可以同时获取提醒和quot

toDigits :: Integer -> [Integer]
toDigits n = case n `quotRem` 10 of
  (0, m) -> [m] -- only one digit was left
  (q, m) -> toDigits q ++ [m]

请注意,此函数效率有点低,如果我们同时反转数字,我们可以加快速度:

toDigitsRev :: Integer -> Integer
toDigitsRev n = case n `quotRem` 10 of
  (0, m) -> [m]               -- only one digit was left
  (q, m) -> m : toDigitsRev q -- add current digit left

无论哪种方式,我们都要检查toDigits的新版本是否成立:

*Main> quickCheck toDigits_prop
+++ OK, passed 100 tests.

奥莱特。让我们检查一下是否通过了所有测试:

*Main> numValid creditcards
94

现在似乎一切都很好。所以关键是要检查你的功能的属性。请注意,可以更轻松地编写多个函数,例如

doubleSecond :: Num a => [a] -> [a]
doubleSecond xs = zipWith ($) (cycle [id, (2*)]) xs
-- or
doubleSecond (x:y:xs) = x : 2 * y : doubleSecond xs
doubleSecond xs       = xs

sumDigits :: [Integer] -> Integer
sumDigits xs = sum (concatMap toDigits xs)