-
Notifications
You must be signed in to change notification settings - Fork 12
/
E06_CreditCardNumberValidator.hs
118 lines (96 loc) · 4.1 KB
/
E06_CreditCardNumberValidator.hs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
module E06_CreditCardNumberValidator where
import Common
import Data.Char (digitToInt)
{-
This exercise is based on the homework assignments in Erik Meijer's
MOOC “Introduction to Functional Programming”.
A credit card number is valid if it obeys certain rules.
If a validation function is broken down to a combination of several small
and simple functions, it's easy to convince yourself and others
of your validation function to be correct.
The validation algorithm is explained in FP101x as follows:
- 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
- If the result equals 0, then number is valid.
Example:
- Input number is 4012888888881881
- Double every other digit, starting from right (reversed):
[1,16,8,2,8,16,8,16,8,16,8,16,2,2,0,8]
- Sum these digits (note that two-digit numbers has to be considered as two
digits again): 90
- Calculate the modulus of 90 over 10: 0
- This means that the number is valid
-}
{-
Step 1:
Convert a numerical representation of the credit card number to a String
-}
numberToString :: Int -> String
numberToString = _YOUR_CODE_HERE
{-
Step 2:
Split string on single digits, and represent them as Ints.
Hint: There is a function called `digitToInt` that converts a single Char to
an Int. Now, keeping in mind that String = [Char], it looks like we
would want to apply `digitToInt` to every element of the string...
-}
stringToDigitList :: String -> [Int]
stringToDigitList = _YOUR_CODE_HERE
{-
Step 3:
Double every other element, starting from the right-most digit.
To reverse a list, you can use the `reverse` function.
There are many ways to do this, but the (partial) solution here uses
`zipWith`, which takes a function, two lists and combines the elements using
the given function. For example:
zipWith (+) [1,2,3] [3,2,1]
= [1+3, 2+2, 3+1]
= [4,4,4]
Can you see what `pattern` below will evaluate to? If so, maybe we combine
that pattern and the reversed list to get what we want?
-}
doubleEveryOtherElement :: [Int] -> [Int]
doubleEveryOtherElement xs = let xs' = reverse xs
pattern = 1:2:pattern
in reverse $ zipWith _YOUR_CODE_HERE xs' pattern
{-
Step 4:
Sum the digits in a list. Remember to split numbers with more than 1 digit.
Attack plan:
1) Convert each integer to a string, e.g.: [1,5,10] -> ["1","5","10"]
2) Concatenate the list elements, e.g. ["1","5","10"] -> "1510"
3) Convert each character to the corresponding integer, e.g. "1510" -> [1,5,1,0]
4) Sum the digits using the `sum` function
(Hint: now is the time to use the `numberToString` and `stringToDigitList`
functions you defined above.)
-}
sumDigitList :: [Int] -> Int
sumDigitList xs = let xs1 = map _YOUR_CODE_HERE xs -- step 1
xs2 = concat xs1 -- step 2
xs3 = _YOUR_CODE_HERE -- step 3
in sum xs3
{-
Step 5:
Calculate the modulus of a number over 10. The modulo function in Haskell is
`mod`. Remember that you can partially apply a function.
-}
mod10 :: Int -> Int
mod10 = _YOUR_CODE_HERE
{-
Step 6:
Tie it all together in one single function for determining if a credit card
number is valid. Remember that you compose functions using (.):
f . g = \x -> f (g x)
Remember the recipe:
- 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
- If the result equals 0, then number is valid.
-}
isValidCreditCardNumber :: Int -> Bool
isValidCreditCardNumber n = let digitList = (stringToDigitList . numberToString) n :: [Int]
checksum = _YOUR_CODE_HERE
in checksum == 0