跳去內容

迴圈

出自維基百科,自由嘅百科全書

迴圈粵拼wui4 hyun1英文loop)係一種控制流程陳述式

一個迴圈係一串陳述式,特徵係喺段嗰度淨係講咗一次,但會喺個程式行嗰陣行幾次:一個迴圈會掕住若干句陳述式同重複條件,個程式會按照個迴圈所指定嘅條件,重複噉執行掕住嗰柞陳述式若干次,直至個條件達到為止,而每一次個程式檢查「個條件達到未」就係一個迭代(iteration)。迴圈嘅使用令到程式編寫方便好多,同一段要用多次嘅碼淨係需打一次[1]。廿一世紀常用嘅程式語言冚唪唥都會包含迴圈,而一啲高階程式語言仲會有多過一種迴圈,例如 C 程式語言C++、同 C# 等都支援好幾種迴圈嘅使用,當中 C 仲可以用一種陳述式包嗮所有種類嘅迴圈[2]

For 迴圈

[編輯]
For 迴圈嘅控制流程圖
内文:For 迴圈

For 迴圈(for loop)係幾乎所有程式語言都有嘅迴圈。一個 for 迴圈會有個關鍵字,後面有一個條件,而跟住嗰幾行碼就係掕住碼,衹要個條件一日係真,個程式會一路係噉行柞掕住碼,例如係以下呢段用 Java 寫嘅碼噉[3]

for (int i = 0; i < 100; i++)  // 設一個變數 i,其數值係 0;衹要 i 細過 100,就一路行 {} 以內嘅碼,每行一次將 i 數值加 1(所以段碼會行 100 次)。
{
    System.out.print(i); // output i 嘅數值
    System.out.print(' '); // 後面要有 space
}
System.out.println();
// 呢段碼會出嘅係 0 至 99 嘅一串數字,而且每個數字之間有個 space。

喺一個 for 迴圈當中用嚟數迭代嗰個變數(此後叫「i」)跳嘅一步好多時可以預設當做 1:原則上,i 每次跳可以跳多過 1 而成個 for 迴圈功能不變,例如 (int i = 0; i < 200; i = i + 2) 查實一樣會令段掕住碼行 100 次;不過喺實際應用上,好多人都嫌吓吓要指明「i 每步要跳 1」麻煩,所以唔少程式語言會預設咗 i 每步都係跳 1,唔使用家講明。好似係 MATLAB[4]

    for n = 1:100 % 有個變數 n,其數值係 1;衹要 n 喺 1 同 100 之間,就一路行直至 end 為止嘅碼,每行一次將 n 數值加 1(所以段碼會行 100 次)。
        ............
    end

While 迴圈

[編輯]
内文:While 迴圈

While 迴圈(while loop)係廿一世紀初多數程式語言都有嘅迴圈。一個 while 迴圈會有一個條件,同一柞掕住嘅碼。個程式會評估個條件,如果個條件係真,噉就會行柞掕住碼,行完一次之後再睇吓個條件係咪真,如果係就再行多次柞掕住碼,一路重複直至個條件唔係真止。While 迴圈一個應用例子係電子遊戲編程:一隻電子遊戲嘅一場對局可以想像成個遊戲程式一路做場對局嘅運算,直至某個 GAME OVER 條件(例如玩家角色生命值變咗 0)達到為止。以下呢段 MATLAB 碼用咗 while 迴圈計 10 嘅階乘(factorial)[5]

    n = 10; % 設 n 做 10。
    f = n; % 設 f 等同 n。
    while n > 1 % 衹要 n 大過 1,就一路做以下嘅嘢:
        n = n - 1; % 將 n 數值下降 1。
        f = f * n; % 將 f 變成 f 乘 n。
    end
    disp(['n! = ' num2str(f)]) % show "n! = " 同 f 最後個數值;呢段碼計出嘅係 10 嘅 factorial。

Do-while 迴圈

[編輯]

Do-while 迴圈(do-while loop)係同 while 迴圈好相似嘅迴圈,分別在於幾時評估個條件:一個 do-while 迴圈都係會有一個條件,同一柞掕住嘅碼。個程式會行一次段掕住碼,睇吓個條件係咪真,如果係,噉就再行多次段掕住碼,跟住再睇吓個條件係咪真,如此類推。喺一個程式嘅執行過程當中,一個 do-while 迴圈起碼會行一次,而一個 while 迴圈有機會可以完全一次都唔行,所以 do-while 迴圈有陣時可以做啲 while 迴圈做唔到嘅效果,不過有好多受歡迎嘅程式語言都冇 do-while 迴圈嘅功能(例子有 Python)[6]

以下呢段 Java 碼就用咗 do-while 迴圈[7]

public class DoWhileExample {  
public static void main(String[] args) {  
    int i = 1; // 設 i 嘅數值係 1。  
    do{  
        System.out.println(i); // show i 嘅數值。
    i++;  // 將 i 嘅數值上升 1。
    }while(i <= 10);  // do {} 入面嘅碼 while i 細過或者等如 10。
}  
} // 呢段碼會將 1 到 10 嘅數字 show 喺 output 嗰度。

