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

# あわせて読みたい

| 言語 | リンク | 
|:-----------:|:------------|
| Ruby | [マイナンバーのチェックデジットを計算する](http://qiita.com/qube81/items/fa6ef94d3c8615b0ce64) |
| C++ | [C++でマイナンバーのチェックデジットを計算する](http://qiita.com/yumetodo/items/600ca0df422010cbc4c1) |
| Python | [Python マイナンバー検証用モジュールを公開](http://qiita.com/haminiku/items/bcf4bac82bd1ca62c746) |
| PHP | [マイナンバーのチェックデジットを計算する PHP版](http://qiita.com/rana_kualu/items/f36275032bbc6a3f18b4) |
| C# | [C#でマイナンバーのチェックデジットを計算する](http://qiita.com/whitebell/items/5982055e6768960496db) |
| SQL | [マイナンバーのチェックデジットを計算する（SQLで）](http://qiita.com/damassima/items/873a5ba95c32d971eb26) |
| PowerShell | [WindowsPowerShell & C#マイナンバーのチェックデジットを計算する](http://qiita.com/shoooo/items/42bbdd9023c14661da0a) |
| JavaScript, Groovy | [JavaScriptとGroovyでマイナンバーのチェックデジットを計算する](http://qiita.com/mas0061/items/19ffdda24a9df5f4e6e2) |
| Haskell | [Haskellでマイナンバーのチェックデジットを計算する](http://qiita.com/lotz/items/e1f3c649227271c381f6) |
| Haskell | [マイナンバー数字列の確認](http://qiita.com/nobsun/items/fc151dea83114146ad51) |
| Java | [マイナンバーのチェックデジットを計算する（Java編）](http://qiita.com/deaf_tadashi/items/2e7249371f2138a6a7fc) |
| C, Go, Bash, Excel etc... | [マイナンバーのチェックデジットの検算をざっくり1割くらい高速化する(計ってないので知らんけど)](http://qiita.com/genta/items/fb25ce50592134d3fbb8) |
| Go | [マイナンバーのチェックデジットをGoで計算する](http://qiita.com/qube81/items/f66a38b28ec58bc5c4da) |
| Swift | [マイナンバーのチェックデジットをSwiftで計算する](http://qiita.com/melodydance/items/4b442e3094fdea031065) |
| Vim script | [Vim からマイナンバーを検証出来るプラグイン書いた。](http://mattn.kaoriya.net/software/vim/20151117174330.htm) |
| Gauche | [Scheme(Gauche)でマイナンバーのチェックデジットを計算する](http://qiita.com/X___MOON___X/items/70b81090beb003534cd8) |
| OCaml | [OCaml でマイナンバーのチェックデジットを計算する](http://qiita.com/outerinside/items/dd74dd84a8b0b5884df2) |
| Rust | [My Number validator for Rust](https://github.com/Tomohiro/mynumber) |

# 説明

流行に乗ってマイナンバーのチェックデジットを計算してみます。
Ruby 版を真似て書いています。

### Perl5

```perl:mynumber_validate.pl
#!/usr/bin env perl

use v5.18;
use List::Util 'sum';
use feature qw(say switch);
no warnings 'experimental';

&main;exit;

sub main{
  say validate_my_number('123456789010'); #=> false
  say validate_my_number('123456789011'); #=> false
  say validate_my_number('123456789012'); #=> false
  say validate_my_number('123456789013'); #=> false
  say validate_my_number('123456789014'); #=> false
  say validate_my_number('123456789015'); #=> false
  say validate_my_number('123456789016'); #=> false
  say validate_my_number('123456789017'); #=> false
  say validate_my_number('123456789018'); #=> true
  say validate_my_number('123456789019'); #=> false
  say validate_my_number('023456789013'); #=> true (先頭が0の場合)
}

sub validate_my_number{
  # 整数列化
  my @digits = split'', $_[0];

  # 12桁しか認めない
  return 'false' unless @digits == 12;

  # チェックデジットを分離します
  my $check_digit = pop @digits;

  # P(n) 定義
  my $P = sub {
      my @digits = @_;
      return sub {
        my $n = $_[0];
        return $digits[ -$n ];
      };
  }->(@digits);

  # Q(n) 定義
  my $Q = sub {
        my $n = $_[0];
        return ($n<= 6) ? $n+1 : $n-5;
  };

  # 数列の和を11で割った余りを計算します
  my $remainder = ( sum map{ $P->($_) * $Q->($_) } 1..11 ) % 11;

  given( $remainder ){
    when ([0,1]){
      return $check_digit == 0 ? 'true' : 'false';
    }
    default{
      return $check_digit == 11 - $remainder ? 'true' : 'false';
    }
  }
}
```

### Perl6

```perl:perl6_mynumber_validate.pl
use v6;

sub MAIN (){
  say validate_my_number('123456789010'); #=> false
  say validate_my_number('123456789011'); #=> false
  say validate_my_number('123456789012'); #=> false
  say validate_my_number('123456789013'); #=> false
  say validate_my_number('123456789014'); #=> false
  say validate_my_number('123456789015'); #=> false
  say validate_my_number('123456789016'); #=> false
  say validate_my_number('123456789017'); #=> false
  say validate_my_number('123456789018'); #=> true
  say validate_my_number('123456789019'); #=> false
  say validate_my_number('023456789013'); #=> true (先頭が0の場合)
}

sub validate_my_number( Str $mynumber ){
  # 整数列化
  my @digits = $mynumber.comb;

  # 12桁しか認めない
  return False unless @digits.elems == 12;

  # チェックデジットを分離します
  my $check_digit = @digits.pop;

  # P(n) 定義
  my $P = sub ( @digits ) {
      return sub ( Int $n ) {
        return @digits[ *-$n ];
      };
  }.(@digits);

  # Q(n) 定義
  my $Q = sub ( Int $n ) {
    return ($n <= 6) ?? $n + 1 !! $n - 5;
  };

  # 数列の和を11で割った余りを計算します
  my $remainder = ( [+] (1..11).map:{ $P.(+$_) * $Q.(+$_) } ) % 11;

  given $remainder {
    when 0|1 {
      return $check_digit == 0;
    }
    default {
      return $check_digit == (11 - $remainder);
    }
  }
}
```

おわりです。

# 参考や注釈

* [行政手続における特定の個人を識別するための番号の利用等に関する法律の規定による通知カード及び個人番号カード並びに情報提供ネットワークシステムによる特定個人情報の提供等に関する省令](http://law.e-gov.go.jp/announce/H26F11001000085.html)

* [Perl6::Overview::Operator](http://search.cpan.org/dist/Perl6-Doc-0.36/lib/Perl6/Doc/Overview/Operator.pod)
* [Perl 6 Types](http://doc.perl6.org/type.html)

* [Perl6導入とHello, world!](http://qiita.com/B73W56H84/items/2153e6211ed617b4605d)
* [Perl5,Perl6で配列の末尾からひとつ前の要素を取り出す](http://qiita.com/B73W56H84/items/d8352d06f83f9ec29ec4)