Luhn-acy in Haskell, Scala, and yet another Python version
Joey "Happiest Accordian Guyeek" de Villa
has
a couple
of
weblog entries
about the Luhn Algorithm, used for a checksum
on some credit card numbers.
Joey has been getting some examples in several
languages; I thought I'd contribute a Haskell
version and a Scala version, and the recursive
Python version that I did first.
Here's my Haskell one, tested (a little bit)
on both hugs and GHC:
-- These five lines are the actual code:
luhn_sub 0 _ = 0
luhn_sub n m =
(luhn_sub (n `div` 10) (3 - m)) + do_dig (m * (n `mod` 10))
where do_dig n = if n <= 9 then n else n - 9
luhn str = (luhn_sub (read str) 1) `mod` 10 == 0
-- These five lines run a small pair of tests:
t9 base = (map (base ++) (map show [0..9]))
prTest =
let test_strs = concat $ map t9 ["234234", "5128960128"] in
let results = map (\s -> (s ++ " -> " ++ show (luhn s)))
test_strs in
mapM_ putStrLn results
Thanks for blowing my whole damn day, Joey! :-D
Here's the Scala version:
(see http://radio.weblogs.com/0100945/2002/07/04.html )
module Luhn with {
def do_dig (dig: Long): Long = {
if (dig <= 9) dig
else dig-9;
}
def luhn_sub (nr:Long, mul:Long): Long = {
if (nr == 0) { 0; }
else {
val d = mul * (nr % 10);
luhn_sub(nr/10, 3-mul) + do_dig(d)
}
}
// I guess the java.lang... call is the normal Scala way
// to convert a Scala string to a Long -- I can't figure out
// much from the Scala libraries. Need Long instead of Int
// because them Credit Card numbers are big.
def luhn (nrStr:String):Boolean = {
val nr = java.lang.Long.parseLong(nrStr);
val lsum = luhn_sub(nr, 1);
lsum % 10 == 0
}
// -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
// Above is the Luhn algorithm code. From here down, it's
// all just testing code.
// -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
def lrange (lo: Long, pastHi: Long): List[Long] = {
if (lo >= pastHi) []
else lo :: lrange(lo + 1, pastHi);
}
def test_tens () = {
def tens (s:String) = {
for( val dig <- lrange(0,10) ) yield {
val sd = s + dig;
System.out.println( "Luhn(" + sd + ") = " + luhn(sd) )
}
}
tens( "234234" );
tens( "5128960128" );
nil
}
}
Luhn.test_tens();
And, finally, here's the recursive Python version that
preceded the other FP solutions:
def luhn( ccStr ):
def do_dig ( d ):
if d > 9: d -= 9
return d
def sub ( ccNr, mult ):
if ccNr:
return sub( ccNr/10, 3 - mult ) +
do_dig( mult * (ccNr%10) )
return 0
return ( sub(long(ccStr),1) % 10) == 0
def test ( ):
for tn in [ 234234, 5128960128 ]:
for i in range(9):
tnt = tn * 10 + int(i)
print tnt, luhn(`tnt`)
if __name__ == '__main__':
test()
12:43:32 AM