発端
マイナンバーのチェックデジットを計算する https://t.co/hd5vkkS6sP D言語への翻訳で Advent calendar の記事になるかもな
— 大堀龍一 (Ryuichi OHORI) (@__DaLong) 2015, 12月 4
やってみよう。
実装
どやさ。
mynumber.d
import std.algorithm.iteration : map;
import std.functional : unaryFun;
import std.numeric : dotProduct;
import std.range : dropOne, iota, repeat, zip;
enum isValidMyNumber(ulong x) =
x < 10UL^^12 &&
x.repeat
.zip(0.iota(12).map!"10UL^^a")
.map!"a[0]/a[1]%10"
.unaryFun!(n12r => n12r.front ==
n12r.dropOne
.dotProduct(1.iota(12).map!"a<7?a+1:a-5")
.unaryFun!"a%11"
.unaryFun!"a<2?0:11-a");
// ulong
static assert(! isValidMyNumber! 123456789010);
static assert(! isValidMyNumber! 123456789011);
static assert(! isValidMyNumber! 123456789012);
static assert(! isValidMyNumber! 123456789013);
static assert(! isValidMyNumber! 123456789014);
static assert(! isValidMyNumber! 123456789015);
static assert(! isValidMyNumber! 123456789016);
static assert(! isValidMyNumber! 123456789017);
static assert( isValidMyNumber! 123456789018);
static assert(! isValidMyNumber! 123456789019);
static assert( isValidMyNumber! 23456789013);
static assert(! isValidMyNumber!9123456789010);
UFCS チェインのために std.functional.unaryFun
を挟んでいますが、ナイーブにラムダ記法を使って (a => expr)(value)
と即時呼び出しすると括弧が多くなるし、流れが前後するので読みにくくなるわけです。
えぇ、引数に文字列や文字からなる input range なんかを渡したいという需要もありますよね。
mynumber.d
import std.range.primitives : ElementType, isInputRange;
import std.traits : isSomeChar, isSomeString;
template isValidMyNumber(alias x)
if (isSomeString!(typeof(x)) ||
isInputRange!(typeof(x)) &&
isSomeChar!(ElementType!(typeof(x)))) // てきとー
{
import std.algorithm.searching : all;
import std.ascii : isDigit;
import std.range : walkLength;
static if (x.walkLength == 12 && x.all!isDigit)
{
import std.conv : to;
alias isValidMyNumber = isValidMyNumber!(to!ulong(to!dstring(x)));
}
else
{
enum isValidMyNumber = false;
}
}
// string
static assert(! isValidMyNumber! "123456789010");
static assert(! isValidMyNumber! "123456789011");
static assert(! isValidMyNumber! "123456789012");
static assert(! isValidMyNumber! "123456789013");
static assert(! isValidMyNumber! "123456789014");
static assert(! isValidMyNumber! "123456789015");
static assert(! isValidMyNumber! "123456789016");
static assert(! isValidMyNumber! "123456789017");
static assert( isValidMyNumber! "123456789018");
static assert(! isValidMyNumber! "123456789019");
static assert( isValidMyNumber! "023456789013");
static assert(! isValidMyNumber! "23456789013");
static assert(! isValidMyNumber!"9123456789010");
static assert(! isValidMyNumber! "o23AS67890I3");
// InputRange
import std.range : chain;
static assert(! isValidMyNumber!( "123456789010".chain("")));
static assert(! isValidMyNumber!( "123456789011".chain("")));
static assert(! isValidMyNumber!( "123456789012".chain("")));
static assert(! isValidMyNumber!( "123456789013".chain("")));
static assert(! isValidMyNumber!( "123456789014".chain("")));
static assert(! isValidMyNumber!( "123456789015".chain("")));
static assert(! isValidMyNumber!( "123456789016".chain("")));
static assert(! isValidMyNumber!( "123456789017".chain("")));
static assert( isValidMyNumber!( "123456789018".chain("")));
static assert(! isValidMyNumber!( "123456789019".chain("")));
static assert( isValidMyNumber!( "023456789013".chain("")));
static assert(! isValidMyNumber!( "23456789013".chain("")));
static assert(! isValidMyNumber!("9123456789010".chain("")));
static assert(! isValidMyNumber!( "o23AS67890I3".chain("")));
static assert(! isValidMyNumber!( "l23AS67890I3".chain("")));
文字列やそれっぽいのを ulong
に変換して最初の実装に丸投げするだけのやっつけテンプレートです。static assert
部で文字列から input range(非文字列扱い)をでっち上げるために std.range.chain
を使ったのは秘密です。
感想
D 言語のマルチパラダイム性をアピールするチャンスだと思いました。