沒有控制結構(control structures)的話,程式就只是一串依序執行的指令。這對極簡單的程式沒問題,但多數程式——像前面的行車路線——沒那麼單純。

行車路線裡有這樣的敘述:「持續走 Main Street,直到看見右手邊有一座教堂」、「如果因施工而封路……」。這些敘述就是控制結構,它們把程式的執行流程從單純的順序執行,改造成更複雜也更有用的流程。

本書使用類 C 風格的虛擬碼:每條指令以分號結尾,指令集合以大括號與縮排分組。

If-Then-Else#

以行車路線為例:Main Street 可能正在施工。若是如此,需要一組特別的指令來處理;否則就照原本的指令走。這類特殊情況可以用最自然的控制結構之一——if-then-else 結構來表達:

If (condition) then
{
    Set of instructions to execute if the condition is met;
}
Else
{
    Set of instruction to execute if the condition is not met;
}

前述行車路線的 if-then-else 虛擬碼版本大致如下:

Drive down Main Street;
If (street is blocked)
{
  Turn right on 15th Street;
  Turn left on Pine Street;
  Turn right on 16th Street;
}
Else
{
  Turn right on 16th Street;
}

每條指令自成一行,各組條件指令以大括號分組並縮排以利閱讀。在 C 與許多其他語言中,then 關鍵字是隱含的、因此被省略,所以上面的虛擬碼也一併省略。

語法差異只是表皮:then 與大括號的省略規則

當然,有些語言的語法要求 then 關鍵字——例如 BASIC、Fortran,甚至 Pascal。這類語法差異只是表皮,底層結構仍然相同。一旦程式設計師理解了這些語言想傳達的概念,學習各種語法變體就相當容易。由於後續章節會使用 C,本書的虛擬碼採類 C 語法,但別忘了虛擬碼可以有很多形式。

類 C 語法的另一條常見規則是:當大括號內只有一條指令時,大括號可以省略。為了可讀性,縮排仍是好習慣,但語法上並非必要。依此規則改寫,可得到等價的虛擬碼:

Drive down Main Street;
If (street is blocked)
{
  Turn right on 15th Street;
  Turn left on Pine Street;
  Turn right on 16th Street;
}
Else
  Turn right on 16th Street;

這條規則適用於本書提到的所有控制結構,而規則本身也能用虛擬碼描述:

If (there is only one instruction in a set of instructions)
  The use of curly braces to group the instructions is optional;
Else
{
  The use of curly braces is necessary;
  Since there must be a logical way to group these instructions;
}

連語法本身的描述,都可以視為一支簡單的程式。

if-then-else 有一些變體,例如 select/case 敘述,但邏輯基本相同:若這件事發生就做這些事,否則就做那些事(而「那些事」裡還可能包含更多 if-then)。

While / Until 迴圈#

另一個基本概念是 while 控制結構,它是迴圈的一種。程式設計師常常需要重複執行一組指令;程式靠迴圈達成,但必須有一組告訴它何時停止的條件,否則就會無限跑下去。

while 迴圈的意思是:當條件為真時,重複執行接下來的指令集。一隻飢餓老鼠的簡單程式可能是這樣:

While (you are hungry)
{
  Find some food;
  Eat the food;
}

只要老鼠還餓,while 之後的兩條指令就會重複。老鼠每次找到的食物量可能從一小塊碎屑到一整條麵包不等;同樣地,指令集被執行的次數也會隨找到多少食物而變化。

until 迴圈是 while 的變體,Perl 提供這種語法(C 沒有)。until 迴圈就是把條件敘述反轉的 while 迴圈:

Until (you are not hungry)
{
  Find some food;
  Eat the food;
}

邏輯上,任何 until 式的敘述都能轉換成 while 迴圈——只要把條件反轉即可。

行車路線中的「持續走 Main Street 直到右手邊出現教堂」就能這樣改寫:

While (there is not a church on the right)
   Drive down Main Street;

For 迴圈#

另一種迴圈控制結構是 for 迴圈,通常用在程式設計師想要迴圈固定次數時。「在 Destination Road 直行 5 英里」可以轉換成:

For (5 iterations)
  Drive straight for 1 mile;

實際上,for 迴圈只是帶計數器的 while 迴圈

同樣的敘述可以寫成:

Set the counter to 0;
While (the counter is less than 5)
{
  Drive straight for 1 mile;
  Add 1 to the counter;
}

類 C 語法的 for 迴圈讓這件事更明顯:

For (i=0; i<5; i++)
  Drive straight for 1 mile;

計數器叫做 ifor 敘述被分號拆成三段:

  • 第一段宣告計數器並設定初始值,此處為 0
  • 第二段像個使用計數器的 while 敘述:只要計數器符合此條件就繼續迴圈。
  • 第三段描述每次迭代要對計數器做什麼動作。此處 i++ 是「把計數器 i 加 1」的簡寫。

組合起來:完整的行車路線#

用上全部控制結構,開頭那份行車路線就能轉換成類 C 虛擬碼:

Begin going East on Main Street;
While (there is not a church on the right)
  Drive down Main Street;
If (street is blocked)
{
  Turn right on 15th Street;
  Turn left on Pine Street;
  Turn right on 16th Street;
}
Else
  Turn right on 16th Street;
Turn left on Destination Road;
For (i=0; i<5; i++)
  Drive straight for 1 mile;
Stop at 743 Destination Road;