循环

四种循环 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 核心区别

  1. 判断逻辑相反
  • while 条件 do:条件成立 → 继续循环;条件不成立 → 退出
  • repeat ... until 条件:条件成立 → 退出;条件不成立 → 继续循环
  1. 执行次数保证不同
  • 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)说明

  1. 对标 C 语言 for 循环,但限制更强:只能遍历连续序数(整数、枚举);
  2. 支持 downto 倒序遍历:for I := 9 downto 0 do
  3. 上下边界 Low(xx) / High(xx) 仅在循环开始计算 1 次,运行效率高;
  4. 循环结束后变量 I 的值属于未定义状态,编译器会警告; 仅用 Break / Exit 提前跳出循环时,I 会保留当前数值。

for-in 遍历(现代 foreach 风格)

可遍历四类数据,无需手动操作下标:

  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 两个枚举值
  Animals := [Duck, Cat];

  // for-in 遍历集合内所有存在的成员
  for AK in Animals do
  begin
    WriteLn('Found animal: ', 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<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)

说明:该示例用到类与泛型,新手暂时看不懂可跳过,后续面向对象章节会讲解。

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注