"Hello, world!"の話

本日は私、SHOOが[某所に起稿](http://ja.uncyclopedia.info/wiki/D%E8%A8%80%E8%AA%9E#.E3.83.97.E3.83.AD.E3.82.B0.E3.83.A9.E3.83.A0.E3.82.BD.E3.83.BC.E3.82.B9.E3.81.AE.E4.BE.8B)した `H.e.l.l.o. w.o.r.l.d! D` について解説いたします。

まずはそのプログラムをご覧に入れましょう

```d
import std.string, std.range, std.algorithm, std.conv, std.array;

struct D
{
    string msg;
    D opDispatch(string s)() @property
    {
        return typeof(return)(
            ((a, b)=>a.equal(b))(
                msg.retro().cycle().take(2),
                msg.chain(s).retro().take(2).array().retro())
                ? ((a, b)=>(
                    a.insertInPlace(msg.countUntil(b)+1, ", "), a.idup)
                  )(msg.dup, msg.retro().take(2).array().retro())~s
                : text(msg,s));
    }
    string d(T)() @property
    {
        return text(msg, T.stringof.toLower(), "! ", T.stringof);
    }
}
struct H
{
    static D opDispatch(string s)() @property { return D(typeof(this).stringof~s); }
}

pragma(msg, H.e.l.l.o. w.o.r.l.d! D);

void main() {}
```


# "Hello, world!"はもう古い

過去の"Hello, world!"プログラムは

```c
#include <stdio.h>
int main(int argc, const char *argv[])
{
	printf("Hello, world! C\n");
	return 0;
}
```

表示のために実行が必要

＿人人人人人人＿
＞　ダサい！　＜
￣^Y^Y^Y^Y^￣

D言語なら `pragma(msg, ...)` やCTFE、テンプレートメタプログラミングを使うことで

コンパイル時に任意の処理を行った結果の表示が可能

```d
pragma(msg, H.e.l.l.o. w.o.r.l.d! D);
```

＿人人人人人人人人人人人人人人＿
＞　こんなにもスタイリッシュ　＜
￣^Y^Y^Y^Y^Y^Y^Y^Y^Y^Y^Y^Y^￣

# 経緯
## ○月×日 D言語が Dark Language だなんだとdisられる

夜、仕事から帰って、おもむろにTwitterをやった。
こいつら、Dark Languageだなんだときっとデマカセにちがいねェ。
俺たちをばかにしやがって。

## ○月△日 アンサイクロペディアの"Hello, world!"が壊れていることに気づく

今日、アンサイクロペディアの記事から昔の"Hello, world!"の世話を頼まれた。
皮をひんむいたJavaのような奴だ。
コードが通らないってんで、dmdに投げこんだら、奴ら、writelnのproperty文が通らなかったりDeprecationを引き出したり
喚いたあげくやっと死にやがる。

## △月□日 UFCSの登場

今朝５時頃、宇宙服みてえなダブルクォーテーションを着た"Hello, world!"に突然たたき起こされて俺は興奮した。
なんでも、D言語に新機能の追加があったらしい。
コントリビューターの連中ときたら、夜も寝ないでコミットばかりやってるからこんな事になるんだ。

## ○月▽日 ダブルクォーテーションの削除を決意

昔からこのいまいましい宇宙服をつけたままなんで、背中がむれちまって妙にかゆい。
いらいらするんで、腹いせにあの"Hello, world!"のダブルクォーテーションを抜きにしてやった。
いい気味だ。

## ○月○日 UFCSとopDispatchの組み合わせ

あまりに背中がかゆいんで医務室にいったら、背中にでっけえopDispatchを貼られた。
それから、もう俺は宇宙服を着なくていいと医者がいった。
おかげで今夜はよく眠れそうだぜ。

## ○月◎日 セミコロンを使ったら負けかと思っている

朝起きたら、opDispatchだけでなくRangeにもUCFSができてやがった。
引用符の代わりにセミコロンがやけに煩いんで、足引きずって見に行ったら数が増えてる。
引用符を二個抜いたくらいで増えやがって。
おえら方に見つかったら大変だ。

## ○月◇日 特徴のある部分を使ってカンマ挿入位置を探す

昨日、このロジックから逃げ出そうとしたカンまが一つ、射さつされた、て　はなしだ。
夜、からだ中　あついかゆい。
胸のはれ物　かきむしたら　肉がくさり落ちやがた。
いったいおれ　どうな　て

## ○月♡日 変数が必要ならラムダ式を使う

とくちょう　みつ　けた　も　とてもかゆい
変数　ひつようの、らむだ　のしき　つかう

## ○月⊿日 最後のエクスクラメーションはテンプレートを使って締めくくる

かゆい　かゆい　エクスクラメーションーきた
ひどいてんぷれなんて　ころし
うまかっ　です。
4

D言語
ｳﾏｰ


# 解説(日本語OFFでお届けいたします)

```d
import std.algorithm: equal;
unittest
{
	// UCFS, equal
	static assert("Hel".equal("Hel") == equal("Hel", "Hel"));
	
	// lambda syntax
	static assert( (a => a+1)(1) == 2 );
	static assert( (a => a+1)(1) == (delegate int(int a){return a+1;})(1) );
	static assert( (a => a+1)(1) == ((int a){return a+1;})(1) );
	static assert( (a => a+1)(1) == ((a){return a+1;})(1) );
}
```

```d
import std.algorithm;
import std.algorithm: countUntil;
import std.range: retro, cycle, take, array, chain;
unittest
{
	// range compare
	static assert( __traits(compiles, "leH" == "leH"));
	static assert(!__traits(compiles, "Hel".retro() == "leH"));
	static assert( __traits(compiles, "Hel".retro().equal("leH")));
	static assert("Hel".retro().equal("leH"));
	
	// retro
	static assert("Hel".retro().equal("leH"));
	
	// take
	static assert("HelHelHelHelHelHelHel".take(7).equal("HelHelH"));
	
	// cycle + take
	static assert("Hel".cycle().take(7).equal("HelHelH"));
	
	// chain
	static assert("Hel".chain("l").equal("Hel" ~ "l"));
		
	// countUntil
	static assert("Hellowo".countUntil("ow") == "Hell".length);
	
	// /*take + retro*/ take + array + retro
	static assert(!__traits(compiles, "Hel".take(2).retro().equal("eH")));
	static assert("Hel".take(2).array().retro().equal("eH"));
}
```

```d
import std.array: insertInPlace;
unittest
{
	/* insertInPlace ... (´・ω・｀) */
	string msg = "owo";
	msg.insertInPlace(1, ", ");
	assert(msg == "o, wo");
}
```

```d
import std.array;
unittest
{
	/* lambda + insertInPlace ... (｀・ω・´) */
	static assert((a=>(a.insertInPlace(1, ", "), a))("owo".dup) == "o, wo");
}
```

```d
import std.conv: text, toLower;
unittest
{
	// text
	static assert(text("a", 1) == "a1");
	
	// toLower
	static assert("AbCdE".toLower() == "abcde");
}
```

```d
unittest
{
	struct H
	{
		// 型名 H に . 以降の文字列を連結
		static string opDispatch(string s)() @property
		{
			return typeof(this).stringof~s;
		}
	}
	static assert(H.e == "He");
}
```

```d
unittest
{
	struct D {string msg;}
	struct H
	{
		// 型名 H に . 以降の文字列を連結したのを D 型にする
		static D opDispatch(string s)() @property
		{
			return D(typeof(this).stringof~s);
		}
	}
	static assert(H.e == D("He"));
}
```

```d
unittest
{
	struct D
	{
		string msg;
		// . 以降の文字列を連結
		D opDispatch(string s)() @property
		{
			return typeof(return)(text(msg, s));
		}
	}
	struct H
	{
		static D opDispatch(string s)() @property { return D(typeof(this).stringof~s); }
	}
	static assert(H.e.l == D("He").l);
	static assert(D("He").l == D("Hel"));
	static assert(H.e.l.l.o.w.o.r.l.d == D("Helloworld"));
}
```

```d
import std.algorithm, std.range;
unittest
{
	/+
	(a, b)=>a.equal(b))(
	        msg.retro().cycle().take(2),
	        msg.chain(s).retro().take(2).array().retro())
	+/
	// msgの最後2文字の逆順を得る
	auto left(string msg, string s)
	{
		return msg.retro().cycle().take(2);
	}
	static assert(equal(left("He","l"),        "eH"));
	static assert(equal(left("Hel","l"),       "le"));
	static assert(equal(left("Hell","o"),      "ll"));
	static assert(equal(left("Hello","w"),     "ol"));
	static assert(equal(left("Hellow","o"),    "wo"));
	static assert(equal(left("Hellowo","r"),   "ow"));
	static assert(equal(left("Hellowor","l"),  "ro"));
	static assert(equal(left("Helloworl","d"), "lr"));
	
	// msgとsを連結したものの最後2文字を得る
	auto right(string msg, string s)
	{
		return msg.chain(s).retro().take(2).array().retro();
	}
	static assert(equal(right("He","l"),        "el"));
	static assert(equal(right("Hel","l"),       "ll"));
	static assert(equal(right("Hell","o"),      "lo"));
	static assert(equal(right("Hello","w"),     "ow"));
	static assert(equal(right("Hellow","o"),    "wo"));
	static assert(equal(right("Hellowo","r"),   "or"));
	static assert(equal(right("Hellowor","l"),  "rl"));
	static assert(equal(right("Helloworl","d"), "ld"));
	
	// 要するに msgとsを連結した最後の3文字が回文になっているかを判定
	bool test(string msg, string s)
	{
		return equal(left(msg,s), right(msg,s));
	}
	static assert(!test("He","l"));
	static assert(!test("Hel","l"));
	static assert(!test("Hell","o"));
	static assert(!test("Hello","w"));
	static assert( test("Hellow","o")); // <<=========
	static assert(!test("Hellowo","r"));
	static assert(!test("Hellowor","l"));
	static assert(!test("Helloworl","d"));
}
```

```d
import std.array, std.algorithm, std.range;
unittest
{
	/+
	((a, b)=>(
	  a.insertInPlace(msg.countUntil(b)+1, ", "), a.idup)
	)(msg.dup, msg.retro().take(2).array().retro())~s
	+/
	
	// msgのコピー(編集可能)を返す
	auto left(string msg, string s)
	{
		return msg.dup;
	}
	static assert(left("Hellow", "o") == "Hellow");
	
	// msgの最後2つを得る
	auto right(string msg, string s)
	{
		return msg.retro().take(2).array().retro();
	}
	static assert(right("Hellow", "o").equal("ow"));
	
	// aの中からbを見つけてその箇所に", "を挿入
	string test(R)(char[] a, R b)
	{
		a.insertInPlace(a.countUntil(b)+1, ", ");
		return a.idup;
	}
	static assert(test(left("Hellow", "o"), right("Hellow", "o")) == "Hello, w");
}
```

```d
import std.string, std.range, std.algorithm, std.conv, std.array;
unittest
{
	struct D
	{
		string msg;
		// まとめると
		D opDispatch(string s)() @property
		{
			return typeof(return)(
			    ((a, b)=>a.equal(b))(
			        msg.retro().cycle().take(2),
			        msg.chain(s).retro().take(2).array().retro())
			        ? ((a, b)=>(
			            a.insertInPlace(msg.countUntil(b)+1, ", "), a.idup)
			          )(msg.dup, msg.retro().take(2).array().retro())~s
			        : text(msg,s));
		}
	}
	struct H
	{
		static D opDispatch(string s)() @property { return D(typeof(this).stringof~s); }
	}
	
	static assert(H.e.l.l.o. w.o.r.l.d == D("Hello, world"));
}
```

```d
import std.string, std.range, std.algorithm, std.conv, std.array;
unittest
{
	struct D
	{
		string msg;
		// 最後のdだけ関数を別途用意して "d! D" を連結させる
		string d(T)() @property
		{
			return text(msg, T.stringof.toLower(), "! ", T.stringof);
		}
	}
	
	static assert(D("Hello, worl").d!D == "Hello, world! D");
}
```

# まとめ

組み合わせると

```d
import std.string, std.range, std.algorithm, std.conv, std.array;

struct D
{
    string msg;
    D opDispatch(string s)() @property
    {
        return typeof(return)(
            ((a, b)=>a.equal(b))(
                msg.retro().cycle().take(2),
                msg.chain(s).retro().take(2).array().retro())
                ? ((a, b)=>(
                    a.insertInPlace(msg.countUntil(b)+1, ", "), a.idup)
                  )(msg.dup, msg.retro().take(2).array().retro())~s
                : text(msg,s));
    }
    string d(T)() @property
    {
        return text(msg, T.stringof.toLower(), "! ", T.stringof);
    }
}
struct H
{
    static D opDispatch(string s)() @property { return D(typeof(this).stringof~s); }
}

pragma(msg, H.e.l.l.o. w.o.r.l.d! D);

void main() {}
```

つまり

ここまで書いた処理

＿人人人人人人人人人人人人人人人人人人人＿
＞　全部コンパイル時に評価可能！！！！　＜
￣^Y^Y^Y^Y^Y^Y^Y^Y^Y^Y^Y^Y^Y^Y^Y^Y^Y^￣

お後がよろしいようで


お次は4日目 [@__DaLong](http://qiita.com/users/__DaLong) です。