我被困在由Erik教授的关于edX的函数式编程课程的第二实验室。我将复制粘贴在这里的作业:
在本实验室中,您将为信用卡实现一个验证算法。该算法遵循以下步骤: 从最右边开始的每一秒数字值的两倍。将加倍值的数字和原始数字中毫无疑问的数字相加。计算之和的模量除以10。 如果结果等于0,则该数字是有效的。下面是数字4012888888881881上每一步的结果的一个例子。 为了从最右边的数字开始,我们产生了一个反向的数字列表。然后,我们把每秒钟的数字翻两倍。 结果:1,16,8,2,8,16,8,8,16,16,16,2,0,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,但没有真正找到,任何帮助都是非常感谢的。
发布于 2016-04-10 16:21:01
让我们试着找出那些错误。我们将使用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上只有一位数,而我们最初期望的是两位数。让我们检查一下toDigits在10上的结果
*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)https://stackoverflow.com/questions/36531658
复制相似问题