Delphi 用に FizzBuzz コレクションを作る


はじめに

これは FizzBuzz Advent Calendar 2017 とも Delphi Advent Calendar 2017 とも全く関係のない記事です (w

Delphi って begin~end があるのでコードを極端に短く書くのは苦手です。コードゴルフとかはその辺りを考慮しないので特定の言語が常に上位に来る傾向があります。FizzBuzz も例にもれず、Delphi で普通に書いたら長くなります...読みやすいですけどね。

それと、とあるメソッドや文法が標準で実装されているかで短く書けますよね。例えば最小公倍数 (LCM)三項演算子が実装されていると短く書けます。

それだったら...

コード

FizzBuzz を返すコレクションを考えてみます。コードはこんな感じで書けるといいですね。

procedure TForm1.Button1Click(Sender: TObject);
var
  s: string;
begin
  Memo1.Clear;
  for s in FizzBuzz(100) do
    Memo1.Lines.Append(s);
end;

FizzBuzz がリストアップされるコレクションを使えばコードが短くて済みます。フォームはこんな感じでボタンとメモだけが貼ってあります。

実装はこうなりました。Delphi 2005 以降で動作すると思います。恐らく FPC でも動作するでしょう。

uFizzBuzzCollection.pas
unit uFizzBuzzCollection;

{$IFDEF FPC}
{$modeswitch ADVANCEDRECORDS}
{$ENDIF}

interface

uses
  SysUtils, Types;

type
  TFizzBuzzEnumerator = class;

  TFizzBuzz  = record
  private
    Ffrom: Integer;
    Fthrough: Integer;
  public
    constructor Create(from: Integer; through: Integer);
    function GetEnumerator: TFizzBuzzEnumerator;
  end;

  TFizzBuzzEnumerator = class
    Container: TFizzBuzz;
    Index: Integer;
  public
    constructor Create(AContainer : TFizzBuzz);
    function GetCurrent: string;
    function MoveNext: Boolean;
    property Current: string read GetCurrent;
  end;

  function FizzBuzz(from: Integer; through: Integer): TFizzBuzz; overload
  function FizzBuzz(Count: Integer): TFizzBuzz; overload;

implementation

function FizzBuzz(from, through: Integer): TFizzBuzz;
begin
  Result := TFizzBuzz.Create(from, through);
end;

function FizzBuzz(Count: Integer): TFizzBuzz;
begin
  Result := TFizzBuzz.Create(1, Count);
end;

{ TFizzBuzz }

constructor TFizzBuzz.Create(from, through: Integer);
begin
  Ffrom := from;
  Fthrough := through;
end;

function TFizzBuzz.GetEnumerator: TFizzBuzzEnumerator;
begin
  Result := TFizzBuzzEnumerator.Create(Self);
end;

{ TFizzBuzzEnumerator }

constructor TFizzBuzzEnumerator.Create(AContainer: TFizzBuzz);
begin
  inherited Create;
  Container := AContainer;
  Index := -1;
end;

function TFizzBuzzEnumerator.GetCurrent: string;
var
  v: Integer;
begin
  v := Container.Ffrom + Index;
  if ((v mod 3) +  (v mod 5)) = 0 then
    Result := 'Fizz Buzz'
  else if (v mod 3) = 0 then
    Result := 'Fizz'
  else if (v mod 5) = 0 then
    Result := 'Buzz'
  else
    Result := IntToStr(v);
end;

function TFizzBuzzEnumerator.MoveNext: Boolean;
begin
  result := False;
  if ((Container.Ffrom + Index + 1) > Container.Fthrough) then
    Exit;
  result := True;
  Inc(Index);
end;

end.

ちゃんと動作しますね!

おわりに

これで Delphi でも FizzBuzz を短く書けるようになりました...まぁ、Delphi はその場で変数の宣言ができないので、他の言語でFizzBuzz コレクションを実装されたらあまり意味はないんですけどね (w

追記:
Delphi 10.3 Rio にて変数のインライン宣言が可能になりました!

procedure TForm1.Button1Click(Sender: TObject);
begin
  Memo1.Clear;
  for var s in FizzBuzz(100) do
    Memo1.Lines.Append(s);
end;

型推論もあるので多少は短く書けるようになりました!

See Also: