Haskell 对于初学者|整数列表的luhn算法
我已经看到了这个解决方案:Haskell 对于初学者|整数列表的luhn算法,haskell,luhn,Haskell,Luhn,我已经看到了这个解决方案: doubleAndSum :: [Int] -> Int doubleAndSum = fst . foldr (\i (acc, even) -> (acc + nextStep even i, not even)) (0,False) where nextStep even i | even = (uncurry (+) . (`divMod` 10) . (*2)) i | otherwise
doubleAndSum :: [Int] -> Int
doubleAndSum = fst . foldr (\i (acc, even) -> (acc + nextStep even i, not even)) (0,False)
where
nextStep even i
| even = (uncurry (+) . (`divMod` 10) . (*2)) i
| otherwise = i
myLuhn :: Int -> Bool
myLuhn = (0 ==) . (`mod` 10) . doubleAndSum . (map (read . (: ""))) . show
testCC :: [Bool]
testCC = map myLuhn [49927398716, 49927398717, 1234567812345678, 1234567812345670]
-- => [True,False,False,True]
然而,我不明白,因为我是哈斯克尔的新手
luhn :: [Int] -> Bool
luhn w x y z = (luhnDouble w + x + luhnDouble y + z) `mod` 10 == 0
luhnDouble :: Int -> Int
luhnDouble x | 2* x <= 9 = 2*x
| otherwise = (2*x)-9
luhn::[Int]->Bool
luhn w x y z=(luhnDouble w+x+luhnDouble y+z)`mod`10==0
luhnDouble::Int->Int
luhnDouble x | 2*x老实说,这个例子相当神秘。它过度使用无点样式,即省略显式函数参数。这有时可以使代码变得漂亮和简洁,但也可以使代码变得相当神秘
让我们从这里开始:
(uncurry (+) . (`divMod` 10) . (*2)) i
首先,由于您只是将所有内容应用于参数i
,因此不需要真正的合成管道——您不妨编写它
uncurry (+) $ (`divMod` 10) $ (*2) i
≡ uncurry (+) $ (`divMod` 10) $ i*2
≡ uncurry (+) $ (i*2)`divMod`10
≡ let (d,r) = (i*2)`divMod`10
in d+r
因此,可以编写nextStep
nextStep isEven i
| isEven = d+r
| otherwise = i
where (d,r) = (i*2)`divMod`10
(我避免使用变量名偶数,这也是检查数字是否偶数的标准函数名!)
或者,您也可以在此处调用luhnDouble
函数,该函数实际上以更详细的方式计算相同的内容:
nextStep isEven i
| isEven = luhnDouble i
| otherwise = i
那你就有这个折叠。它基本上做了三件互锁的事情:1。在偶数和奇数之间切换2。对每个列表元素应用nextStep
,以及偶数3。总结结果
我不同意用一次折叠来完成所有这些是个好主意†;写出来要清楚得多:
doubleAndSum = sum
. map (\(isEven, i) -> nextStep isEven i) -- or `map (uncurry nextStep)`
. zip (cycle [False, True]) -- or `iterate not False`
. reverse
只需将False
与输入列表的最后一个元素(而不是其头部)对齐,就可以使用反向
;这有点难看,但没有批评
map
和zip
的组合有一个标准的快捷方式,可以在一个步骤中同时执行这两项操作:
doubleAndSum = sum
. zipWith nextStep (cycle [False, True])
. reverse
至于myLuhn
:我实际上可以用无点的风格来写,但我会稍微打断一下。具体来说,
decimalDigits :: Int -> [Int]
decimalDigits = map (read . (: "")) . show
(:“”)
所做的是,它将单个字符放入单个字符串中。也可以写入读取。纯
那么
或
†可能有这样一种情况,即单个遍历对性能有好处,但是如果您在该级别上思考,那么几乎可以肯定,它不应该是列表上的惰性右折叠,而应该是未绑定向量上的严格左折叠。无论如何,GHC通常可以将单独的折叠y操作融合到单个遍历中。感谢您解码这段自命不凡的代码。:-)而且,可能比read更好。(:“”)
至少有两种方式。当然,我怎么会错过呢!
myLuhn = (0 ==) . (`mod` 10) . doubleAndSum . decimalDigits
myLuhn x = doubleAndSum (decimalDigits x)`mod`10 == 0