cf. マイナンバーのチェックデジットを計算する
n+1 匹目のドジョウに挑戦。
mynumber.ml
let digits_pattern = Str.regexp "^[0-9]+$"
let checkdigit n =
let q i =
if i < 6 then i + 2
else i - 4 in
let rec accumulate n i acc =
if n = 0 then 11 - (acc mod 11)
else accumulate (n / 10) (i + 1) (acc + (n mod 10) * (q i)) in
if String.length n <> 11
|| not (Str.string_match digits_pattern n 0) then None
else Some (accumulate (int_of_string n) 0 0)
let valid n =
String.length n = 12
&& Str.string_match digits_pattern n 0
&& let c = (int_of_char n.[11]) - (int_of_char '0') in
let m = String.init 11 (fun i -> n.[i]) in
match checkdigit m with
| None -> false
| Some d -> c = d
let () =
List.iter
(fun n -> Printf.printf "%s: %b\n" n @@ valid n)
[ "123456789010";
"123456789011";
"123456789012";
"123456789013";
"123456789014";
"123456789015";
"123456789016";
"123456789017";
"123456789018";
"123456789019";
"023456789013" ]
checkdigit
はマイナンバーの先頭11桁を string
として取ってチェックディジットを計算する関数。入力文字列が数字じゃなかったり桁が足りない場合失敗するため戻り値の型は int option
。
valid
はマイナンバー12桁を string
で受けて checkdigit
で得たチェックディジットが与えられたものと同じか突き合わせるだけ。
入力チェックは横着して正規表現。Str
がサポートする正規表現は PCRE じゃないので \d
を [0-9]
としています。\A
や \z
もないので入力が改行を含む場合は知らない。
整数型が11桁以上必要なので 32bit 環境だと動かないと思います。
コンパイルして実行:
$ ocamlfind ocamlc -linkpkg -package str mynumber.ml
$ ./a.out
123456789010: false
123456789011: false
123456789012: false
123456789013: false
123456789014: false
123456789015: false
123456789016: false
123456789017: false
123456789018: true
123456789019: false
023456789013: true
合ってますね。