Foreach 迴圈

[編輯]

Foreach 迴圈(for each loop)係一種迴圈,會叫個程式攞某柞嘢,(for)柞嘢當中每一件(each)行段掕住碼一次。Foreach 迴圈最常係夾埋數組(array;一個數組包含一列各自獨立嘅數值)一齊用,例子有以下呢段 Java 碼[8]

class ForEachExample1{  
  public static void main(String args[]){  
   int arr[] = {12,13,14,44}; // 整一個 array,名為 arr,個 array 包含 12、13、14、同 44 呢幾個數值。
   for(int i:arr){ // for arr 入面每個元素,做...
     System.out.println(i);  // show i 嘅數值出嚟睇。
   }  
 }   
} // 呢段碼會喺 output 嗰度 show 出 12、13、14、同 44 呢幾個數字。

就算個數組入面嘅嘢唔係數字,foreach 迴圈都行得通[8]

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

   for(String s:list){ // for list 嘅每個元素,做...
     System.out.println(s); // show 個元素出嚟睇。
   }
 } // 呢段碼會喺 output 嗰度 show 出「vimal sonoo ratan」。
Foreach 圖解

無限迴圈

[編輯]
内文:無限迴圈

無限迴圈(infinite loop)係指一個永遠都行唔完嘅迴圈,原因可能係因為個迴圈根本冇終止條件、有一個冇可能達得到嘅終止條件、又或者有一啲會搞到個迴圈重新啟動嘅陳述式-通常係個編程員無意中犯錯先會噉。喺原始啲嘅廿世紀電腦當中,無限迴圈會搞到部電腦輕機,不過先進啲嘅電腦會識得喺出現無限迴圈嗰時話俾個使用者知,等個使用者決定好唔好繼續行落去,又或者俾個用家喺無限迴圈出現嗰時人手終止個程式。

無限迴圈例子有以下呢啲[9][10]

一段 Java 碼:

while (true) // 當「true」係真嘅時候一路做以下嘅嘢;因為 true by definition 就係真,所以呢個 loop 永遠唔會完。
    System.out.println("Infinite Loop");

一段 Visual Basic 碼:

dim x as integer ' 設 x 呢個變數,佢係一個整數
do while x < 5 ' do 以下嘅嘢 while x 細過 5
  x = 1 ' 設 x 做 1
  x = x + 1 ' 設 x 做 x + 1
loop
' 呢段碼個迴圈開頭會將 x 嘅數值設返做 1,所以 x 嘅數值永遠都唔會等如或者大過 5(有一個冇可能達得到嘅終止條件)。

提早離開迴圈

[編輯]

有陣時,個編程員會想個程式喺某個條件達到嗰時停止迴圈。例如家陣有段碼,用個 while 迴圈係噉耖一柞數據,由頭耖到落尾,理想嘅話,個程式喺搵到想要嗰個數據嗰時,會離開個迴圈,而唔係繼續耖柞數據。因為呢個緣故,廿一世紀初嘅程式語言多數都會支援 break(離開迴圈)嘅功能;同時,Visual basic 有 exit,而 Perl 有 last,兩者都係做同樣嘅功能-即刻終止個迴圈,令個程式直接行個迴圈嘅下一行陳述式。呢種情況有時會俾人嗌做「提早離開迴圈」(early-exit loop)[11]

提早離開迴圈典型嘅做法係有個迴圈,喺個迴圈入面有一個條件陳述式,如果一個條件成立,個程式就要行 break,否則個程式就繼續行個迴圈。好似係以下呢段用 C 寫嘅碼噉[11]

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

呢段碼嘅輸出會係噉嘅[11]

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

離開決策

[編輯]

某啲程式語言仲支援「視乎個迴圈有冇 break,決定行唔行某一段碼」,例如以下呢段 Python 碼[12]

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

呢段碼會俾:

found

而如果將段碼改做:

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

就會出:

The loop didn't break

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

多層離開

[編輯]

喺要應付嵌套住嘅迴圈(nested loop;喺另一個迴圈內嘅迴圈)嗰陣,break 嘅使用就比較撈絞[13][14][15]

  • 喺好多程式語言入面(例如 Python),break 衹會終止一層迴圈,即係話如果有一個嵌套住嘅迴圈嗰度做 break,外層嗰個迴圈會如常噉繼續行;
  • 另一方面,有啲程式語言會俾用家用 break 一吓離開嗮行緊嘅迴圈,而喺理論性嘅編程研究上,呢個動作就係所謂嘅多層離開(multi-level break);
  • 此外,常見嘅做法仲有將個迴圈擺喺一個子程式當中,再喺個子程式裏面用 return(終止個子程式)一吓離開成個(包含咗一大柞迴圈嘅)子程式;
  • 再唔係有人會索性用 goto 離開一柞行緊嘅迴圈。

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

