はじめに
今日のお話
Elixir を触ってみたので、その話です。Elixir 初心者です。
私は普段は Ruby でお仕事しています。
やったこと
http://qiita.com/qube81/items/fa6ef94d3c8615b0ce64
マイナンバーのチェックデジットを計算してみます。上のリンクは Ruby 版で、その後いろいろな言語でやってみた投稿がありました。
Elixir 版がなかった(ようにみえる)のでやってみました。
どういう計算をするのか詳しくは上のリンクを見てください。
やってみた
最初に
文字列の流さが12文字以外のものはチェックできないので、それをはじくために verify_length
を定義してみます。
def verify_length(number) do
cond do
number |> String.length != 12 -> false
true -> true
end
end
cond は最初に真となるとき実行してくれます。真になるものがないときはエラーになります。
|>
はパイプライン演算子です。値を次の関数の第一引数に渡すことができます。
String.reverse("123") # => "321"
こうすべきところが、以下のように書けます。
"123" |> String.reverse # => "321"
計算方法
下一桁が検査用数字と一致しているかを見ればOKです。
検査用数字 : $11 - \Bigl( \displaystyle\sum_{n=1}^{11} P_n \times Q_n \Bigr) % 11$
ただし $\Bigl( \displaystyle\sum_{n=1}^{11} P_n \times Q_n \Bigr) % 11 ≦1 $の場合は、$0$とする
$Pn$ : 個人番号を構成する検査用数字以外の十一桁の番号の最下位の桁を $1$ 桁目としたときの $n$ 桁目の数字
$Qn$ : $1≦n≦6$ のとき $n+1$、 $7≦n≦11$ のとき $n-5$
Pn を定義してみます
def p(number, num) do
number |> String.reverse |> String.at(num-1) |> String.to_integer
end
Qn を定義してみます
def q(num) do
cond do
1 <= num && num <= 6 -> num + 1
7 <= num && num <= 11 -> num - 5
end
end
検査用数字 (check_number関数) はこんな感じでしょうか
while みたいな loop はないので再帰を使います。
def check_number(number) do
mod = number |> sigma(11) |> rem(11)
cond do
mod <= 1 -> 0
true -> 11 - mod
end
end
def sigma(number, num) do
cond do
num == 0 -> 0
true -> sigma(number, num-1) + p(number, num) * q(num)
end
end
まとめるとこうなりました
defmodule VerifyMyNumber do
def verify(number) do
# 下一桁を取得
check_digit = p(number, 1)
# 残り11桁
n = number |> String.slice(0, 11)
cond do
!verify_length(number) -> false
true -> check_digit == check_number(n)
end
end
def verify_length(number) do
cond do
number |> String.length != 12 -> false
true -> true
end
end
def check_number(number) do
mod = number |> sigma(11) |> rem(11)
cond do
mod <= 1 -> 0
true -> 11 - mod
end
end
def sigma(number, num) do
cond do
num == 0 -> 0
true -> sigma(number, num-1) + p(number, num) * q(num)
end
end
def p(number, num) do
number |> String.reverse |> String.at(num-1) |> String.to_integer
end
def q(num) do
cond do
1 <= num && num <= 6 -> num + 1
7 <= num && num <= 11 -> num - 5
end
end
end
IO.puts "123456789010: #{VerifyMyNumber.verify("123456789010")}"
IO.puts "123456789011: #{VerifyMyNumber.verify("123456789011")}"
IO.puts "123456789012: #{VerifyMyNumber.verify("123456789012")}"
IO.puts "123456789013: #{VerifyMyNumber.verify("123456789013")}"
IO.puts "123456789014: #{VerifyMyNumber.verify("123456789014")}"
IO.puts "123456789015: #{VerifyMyNumber.verify("123456789015")}"
IO.puts "123456789016: #{VerifyMyNumber.verify("123456789016")}"
IO.puts "123456789017: #{VerifyMyNumber.verify("123456789017")}"
IO.puts "123456789018: #{VerifyMyNumber.verify("123456789018")}"
IO.puts "123456789019: #{VerifyMyNumber.verify("123456789019")}"
IO.puts "023456789013: #{VerifyMyNumber.verify("023456789013")}"
実行結果
123456789010: false
123456789011: false
123456789012: false
123456789013: false
123456789014: false
123456789015: false
123456789016: false
123456789017: false
123456789018: true
123456789019: false
023456789013: true
よさそうに見えます。
感想
- パイプライン演算子いいですね!!
- もっときれいに書けると思います。