元ネタ: マイナンバーのチェックデジットを計算する のアルゴリズムを他の言語に翻訳するのが流行っているらしいのでHaskellでもやってみました。マイナンバーの解説とかは書かないので元記事をぜひ読んでください。短いし読みやすいのですぐ読めると思います
validateMyNumber :: Int64 -> Bool
validateMyNumber myNumber
| length (show myNumber) <= 12 =
let (x:pn) = take 12 . map (`mod` 10) . iterate (`div` 10) $ myNumber
qn = [if n <= 6 then n + 1 else n - 5 | n <- [1..11]]
y = (`mod` 11) . sum . zipWith (*) qn $ pn
in x == if y <= 1 then 0 else (11 - y)
| otherwise = False
1行ずつ解説していきます
validateMyNumber :: Int64 -> Bool
マイナンバーが正しいかどうかを判定する関数はマイナンバー(Int64)を受け取って正しいかどうか(Bool)を返します。副作用はありません
validateMyNumber myNumber
受け取ったマイナンバーに myNumber という名前をつけます。
| length (show myNumber) <= 12 =
ガードを使って12桁以下の場合で分岐します。他にも書き方はあると思いますがわかりやすそうな実装にしました。
let (x:pn) = take 12 . map (`mod` 10) . iterate (`div` 10) $ myNumber
let 節の始まりです。まずiterate
でmyNumberを10で何回も割ったリストを作ります。具体例で見ると
>>> iterate (`div` 10) 12345
[12345,1234,123,12,1,0,0,0,0,0,...
このようになり後ろに0が無限個続きます。そしてmap
で`各要素の10で割った余りを求めます
>>> map (`mod` 10) . iterate (`div` 10) $ 12345
[5,4,3,2,1,0,0,0,0,0,..
最後に take 12
で必要な個数だけ取ってきます
>>> take 12 . map (`mod` 10) . iterate (`div` 10) $ 12345
[5,4,3,2,1,0,0,0,0,0,0,0]
さらにlet (x:pn) =
というパターンマッチを行うことでx
に検査用数字を、pn
に計算で使う$Pn$を代入しています。
qn = [if n <= 6 then n + 1 else n - 5 | n <- [1..11]]
定義通りに$Qn$を計算します
y = (`mod` 11) . sum . zipWith (*) qn $ pn
定義通りに$\Bigl( \displaystyle\sum_{n=1}^{11} P_n \times Q_n \Bigr) % 11 $を計算してy
と名づけておきます。
in x == if y <= 1 then 0 else (11 - y)
検査用数字と計算した値が同じかどうかを判定します。この結果がそのまま関数の返り値になります。
| otherwise = False
桁数が12よりも大きければ正しいマイナンバーではありません。
テストも含めた全てのコードを貼っておきます。[gist]
import GHC.Int
validateMyNumber :: Int64 -> Bool
validateMyNumber myNumber
| length (show myNumber) <= 12 =
let (x:pn) = take 12 . map (`mod` 10) . iterate (`div` 10) $ myNumber
qn = [if n <= 6 then n + 1 else n - 5 | n <- [1..11]]
y = (`mod` 11) . sum . zipWith (*) qn $ pn
in x == if y <= 1 then 0 else (11 - y)
| otherwise = False
main :: IO ()
main = do
print $ validateMyNumber 123456789010 -- False
print $ validateMyNumber 123456789011 -- False
print $ validateMyNumber 123456789012 -- False
print $ validateMyNumber 123456789013 -- False
print $ validateMyNumber 123456789014 -- False
print $ validateMyNumber 123456789015 -- False
print $ validateMyNumber 123456789016 -- False
print $ validateMyNumber 123456789017 -- False
print $ validateMyNumber 123456789018 -- True
print $ validateMyNumber 123456789019 -- False
print $ validateMyNumber 023456789013 -- True
##他言語での実装