循环

<image>

循环是计算机科学运算领域的用语，也是一种常见的控制流程。循环是一段在程式中只出现一次，但可能会连续执行多次的程式码。循环中的程式码会执行特定的次数，或者是执行到特定条件成立时结束循环，或者是针对某一集合中的所有项目都执行一次。

在一些函数程式语言（例如Haskell和Scheme）中会使用递归或不动点组合子来达到循环的效果，其中尾部递归是一种特别的递归，很容易转换为迭代。

## 指定执行次数的循环(for loop)

大部分程式语言都提供循环的指令，可以依指定的次数重复执行一段程式。

若指定的次数N小于1，程式语言会忽略整个循环不去执行，若指定的次数N为1，则循环只会执行一次。

在循环进行时，循环计数器也会随着变化，大部分的程式语言可以允许循环计数器上数或是下数，每次的变化量可以是1或是其他不为0的数值。

```
   FOR I = 1 TO N            '''for''' I := 1 '''to''' N '''do''' '''begin'''
       xxx                       xxx
   NEXT I                    '''end''';

   DO I = 1,N                '''for''' ( I=1; I<=N; ++I ) {
       xxx                       xxx
   END DO                    }
```

在许多程式语言中，循环计数器要使用整数才能得到准确的结果。由于硬件的限制，在循环计数器使用浮点数时，结果可能会不符预期，如以下的循环

```
   '''for''' X := 0.1 '''step''' 0.1 '''to''' 1.0 '''do'''<br>
```

依其四舍五入的误差、硬件及编译器的差异，不一定会执行10次，可能只会执行9次。而且X的数值可能会有些误差，不是预期的0.1, 0.2, 0.3, ..., 1.0。

## 指定条件的循环(while loop/doWhile loop)

大多数的程式语言都有指令，可以在特定条件成立时继续循环的进行，或是特定条件不成立时继续循环的进行，进行到特定条件成立为止。前者一般会标示while，后者一般会标示until。

其判断条件可能在循环一开始就进行，或是在循环最后才进行。前者的循环不一定会执行，而后者1的循环至少会执行一次。

```
   DO WHILE (test)           '''repeat''' 
       xxx                       xxx 
   LOOP                      '''until''' test;

   '''while''' (test) {            '''do'''
       xxx                       xxx
   }                         '''while''' (test);
```

## 指定集合的循环

许多程式语言支援一种特别的循环，可以针对一个阵列中的元素或是一个集合中的所有成员进行循环中的指令，包括Ada、D语言、Smalltalk、Perl、C#、Visual Basic、Ruby、Python、Java、JavaScript、Fortran 95等程式语言都有这类的循环结构：

```
   someCollection '''do''': [:eachElement |xxx].

   '''foreach''' (item; myCollection) { xxx }

   '''foreach''' someArray { xxx }

   Collection<String> coll; '''for''' (String s : coll) {}

   '''foreach''' ('''string''' s '''in''' myStringCollection) { xxx }

   $someCollection | ForEach-Object { $_ }

   '''forall''' ( index = first:last:step... )
```

## 泛用循环结构

有些程式语言有泛用循环结构，可以用来表示指定次数或指定条件的循环，像C语言的for指令或是Common Lisp语言中的do指令都是这类的例子，不过为了程式的可读性考量，在这些程式语言中还是尽量使用一些含义较明确的指令（如while指令）。

## 无穷循环

无穷循环一般会用在有一段程式需要永远执行，或是该程式在没有发生特殊事件（如故障）时需要永远执行的场合，例如一个事件驱动的程式需要持续执行循环，处理发生的事件，直到使用者结束或中断程式为止。

若在指定条件的循环中，其判断条件用到的变数数值永远不会改变，这种程式错误也会使得此循环变成无穷循环。

## 提早结束整个循环

当使用指定次数的循环查表时，会希望在查到需要的资料时就可以直接结束循环的进行，有些程式语言可以用`break`或`exit`的指令达到这様的功能，这些指令会结束这个循环，接着会执行循环后面的指令。若此循环在副程式中，也可以用`return`中断循环的进行，同时离开副程式。

以下是Ada程式语言的一个范例，利用`exit ... when...`的方式中提早结束循环。

```ada
with Ada.Text IO;
with Ada.Integer Text IO;

procedure Print_Squares is 
    X : Integer;
begin
    Read_Data : loop
        Ada.Integer Text IO.Get(X);
    exit Read_Data when X = 0;
        Ada.Text IO.Put (X * X);
        Ada.Text IO.New_Line;
    end loop Read_Data;
end Print_Squares;
```

Python支援一个特别的条件判断式，可以根据最近使用循环是否曾用`break`提早结束而做不同的处理，举例如下：

```python
for n in set_of_numbers:
    if isprime(n):
        print "Set contains a prime number"
        break
else:
    print "Set did not contain any prime numbers"
```

上例中的`else`子句是`for`循环的一部分，不是内层`if`区块的一部分。Python语言的`for`循环及`while` 循环都支援`else`子句，当循环没有用`break`提早结束时就会执行。

## 循环的特殊指令

有时在使用循环的程式中会希望在特定情形下跳过目前循环区块的指令，回到循环开始执行下一个循环，一般这类的指令会命名为`continue`、`skip`或`next`，其效果是提早结束这次循环的进行，继续进行下一个循环，若此循环已经是最后一次执行，这类指令会结束循环的进行，继续进行后续的指令。