多層離開嘅諗頭喺理論電腦科學(theoretical computer science)當中引起唔少人思考:喺 1973 年,有電腦科學家執咗吓結構化程式定理,跟手仲證明,衹要一隻程式語言有多層離開嘅功能,就可以避免加多啲變數-是但搵個整數 n,都會有個程式具有一個離開 n 個迴圈嘅多層離開,而呢個程式可以重寫做一個具有離開 m 個迴圈(m < n)、而且變數數量一樣或者更少嘅程式。既然多層離開嘅使用容許一個程式有少啲變數,即係呢種功能喺某啲情況下可以減低程式嘅複雜性[17][18]

其他相關功能

[編輯]
  • 跳去下一個迭代:喺某啲編程應用當中,編程員有陣時會想個程式跳過個迴圈嘅剩餘部份,直接進入下一個迭代;有啲程式語言有陳述式做噉嘅嘢,例如多數語言有嘅 continue、Perl 同 Ruby 當中有嘅 next、同 skip。呢啲陳述式一定要搭配迴圈使用,會結束目前嗰個迭代,直接跳去行下一個迭代,而如果個迭代係最後一個,個 continue 會直接終止個迴圈。例如係以下呢段 C 碼[19]
# include <stdio.h>
int main() /* 呢段碼會俾個用家輸入若干個數字,再將啲數字加埋一齊。 */
{
    int i;
    double number, sum = 0.0;
    for(i = 1; i <= 10; ++i)
    {
        printf("Enter a n%d: ",i);
        scanf("%lf",&number);
        if (number < 0.0)
        {
            continue; /* 如果輸入嗰個數字細過 0,跳去下一個迭代。 */
        }
        sum += number;
    }
    printf("Sum = %.2lf",sum);
    return 0;
}

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

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 嘅陳述式,會令個程式重新行過個迭代[20]
  • 重新做個迴圈:Ruby 有 retry 嘅陳述式,會令個程式將成個迴圈重新行過[20]

變量同不變量

[編輯]

迴圈變量(loop variant)同迴圈不變量(loop invariant)係用嚟評估迴圈嘅機制[21]

  • 迴圈變量指一個數值會隨住迭代而減少嘅變數,設嚟監察個迴圈運作係咪如常。例如係喺以下呢段 Whiley 碼當中,|items| - i 會係一個啱用嘅迴圈變量[22]
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 碼[23]
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] 數值理應會維持不變。

[編輯]
  1. McShaffry, M. (2014). Game coding complete. Nelson Education. p. 34.
  2. Definition of a Loop. ThoughtCo.
  3. Loops in Java.
  4. Loop Control Statements. MathWorks.
  5. while. MathWorks.
  6. Python Do While Loop.
  7. Java do-while Loop.
  8. 8.0 8.1 Java For-each Loop | Enhanced For Loop.
  9. Hoare, C. A. R. (1969). An axiomatic basis for computer programming. Communications of the ACM, 12(10), 576-580.
  10. Meerbaum-Salant, O., Armoni, M., & Ben-Ari, M. (2011, June). Habits of programming in scratch. In Proceedings of the 16th annual joint conference on Innovation and technology in computer science education (pp. 168-172). ACM.
  11. 11.0 11.1 11.2 C – break statement in C programming 互聯網檔案館歸檔,歸檔日期2019年9月6號,..
  12. Python break and continue 互聯網檔案館歸檔,歸檔日期2019年9月6號,..
  13. comp.lang.c FAQ list. "Question 20.20b".
  14. Breaking out of two loops 互聯網檔案館歸檔,歸檔日期2019年9月6號,..
  15. David Anthony Watt; William Findlay (2004). Programming language design concepts. John Wiley & Sons. pp. 215–221.
  16. [Python-3000 Announcing PEP 3136] 互聯網檔案館歸檔,歸檔日期2019年9月6號,., Guido van Rossum.
  17. Kozen, Dexter (2008). The Böhm–Jacopini Theorem Is False, Propositionally (PDF). Lecture Notes in Computer Science. 5133. pp. 177–192.
  18. Kosaraju, S. Rao. "Analysis of structured programs," Proc. Fifth Annual ACM Syrup. Theory of Computing, (May 1973), 240-252; also in J. Computer and System Sciences, 9, 3 (December 1974). cited by Donald Knuth (1974). "Structured Programming with go to Statements". Computing Surveys. 6 (4): 261–301.
  19. C break and continue 互聯網檔案館歸檔,歸檔日期2019年9月6號,..
  20. 20.0 20.1 Flanagan, D., & Matsumoto, Y. (2008). The Ruby Programming Language: Everything You Need to Know. O'Reilly Media, Inc.
  21. Meyer, Bertrand (1991). Eiffel: The Language. Prentice Hall. pp. 129–131.
  22. Winskel, Glynn (1993). The Formal Semantics of Programming Languages: An Introduction. Massachusetts Institute of Technology. pp. 32–33, 174–176.
  23. David Gries. "A note on a standard strategy for developing loop invariants and loops." Science of Computer Programming, vol 2, pp. 207–214. 1984.