控制流程

控制流程（粵拼：hung3zai3lau4cing4；英文：control flow/flow of control）喺電腦科學上係指一個程式當中嘅個體陳述式同子程式等以乜嘢次序執行：喺最簡單嘅情況之下，部電腦會將一個程式逐句逐句執行；但現實嘅電腦程式好少可會咁簡單，所以常用嘅程式語言冚唪唥都會包含控制流程陳述式（control flow statement）嚟俾用家操控（例如）「喺乜情況下會執行呢柞陳述式，乜情況下唔會」，或者「幾時要重複執行呢柞陳述式」呀噉。
控制流程係現代程式編寫上不可或缺嘅一部份。有好多常用嘅功能都係要用控制流程先會做到嘅。舉個簡單例子說明，一隻電子遊戲嘅程式要喺每個時間點更新個遊戲世界嘅狀態，而喺一般嘅動作遊戲當中，如果某個敵人嘅生命值變咗 0，噉嗰個敵人就要死亡，跟住個程式要將佢由個遊戲世界移走，會用到以下噉嘅碼：
呢段碼會令到個程式「如果達到呢個條件，先行呢幾行碼」－令個程式唔係簡單噉逐句逐句行嗮佢，所以係控制流程技術嘅一種。
一個控制流程可以用控制流程圖（control-flow graph）嚟表達，附圖就係一個例子。一幅控制流程圖會用一個個節點（node），每個節點代表一段一次過要做晒嘅嘢，而節點之間會有箭咀嘅線代表嗰個位有可能會跳去箭咀指住嘅位。喺電腦科學上做編程上嘅研究嗰陣，控制流程圖經常用嚟清楚噉表達一個程式。
控制流程大致上可以分做幾類，以下呢五類喺多數嘅主流程式語言當中都可以搵到：
喺電腦科學上，一個標記（label）係為源碼當中某一個位置俾嘅名或者號碼（除此之外冇任何功能）。段源碼第啲部份嘅控制流程機能可能會靠一個位置嘅標記嚟跳去呢個位置，例如goto（睇下面）就會噉做。
程式行號（line number）係喺好多程式語言（包括Python同MATLAB等）當中都有嘅一種標記，指段源碼每行前面都有一個自然數，呢啲數字一般都會負責指明嗰行係段源碼入面嘅第幾行。不過都有啲程式語言唔使啲行號連續，淨係需要啲行號係愈落個數愈大就得，好似係以下呢段BASIC碼噉，第一行碼嘅行號係「10」，而第二行碼嘅行號係「20」：
喺某啲程式語言裏面，一個標記會係一個可以用自然語言寫成嘅識別碼（identifier），例如C 程式語言或者Ada就係噉。呢啲語言嘅做法係識別碼會喺嗰行碼嘅起始嗰度出現，跟住有個冒號，冒號後面掕住嗰行碼。例如喺 C 入面：
Goto（源自英文「go to」，「走去」嘅意思）係最原始嗰種控制流程。通常用法係放「goto」喺一個標記前面，意思係叫部電腦喺嚟到呢一行嗰陣，直接無條件噉跳去個標記所標示嗰個位置，再行標記嗰行嘅碼，當中「goto」使唔使大細階就視乎程式語言而定：
喺組合語言等低級程式語言裏面，對應 goto 嘅 jump 或者 branch 基本上係控制流程嘅唯一方法。但喺高級程式語言裏面，goto 到咗今下經已唔多常用，因為 goto 俾人覺得佢好唔方便－如果喺一隻用數字做標記嘅程式語言入面用 goto，而打後個編程者加咗幾段碼嘅話，就可能會搞到啲標記走嗮位，要人手噉逐個逐個 goto 執返好佢；所以喺實際嘅高級語言編程裏面，啲人好少可用 goto（睇埋下面結構化程式定理）。
喺編程上，子程式（subroutine）係指一串包裝埋一齊嘅指令，通常用嚟做某樣特定嘅工作：一個子程序會包含若干句陳述式，當段源碼入面有一行講明要用嗰個子程序嗰陣，個程式就會行個子程序－子程序可以用嚟界定一啲個程式要做多次嘅工作，等個程式員唔使吓吓都成段碼複製一次貼落去要用嘅位嗰度，例如係以下呢段虛擬碼，就會令個程式行子程式 a 三次，等個程式員唔使將子程式 a 段碼寫三次，可以慳返啲位：
結構化編程（structured programming）係一種源自 1950 年代嘅編程範式，旨在運用各種嘅控制流程（同用呢啲控制流程取代 goto）嚟令電腦程式更加清晰同高品質，以及減少程式製作所需嘅時間。舉個例說明，如果冇咗子程式嘅使用，頭先個程式就會變成：
呢種做法有多種唔好處：個編程員喺編程嗰陣要重複將同一段碼寫幾次；如果佢想改子程式 a 入面嘅陳述式，佢就要改成三次（而用咗子程式做法嘅情況淨係需要改一次）；喺現實嘅程式製作當中，一個程序閒閒地可以需要重用十次以上，所以子程式嘅使用慳咗好多時間精神。再普遍啲講，對控制流程嘅運用－結構化編程嘅諗頭－幫編程員慳咗好多時間精神。
結構化程式定理（structured program theorem）係喺 1966 年出嘅一條定理，革新咗程式編寫呢個領域。條定理如下：是但搵個用咗 goto 嘅程式，呢個程式都可以轉化做一個冇任何 goto 喺入面、而且功能上完全一樣嘅程式，後者當中只係需要有條件運算式同迴圈。打後仲有學者指出，查實就連條件運算式都唔係必要嘅。而呢條定理表示咗，任何一個（提出當時相對普遍嘅）有 goto 嘅程式都可以變做冇 goto 而唔喪失功能－為結構化編程奠咗基。
結構化程式定理一個簡化版嘅證明如下：想像有一段用咗若干個 goto 嘅碼，而佢啲 goto 冚唪唥都指咗向正確位置。當Si{\displaystyle S_{i}}做第i{\displaystyle i}句陳述式，整一個變數l{\displaystyle l}代表現時位置，將成段碼搵個 while 迴圈包住：
然後再跟以下規則改寫段碼：
做咗呢三個步驟之後，得出嗰段碼喺功能上會同原本嗰段一樣，但冇嗮啲 goto －變咗一段更加易用又不失原有功能嘅源碼。呢條定理嘅證明令到編程呢個領域嘅人放棄咗「一個程式梗要有 goto」嘅諗法，確立 goto 係一樣不必要（而且撈絞）嘅嘢，而有電腦科學家指出，放棄 goto 嘅使用令電腦程式變得更加易明同易改。
條件決策係控制流程陳述式一種，大致上有兩類：
條件陳述式（conditional statement；if... then...）係絕大多數程式語言都會有嘅一種陳述式，功能係視乎情況決定係咪要做某啲運算同採取某啲行動：一個條件陳述式會掕住一柞碼同一句佢要評估嘅條件，當個程式行到個條件陳述式嗰時，如果個條件係真，個程式就會行條件陳述式掕住嗰柞碼（if 「個條件係真」 then 「行個條件陳述式掕住嘅碼」），否則個程式就唔會行嗰柞碼。典型嘅形態有以下呢啲：
比較少見嘅條件陳述式玩法有：
開關陳述式（switch statement）會攞個特定變數嘅數值，將個數值同一柞預先講好咗嘅常數嘅數值比較，再選擇同個變數吻合嗰個常數，做嗰個常數後面掕住嗰柞碼。喺多數程式語言入面，段碼仲會掕住一個ELSE，表示「如果嗰個變數唔吻合任何一個常數嘅值，要行嘅碼」。原則上，開關陳述式係不必要嘅，因為一個開關陳述式可以寫做一個有大量ELSE IF嵌套在內嘅條件陳述式，但一般認為，開關陳述式喺要應對好多個個案嗰陣會清楚簡潔啲。開關陳述式嘅典型形態如下：
迴圈（loop）係另一種不可或缺嘅控制流程陳述式。一個迴圈係一串陳述式，特徵係喺段碼嗰度淨係講咗一次，但會喺個程式行嗰陣行幾次：一個迴圈會掕住若干句陳述式同重複條件，個程式會按照個迴圈所指定嘅條件，重複噉執行掕住嗰柞陳述式若干次，直至個條件達到為止，而每一次個程式檢查「個條件達到未」就係一個迭代（iteration）。迴圈嘅使用令到程式編寫方便好多，同一段要用多次嘅碼淨係需打一次。廿一世紀常用嘅程式語言冚唪唥都會包含迴圈，而一啲高級程式語言仲會有多過一種迴圈，例如C 程式語言、C++、同C#等都支援好幾種迴圈嘅使用，當中 C 仲可以用一種陳述式包嗮所有種類嘅迴圈。
For 迴圈（for loop）係幾乎所有程式語言都有嘅迴圈。一個 for 迴圈會有個關鍵字，後面有一個條件，而跟住嗰幾行碼就係掕住碼，只要個條件一日係真，個程式會一路係噉行柞掕住碼，例如係以下呢段用Java寫嘅碼噉：
喺一個 for 迴圈當中用嚟數迭代嗰個變數（此後叫「i」）跳嘅一步好多時可以預設當做 1：原則上，i每次跳可以跳多過 1 而成個 for 迴圈功能不變，例如(int i = 0; i < 200; i = i + 2)查實一樣會令段掕住碼行 100 次；不過喺實際應用上，好多人都嫌吓吓要指明「i每步要跳 1」麻煩，所以唔少程式語言會預設咗i每步都係跳 1，唔使用家講明。好似係MATLAB噉：
While 迴圈（while loop）係廿一世紀初多數程式語言都有嘅迴圈。一個 while 迴圈會有一個條件，同一柞掕住嘅碼。個程式會評估個條件，如果個條件係真，噉就會行柞掕住碼，行完一次之後再睇吓個條件係咪真，如果係就再行多次柞掕住碼，一路重複直至個條件唔係真止。While 迴圈一個應用例子係電子遊戲編程：一隻電子遊戲嘅一場對局可以想像成個遊戲程式一路做場對局嘅運算，直至某個GAME OVER條件（例如玩家角色生命值變咗 0）達到為止。以下呢段 MATLAB 碼用咗 while 迴圈計 10 嘅階乘（factorial）：
Do-while 迴圈（do-while loop）係同 while 迴圈好相似嘅迴圈，分別在於幾時評估個條件：一個 do-while 迴圈都係會有一個條件，同一柞掕住嘅碼。個程式會行一次段掕住碼，睇吓個條件係咪真，如果係，噉就再行多次段掕住碼，跟住再睇吓個條件係咪真，如此類推。喺一個程式嘅執行過程當中，一個 do-while 迴圈起碼會行一次，而一個 while 迴圈有機會可以完全一次都唔行，所以 do-while 迴圈有陣時可以做啲 while 迴圈做唔到嘅效果，不過有好多受歡迎嘅程式語言都冇 do-while 迴圈嘅功能（例子有 Python）。
以下呢段 Java 碼就用咗 do-while 迴圈：
Foreach 迴圈（for each loop）係一種迴圈，會叫個程式攞某柞嘢，為（for）柞嘢當中每一件（each）行段掕住碼一次。Foreach 迴圈最常係夾埋數組（array；一個數組包含一列各自獨立嘅數值）一齊用，例子有以下呢段 Java 碼：
就算個數組入面嘅嘢唔係數字，foreach 迴圈都行得通：
無限迴圈（infinite loop）係指一個永遠都行唔完嘅迴圈，原因可能係因為個迴圈根本冇終止條件、有一個冇可能達得到嘅終止條件、又或者有一啲會搞到個迴圈重新啟動嘅陳述式－通常係個編程員無意中犯錯先會噉。喺原始啲嘅廿世紀電腦當中，無限迴圈會搞到部電腦輕機，不過先進啲嘅電腦會識喺出現無限迴圈嗰時話俾個使用者知，等個使用者決定好唔好繼續行落去，又或者俾個用家喺無限迴圈出現嗰時人手終止個程式。
無限迴圈例子有以下呢啲：
一段 Java 碼：
一段Visual Basic碼：
有陣時，個編程員會想個程式喺某個條件達到嗰時停止迴圈。例如家陣有段碼，用個 while 迴圈係噉耖一柞數據，由頭耖到落尾，理想嘅話，個程式喺搵到想要嗰個數據嗰時，會離開個迴圈，而唔係繼續耖柞數據。因為呢個緣故，廿一世紀初嘅程式語言多數都會支援break（離開迴圈）嘅功能；同時，Visual basic 有exit，而 Perl 有last，兩者都係做同樣嘅功能－即刻終止個迴圈，令個程式直接行個迴圈嘅下一行陳述式。呢種情況有時會俾人嗌做「提早離開迴圈」（early-exit loop）。
提早離開迴圈典型嘅做法係有個迴圈，喺個迴圈入面有一個條件陳述式，如果一個條件成立，個程式就要行break，否則個程式就繼續行個迴圈。好似係以下呢段用 C 寫嘅碼噉：
呢段碼嘅輸出會係噉嘅：
某啲程式語言仲支援「視乎個迴圈有冇break，決定行唔行某一段碼」，例如以下呢段 Python 碼：
呢段碼會俾：
而如果將段碼改做：
就會出：
即係話else:跟住嗰段碼只會喺個迴圈冇break嗰陣被執行。
喺要應付嵌套住嘅迴圈（nested loop；喺另一個迴圈內嘅迴圈）嗰陣，break嘅使用就比較撈絞：
有唔少編程專家都懷疑「容許用家一嘢離開嗮所有行緊嘅迴圈」係咪一樣好嘢，例如喺廿一世紀初嗰陣，Python 就試過有人提議加多層離開功能，但就俾人以「呢個功能會造成多餘嘅複雜性，而佢嘅用途太少，根本唔值得加」為由否決。
多層離開嘅諗頭喺理論電腦科學（theoretical computer science）當中引起唔少人思考：喺 1973 年，有電腦科學家執咗吓結構化程式定理，跟手仲證明，只要一隻程式語言有多層離開嘅功能，就可以避免加多啲變數－是但搵個整數n，都會有個程式具有一個離開n個迴圈嘅多層離開，而呢個程式唔可以重寫做一個具有離開m個迴圈（m<n）、而且變數數量一樣或者更少嘅程式。既然多層離開嘅使用容許一個程式有少啲變數，即係呢種功能喺某啲情況下可以減低程式嘅複雜性。
呢段碼嘅輸出望落會係噉嘅：
迴圈變量（loop variant）同迴圈不變量（loop invariant）係用嚟評估迴圈嘅機制：
有好多隻程式語言會容許非區部控制流程（non-local control flow）嘅功能，即係話俾一個程式離開某個流程，跟住再喺某個特定嘅點返入去個流程嗰度。常見嘅非區部控制流程功能有以下呢啲：
非區部控制流程最簡單嘅做法係用 goto：例如家陣有一個迴圈，個編程員想個程式喺某啲情況下，跳出個迴圈外面，跟住喺某啲情況下又跳返入去；最直接嘅做法係喺個迴圈入面加好似以下噉嘅條件陳述式：
然後再喺外面某啲地方加 goto 條件陳述式，令個程式喺某啲情況跳返入個迴圈嗰度。
例外處理（exception handling）喺電腦編程上係指對異常個案嘅處理。廿一世紀嘅程式語言多數都會有一啲特定嘅機制俾用家做例外處理－包括喺行一個迴圈，撞到一個異常情況嗰陣，叫個程式去行第段（迴圈以外嘅）碼再返去個迴圈嗰度。例如 C++ 就有噉嘅功能：
呢段碼會俾噉嘅輸出：
以上呢個例子用咗throw一個整數x，但catch可以配合任何數量或者類型嘅變數使用，例如throw兩個整數變數，又或者throw三個float類型嘅變數呀噉。如果冇任何一個catch吻合掟咗出去嗰個或柞變數，個程式通常會去個try以外搵相應嘅catch（呢點視乎程式語言可能有異），如果搵唔到就會向用家顯示一個「出錯」嘅信息。
第啲程式語言當中都有啲類似嘅功能：受到 C++ 影響，有多款程式語言都會用catch呢個字做同樣嘅功能，例子有 Java 同 C#；另一方面，又有啲語言（例如 Ada）興用exception呢個關鍵字嚟做例外處理同用第啲關鍵字做catch嘅功能。好似係以下呢段用AppleScript寫成嘅碼噉：
某啲程式語言當中有finally嘅功能，配合try嚟使用。無論個程式點樣離開個try，finally掕嗰柞碼都會被執行。喺實際編程上，呢種功能可以要嚟確保個程式喺行完個try之後實會關某個檔案或者同數據庫斷線（喺某啲情況下可以慳好多資源）。包括 Java、C#、同 Python 等都具有呢種功能，例如以下呢段 C# 碼：
而因為「想喺做完個迴圈之後關咗個檔案佢」好普遍，有啲程式語言甚至索性有功能做呢樣嘢。例如係 C# 噉：
C# 程式語言內置咗，當個程式離開using嗰陣，個編譯器會確保啲記憶體會釋放stm呢嚿嘢（stm係一個檔案等），即係教部電腦暫時將個檔案開咗佢，用完就將佢由記憶體嗰度釋放。Python 嘅with以及 Ruby 嘅File.open都會達到類似嘅效果。
協程（coroutine）係子程式嘅一種廣義化。當個程式要求行一個子程式嗰時，佢會由個子程式開頭嗰度行，行到尾嗰時就離開個子程式；一個子程式只會行一次，而喺每次行個子程式之間嗰段時間，部電腦唔會儲住「個子程式喺乜狀態」嘅資訊；相比之下，一個協程（協程 A）可以要求另一個協程（協程 B），但離開協程 A 去行協程 B 之後，個程式會記住佢喺邊個點離開協程 A 同埋協程 A 當時嘅狀態，行完協程 B 之後可以返去原先喺協程 A 個位繼續行。一段用協程嘅碼望落大致會係噉嘅：
控制流程嘅技術喺遊戲編程（game programming；編寫一隻電子遊戲嘅程式）上不可或缺：一隻電子遊戲程式嘅根基係遊戲迴圈（game loop）－隻遊戲嘅程式要係噉重複做同一樣嘅工作，攞玩家俾嘅輸入以及隻遊戲喺上一刻嘅狀態，再按照隻遊戲嘅法則計算出遊戲世界下一刻嘅狀態應該係點。即係話一隻電子遊戲大致上可以想像成噉嘅碼：
以上呢段碼就噉睇落好簡單，但查實可以好複雜：輸入可以包括咗鍵盤、踎士同遊戲機手掣等等，而且控制方案嘅設計可以高深得好緊要（例：「要用邊啲掣做輸入先可以令玩家覺得舒服就手？」）；更新個遊戲世界即係要攞玩家嘅輸入加埋個世界前一刻嘅狀態，用一大堆（表示個遊戲世界運作法則嘅）碼計出下一刻嘅狀態應該係點，例如如果玩家撳咗「向前」，玩家所控制嘅角色嘅位置就要向住佢面向嘅方向改變，而改變幅度由個角色嘅移動速度決定；跟手個程式仲要有計將個新狀態顯示俾喺個熒光幕上面，等個用家有得睇。喺現實應用裏面，上述嘅過程閒閒地可以涉及幾萬行碼。
用以下呢段食鬼嘅虛擬碼為例：
以上嘅碼用咗 while 迴圈（令隻遊戲只要 GAME OVER 條件未達到就一路行）、foreach（處理嗮遊戲世界入面每一件物件）、同條件運算式（如果某啲條件達到，某件物件就要死亡）等嘅多種控制流程機制－控制流程令電子遊戲成為可能。