for in do の活用 (Delphi 2005 以降)
Delphi 2005 以降では、他言語で言う所の foreach に相当する for in do が使えます。for in do が使えるようになって結構経ちますし、Unicode 版 Delphi ではもれなく使えます。for in do 対応のクラスも増えてきましたので、積極的に使ってみましょう。
for in do の構文
for in do の構文は以下のようになっています。
for Element in ArrayExpr do Stmt;
for Element in StringExpr do Stmt;
for Element in SetExpr do Stmt;
for Element in CollectionExpr do Stmt;
for Element in Record do Stmt;
コンテナには
配列式
文字列式
設定 (Set) 式
クラスまたはインターフェイス
が利用可能です。また、RTL の以下のクラスと下位クラスでは for in do 構文が利用可能です。
ActnList.TCustomActionList
Classes.TCollection
Classes.TComponent
Classes.TInterfaceList
Classes.TList
Classes.TStrings
ComCtrls.TListItems
ComCtrls.TToolBar
ComCtrls.TTreeNodes
DB.TFields
Menus.TMenuItem
詳しくは Docwiki の "4.5.4 for 文を使用するコンテナの繰り返し" を参照してください。
配列を回す
簡単な所で、配列を回してみます。
var
StrArr: array of string ;
i: Integer;
begin
for i:=Low(StrArr) to High(StrArr) do
Memo1.Lines.Add(StrArr[i]);
end ;
文字列の動的配列は for to do を使ってこのように回していました。for in do なら?
var
StrArr: array of string ;
s: string ;
begin
for s in StrArr do
Memo1.Lines.Add(s);
end ;
このようになります。数値配列なら
var
IntArr: array of Integer;
i: Integer;
begin
for i in IntArr do
Memo1.Lines.Add(IntToStr(i));
end ;
こんな感じです。
文字を回す
今度は文字を回してみます。
var
s: string ;
i: Integer;
begin
s := 'ABCDEFG' ;
for i:=1 to Length(s) do
Memo1.Lines.Add(s[i]);
end ;
for in do なら?
var
s: string ;
c: char;
begin
s := 'ABCDEFG' ;
for c in s do
Memo1.Lines.Add(c);
end ;
このようになります。
Set 式を回す
同様に Set 式を回してみます。
var
i: Integer;
begin
for i in [0 ..5 , 7 , 9 ] do
Memo1.Lines.Add(IntToStr(i));
end ;
...という事は?
var
fs: TFontStyle;
begin
for fs in Font.Style do
if fs = fsBold then
Memo1.Lines.Add('Include Bold' );
end ;
イロイロ回せますね。但し、
var
i: Integer;
begin
for i in [100 , 200 , 100 ] do
Memo1.Lines.Add(IntToStr(i));
end ;
これの結果が Delphi XE 7 以降だと "100 200 100" 、それよりも前の Delphi だと "100 200" となる事に注意が必要です。Delphi XE7 以降だと [100, 200, 100] は配列定数式 、それよりも前の Delphi だと集合 として扱われます。Delphi XE7 以降で [100, 200, 100] を集合として扱うようにするには ( ) で括ります。
var
i: Integer;
begin
for i in ([100 , 200 , 100 ]) do
Memo1.Lines.Add(IntToStr(i));
end ;
ジェネリクス配列を使って文字列を回す (Delphi 2010 以降)
ちょっとキモいコードですが、
var
s: string ;
begin
for s in TArray<string >.Create('AQUACURE' ,'SAFSPRIN' , 'ADRAVIL' ) do
Memo1.Lines.Add(s);
end ;
知っておくと便利です (元ネタ: for-in-doにもっと頑張ってもらう。(全力わはー) )。
SplitString を使って文字列を回す (Delphi XE 以降)
ジェネリクス(文字)配列を回す方法と同じ用途で使うのであれば、SplitString() で代用できます。
var
s: string ;
begin
for s in SplitString('AQUACURE,SAFSPRIN,ADRAVIL' , ',' ) do
Memo1.Lines.Add(s);
end ;
なお、SplitString() は XE から追加されていますが、元ネタになった SplitString() は XMLDoc.pas の中にローカル関数として Delphi 7 から存在します。
正規表現のマッチ結果を回す (Delphi XE 以降)
Delphi XE には正規表現クラスとして、TRegEx が実装されています。この TRegEx のマッチ結果を回してみましょう。
uses
..., RegularExpressions;
var
Match: TMatch;
begin
for Match in TRegEx.Matches('ABCABCABC' , 'C' ) do
Memo1.Lines.Add(Format('%s (%d, %d)' , [Match.Value, Match.Index, Match.Length]));
end ;
短いですねー。
正規表現のマッチ結果を回す (Delphi 2005 以降)
SkRegExp は Delphi 2005 以降で使える正規表現クラスです。SkRegularExpressions.pas を併用する事により Delphi 2005 等でもマッチ結果を for in do で回す事ができます。
uses
..., SkRegularExpressions;
var
Match: TMatch;
begin
for Match in TRegEx.Matches('ABCABCABC' , 'C' ) do
Memo1.Lines.Add(Format('%s (%d, %d)' , [Match.Value, Match.Index, Match.Length]));
end ;
純正の TRegEx を簡単にリプレスできますし、その逆も簡単です。
ファイル検索結果を回す (Delphi 2010 以降)
特定のフォルダにあるファイルを再帰で取得して列挙するには FindFirst() / FindNext() / FindClose() を使うのが一般的です。
procedure TForm1.EnumFiles(const aDir, aFileMask: String );
procedure _EnumFiles(const aDir, aFileMask: String );
var
sr: TSearchRec;
begin
if FindFirst(aDir + '*.*' , faAnyFile, sr) = 0 then
begin
repeat
if (sr.Attr and faDirectory) = faDirectory then
begin
if StringReplace(sr.Name, '.' , '' ,[rfReplaceAll]) <> '' then
_EnumFiles(aDir + sr.Name + PathDelim, aFileMask);
end
else
begin
if Masks.MatchesMask(sr.Name, aFileMask) then
Memo1.Lines.Add(aDir + sr.Name);
end ;
until FindNext(sr) <> 0 ;
FindClose(sr);
end ;
end ;
begin
_EnumFiles(IncludeTrailingPathDelimiter(aDir), aFileMask);
end ;
procedure TForm1.Button1Click(Sender: TObject);
begin
EnumFiles('C:\TEST' , '*.pas' );
end ;
こんな感じになるかと思います。これを、Delphi 2010 から追加された TDirectory でやってみましょう。
uses
..., IOUtils;
procedure TForm1.Button1Click(Sender: TObject);
var
FileName: String ;
begin
for FileName in TDirectory.GetFiles('C:\TEST' , '*.pas' , TSearchOption.soAllDirectories) do
Memo1.Lines.Add(FileName);
end ;
めちゃめちゃスッキリ書けます。
for in do に対応したクラスを作るには?
詳細は Docwiki の "4.5.4 for 文を使用するコンテナの繰り返し" を参照してください。
for in do は動的配列にも使えるのですから、小規模な列挙であれば戻り値として動的配列を返す関数やクラスメソッドを実装すればいい事になります。例として文字列の動的配列である TStringDynArray (array of string ) を返す関数を作ってみます。
uses
..., Types;
function TForm1.EnumTest: TStringDynArray;
begin
SetLength(result, 3 );
result[0 ] := 'ABC' ;
result[1 ] := 'DEF' ;
result[2 ] := 'GHI' ;
end ;
procedure TForm1.Button1Click(Sender: TObject);
var
s: string ;
begin
for s in EnumTest do
Memo1.Lines.Add(s);
end ;
大規模な...または大規模になる可能性のある集合を列挙する場合にはちゃんと GetEnumerator() を実装して下さい。