Hatena::Grouphaskell

すばらしき functional programming

2008-07-14クレジットカードのチェックディジット

クレジットカードの最後の桁はチェックディジット(検査桁)です。アルゴリズムももちろん公開されています。たとえば、http://www.merriampark.com/anatomycc.htm (中盤以降)。

1桁置きに2倍して(10以上になった場合は9をひく)足し合わせ、10の倍数になっているかを確認すればよいのですが、ミソはチェックディジットである最後の桁を2倍しないように奇数桁目を2倍するのか偶数桁目を2倍するのか決めること。クレジットカード番号の長さは13桁~16桁で、偶数か奇数も決まっていない (知ってました? 奇数桁のものでお目にかかるのは AMEX くらいだと思うのですが、各種申込書なんかではいかにも 4桁-4桁-4桁-4桁 でさせたそうなものもあります。AMEX のカードには 4桁-6桁-5桁 でプリントされていますよ) ので、前述のURLのサンプルプログラム(java)でも最後の桁から最初の桁に向かって処理しています。

Haskell だと、こんな感じ?

{-
 - valid card  -- validate credit card check digit
 -}

cardNumber :: String -> [Int]
cardNumber [] = []
cardNumber (d:ds) | isDigit d = (fromEnum d - fromEnum '0'):cardNumber ds
                      where isDigit d = '0' <= d && d <= '9'
cardNumber (d:ds) = cardNumber ds  -- remove space or any other chars.


luhn :: [Int] -> [Int]  -- note that card digits are reversed
luhn []      = []
luhn (a:[])  = [a]
luhn (a:b:x) = a:oddDigit b:luhn x
                 where oddDigit n = if n < 5 then n*2 else n*2 - 9

validCard :: String -> Bool
validCard x = sum (luhn (reverse (cardNumber x))) `mod` 10 == 0

前述 URL からの例題

Main> validCard "4417 1234 5678 9112"
False
Main> validCard "4417 1234 5678 9113"
True
Main>