Skip to content

Instantly share code, notes, and snippets.

@alskipp
Last active April 30, 2016 14:12
Show Gist options
  • Save alskipp/55b28891412ae0cf0d51 to your computer and use it in GitHub Desktop.
Save alskipp/55b28891412ae0cf0d51 to your computer and use it in GitHub Desktop.
Credit card validation in Swift for Swift London Meetup
// Swift 2.0
/* Function composition operator */
infix operator •> { associativity left precedence 150 }
func •> <A,B,C> (f:A -> B, g:B -> C ) -> A -> C {
return { g(f($0)) }
}
extension Array where T: IntegerType {
func sum() -> T {
return self.reduce(0, combine: +)
}
}
func toDigitsRev(num:Int) -> [Int] {
if case 0...9 = num { return [num] }
return [num % 10] + toDigitsRev(num/10)
}
func doubleSecond(nums:[Int]) -> [Int] {
return nums.enumerate().map { i, x in
i % 2 == 0 ? x : x * 2
}
}
func sumDigits(nums:[Int]) -> Int {
return nums.flatMap(toDigitsRev).sum()
}
let isValid =
toDigitsRev
•> doubleSecond
•> sumDigits
•> { $0 % 10 == 0 }
/* Example */
isValid(4716347184862961) // > true
isValid(5716347184862961) // > false
// Swift 2.0
// Pushing the function composition idea even further 😈
/* Curried flatMap & reduce functions (taking function as first arg) */
func flatMap<A,B>(f:A -> [B])(_ xs:[A]) -> [B] {
return xs.flatMap(f)
}
func reduce<A,B>(f:(B, A) -> B)(_ i:B)(_ xs:[A]) -> B {
return xs.reduce(i, combine: f)
}
infix operator •> { associativity left precedence 150 }
func •> <A,B,C> (f:A -> B, g:B -> C ) -> A -> C {
return { g(f($0)) }
}
/* Implementation */
func toDigitsRev(num:Int) -> [Int] {
if case 0...9 = num { return [num] }
return [num % 10] + toDigitsRev(num/10)
}
func doubleSecond(nums:[Int]) -> [Int] {
return nums.enumerate().map { i, x in
i % 2 == 0 ? x : x * 2
}
}
let sum = reduce(+)(0)
let sumDigits = flatMap(toDigitsRev) •> sum
let isValid =
toDigitsRev
•> doubleSecond
•> sumDigits
•> { $0 % 10 == 0 }
/* Example */
isValid(4716347184862961) // > true
isValid(5716347184862961) // > false
// Swift 1.2
/* A few handy functions to simplify(!) code */
infix operator |> { associativity left precedence 150 } // Pipe-Forward (F#)
func |> <T,U> (lhs:T, rhs:T -> U ) -> U {
return rhs(lhs)
}
infix operator >>= { associativity left } // bind (Haskell)
func >>= <T,U>(elements:[T], f:T -> [U]) -> [U] {
return reduce(elements, []) { $0 + f($1) }
}
func sum(xs:[Int]) -> Int {
return reduce(xs, 0, +)
}
/* Implementation of credit card validation */
func toDigitsRev(num:Int) -> [Int] {
switch num {
case 0...9 : return [num]
default : return [num % 10] + toDigitsRev(num/10)
}
}
func doubleSecond(nums:[Int]) -> [Int] {
return map(enumerate(nums)) { i, x in
i % 2 == 0 ? x : x * 2
}
}
func sumDigits(nums:[Int]) -> Int {
return sum(nums >>= toDigitsRev)
}
func isValid(num:Int) -> Bool {
return toDigitsRev(num) |> doubleSecond |> sumDigits % 10 == 0
}
/* Usage example */
isValid(4716347184862961) // > true
isValid(5716347184862961) // > false
{- Haskell implementation of creditcard validation -}
import Control.Monad
toDigitsRev :: Integer -> [Integer]
toDigitsRev n
| n < 10 = [n]
| otherwise = n `mod` 10 : toDigitsRev (n `div` 10)
doubleSecond :: [Integer] -> [Integer]
doubleSecond [] = []
doubleSecond (x:y:ys) = x : y * 2 : doubleSecond ys
doubleSecond (x:xs) = x : xs
sumDigits :: [Integer] -> Integer
sumDigits xs = sum $ xs >>= toDigitsRev
isValid :: Integer -> Bool
isValid = (0 ==) . (`mod` 10) . sumDigits . doubleSecond . toDigitsRev
{- Example Usage
isValid 4716347184862961 -- True
isValid 5716347184862961 -- False
-}
@alskipp
Copy link
Author

alskipp commented Nov 4, 2014

A description of the algorithm is here http://en.wikipedia.org/wiki/Luhn_algorithm
The Swift code is heavily influenced by my Haskell implementation for the following online course: http://t.co/G9YfG1og2t
Swift London: http://www.meetup.com/swiftlondon/

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment