Help us understand the problem. What is going on with this article?

Delphi で前前前世

はじめに

面白そうなお題を目にしました。
image.png

これを Delphi でやってみます。使用する Delphi10.4 Sydney です。

コード

ルールを確認します。

ルール

  • then().then().then().世()のようなメソッドチェーンで "前前前世" と末尾に改行付きで出力すればOKです。
  • PromiseFuture等の言語機能があればぜひ活用してください。

■ 基本的な考え方

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)

コンソールアプリケーションとして作ってみるとこんな感じになります。

RADWIMPS.dpr
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 }

実行してみると...エラーになりますよね?
image.png
program で プログラム名が RADWIMPS になっているのに、メインブロック中で識別子として RADWIMPS (TRADWIMPS 型) を使っているからです。これを回避するには、

RADWIMPS.dpr
{$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 ヘッダを削ります。プログラムヘッダなんて飾りです!
image.png
今度は大丈夫でした。

・補足

標準 Pascal ではプログラム名は識別子ではないため、

program Sin(Output);
begin
  Writeln(Sin(0));
end.

このようなコードも通ります (Sin は Pascal の標準関数です)。
image.png
Delphi ではプログラムヘッダのパラメータは無視されますが、プログラム名は有効な識別子として扱われます。但し、先述のようにプログラムヘッダそのものを省略する事が可能です。

See also:

■ コード例 (Free Pascal)

クラスでもいいのですが、レコード型の方がお手軽です。

レコード型の代わりに オブジェクト型 を使う事もできます。今回のコードだと recordobject に置換するだけです。

Delphi における オブジェクト型 の使用は非推奨となっていますが 2Free 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 での高度なレコード型はオプション扱いなので、高度なレコード型を使いたい場合にはコンパイラ指令 $modeswitchadvancedrecords を指定します。

どうせコンパイラ指令を追加しなくてはならないのなら、{$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 もインストールされているため、コードをスクリプトとして実行させる事が可能です。
image.png
コードの先頭に #!/usr/bin/env instantfpc を追加し、実行権限を与えるだけなのでお手軽です。

・補足

image.png
Free Pascal のコードは ideone.com で試す事ができます。Pascal は 2 種類あるので、試す際には FPC の方を選んでください。

See also:

■ コード例 (MPW Pascal)

Object Pascal の始祖である MPW Pascal (Apple Object Pascal) でもメソッドチェーンは可能です。

・一般的なコード

一般的なコードはこうなります。

RADWIMPS.p
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 を使うので usesObjIntf を追加。
  • 日本語が使えません (使い方がわかりません)。
  • Result 変数が使えないので、メソッド名に結果を返しています。
  • Then は予約語ですが、& による重複回避ができないので _Then() にしてお茶を濁しています。
  • 関数の結果を読み捨てられないので、メソッド Se() は手続き (procedure) で実装しています。

image.png

・TObject から派生しないコード

クラスを TObject から派生しなければちょっと短く書けます。

RADWIMPS.p
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:

おわりに

Delphi でのメソッドチェーンといえば、大作があるのです。Jim McKeeth 氏作の『Code Monkey』です。
image.png
まずはこれを観て!

ソースコードは Jim 氏のサイトからダウンロードできます。

メソッドチェーンできる高度なレコード型を使って、コード補完でデタラメに入力しているのではなく、ちゃんとしたプログラムになっており、このコードは実際に実行可能です。
image.png
実行すると歌詞が流れます。


  1. & による識別子の重複回避は Delphi 2005 から使えます。 

  2. Delphi では比較的新しいバージョンでないとオブジェクト型によるメソッドチェーンはできません。具体的には Delphi XE5 以降でないと正しく動作しません。非推奨と言いながら不具合修正されているのは面白いですね。なお、クラスによるメソッドチェーンは Delphi 1 から可能です。 

ht_deko
とある熊本の障害復旧(トラブルシューター)
https://ht-deko.com/
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした