Haskellでマイナンバーのチェックデジットを計算する

元ネタ: [マイナンバーのチェックデジットを計算する](http://qiita.com/qube81/items/fa6ef94d3c8615b0ce64) のアルゴリズムを他の言語に翻訳するのが流行っているらしいのでHaskellでもやってみました。マイナンバーの解説とかは書かないので元記事をぜひ読んでください。短いし読みやすいのですぐ読めると思います

----

```hs
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
```

１行ずつ解説していきます

```hs
validateMyNumber :: Int64 -> Bool
```

マイナンバーが正しいかどうかを判定する関数はマイナンバー（Int64）を受け取って正しいかどうか（Bool）を返します。副作用はありません

```hs
validateMyNumber myNumber
```

受け取ったマイナンバーに myNumber という名前をつけます。

```hs
  | length (show myNumber) <= 12 =
```

ガードを使って12桁以下の場合で分岐します。他にも書き方はあると思いますがわかりやすそうな実装にしました。

```hs
      let (x:pn) = take 12 . map (`mod` 10) . iterate (`div` 10) $ myNumber
```

let 節の始まりです。まず`iterate`でmyNumberを10で何回も割ったリストを作ります。具体例で見ると

```hs
>>> iterate (`div` 10) 12345
[12345,1234,123,12,1,0,0,0,0,0,...
```

このようになり後ろに0が無限個続きます。そして`map`で`各要素の10で割った余りを求めます

```hs
>>> map (`mod` 10) . iterate (`div` 10) $ 12345
[5,4,3,2,1,0,0,0,0,0,..
```

最後に `take 12` で必要な個数だけ取ってきます

```hs
>>> 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$を代入しています。

```hs
          qn = [if n <= 6 then n + 1 else n - 5 | n <- [1..11]]
```

定義通りに$Qn$を計算します

```hs
          y = (`mod` 11) . sum . zipWith (*) qn $ pn
```

定義通りに$\Bigl( \displaystyle\sum_{n=1}^{11} P_n \times Q_n \Bigr) \% 11 $を計算して`y`と名づけておきます。

```hs
      in  x == if y <= 1 then 0 else (11 - y)
```

検査用数字と計算した値が同じかどうかを判定します。この結果がそのまま関数の返り値になります。

```hs
  | otherwise = False
```

桁数が12よりも大きければ正しいマイナンバーではありません。

テストも含めた全てのコードを貼っておきます。[[gist](https://gist.github.com/lotz84/333d1ebc9153a703cac6)]

```hs
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
```

##他言語での実装
* [C++でマイナンバーのチェックデジットを計算する](http://qiita.com/yumetodo/items/600ca0df422010cbc4c1)
* [Python マイナンバー検証用モジュールを公開](http://qiita.com/haminiku/items/bcf4bac82bd1ca62c746)
* [マイナンバーのチェックデジットを計算する PHP版](http://qiita.com/rana_kualu/items/f36275032bbc6a3f18b4)
* [Perl5,Perl6でマイナンバーのチェックデジットを計算する](http://qiita.com/B73W56H84/items/fa7fcadf7779ac7dbe8b)
* [マイナンバーのチェックデジットを計算する（SQLで）](http://qiita.com/damassima/items/873a5ba95c32d971eb26)
* [WindowsPowerShell & C#マイナンバーのチェックデジットを計算する](http://qiita.com/shoooo/items/42bbdd9023c14661da0a)
* [C#でマイナンバーのチェックデジットを計算する](http://qiita.com/whitebell/items/5982055e6768960496db)