如何解决对于初学者|整数列表的luhn算法
我已经看到了此解决方案:
.loadTrustMaterial
但是,我不了解它,因为我是Haskell的新手。
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,True]
我了解此算法的简化版本只有四位数字。
但是,我不知道如何为任何长度的数字列表编写算法版本。
解决方法
老实说,这个例子很神秘。它过度使用了无点样式,即省略了显式的函数参数。有时 可以使代码简洁明了,但也可以使代码变得更加隐秘。
让我们从这里开始:
(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
(我避免使用变量名even
,它也是检查数字是否为偶数的标准函数的名称!)
或者,您可以在此处调用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
只需要reverse
来使False
与输入列表的 last 元素对齐,而不是使其头部对齐;这有点难看,但并不严格。
map
和zip
的组合具有一个标准的快捷方式,可同时执行以下操作:
doubleAndSum = sum
. zipWith nextStep (cycle [False,True])
. reverse
至于myLuhn
:以无点样式编写实际上是IMO可以的,但我会稍微介绍一下。具体来说,
decimalDigits :: Int -> [Int]
decimalDigits = map (read . (: "")) . show
(:"")
的作用是,将单个字符放入单例字符串中。也可以写成read . pure
。
然后
myLuhn = (0 ==) . (`mod` 10) . doubleAndSum . decimalDigits
或
myLuhn x = doubleAndSum (decimalDigits x)`mod`10 == 0
†可能会发生这样的情况,即单个遍历对性能有好处,但是,如果您在该级别上考虑,那么几乎应该肯定不是列表上的懒惰对折,但在未装箱的矢量上严格左折。无论如何,GHC经常可以将单独的fold-y操作融合为一个遍历。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。