ループ

4種類のループ構文:for / while / repeat / for-in

まず下記のサンプルコードをご覧ください。前回講義のコードファイルmydemo.dprを修正し、拡張子をpasに変更しmydemo.pasとします。これはPascalソースファイルの標準拡張子です。Notepad++など構文ハイライト対応のコードエディタを導入すると、コードの可読性が大幅に向上します。

下記は全行に解説コメントを付記した完全実行サンプルです。コード掲載後、各構文を一つずつ順を追って解説します。

// 2.8 ループ構文:for、while、repeat、for..in
{$ifdef FPC} {$mode objfpc}{$H+}{$J-} {$endif}
// FPCコンパイラディレクティブ:オブジェクト指向モード有効、長文字列対応、文字列自動参照カウント無効
{$ifdef MSWINDOWS} {$apptype CONSOLE} {$endif}
// Windows環境設定:コンソール専用アプリとして定義(GUIウィンドウなし)
{$R+} // 配列境界チェックを有効、添字オーバーフローのデバッグに便利
var
  MyArray: array [0..9] of Integer; // 整数型配列、添字0~9、計10個の要素
  I: Integer; // ループカウンタ変数
begin
// 1. 昇順forループ:配列初期化、0~9の二乗値を格納
for I := 0 to 9 do
    MyArray[I] := I * I;

// 2. 基本forループで配列添字を走査し、二乗値を出力
for I := 0 to 9 do
    WriteLn('二乗値:', MyArray[I]);

// 3. Low/High関数で配列の上下限を自動取得、0や9を固定値で書かないため拡張性に優れる
for I := Low(MyArray) to High(MyArray) do
    WriteLn('二乗値:', MyArray[I]);

// 4. whileループ:先に条件判定を行い、条件成立時のみループ本体を実行
  I := 0;
while I < 10 do
begin
    WriteLn('二乗値:', MyArray[I]);
    I := I + 1; // 同じ動作の記法:I+=1 / Inc(I) いずれも等価
end;

// 5. repeat-untilループ:本体を1回実行した後、末尾で終了条件を確認
  I := 0;
repeat
    WriteLn('二乗値:', MyArray[I]);
    Inc(I); // カウンタ変数をインクリメント
until I = 10;

// 6. for-in走査:添字を操作せず配列の要素本体の値を直接取得
for I in MyArray do
    WriteLn('二乗値:', I);
end.Code language: PHP (php)

以下はコメントを全て削除した生コードです

{$ifdef FPC} {$mode objfpc}{$H+}{$J-} {$endif}
{$ifdef MSWINDOWS} {$apptype CONSOLE} {$endif}
{$R+}

var
  MyArray: array [0..9] of Integer;
  I: Integer;

begin

for I := 0 to 9 do
    MyArray[I] := I * I;

for I := 0 to 9 do
    WriteLn('二乗値:', MyArray[I]);

for I := Low(MyArray) to High(MyArray) do
    WriteLn('二乗値:', MyArray[I]);

  I := 0;
while I < 10 do
begin
    WriteLn('二乗値:', MyArray[I]);
    I := I + 1;
end;

  I := 0;
repeat
    WriteLn('二乗値:', MyArray[I]);
    Inc(I);
until I = 10;

for I in MyArray do
    WriteLn('二乗値:', I);

end.Code language: PHP (php)

これから各ループのコードロジックを段階的に解説します

forループ

forループを使用して配列に値を代入する

{$ifdef FPC} {$mode objfpc}{$H+}{$J-} {$endif}
{$ifdef MSWINDOWS} {$apptype CONSOLE} {$endif}
{$R+}

var
  MyArray: array [0..9] of Integer;
  I: Integer;

begin

for I := 0 to 9 do
    MyArray[I] := I * I;

end.Code language: PHP (php)

このコードは三つの区間に分かれています。上部にプリコンパイラ命令、中間のvarセクションで変数(配列1つ、整数1つ)を宣言、最後にbegin~end.で囲まれた実行ブロックにforループが記述されており、0から9までの値をIに順番に代入し、添字経由で配列要素に値を格納します。

MyArray[0]~MyArray[9]までの10個の配列要素に一括で値を設定します。*記号は乗算演算子です。

MyArray[0] := 0 * 0;

MyArray[1] := 1 * 1;

MyArray[9] := 9 * 9;

続いて最初のforループ下部に別のforループを追加し、配列内の各要素の値を読み出します。

コンパイル・実行後、各要素の値が出力されます

出力される数値はすべて0~9の二乗となり、先ほどの想定通りの結果になります。

配列の添字範囲が不明な場合は、組み込み関数を使って上下限を取得できます。

上記のLow(配列名)は配列の最小添字を取得する関数です

  • Low(型名/変数)順序数型・配列・集合最小値/下限を取得
  • High(型名/変数)順序数型・配列・集合最大値/上限を取得

whileループとrepeatループ

