はじめに
これを Delphi でやってみます。使用する Delphi は 10.4 Sydney です。
コード
ルールを確認します。
ルール
-
then().then().then().世()
のようなメソッドチェーンで "前前前世" と末尾に改行付きで出力すればOKです。 -
Promise
やFuture
等の言語機能があればぜひ活用してください。
■ 基本的な考え方
Delphi 2006 から導入された高度なレコード型 (Advanced Record) を使えばメソッドチェーン (Method Chaining) を実現できます。クラスでもいいのですが、レコード型の方がお手軽です。
type
TRADWIMPS = record
function &Then: TRADWIMPS;
function 世: TRADWIMPS;
end;
function TRADWIMPS.&Then: TRADWIMPS;
begin
Write('前');
Result := Self;
end;
function TRADWIMPS.世: TRADWIMPS;
begin
Writeln('世');
Result := Self;
end;
Then()
に &
が付いているのは Pascal では then が予約語だからです。Delphi では予約語とカブる識別子の先頭に &
を付けることで名前重複の問題を回避できます 1。この機能は他言語のライブラリなどを移植する時に重宝します。
また、Delphi 2009 以降だと Unicode に対応しているので、世()
という名前のメソッドが通ります。
See also:
■ コード例 (Delphi)
コンソールアプリケーションとして作ってみるとこんな感じになります。
program RADWIMPS;
{$APPTYPE CONSOLE}
type
TRADWIMPS = record
function &Then: TRADWIMPS;
function 世: TRADWIMPS;
end;
function TRADWIMPS.&Then: TRADWIMPS;
begin
Write('前');
Result := Self;
end;
function TRADWIMPS.世: TRADWIMPS;
begin
Writeln('世');
Result := Self;
end;
begin
var RADWIMPS: TRADWIMPS;
RADWIMPS.&Then.&Then.&Then.世;
end. { MAIN }
実行してみると...エラーになりますよね?
program で プログラム名が RADWIMPS
になっているのに、メインブロック中で識別子として RADWIMPS (TRADWIMPS 型) を使っているからです。これを回避するには、
{$APPTYPE CONSOLE}
type
TRADWIMPS = record
function &Then: TRADWIMPS;
function 世: TRADWIMPS;
end;
function TRADWIMPS.&Then: TRADWIMPS;
begin
Write('前');
Result := Self;
end;
function TRADWIMPS.世: TRADWIMPS;
begin
Writeln('世');
Result := Self;
end;
begin
var RADWIMPS: TRADWIMPS;
RADWIMPS.&Then.&Then.&Then.世;
end. { MAIN }
先頭の PROGRAM ヘッダを削ります。プログラムヘッダなんて飾りです!
今度は大丈夫でした。
・補足
標準 Pascal ではプログラム名は識別子ではないため、
program Sin(Output);
begin
Writeln(Sin(0));
end.
このようなコードも通ります (Sin は Pascal の標準関数です)。
Delphi ではプログラムヘッダのパラメータは無視されますが、プログラム名は有効な識別子として扱われます。但し、先述のようにプログラムヘッダそのものを省略する事が可能です。
See also:
■ コード例 (Free Pascal)
クラスでもいいのですが、レコード型の方がお手軽です。
レコード型の代わりに オブジェクト型
を使う事もできます。今回のコードだと record を object に置換するだけです。
Delphi における オブジェクト型
の使用は非推奨となっていますが 2、Free Pascal (FPC) では別に使用制限されていなかったと思います。
・オブジェクト型を使う場合
FPC でオブジェクト型
を使ったコードは次のようになります。
type
TRADWIMPS = object
function &Then: TRADWIMPS;
function Se: TRADWIMPS;
end;
function TRADWIMPS.&Then: TRADWIMPS;
begin
Write('前');
&Then := Self;
end;
function TRADWIMPS.Se: TRADWIMPS;
begin
Writeln('世');
Se := Self;
end;
var
RADWIMPS: TRADWIMPS;
begin
RADWIMPS.&Then.&Then.&Then.Se;
end. { MAIN }
- レコード型の代わりにオブジェクト型を使っている。
-
世()
は使えないのでSe()
に変更。 - Result 変数が使えないのでルーチン名に値を代入して結果を返している。
- インライン変数宣言ができないので var ブロックで変数宣言。
・高度なレコード型を使う場合
FPC での高度なレコード型はオプション扱いなので、高度なレコード型を使いたい場合にはコンパイラ指令 $modeswitch
に advancedrecords
を指定します。
どうせコンパイラ指令を追加しなくてはならないのなら、{$mode objfpc}
(と {$H+}
) を追加すると Object Pascal に対応するので、Result 変数が使えるようになって便利です。
{$mode objfpc}{$H+}{$modeswitch advancedrecords}
type
TRADWIMPS = record
function &Then: TRADWIMPS;
function Se: TRADWIMPS;
end;
function TRADWIMPS.&Then: TRADWIMPS;
begin
Write('前');
Result := Self;
end;
function TRADWIMPS.Se: TRADWIMPS;
begin
Writeln('世');
Result := Self;
end;
var
RADWIMPS: TRADWIMPS;
begin
RADWIMPS.&Then.&Then.&Then.Se;
end. { MAIN }
{$mode objfpc}{$H+}{$modeswitch advancedrecords}
の部分を {$mode delphi}
に変更し、 Delphi 互換モードで動作させる事も可能です。
・スクリプトとして実行
FPC をインストールすると、スクリプト実行環境である InstantFPC もインストールされているため、コードをスクリプトとして実行させる事が可能です。
コードの先頭に #!/usr/bin/env instantfpc
を追加し、実行権限を与えるだけなのでお手軽です。
・補足
Free Pascal のコードは ideone.com で試す事ができます。Pascal は 2 種類あるので、試す際には FPC
の方を選んでください。
See also:
- <6> Dephi のオブジェクト指向拡張 (Pascal へのオブジェクト指向拡張の歴史と Delphi) (Qiita)
- Lazarus (lazarus-ide.org)
- InstantFPC (Free Pascal Wiki)
- ideone.com
- Mode Delphi (Lazarus Wiki)
■ コード例 (MPW Pascal)
Object Pascal の始祖である MPW Pascal (Apple Object Pascal) でもメソッドチェーンは可能です。
・一般的なコード
一般的なコードはこうなります。
program RADWIMPS;
uses
ObjIntf;
type
TRADWIMPS = object(TObject)
function _Then: TRADWIMPS;
procedure Se;
end;
function TRADWIMPS._Then: TRADWIMPS;
begin
Write('Zen');
_Then := SELF;
end;
procedure TRADWIMPS.Se;
begin
WriteLn('Se');
end;
var
RADWIMPS: TRADWIMPS;
begin
New(RADWIMPS);
RADWIMPS._Then._Then._Then.Se;
RADWIMPS.Free;
end. { MAIN }
- PROGRAM ヘッダがないと怒られるので追加。但し、プログラム名は識別子とみなされません。
- クラスを使います。
- TObject を使うので uses に
ObjIntf
を追加。 - 日本語が使えません (使い方がわかりません)。
- Result 変数が使えないので、メソッド名に結果を返しています。
-
Then は予約語ですが、
&
による重複回避ができないので_Then()
にしてお茶を濁しています。 - 関数の結果を読み捨てられないので、メソッド
Se()
は手続き (procedure) で実装しています。
・TObject から派生しないコード
クラスを TObject から派生しなければちょっと短く書けます。
program RADWIMPS;
type
TRADWIMPS = object
function _Then: TRADWIMPS;
procedure Se;
end;
function TRADWIMPS._Then: TRADWIMPS;
begin
Write('Zen');
_Then := SELF;
end;
procedure TRADWIMPS.Se;
begin
WriteLn('Se');
end;
var
RADWIMPS: TRADWIMPS;
begin
New(RADWIMPS);
RADWIMPS._Then._Then._Then.Se;
Dispose(RADWIMPS);
end. { MAIN }
短く書けますが、Free()
メソッドは使えなくなるので、破棄処理は Dispose()
で行う必要があります。
・補足
FPC で試す場合には PROGRAM ヘッダと uses 句を削除した上で、{$MODE MacPas}
コンパイラ指令を先頭に付けてください。
See also:
- <3> Macintosh 用 Pascal のオブジェクト指向拡張 (Pascal へのオブジェクト指向拡張の歴史と Delphi) (Qiita)
- Object Pascal (Wikipedia)
- ラリー・テスラー (Wikipedia)
- Mode MacPas (Lazarus Wiki)
おわりに
Delphi でのメソッドチェーンといえば、大作があるのです。Jim McKeeth 氏作の**『Code Monkey』**です。
まずはこれを観て!
ソースコードは Jim 氏のサイトからダウンロードできます。
メソッドチェーンできる高度なレコード型を使って、コード補完でデタラメに入力しているのではなく、ちゃんとしたプログラムになっており、このコードは実際に実行可能です。
実行すると歌詞が流れます。