LoginSignup
8
7

More than 5 years have passed since last update.

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

Posted at

はじめに

この記事はSansanアドベントカレンダー8日目です。

今日のお話

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)

    # 残り11n = 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

よさそうに見えます。

感想

  • パイプライン演算子いいですね!!
  • もっときれいに書けると思います。
8
7
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
8
7