while内のbegin end;で囲まれたループ本体の末尾で変数Iに1が加算されます。加算完了後、実行フローはwhile I < 10 doの行に戻り再度条件判定を行います。条件が成立すれば2回目のループ本体を実行、Iが再度インクリメントされ再判定…これを繰り返し、I<10が偽となった時点でwhileループが終了します。つまりIが10になるとループを抜けます。

while と repeat-until の根本的な違い

  1. 条件判定ロジックが逆
  • while 条件 do:条件成立 → ループ継続、不成立 → ループ終了
  • repeat ... until 条件:条件成立 → ループ終了、不成立 → ループ継続
  1. 最低実行回数が異なる
  • while:最初の条件が満たされない場合、ループ本体が1回も実行されない
  • repeat-until:条件に関わらず、必ず1回はループ本体が実行される

C、C++、Javaなどの言語を学んだ方であれば理解しやすいです。repeat untilはdo whileとほぼ同じ動作をします。まずループ本体を1回実行し、until後の条件を判定、条件が偽なら再度ループします。

repeat untilにはbegin endが不要で、repeat~until自体が一つのコードブロックとして扱われます。手動でbegin endを追加しても動作しますが、記述する必要はありません。

repeat untilの字面通りの意味は「~を繰り返す、条件に至るまで」であり、単語の意味から動作が容易に想像できます。


走査処理ではMyArray内の値が順番にIに代入され、ループ内でIを直接操作可能です。添字のインクリメント処理を自分で記述する必要がなく、添字自体も使用しません。配列要素を全て走査し終わると処理が完了します。

通常forループ(for I := ... to/downto)解説

  1. C言語のforループに似ていますが制限が厳しく、連続した順序数(整数、列挙型)のみ走査可能;
  2. downtoキーワードによる逆順走査に対応:for I := 9 downto 0 do
  3. 上下限を取得するLow(変数) / High(変数)はループ開始時に1回だけ計算されるため実行効率が高い;
  4. ループが正常終了した後の変数Iの値は未定義となりコンパイラ警告が出力されます。Break / Exitで途中脱出した場合はIに現在の数値が保持されます。

for-in走査(最新のforeach形式)

添字を手動操作せず、4種類のデータ型を直接走査できます:

  1. 配列:配列内の各要素の値を直接取得(上記サンプルで実演済み)
  2. 列挙型
{$ifdef FPC} {$mode objfpc}{$H+}{$J-} {$endif}
{$ifdef MSWINDOWS} {$apptype CONSOLE} {$endif}
{$R+}

type TAnimalKind = (Duck,Cat,Dog);

var AK:TAnimalKind;
begin
  for AK in TAnimalKind do 
    WriteLn(AK);
end.Code language: PHP (php)
  1. 集合型
{$ifdef FPC} {$mode objfpc}{$H+}{$J-} {$endif}
{$ifdef MSWINDOWS} {$apptype CONSOLE} {$endif}

type
  // 列挙型定義
  TAnimalKind = (Duck, Cat, Dog);
  // 列挙型を元に集合型を定義
  TAnimals = set of TAnimalKind;

var
  Animals: TAnimals;
  AK: TAnimalKind;
begin
  // 集合にDuck、Catの2つの列挙値を代入
  Animals := [Duck, Cat];

  // for-inで集合内に存在する全メンバーを走査
  for AK in Animals do
  begin
    WriteLn('発見した動物:', AK);
  end;
end.Code language: PHP (php)
  1. ジェネリックリスト(後続講義で学習)
{$ifdef FPC} {$mode objfpc}{$H+}{$J-} {$endif}
{$ifdef MSWINDOWS} {$apptype CONSOLE} {$endif}
uses
  SysUtils, Generics.Collections; // ジェネリックコンテナユニットを読み込み

type
  TMyClass = class  // 数値とその二乗を保持する独自クラス
    I, Square: Integer;
  end;
  // ジェネリックオブジェクトリストを実体化、オブジェクトライフサイクル自動管理
  TMyClassList = {$ifdef FPC}specialize{$endif} TObjectList&lt;TMyClass&gt;;
var
  List: TMyClassList;
  C: TMyClass;
  I: Integer;
begin
  List := TMyClassList.Create(true); // true:リスト破棄時に内部オブジェクトを自動解放
try
  // ループでオブジェクトを作成しリストへ追加
for I := 0 to 9 do
begin
      C := TMyClass.Create;
      C.I := I;
      C.Square := I * I;
      List.Add(C);
end;
  // for-inでリスト内の各独自オブジェクトを直接走査
for C in List do
      WriteLn(C.I, ' の二乗は ', C.Square, ' です');
finally
    FreeAndNil(List); // リストを安全に解放、メモリリークを防止
end;
end.
Code language: HTML, XML (xml)

備考:本サンプルはクラスとジェネリクスを使用しているため初心者は一旦読み飛ばして構いません。後続のオブジェクト指向講義で詳しく解説します。

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です