#ヤター!気持ち悪いのできたよー!
これは Delphi Advent Calendar 1日目の記事です。
先日、FMX のソースを見ていると面白いコードがあることに気がつきました。
↓こんなコードです。
function SwitchCase(const A: Integer): Integer;
label
Case0, Case1, CaseElse;
begin
Result := 0;
case A of
0: goto Case0;
1: goto Case1;
else goto CaseElse;
end;
Case0:
Inc(Result);
Case1:
Inc(Result);
CaseElse:
Inc(Result);
end;
label を利用して C-Style な switch-case 文を実現しています。
case 文からわざわざ label に飛ばして飛んだ所から下に向かって実行していくというものです。
また、先日、こんなコードも書きました。
三項演算子が (ちょっと) 羨ましい Delphi ユーザーの集い
IfExp - ternary operator like C. Exapmple: IfExp<String>(1 > 0) - 'true' or 'false'; // return 'true' #delphi https://t.co/kSCp2iIBBr
— HOSOKAWA Jun (@pik) 2015, 7月 23
演算子オーバーロードを使って、擬似的に C の3項演算子を実現しています。
また、Quality Central に case 文に文字列を使えるようにして!という要望を出していました。
(QC に繋がらないのでリンクは割愛)
さて、ここで、ひらめきました!
演算子オーバーロードを使ったら、型に縛られない case 文作れるんじゃね!?
そして出来上がったのがコチラになります。
使い方は非常に簡単です。
Swtich<型>(セレクタ)
or 値 - (procedure begin 実行したい内容 end)
or 値 - (procedure begin 実行したい内容 end)
:
:
と書けば通常の case 文同様、セレクタと値が一致した時に無名関数が実行されます。
たとえば、文字列の場合
SwitchStr('hello')
or 'foo' - (procedure
begin
Writeln('Foo !');
end)
or 'hello' - (procedure
begin
Writeln('hello, world!'); // これが出力されます
end)
or 'bar' - (procedure
begin
Writeln('Bar !');
end)
更に、比較関数を独自に定義できます。
SwitchStr('bar')
.SetComparer( // 比較関数を定義します。ここでは全て大文字に変換して比較するようにします
function(const L, R: String): Boolean
begin
Result := L.ToUpper = R.ToUpper
end
)
or 'FOO' - (procedure
begin
Writeln('FOO');
end)
or 'BAR' - (procedure // 大文字ですが、定義した比較関数によってここがマッチします。
begin
Writeln('BAR');
end)
or 'BUZ' - (procedure
begin
Writeln('BAZ');
end);
さらに、case 文同様、複数の値を書けたり、変数や関数も使用できます。
SwitchStr('hello')
// 複数の値を列挙できます
or '012'
or '34567'
or '89' - (procedure
begin
Writeln('Numbers');
end)
// 変数や関数も使用できます。
or (function: String begin Result := 'Bar' end)()
or sLineBreak - (procedure
begin
Writeln('Variable & Function');
end);
さらに! C言語風の switch-case 文も実現できます!
Switch<Single>(1.0).CStyle // CStyle メソッドを呼び出すと C言語の switch-case のような動作になります
or 1.0 - (procedure
begin
Writeln('1.0'); // 実行されます
end)
or 1.1 - (procedure
begin
Writeln('1.1'); // 実行されます
Switch<Single>.Break; // C言語同様 Break 文を呼び出すと抜けます
end)
or 1.2 - (procedure
begin
Writeln('1.2');
end);
と、まあこんなのを作って悦に入っていたのですが…
Delフサさんが、ほぼ同じ物を4年も前に作成されておりまして…
でも、いまさら、内容を変更できないので、そのまま記事にした次第であります…
oh.../(^o^)\
プログラム的には、Operator を使って一個の式にしているだけです。
or や - の戻り値を自分自身の型にすることで、or - or - と何個も繋げられるようにしています。戻り値が同じなので or or or という接続もできます。
変数は全てスタティック変数なので、メモリのオーバーヘッドはほとんど無いはずです。
また、Compare メソッドの中で使っている GetTypeKind や SizeOf を使ってコンパイル時解決する部分は、Lynaたんさんの全力わはーの記事が参考になりました!
operator を使うと気持ち悪いのが作れて楽しいですね!!
なお、今回のコードは結構本気で使えるんじゃないかと思っているのですが、いかがでしょう?