四种循环 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('Square is ', MyArray[I]);
// 3. Low/High 自动取数组上下界,无需手写0和9,扩展性更强
for I := Low(MyArray) to High(MyArray) do
WriteLn('Square is ', MyArray[I]);
// 4. while循环:先判断条件,满足才执行循环体
I := 0;
while I < 10 do
begin
WriteLn('Square is ', MyArray[I]);
I := I + 1; // 自增写法:I+=1 / Inc(I) 三者等价
end;
// 5. repeat-until 循环:先执行一次,末尾再判断退出条件
I := 0;
repeat
WriteLn('Square is ', MyArray[I]);
Inc(I); // 计数器自增
until I = 10;
// 6. for-in 遍历:直接取出数组里的元素值,不操作下标
for I in MyArray do
WriteLn('Square is ', 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('Square is ', MyArray[I]);
for I := Low(MyArray) to High(MyArray) do
WriteLn('Square is ', MyArray[I]);
I := 0;
while I < 10 do
begin
WriteLn('Square is ', MyArray[I]);
I := I + 1;
end;
I := 0;
repeat
WriteLn('Square is ', MyArray[I]);
Inc(I);
until I = 10;
for I in MyArray do
WriteLn('Square is ', I);
end.Code language: Delphi (delphi)
下面我们逐步分析里面的每个循环的代码
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: Delphi (delphi)
本段代码分三部分,顶部是预编译命令,中间var是声明变量。一个数组和一个整数,然后主执行部分begin 到end. 里面是一个for循环 从0到9把值赋给了I 然后用下标给
MyArray[0] ~ MyArray[9] 这十个数组的元素赋值。相当于如下,*是乘号。
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,当执行完递增1后,代码会跳转到while I < 10 do 这里 ,再此进行判断。如果成立,就执行第二遍循环体,然后I又递增1,然后又判断….如此往复,知道I<10 不成立,才会结束这个while循环。也就是当I的值变成了10 ,就不成立。


while 与 repeat-until 核心区别
- 判断逻辑相反
while 条件 do:条件成立 → 继续循环;条件不成立 → 退出repeat ... until 条件:条件成立 → 退出;条件不成立 → 继续循环
- 执行次数保证不同
- while:条件不满足时,循环一次都不执行
- repeat-until:无论条件如何,至少执行一遍循环体
如果学过C C++ java 等这些语言,就很好理解 repeat until 和do while 基本一样的功能。先执行一遍循环体,然后再判断until后面的条件。如果成立就继续循环体,。
repeat until 没有begin end 本身repeat until 就是一个块。读者可以写begin end也可以不写,建议不需要。
repeat until,的意思就是重复…直到。英文的意思就很好理解。

遍历依次就把MyArray里面的值,赋值给I,然后里面就可以操作I了。知道遍历完毕,不需要我们递增下标,甚至都不需要下标。
普通 for 循环(for I := ... to/downto)说明
- 对标 C 语言 for 循环,但限制更强:只能遍历连续序数(整数、枚举);
- 支持
downto倒序遍历:for I := 9 downto 0 do; - 上下边界
Low(xx)/High(xx)仅在循环开始计算 1 次,运行效率高; - 循环结束后变量 I 的值属于未定义状态,编译器会警告; 仅用
Break/Exit提前跳出循环时,I 会保留当前数值。
for-in 遍历(现代 foreach 风格)
可遍历四类数据,无需手动操作下标:
- 数组:直接读取数组内每一个元素值(示例中已演示)
- 枚举类型
{$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)

- 集合类型
{$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 两个枚举值
Animals := [Duck, Cat];
// for-in 遍历集合内所有存在的成员
for AK in Animals do
begin
WriteLn('Found animal: ', AK);
end;
end.Code language: PHP (php)

- 泛型列表 (后续章节学习)
{$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<TMyClass>;
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('Square of ', C.I, ' is ', C.Square);
finally
FreeAndNil(List); // 安全释放列表,防止内存泄漏
end;
end.
Code language: HTML, XML (xml)
说明:该示例用到类与泛型,新手暂时看不懂可跳过,后续面向对象章节会讲解。