程式迴圈

**迴圈**是電腦科學運算領域的用語，也是一種常見的控制流程。迴圈是一段在程式中只出現一次，但可能會連續執行多次的程式碼。迴圈中的程式碼會執行特定的次數，或者是執行到特定條件成立時結束迴圈，或者是針對某一集合中的所有項目都執行一次。

在一些函式程式語言（例如 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**  
```

依其四捨五入的誤差、硬體及編譯器的差異，不一定會執行 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...` 的方式中提早結束迴圈。

Python 支援一個特別的條件判斷式，可以根據最近使用迴圈是否曾用 `break` 提早結束而做不同的處理，舉例如下：

上例中的 `else` 子句是 `for` 迴圈的一部份，不是內層 `if` 區塊的一部份。Python 語言的 `for` 迴圈及 `while` 迴圈都支援 `else` 子句，當迴圈沒有用 `break` 提早結束時就會執行。

## 迴圈的特殊指令

有時在使用迴圈的程式中會希望在特定情形下跳過目前迴圈區塊的指令，回到迴圈開始執行下一個迴圈，一般這類的指令會命名為 `continue`、`skip` 或 `next`，其效果是提早結束這次迴圈的進行，繼續進行下一個迴圈，若此迴圈已經是最後一次執行，這類指令會結束迴圈的進行，繼續進行後續的指令。

像 Perl 及 Ruby 等程式語言有 `redo` 指令，可以重新執行目前的迴圈，若在指定次數的迴圈中，其迴圈計數器的數值不會變化。Ruby 程式語言有 `retry` 指令，可以讓迴圈計數器回到初值，重新執行整個迴圈。

## 迴圈變式及迴圈不變式

迴圈變式及迴圈不變式是判斷迴圈正確性的工具。

迴圈變式是一個初值不為負的整數表示式，在每次執行迴圈時迴圈變式的數值需減少，但在正常的迴圈執行過程中迴圈變式的數值不會變成負值。迴圈變式用來確保迴圈會結束。

迴圈不變式是一個和迴圈有關的判斷式，在第一次進入迴圈之前，迴圈不變式的值需為真，在後續每一次執行迴圈時，其值也要為真。當迴圈正確的結束時，其終止條件和迴圈不變式都會成立。迴圈不變式可用來監控在迴圈進行時，某一指定性質的狀態。

像是 Eiffel 之類的程式語言本身就有支援迴圈變式及迴圈不變式，其他語言可能需要有附加元件才能支援此功能，例如 Java 就需要配合 Java 建模語言規範的 loop statements （頁面存檔備份，存於網際網路檔案館）才能支援此機能。

## 不同語言的迴圈比較表

1. **a** 此項只考慮專用的無窮迴圈程式結構，因此 `while (true)` 和 `for ( ; ; )` 都不算在內。
2. **a** **b** **c** **d** **e** **f** **g** **h** C 語言的 `for (_init_; _test_; _increment_)` 迴圈是一個通用的迴圈指令，累加量也不一定要為 1。
3. **a** **b** **c** 在 C、C++ 及 C# 中，跳出多層迴圈可以用 label 和 goto 指令達到。
4. **a** 在 PHP 5 中已支援 （頁面存檔備份，存於網際網路檔案館）配合物件的迴圈。
5. **a** **b** **c** 指定次數的迴圈可以用重覆 incrementing list 或 generator 的方式來達到其效果，例如 Python 中的 `range()`。
6. **a** **b** **c** **d** **e** 可以用例外處理來跳出多層迴圈。
7. **a** 沒有專門指令，但 `while` 指令可以用作此用途。
8. **a** 沒有專門指令，但使用者可以定義通用迴圈指令。
9. **a** 正在計劃的 C++0x 標準中已加入以範圍為基礎的 for 迴圈。標準模板庫（STL）中有 `std::for_each` 模板函式，可以對 STL 容器（container）的每個元素重複呼叫一個一元函式。製作 STL 容器的巨集也可以達到類似的效果。
10. **a** 利用整數區間的迭代可以達到指定次數迴圈的效果， early exit 可以用多增加一個 exit 的條件來達成。
11. **a** Eiffel 支援保留字 `retry`，不過是用在契約式設計的異常處理，不是迴圈的流程控制指令。
12. **a** 需要配合 Java 建模語言（JML）。

## 參考資料