迴圈

**迴圈**（；）係一種控制流程。喺程式入面，如果部電腦做嘢嘅步驟喺某一步係返轉頭去之前嘅其中一個步驟，兩個步驟之間就構成一個迴圈；迴圈入面嘅嘢會重覆做，每做次叫一個迭代（）。

結構上，最簡單嘅迴圈係無條件返轉頭（叫無限迴圈），但係一般嘅迴圈都只會有條件噉返轉頭；呢種決定返唔返轉頭嘅條件叫終止條件（）。個程式會按照個迴圈所指定嘅條件，重複噉執行迴圈入面嘅嘢若干次，直至個條件達到為止。

喺低級程式語言，點樣整迴圈原則上喺點整都得，但係實際上，迴圈嘅結構通常都有規律，夾埋得幾大類，呢啲類型通常都係用高級程式語言用嘅有關關鍵字嚟做名，最常見嘅迴圈包括所謂 For 迴圈同所謂 While 迴圈，仲有其他，不過以正統嘅結構化編程嘅角度嚟講，最基本嘅迴圈只係得 While 迴圈，任何其他類型嘅迴圈都可以寫成 While 迴圈。

無論係用高級定低級程式語言，迴圈入面嘅碼都係雖然淨係講咗一次，但會喺個程式行唔止一次。喺方便嘅角度嚟睇，用迴圈可以令到程式編寫方便好多，因為同一段要用多次嘅碼淨係需要打一次，不過其實有好多嘢係唔用迴圈根本做唔到嘅。

廿一世紀常用嘅程式語言冚唪唥都有語法寫迴圈（通常係種係種陳述式)，而高階程式語言通常亦有多過一種迴圈，例如 C 程式語言、C++、同 C# 等都支援好幾種迴圈，呢幾隻語言都有一種陳述式可以直接表達差唔多所有種類嘅迴圈。

## 無限迴圈

無限迴圈（）係指一個永遠都行唔完嘅迴圈，亦即係最少同最多都係做無限次；原因可能係因為個迴圈根本冇終止條件、有一個冇可能達得到嘅終止條件、又或者有一啲會搞到個迴圈重新啟動嘅陳述式。無限迴圈好多時係錯誤，可以搞到部電腦輕機。

不過，無限迴圈其實有佢嘅用途，亦即係有可能係特登，某啲程式語言亦有語法直接表達無限迴圈，例如，喺 Ada，

 **loop**
    *迴圈內容*;
 **end loop**;

表示概念上，中間嘅 ｢迴圈內容｣ 會做無限次（實際上，｢迴圈內容｣ 可能包括一個或以上嘅終止條件）。譯成目前常見嘅 C 語系語法通常寫成

 **for** (;;) {
    *迴圈內容*;
 }

喺結構上，無限迴圈可以話係最簡單，但係喺結構化編程嘅角度，最基本嘅迴圈並唔係佢，而係 While 迴圈。

## While 迴圈

所謂 While 迴圈（；喺 Pascal 又叫 ｢While-do 迴圈｣）係指終止條件喺迴圈頂部嘅迴圈，最少做零次，最多做無限次；因為通常係用關鍵字 ｢while｣ 表達，所以咁叫。While 迴圈係喺正統嘅結構化編程入面最基本嘅迴圈；廿一世紀初多數程式語言都有語法直接表達。

## Do-while 迴圈

所謂 Do-while 迴圈（）係指終止條件喺迴圈底部嘅迴圈，最少做一次，最多做無限次；因為有啲語言表示呢種迴圈嘅語法係喺頂有個關鍵字 ｢do｣，喺底又有個關鍵字 ｢while｣，所以咁叫。不過有好多受歡迎嘅程式語言（例如 Python）都冇語法直接表達 do-while 迴圈。

## For 迴圈

所謂 For 迴圈（）係指語法通常用 ｢For｣ 呢個關鍵字表示嘅迴圈；喺某個意義上，幾乎所有程式語言都有呢種迴圈，但係唔同嘅程式語言，用 ｢For｣ 呢個關鍵字表示嘅迴圈其實唔止一種，而係可以分成兩大類（其實仲有第三類，但係第三類通常叫 ｢Foreach 迴圈｣）。

### 本來嘅 For 迴圈
喺早期嘅程式語言（例如 Fortran 同 BASIC），For 迴圈（喺 Fortran 又叫 ｢Do 迴圈｣）一定係由一個數值數到第個數值，中間通常係跳 1，但係編程員可以指定其他數值。例如，喺 Applesoft BASIC（略去行號），

 **FOR** I = 1 **TO** 100 **STEP** 2
    *迴圈內容*
 **NEXT**

