D言語で湯婆婆を実装してみる

# はじめに

元ネタ：[Javaで湯婆婆を実装してみる](https://qiita.com/Nemesis/items/c7192a7c510788d2cba2)（@Nemesis 様）


この記事をきっかけに色々なプログラミング言語で**湯婆婆を実装する**のが流行ってるらしいので便乗します。


## D言語とは？
ご存じない方の為に軽く説明します。
D言語（D Programming Language, dlang）は[Walter Bright](http://www.walterbright.com/)氏によって開発された、C言語の流れを汲みつつもモダンな機能を取り入れ続ける**超イカした**プログラミング言語です。

公式ホームページ：[D Programming Language](https://dlang.org)

## 環境

この記事のコードは以下のバージョンでの動作を確認しています。

- dmd : DMD64 D Compiler v2.094.0
- rdmd : build 20200922

### 実行環境をお持ちでない方へ
D言語を貴方のコンピュータに導入することは人生最良の行動です。
[こちらの記事](https://qiita.com/outlandkarasu@github/items/faa555d5c1d1d19a8fa4)を参考に今すぐインストール！

# ソースコード

## その1
本家の処理をそのままなぞったものが次のコードです。

```d
import std;
//import std.stdio : readln, writeln;
//import std.string : chomp;
//import std.random : Mt19937, unpredictableSeed, uniform;
//import std.conv : to;

void main(){
    writeln("契約書だよ。そこに名前を書きな。");

    dstring name = chomp(readln!dstring);
    writeln("フン。", name, "というのかい。贅沢な名だねぇ。");

    Mt19937 gen;
    gen.seed(unpredictableSeed);
    ulong newindex = uniform(0, name.length, gen);
    dchar newname = to!(dchar[])(name)[newindex];

    writeln("今からお前の名前は", newname, "だ。いいかい、", newname, "だよ。分かったら返事をするんだ、", newname, "！！");
}


```

基本的な文法はC++やJavaに近いものになっています。エントリーポイントは`void main()`です。`import std`により標準ライブラリを一括でインポートしていますが、コメントアウト部分のようにモジュールごと、関数ごとのインポートも可能です。

`func!T`は、型`T`による`func`のテンプレートを意味しています。
例えば`to!(dchar[])(name)`は、引数`name`のキャスト先の型をテンプレートによって`dchar[]`と指定しています。

## その2

```d
import std;

void main(){
    writeln("契約書だよ。そこに名前を書きな。");

    auto name = readln!dstring.chomp;
    writeln("フン。", name, "というのかい。贅沢な名だねぇ。");

    Mt19937 gen;
    gen.seed(unpredictableSeed);
    auto newname = name.choice(gen);

    writeln("今からお前の名前は", newname, "だ。いいかい、", newname, "だよ。分かったら返事をするんだ、", newname, "！！");
}
```

少々書き直しました。`auto`は型推論に使用するキーワードです。
D言語は**UFCS**という機能を採用しており、これによりメンバ関係等にない処理をメソッドチェーン的に記述することが出来ます。
例えば`a.b.c.d`は`d(c(b(a)))`の、`name.choice(gen)`は`choice(name, gen)`のシュガーシンタックスになっています。

## その3
おまけ。

```d
import std;

void main(){
    writeln("契約書だよ。そこに名前を書きな。");

    (name){
        writeln("フン。", name, "というのかい。贅沢な名だねぇ。");
        (newname =>
            writeln("今からお前の名前は", newname, "だ。いいかい、", newname, "だよ。分かったら返事をするんだ、", newname, "！！")
        )((gen){
                gen.seed(unpredictableSeed);
                return name.choice(gen);
            }(Mt19937.init));
    }(readln!dstring.chomp);
}

```

参考：D言語における関数リテラルの記法は以下の通りです。

```d
//普通の関数リテラル
(Type x){
    //hoge
    return fuga;
};
//例
auto square = (real x){
    return x^^2;
};

//ラムダ式
(Type x) => hoge;
//例
auto square = (real x) => x^^2;
```

# 実行
rdmdは、D言語の公式コンパイラdmdによるスクリプト風実行ツールです。

```bash
$ rdmd yubaba.d
契約書だよ。そこに名前を書きな。
千尋
フン。千尋というのかい。贅沢な名だねぇ。
今からお前の名前は千だ。いいかい、千だよ。分かったら返事をするんだ、千！！
```

名前を空白のまま入力した場合、仕様通り**湯婆婆はクラッシュします**。

```bash
$ rdmd yubaba.d
契約書だよ。そこに名前を書きな。

フン。というのかい。贅沢な名だねぇ。
core.exception.AssertError@/home/jj1lis/dlang/dmd-2.094.0/linux/bin64/../../src/phobos/std/random.d(2705): std.random.choice!(dstring, MersenneTwisterEngine!(uint, 32LU, 624LU, 397LU, 31LU, 2567483615u, 11LU, 4294967295u, 7LU, 2636928640u, 15LU, 4022730752u, 18LU, 1812433253u)).choice(ref dstring range, ref MersenneTwisterEngine!(uint, 32LU, 624LU, 397LU, 31LU, 2567483615u, 11LU, 4294967295u, 7LU, 2636928640u, 15LU, 4022730752u, 18LU, 1812433253u) urng) ref: invalid Range supplied. Range cannot be empty
----------------
??:? _d_assert_msg [0x5589c8b818ba]
??:? pure ref @safe immutable(dchar) std.random.choice!(immutable(dchar)[], std.random.MersenneTwisterEngine!(uint, 32uL, 624uL, 397uL, 31uL, 2567483615u, 11uL, 4294967295u, 7uL, 2636928640u, 15uL, 4022730752u, 18uL, 1812433253u).MersenneTwisterEngine).choice(ref immutable(dchar)[], ref std.random.MersenneTwisterEngine!(uint, 32uL, 624uL, 397uL, 31uL, 2567483615u, 11uL, 4294967295u, 7uL, 2636928640u, 15uL, 4022730752u, 18uL, 1812433253u).MersenneTwisterEngine) [0x5589c8b7f442]
??:? _Dmain [0x5589c8b7e03e]
```

D言語の実行時エラー処理は非常に優秀で、どこで例外が発生したか~~長すぎてむしろ見づらい~~一目瞭然です。