Delphiは、for-in スタイルの反復をサポートしています。
自作のクラスが for-in ループをサポートするには次の条件を満たす必要があります。
- クラスまたはインターフェイスは、GetEnumerator() というパブリック インスタンス メソッドを含んでいる必要があります。 GetEnumerator() メソッドは、クラス、インターフェイス、またはレコード型を返す必要があります。
- GetEnumerator() が返すクラス、インターフェイス、レコードには、MoveNext() というパブリック インスタンス メソッドが含まれている必要があります。 MoveNext() メソッドは、Boolean を返す必要があります。
- GetEnumerator() が返すクラス、インターフェイス、レコードには Current というパブリック インスタンス読み取り専用プロパティが含まれている必要があります。 Current プロパティの型は、コレクションに含まれる型である必要があります。
試しに、Fizz-Buzz問題を解くクラスを作成してみました。
1から100までの数をプリントするプログラムを書け。ただし3の倍数のときは数の代わりに「Fizz」と、5の倍数のときは「Buzz」とプリントし、3と5両方の倍数の場合には「FizzBuzz」とプリントすること。
「Fizz-Buzz問題」より
TFizzBuzzクラスはfor-inループをサポートし、Fizz-Buzz問題の解答を返します。
var
FizzBuzz: TFizzBuzz;
S: string;
begin
FizzBuzz := TFizzBuzz.Create;
for s in FizzBuzz do
Writeln(s);
実行結果
1
2
Fizz
4
Buzz
Fizz
7
…
for-inループをサポートするためTFizzBuzzクラスはGetEnumeratorメソッドを持ちます。
GetEnumeratorメソッドはTFizzBuzzEnumeratorオブジェクトを返します。
type
TFizzBuzz = class
public
function GetEnumerator: TFizzBuzzEnumerator;
end;
function TFizzBuzz.GetEnumerator: TFizzBuzzEnumerator;
begin
Result := TFizzBuzzEnumerator.Create;
end;
TFizzBuzzEnumeratorクラスは、for-inループをサポートするためMoveNext()メソッドとCurrentプロパティを持ちます。
type
TFizzBuzzEnumerator = class
private
FIndex: Integer;
function GetCurrent: string;
public
function MoveNext: Boolean;
property Current: string read GetCurrent;
end;
MoveNext()メソッドはループの回数が100回に達したかを判定します。
function TFizzBuzzEnumerator.MoveNext: Boolean;
begin
Result := FIndex < 100;
if Result then
Inc(FIndex);
end;
GetCurrentメソッドは、ループの回数が3の倍数のときは「Fizz」、5の倍数のときは「Buzz」、3と5両方の倍数の場合には「FizzBuzz」、それ以外の時はループの回数を返します。
function TFizzBuzzEnumerator.GetCurrent: string;
begin
if FIndex mod 15 = 0 then
Result := 'FizzBuzz'
else if FIndex mod 3 = 0 then
Result := 'Fizz'
else if FIndex mod 5 = 0 then
Result := 'Buzz'
else
Result := IntToStr(FIndex);
end;
以上で完成しました。
classではなくrecordを使った方がFreeの必要がなくなるので、よりよかったかもしれません。