表示中間嘅 ｢迴圈內容｣ 會做 50 次首先設定變數 *I* 係 1（圖中嘅 ｢設定｣），每個迭代嘅迴圈內容做完之後加 2（圖中嘅 ｢更新｣），直到符合終止條件 ｢*I* 大過 100｣ 為止（圖中嘅 ｢終止條件｣）。譯成目前常見嘅 C 語系語法大概即係

 **for** (*i* = 1; *i* 
my @list = ('vimal', 'sonoo', 'ratan'); # 整一個陣列，個名叫 ｢list｣，入面順序擺 ｢vimal｣、｢sonoo｣、｢ratan｣。
foreach my $s (@list) { # 逐一處理陣列變數 list 入面嘅每個元素，每個迭代處理嘅元素擺入純量變數 s，然後...
   printf "%s\n", $s; # show 個元素出嚟睇。
}


譯成 Java 會寫成類似係咁：


```
import java.util.*;
class ForEachExample2{
  public static void main(String args[]){
   ArrayList<String> list=new ArrayList<String>(); // 整一個陣列，個名叫「list」，用嚟裝 string（文字）。
   list.add("vimal"); // 喺 list 加入「vimal」呢個元素。
   list.add("sonoo"); // 如此類推...
   list.add("ratan");

   for(String s:list){ // 逐一處理變數 list 嘅每個元素，每個迭代處理嘅元素擺入變數 s，然後...
     System.out.println(s); // show 個元素出嚟睇。
   }
 } // 呢段碼會喺 output 嗰度 show 出「vimal sonoo ratan」。
```


## 多重出口迴圈

喺電腦科學理論，多重出口迴圈（；有啲人叫 ｢提早離開迴圈｣，）係指迴圈頂部同底部之外有其他地方插入咗終止條件嘅迴圈，概念上可以睇成無限迴圈入面加插一個或以上終止條件，例如好似係噉（圖七）：

 **loop**
    *迴圈內容甲*;
 
    *迴圈內容丙*;
 
    *迴圈內容戊*;
    ⋮
 **end loop**;

換句話講，有陣時，個編程員會想個程式乎合某個終止條件嗰陣即刻停止個迴圈，但係個位又唔喺頂，又唔喺底。例如假設想用個 While 迴圈係噉耖一柞數據，由頭耖到落尾，喺咁嘅情形之下，理想嘅話，個程式應該係搵到數據就即刻離開個迴圈，而唔係繼續耖柞數據。

直接表示呢種 ｢即刻離開｣ 嘅語法其實唔係新嘢，而係喺20世紀一早已經有，例如 C 嘅 ｢break｣、Ada 嘅 ｢exit｣ 同 Perl 嘅 ｢last｣ 等等，其中 Ada 同 Perl 可以寫到個關鍵字喺陣述式開頭（Ada：｢exit when *終止條件*;｣；Perl：｢last if *終止條件*;｣ 或者 ｢last unless *終止條件嘅相反*;｣），等容易睇到嗰行係迴圈出口。

喺完全冇語法直接表示呢個概念嘅語言，呢種做法有時可以用 Goto 表示。

舉個例，好似係以下呢段用 C 寫嘅碼噉，因為終止條件喺迴圈內容同變數更新中間，離開條件又唔喺頂（唔係 While 迴圈），又唔喺底（唔係 Do-while 迴圈），想即刻離開就要用 ｢break｣：


```c
#include <stdio.h>
int main()
{
   int num = 0;  /* 設定 num 係 0 */
   while (num <= 100) /* 迴圈開始，終止條件甲：num 大過 100 */
   {
      printf("value of variable num is: %d\n", num); /* show 出... */
      if (num == 2) /* 終止條件乙：num 等如 2 */
      {
          break; /* 離開個迴圈，行迴圈之後嗰行碼。 */
      }
      num++; /* 將 num 數值加 1 */
   }
   printf("Out of while-loop");
   return 0;
}
```


呢段碼嘅輸出會係噉嘅：


```c
value of variable num is: 0
value of variable num is: 1
value of variable num is: 2
Out of while-loop
```


### 離開決策
某啲程式語言會將迴圈分類，有喺實際執行嗰陣用到提早離開語法（即係例如源碼寫咗 ｢break｣，而執行途中又真係有 break；亦即係 while、do-while、for 等等結構上有 break 嘅語法全部唔算數）嘅係一類，冇喺實際執行途中提早離開嘅係另外一類，呢啲語言會有語法，等編程員可以判斷某個迴圈喺實際執行嘅途中有冇提早離開，例如以下呢段 Python 碼：


```python
for val in "string":
    if val == "n":
        print("found")
        break
else:
    print("The loop didn't break")
```


呢段碼會俾：


```python
found
```


而如果將段碼改做：


```python
for val in "string":
    if val == "z":
        print("found")
        break
else:
    print("The loop didn't break")
```


就會出：


```python
The loop didn't break
```


即係話 `else:` 跟住嗰段碼衹會喺個迴圈冇 `break` 嗰陣被執行。

## 多層離開
喺電腦科學理論，多層離開（）係指當迴圈入面又有迴圈嗰陣（即係所謂 ｢嵌套住嘅迴圈｣，），入面嘅迴圈可以按佢嘅終止條件直接離開出面嘅迴圈；呢種做法有時可以減少重覆嘅源碼。

有少量嘅程式語言有語法直接表達呢種流程，其中一種做法係喺出面迴圈嘅碼塊加標籤，咁入面嘅迴圈就可以直接指定出面迴圈嘅標籤，達致多層離開；例如 Perl 就有呢種語法。喺關鍵字係 ｢break｣ 啲語言，有啲人會叫呢種流程控制做 ｢multi-level break｣。

喺冇語法直接表達呢種流程嘅語言，有時可以用 goto 表達。但係如果唔想用 goto，想表達呢種流程會比較撈絞，原因係喺好多程式語言入面（例如 Python），表達 ｢即刻離開｣ 嘅語法（例如 Python 嘅 ｢break｣）衹會終止一層迴圈，即係話如果有一個嵌套住嘅迴圈嗰度做即刻離開，外層嗰個迴圈仍然會如常噉繼續行。

喺冇直接支援嘅情形下，另外一個常見嘅做法係將個迴圈擺喺一個子程式當中，再喺個子程式裏面用 ｢return｣（終止個子程式）嚟離開成個（包含咗一大柞迴圈嘅）子程式。

有唔少編程專家都懷疑「容許用家一嘢離開嗮所有行緊嘅迴圈」係咪一樣好嘢，例如喺廿一世紀初嗰陣，Python 就試過有人提議加多層離開功能，但就俾人以「呢個功能會造成多餘嘅複雜性，而佢嘅用途太少，根本唔值得加」為由否決。

多層離開嘅諗頭喺理論電腦科學（theoretical computer science）當中引起唔少人思考：喺 1973 年，有電腦科學家執咗吓結構化程式定理，跟手仲證明，衹要一隻程式語言有多層離開嘅功能，就可以避免加多啲變數是但搵個整數 *n*，都會有個程式具有一個離開 *n* 個迴圈嘅多層離開，而呢個程式**唔**可以重寫做一個具有離開 *m* 個迴圈（*m* 
# include 
int main() /* 呢段碼會俾個用家輸入若干個數字，再將啲數字加埋一齊。 */
{
    int i;
    double number, sum = 0.0;
    for(i = 1; i 

呢段碼嘅輸出望落會係噉嘅：

```c
Enter a n1: 1.1
Enter a n2: 2.2
Enter a n3: 5.5
Enter a n4: 4.4
Enter a n5: -3.4
Enter a n6: -45.5
Enter a n7: 34.5
Enter a n8: -4.2
Enter a n9: -1000
Enter a n10: 12
Sum = 59.70
```


### 重做呢個迭代

又有時，編程員想即刻直接返返去個迴圈內容嘅開頭，連終止條件都唔檢查（圖九）；Perl 同 Ruby 都有個叫 ｢redo｣ 嘅陳述式可以表達呢種流程，可以令個程式重新行過行緊嗰個迭代。喺 Perl，呢種流程嘅其中一種常見嘅用途係喺處理輸入嗰陣用。

濫用呢種流程隨時可以導致代碼難讀化；舉個極端嘅例子，喺 Perl，碼塊算係行一次嘅迴圈，所以下面嘅一行其實係個無限迴圈，但係點睇都唔會睇得出：

    **{redo;}**

### 重新做個迴圈

另外，有時編程員可能想個返返去個迴圈嘅最開頭，即係設定嗰步，重新開始過個迴圈（圖十）。Ruby 曾經可以用一個叫 ｢retry｣ 嘅陳述式表達呢種流程，不過呢種語法只係支援咗好短嘅時間，而且好少人用，已經冇咗好耐。

喺 Perl，因為碼塊係行一次嘅迴圈，呢種流程原則上可以用有標記參數嘅 ｢redo｣ 表達：

 A: {
    **for** (**my** $*i* = 1; $*i* |items| - i 會係一個啱用嘅迴圈變量：


```whiley
function contains([int] items, int item) => bool:
    int i = 0 // 設 i
    //
    while i < |items|:
        if items[i] == item:
            return true
        i = i + 1 // 喺每一個迭代當中，i 數值都會上升 1
    // 「|items| - i」嘅數值一定會隨住迭代而下降。
    // 編程員可以喺度加段陳述式，叫個程式喺 output 嗰度每迭代顯示 (|items| - i) 嘅值，跟住佢就可以用眼 monitor 住個迴圈運作係咪如常。
    return false
```

*迴圈不變量係一個數值理應唔會隨迭代改變嘅變數，都係整嚟監察個迴圈運作係咪如常嘅。例如有以下呢段 C 碼：

```c
int max(int n,const int a[]) { // 呢個子程式會攞一個 array，再搵出個 array 裏面最大嗰個數
    int m = a[0]; // a[0] 係指 a 呢個 array 嘅第 1 個數
    int i = 1;
    while (i != n) { // 如果 i 數值唔等如 n，一路做...
        if (m < a[i])
            m = a[i]; // 如果 m 數值細過 a 嘅第 (i + 1) 個數值，將 m 設做 a[i]
        ++i; // i 數值升 1
    }
    return m;
} // 一個可能嘅迴圈不變量係 a[] 嘅第一個數；個編程員可以加句陳述式，叫個程式每次迭代都 display a[0] 出嚟如果個程式運作正常，a[0] 數值理應會維持不變。
```


## 攷

## 註

## 書目
* 
*