像Perl及Ruby等程式语言有`redo`指令，可以重新执行目前的循环，若在指定次数的循环中，其循环计数器的数值不会变化。Ruby程式语言有`retry`指令，可以让循环计数器回到初值，重新执行整个循环。

## 循环变式及循环不变式

循环变式及循环不变式是判断循环正确性的工具。

循环变式是一个初值不为负的整数表示式，在每次执行循环时循环变式的数值需减少，但在正常的循环执行过程中循环变式的数值不会变成负值。循环变式用来确保循环会结束。

循环不变式是一个和循环有关的判断式，在第一次进入循环之前，循环不变式的值需为真，在后续每一次执行循环时，其值也要为真。当循环正确的结束时，其终止条件和循环不变式都会成立。循环不变式可用来监控在循环进行时，某一指定性质的状态。

像是Eiffel之类的程式语言本身就有支援循环变式及循环不变式，其他语言可能需要有附加元件才能支持此功能，例如Java就需要配合Java建模语言规范的 loop statements 才能支持此机能。

## 不同语言的循环比较表

程式语言｜条件判断式｜循环｜early exit｜continuation｜redo｜retry｜正确性判断机制

循环启始｜循环中间｜循环结尾｜指定次数｜指定集合｜泛用｜无穷loop_infinite｜循环变式｜循环不变式

Ada｜是｜是｜是｜是｜只针对阵列｜否｜是｜多层循环｜否

C｜是｜否｜是｜否 loop_for｜否｜是｜否｜多层循环 deep_break_c｜多层循环 deep_break_c｜否

C++｜是｜否｜是｜否 loop_for｜是｜是｜否｜多层循环 deep_break_c｜多层循环 deep_break_c｜否

C#｜是｜否｜是｜否 loop_for｜是｜是｜否｜多层循环 deep_break_c｜多层循环 deep_break_c

Common Lisp｜是｜是｜是｜是｜是｜是｜是｜多层循环｜否

Eiffel｜是｜否｜否｜是 count_loop_eiffel｜是｜是｜否｜一层循环 count_loop_eiffel｜否｜否｜否 retry_in_eiffel｜是｜是

F#｜是｜否｜否｜是｜是｜否｜否｜否 deep_break_e｜否｜否

FORTRAN 77｜是｜否｜否｜是｜否｜否｜否｜一层循环｜是

FORTRAN 90｜是｜否｜否｜是｜否｜否｜是｜多层循环｜是

FORTRAN 95及后续版本｜是｜否｜否｜是｜阵列｜否｜是｜多层循环｜是

Haskell｜否｜否｜否｜否｜是｜否｜是｜否 deep_break_e｜否｜否

Java｜是｜否｜是｜否 loop_for｜是｜是｜否｜多层循环｜多层循环｜否｜非原生(non-native) requires_JML｜非原生(non-native) requires_JML

JavaScript｜是｜否｜是｜否 loop_for｜是｜是｜否｜多层循环｜多层循环｜否

OCaml｜是｜否｜否｜是｜阵列及列表（list）｜否｜否｜否 deep_break_e｜否｜否

PHP｜是｜否｜是｜否 loop_for loop_for｜是 loop_phpforeach｜是｜否｜多层循环｜多层循环｜否

Perl｜是｜否｜是｜否 loop_for loop_for｜是｜是｜否｜多层循环｜多层循环｜是

Python｜是｜否｜否｜否 loop_range｜是｜否｜否｜多层循环 deep_break_e｜多层循环 deep_break_e｜否

REBOL｜否 while｜是｜是｜是｜是｜否 user｜是｜一层循环 deep_break_e｜否｜否

Ruby｜是｜否｜是｜是｜是｜否｜是｜多层循环 deep_break_e｜多层循环 deep_break_e｜是｜是

Standard ML｜是｜否｜否｜否｜阵列，列表（list）｜否｜否｜否 deep_break_e｜否｜否

Visual Basic .NET｜是｜否｜是｜是｜是｜否｜是｜每种循环一层｜每种循环一层

Windows PowerShell｜是｜否｜是｜否 loop_for｜是｜是｜否｜?｜是

- loop_infinite 此项只考虑专用的无穷循环程式结构，因此`while (true)`和`for ( ; ; )`都不算在内。
- loop forloop_forloop_forloop_forloop_forloop_forloop_forloop_for C语言的`for (''init''; ''test''; ''increment'')`循环是一个通用的循环指令，累加量也不一定要为1。
- deep_break_cdeep_break_cdeep_break_c在C、C++及C#中，跳出多层循环可以用label和goto指令达到。
- loop_phpforeach 在PHP 5中已 支援 配合物件的循环。
- loop_rangeloop_rangeloop_range 指定次数的循环可以用重复incrementing list或generator的方式来达到其效果，例如Python中的`range()`。
- deep_break_edeep_break_edeep_break_edeep_break_edeep_break_e 可以用异常处理来跳出多层循环。
- while 没有专门指令，但`while`指令可以用作此用途。
- user 没有专门指令，但使用者可以定义通用循环指令。
- loop_foreach C++0x标准中已加入以范围为基础的 for 循环。标准模板库（STL）中有`std::for_each`模板函数，可以对STL容器（container）的每个元素重复呼叫一个一元函数。制作STL容器的宏也可以达到类似的效果。
- count_loop_eiffel 利用整数区间的迭代可以达到指定次数循环的效果， early exit可以用多增加一个exit的条件来达成。
- retry_in_eiffel Eiffel支援保留字`retry`，不过是用在契约式设计的异常处理，不是循环的流程控制指令。
- requires_JML 需要配合Java建模语言（JML）。