一、學習目標
重點
- 理解嵌套循環的角色分工:外層決定「做幾輪」,內層決定「每輪做幾次」。
- 能用「格子/表格」視覺化理解:外層走列(row),內層走欄(col)。
- 掌握執行次序與總次數:內層本體通常執行 rows × cols 次。
- 能用嵌套循環完成常見題型:圖案輸出、計數、加總、搜尋。
- 能清楚說明縮排的重要性,並能針對嵌套循環常見錯誤進行除錯。
嵌套循環(nested loop)是「一個循環的內部,再放入另一個循環」。外層循環每做一輪,內層循環就會由頭到尾完整跑一次。
你可以把它想像成:
- 外層:做「第 1 行、第 2 行、第 3 行……」
- 內層:在同一行內,做「第 1 格、第 2 格、第 3 格……」
因此,若外層做 rows 輪,而每輪內層做 cols 次,內層的主體合共做 rows × cols 次。
最常見的例子是「印方格」:
外層每次負責一行;內層每次負責一個星號。內層完成後要換行,才能開始下一行。
若問題只有「一個方向」(例如:印 1 到 10),單一循環已足夠。
但當問題是「兩個方向」:例如座位表(列×欄)、棋盤格(row×col)、二維成績表(學生×測驗),就需要兩層才能自然表達。
- 縮排錯:內層語句沒有比外層多縮排一層,會令程式結構完全改變,甚至直接語法錯誤。
- 換行位置錯:
print()(換行)放到內層,會變成「每印一個字元就換行」。 - 重置位置錯:例如把
col = 0放錯層,會令內層不會由 0 重新開始,造成漏印或無限循環。
學嵌套循環最有效的方法不是死記語法,而是把它看成「兩個方向的重覆」。當你能把問題畫成格子(表格),你就已經掌握了嵌套循環的核心思維:外層走列,內層走欄。
在本章,你不但會看見偽代碼與 Python 的對照,也會用可操作的小遊戲把執行次序「玩出來」:你會親手按「下一步」看到程式走到哪一格、甚麼時候換行、為甚麼總次數是 rows × cols。
最後,你會特別學會縮排在 Python 中的重要性。縮排不只是美觀,它直接決定「哪一句在外層」「哪一句在內層」,因此也最容易成為嵌套循環除錯的關鍵。
二、概念引入:外層與內層在做甚麼?
重點
- 把「rows×cols」想成表格:外層循環負責「第幾列」,內層循環負責「第幾欄」。
- 內層循環每次都由頭跑到尾,然後外層才會進入下一輪。
- 輸出圖案時,常見規則是:內層負責輸出「同一行的內容」,外層每輪結束後再換行。
- 建議用清晰變量名:
row/col比i/j更不易混淆。
在嵌套循環中,你可以先問兩個問題:
- 外層:我要重覆「幾輪」?(例如 3 行)
- 內層:每一輪內,我要重覆「幾次」?(例如每行 5 個字元)
程式執行時的節奏是固定的:
- 外層開始第 1 輪。
- 內層由起點一路跑到終點(完整跑完)。
- 回到外層,進入第 2 輪,再把內層完整跑一次。
因此,當你看到「內層跑完一次」時,就應該聯想到:「外層準備換到下一行/下一列」。
以下以「輸出 rows×cols 的星號方格」為例,示範 DSE 常見的 4 種循環偽代碼寫法。注意:偽代碼與 Python 不完全同語法,但邏輯一致。
讀入 rows
讀入 cols
設 row 由 1至 rows
設 col 由 1至 cols
輸出 "*"(不換行)
輸出換行
讀入 rows
讀入 cols
row = 1
當 row ≤ rows
col = 1
當 col ≤ cols
輸出 "*"(不換行)
col = col + 1
輸出換行
row = row + 1
讀入 rows
讀入 cols
row = 1
執行
col = 1
執行
輸出 "*"(不換行)
col = col + 1
當 col ≤ cols
輸出換行
row = row + 1
當 row ≤ rows
(do while 會「至少執行一次」。此寫法一般假設 rows、cols 為正整數。)
讀入 rows
讀入 cols
row = 1
重覆
col = 1
重覆
輸出 "*"(不換行)
col = col + 1
直至 col > cols
輸出換行
row = row + 1
直至 row > rows
(repeat until 同樣「至少執行一次」。)
兩者最大分別在輸出格式:
- 換行在外層後:每一行輸出完才換行(最常見)。
- 換行在內層:每輸出一個字元就換行,圖案會「直落」變成一條長柱。
常見錯誤包括:
- 外層用
cols,內層用rows(結果圖案旋轉或尺寸錯)。 row/col變量名混用,導致判斷條件寫錯。- 以為外層跑一次,內層不會重新由起點跑(其實會)。
你可以把嵌套循環看成「雙重節拍」:外層每拍一下,就讓內層完整跑完一次。這個節奏不會因題目不同而改變,改變的只是你在內層做甚麼(輸出、加總、計數、判斷)。
最直觀的應用是「輸出方格」:外層代表「行」,內層代表「每行的字元數」。只要你把換行(或輸出下一列)放在外層循環的末端,就能穩定地得到一行一行的輸出。
接下來,請先用下面的互動格子「玩一次」:你按一下「下一步」,就會看到內層如何在同一行逐格前進;當一行走完,程式才會回到外層並換行。
| 項目 | 外層循環(outer loop) | 內層循環(inner loop) |
|---|---|---|
| 主要責任 | 控制「做幾輪」(通常對應行/列) | 控制「每輪做幾次」(通常對應欄/格) |
| 常見變量名 | row、r |
col、c |
| 最常見的輸出 | 每輪末尾做一次換行 | 輸出一個字元/處理一個格子 |
| 最常見錯誤 | 用錯上界、忘記外層更新 | 忘記重置內層計數器、縮排不正確 |
你而家「行緊」邊一格?
按下一步觀察:內層先走完同一行的所有欄,然後才會換到下一行。
真正的 Python 程式輸出會在程式碼下方的黑色輸出框顯示。
模擬輸出(像印方格一樣)
Python 小練習:印出 rows×cols 的方格
請先按 Run 程式(系統會要求你輸入 rows、cols),觀察輸出,再按 核對答案 用隱藏測試檢查。兩題做的是同一件事:第一題用 for loop,第二題用 while loop。
練習 S0-1(for loop)
練習 S0-2(while loop)
Check Point 1:外層與內層的角色
重點
- 外層通常對應「行/列」;內層通常對應「欄/格」。
- 內層完成一次=同一行完成;然後外層才會進入下一行。
- 答題時先畫格子,再對應 row/col,最不易錯。
若你在輸出或掃描表格:外層 = 行(row),內層 = 欄(col)。先把問題畫成格子,再把格子變成兩個方向的重覆,幾乎不會錯。
內層循環寫在外層循環裡面,所以每次外層進入新一輪,程式會重新執行內層循環的初始化與條件檢查。
因此,若你用 while 寫內層,務必在外層每輪開始前把 col 重設,否則 col 會停留在上一輪的值,導致內層不會再次執行。
在輸出圖案題中,最常見的結構是:
- 內層負責輸出一行的內容(不換行)。
- 外層每輪末尾輸出一次換行(結束該行)。
把外層想成「每句說話的句號」,就知道換行應放在外層末尾。
用 i/j 不算錯,但當題目牽涉到表格、座標、圖案時,row/col 會令你更快看出邏輯,亦更容易在除錯時定位問題。
把外層與內層寫反,程式仍可能能跑,但圖案會被「轉置」或尺寸不正確。例如把外層跑 cols、內層跑 rows,結果會變成 cols 行、每行 rows 個字元。
小測:外層/內層分工
(載入中…)
三、執行次序與次數:為甚麼常見答案是 rows × cols?
重點
- 若外層跑
rows輪,而每輪內層跑cols次,內層本體合共跑 rows × cols 次。 - 把「第幾次內層執行」對應到格子:可用追蹤表(trace table)釐清。
- 常見掃描次序是 row-major:先行後列(同一行走完才到下一行)。
- 除錯時可暫時輸出
row/col,用輸出定位「哪一步開始不對」。
只要內層每次都完整跑完,總次數就可以直接相乘:
這是因為外層每輪都「帶住」內層再跑一次。
追蹤表(trace table)就是把每一次循環的變量值記下來,例如:
- 外層:row = 0,1,2...
- 內層:col = 0,1,2,3...
你會看見 col 每行都會由 0 重新開始;每當 col 走到尾,row 才 +1。
以下以 3×4(rows=3, cols=4)示意「先行後列」:
(0,0) (0,1) (0,2) (0,3) (1,0) (1,1) (1,2) (1,3) (2,0) (2,1) (2,2) (2,3)
若把每一格視為一次內層執行,總共有 3×4 = 12 格。
只看最後輸出,很難知道錯在哪裡;但如果你暫時印出 row 與 col,例如:
你就能立即看出:是否漏了某些 col、是否提前換行、是否 row 根本沒有前進。
很多人直覺寫成 rows + cols,但那只代表「兩條邊的長度」,不代表格子總數。格子總數(或內層本體總次數)必須是相乘。
當你能把嵌套循環想成「走格子」,你就會自然得出 rows × cols。因為每一格都會執行一次內層本體,而格子總數正是「列數 × 欄數」。
實戰除錯時,最有效的方法是建立追蹤表:把 row、col 每一步的值寫下來(或暫時用 print 印出)。你會很快發現:錯誤往往不是在內層本體本身,而是「重置」或「更新」的位置放錯了。
下面有兩個互動練習:第一個是「次數計算器」,第二個是「步數 ↔ (row,col) 對應」。做完後,你會更有把握面對「問你執行幾次」這類題目。
總次數
內層本體(例如輸出一個字元)總共會執行:
步數 ↔ 座標
以 row-major(先行後列)計:
Python 小練習:把『次數』與『座標』算出來
練習 S1-1:輸入 rows、cols,輸出內層本體總次數
練習 S1-2:輸入 rows、cols、step(由 1 開始),輸出 (row, col)(由 0 開始)
Check Point 2:總次數與追蹤表
重點
- 內層本體總次數通常是 rows × cols。
- row-major 掃描:同一行走完才到下一行。
- 追蹤表可協助找出「重置」與「更新」放錯位置的錯誤。
當你把每一次內層本體(例如輸出一個字元、加總一個格子)視為一步,step 就等於已處理的格子數。
若 step 由 1 開始,cols 已知:
反過來:
計算:
所以第 7 步是 (1,2)。
有些題目把 step 由 0 開始(第 0 步),這時公式會少了 -1 與 +1。做題前先確認題目定義,避免整體偏移一格。
// 是「整除」,用來求 row;% 是「餘數」,用來求 col。把兩者寫反,會得出完全錯誤的座標。
小測:次數與追蹤
(載入中…)
四、圖案輸出:嵌套循環 + 規則(if 條件)
重點
- 圖案題通常分兩步:先寫嵌套循環骨架,再在內層加入 if 規則決定輸出甚麼。
- 內層輸出通常要「不換行」,外層每輪末尾才換行。
- 常見規則:
row == col(對角線)、(row+col)%2(棋盤格)、邊框判斷(row=0 或 row=rows-1 或 col=0 或 col=cols-1)。 - 善用表格視覺化:先在紙上圈出要輸出的格子,再翻譯成條件。
把圖案想像成一張表格。嵌套循環逐格掃描每一格,在每一格做判斷:輸出 * 還是輸出空格/點號。
最佳做法:
- 先寫好外層/內層(確保換行位置正確)。
- 再在內層加入 if 條件,決定輸出甚麼。
這樣除錯時可以分開處理:先確保「走格子」對,再確保「規則」對。
若希望「黑白相間」,可用:
因為相鄰格子的 (row+col) 會相差 1,奇偶性就會交替。
- 對角線:通常是一個等式(
row == col或row+col==n-1)。 - 邊框:通常是多個 OR 條件(
row==0 or row==rows-1 or col==0 or col==cols-1)。
圖案題的錯誤很多時不是邏輯錯,而是輸出格式錯:多了空格、少了換行、或換行位置錯。建議先用小尺寸(例如 3×4)測試,逐行核對。
圖案輸出其實是一種「逐格判斷」:你走到哪一格,就判斷那一格應該輸出甚麼。只要你能把圖案畫成格子,你就能把規則翻譯成 if 條件。
學習時,請先把注意力放在兩件事:第一,換行是否只在外層末尾出現;第二,if 條件是否只影響「輸出內容」,而不影響走格子的次序。
下面的「圖案實驗室」可以讓你選擇不同規則並點擊任意格子,看清楚該格的 (row,col) 以及為何被判定為輸出/不輸出。
規則說明
—
你點擊的格子
Python 小練習:兩個常見圖案
練習 S2-1:棋盤格(for loop)
練習 S2-2:靠左直角三角形(while loop)
以下示範「輸出靠左直角三角形(高度 n)」的 4 種偽代碼循環寫法(DSE 常見)。
讀入 n
設 row 由 1至 n
設 col 由 1至 row
輸出 "*"(不換行)
輸出換行
讀入 n
row = 1
當 row ≤ n
col = 1
當 col ≤ row
輸出 "*"(不換行)
col = col + 1
輸出換行
row = row + 1
讀入 n
row = 1
執行
col = 1
執行
輸出 "*"(不換行)
col = col + 1
當 col ≤ row
輸出換行
row = row + 1
當 row ≤ n
讀入 n
row = 1
重覆
col = 1
重覆
輸出 "*"(不換行)
col = col + 1
直至 col > row
輸出換行
row = row + 1
直至 row > n
Python 沒有 do while / repeat until;常用 for 或 while 來表達相同邏輯。
Check Point 3:圖案輸出與換行位置
重點
- 內層輸出通常不換行;外層末尾才換行。
- if 規則只決定輸出內容,不應打亂走格子的次序。
- 錯誤多數出在輸出格式(空格/換行)或條件判斷寫錯。
你可以把每一行想成一條字串。內層循環逐個字元輸出,外層循環負責「結束該行」。
在 Python 中,print() 預設會換行;若希望同一行繼續輸出,可用:
待一行完成後,再用一次 print() 產生換行。
若把 print()(換行)放到內層,結果會變成:
* * * * ...
也就是每個字元一行,圖案完全不同。
先寫骨架(含換行)可以確保行列數正確;再寫 if 條件只影響輸出內容。反過來寫,往往很難判斷錯誤來源。
忘記 end="" 會導致每次 print 都換行;而把 end 寫成 "\n" 亦等同換行,效果相同。
小測:換行放在哪裡?
(載入中…)
五、二維資料掃描:把表格資料逐格處理
重點
- 二維資料(例如成績表、棋盤)可以用「列×欄」表示,並以
arr[row][col]存取。 - 嵌套循環是最自然的逐格掃描方式:外層走 row,內層走 col。
- 典型任務:加總、計數、搜尋、找最大/最小、檢查是否符合條件。
- 除錯時先確認 row/col 是否寫反,range 的上界是否正確。
不論你用陣列或清單表示,二維資料都可以視為格子。要逐格處理,就需要兩個索引:row 與 col。
把所有任務都塞進內層「處理」位置,就能完成計數/加總/搜尋等工作。
加總:每格都加進 total。
搜尋:遇到目標值就記錄 found,甚至可以提早停止。
大部分題目都用逐列(外層 row、內層 col)。若題目要求「每欄統計」,可考慮把外層/內層調轉(外層 col、內層 row)。
- range 上界:
range(rows)代表 0 到 rows-1,不要寫成range(rows-1)。 - 顛倒:
arr[col][row]會取錯格或越界。
嵌套循環不只用來「印圖案」,更常用來處理表格資料。當你面對二維資料題目時,第一步通常是把資料讀入成一張表;第二步就是用嵌套循環逐格掃描。
在逐格掃描時,請把「內層本體」視為你的工作區:你要加總、計數、或判斷某條件,都放在內層。外層與內層的責任仍然不變:外層帶你走到下一行,內層帶你走過該行所有欄位。
下面的互動示範提供一張固定的 2×3 表格。你可以按「下一步」看到掃描次序,同時看見加總如何累積;亦可以切換到搜尋模式,看見找到目標時如何停止。
狀態
Python 小練習:加總與搜尋
練習 S3-1:讀入 rows、cols 及表格內容,輸出總和
練習 S3-2:搜尋 target(找到輸出 FOUND,否則輸出 NOT FOUND)
Check Point 4:二維掃描與索引
重點
- 一般 list-of-lists 用
arr[row][col],先列後欄。 - 嵌套循環逐格掃描,內層放「處理該格」的程式碼。
- 索引範圍常由 0 開始:row=0..rows-1,col=0..cols-1。
二維資料最重要的讀法是:arr[row][col] 代表「第 row 列、第 col 欄」那一格。
range(rows) 會產生 0 到 rows-1 的索引。若 rows=3,row 只會是 0、1、2。
row=0: col=0,1,2 row=1: col=0,1,2
搜尋通常用 found 記錄是否找到;計數則用 count += 1 逐格累積。兩者都需要嵌套循環逐格檢查。
在一般 Python list 中,arr[row,col] 不是正確寫法;正確做法是兩層索引:arr[row][col]。
小測:二維掃描
(載入中…)
六、縮排的重要性:嵌套循環最常見的除錯關卡
重點
- 在 Python 中,縮排是語法的一部分:它定義程式區塊,不只是美觀。
- 嵌套循環的縮排決定「哪一句在外層」「哪一句在內層」,一縮錯就會改變執行次數與輸出格式。
- 常見錯誤:換行
print()放錯層、內層計數器重置放錯層、更新變量放錯層造成無限循環。 - 除錯策略:先用小輸入測試,再暫時印出
row/col驗證走格子次序。
在 Python,縮排(通常 4 個空格)用來表示「哪些語句屬於同一個區塊」。例如 for 或 while 後面的縮排區塊,就是循環會重覆執行的部分。
把同一句 print() 放在不同縮排層,含義會完全不同:
- 在內層:每一格都換行。
- 在外層末尾:每一行才換行。
正確(每行印完才換行):
錯誤(每個字元都換行):
縮排錯有兩種後果:
- 語法錯:縮排不一致(例如同一層混用不同數量空格),程式可能直接報錯。
- 邏輯錯:縮排一致但層次錯(例如把換行放入內層),程式能跑但輸出錯。
- col 重置:必須在外層每輪開始時做一次,否則內層可能不會執行。
- row 更新:必須在外層末尾做一次,若放到內層會導致 row 增長過快,甚至提早結束。
在嵌套循環中,縮排不只是「排版」,而是「結構」。你可以把縮排看成括號:同一縮排層就像同一對括號內的語句。
很多同學明明懂 for/while 的概念,卻在輸出題中失分,原因往往是把某句放錯層。最典型是換行:它只應該在完成一整行後出現,否則輸出會「散開」。
下面的互動示範讓你一鍵切換「正確縮排」與「錯誤縮排」,並即時看到輸出有何差別。之後你會做 3 題除錯練習,專門訓練你快速判斷縮排層次。
程式碼(概念示範)
輸出比較
按「播放」後,下方會顯示輸出結果。
Python 除錯小練習(縮排導向)
Debug D1:換行縮排錯(修正後應輸出 3 行,每行 4 個 *)
Debug D2:col 重置位置錯(提示:每一行開始前都要把 col 變回 0)
Debug D3:加總位置錯(提示:total 只能初始化一次)
Check Point 5:縮排與除錯
重點
- 縮排在 Python 中是語法,代表區塊。
- 換行、重置、更新放錯層,會令輸出或次數完全改變。
- 除錯時先用小輸入,再用輸出 row/col 驗證執行次序。
同一句話放在不同縮排,就等於「屬於不同循環」。因此在嵌套循環中,縮排其實是你在回答:這句是外層做?還是內層做?
除錯嵌套循環時,先找「最可能放錯層」的句子:
- 換行
print() - 重置
col = 0 - 更新
row += 1或col += 1
把換行放錯層,輸出會立刻由「方格」變成「直柱」。因此此類題目最先要檢查換行位置。
有些縮排錯誤看起來只差一點,但實際上會令某句由「每行一次」變成「每格一次」。因此不要只憑感覺,請用小輸入驗證。
用 while 時若忘記更新 row 或 col,條件永遠為真就會無限循環。這時程式會「卡住」,應立即檢查更新語句是否存在及是否放在正確縮排層。
小測:縮排觀念
(載入中…)
七、20 題除錯挑戰:偽代碼 → Python(每題只修正一行;提示需點按才顯示)
重點
- 每題會先提供情境/題意(用書面語說清楚要做甚麼),再提供偽代碼與一段有錯的 Python 程式。
- Python 程式中只有一行會標示為
# BUG(不會在程式碼內直接「提示答案」)。 - 你的任務是:只修正那一行(或把它移到正確縮排層),令輸出符合題意與偽代碼。
- 提示已移到題目下方的「提示」摺疊區:請先自行嘗試,必要時才點按展開。
- 建議:先按 Run 程式 觀察錯誤輸出,再按 核對答案 用隱藏測試檢查。
- 如核對失敗,可優先檢查:換行位置、內層計數器重設位置、range 上界、row/col 是否寫反、型別是否一致。
除錯的目標是找出「實際執行」與「期望邏輯」之間的落差。你應該用輸出、追蹤表、或暫時的 print(row, col) 來建立證據。
很多錯誤其實只是一行放錯位置(尤其縮排)。每題只改一行能訓練你快速判斷:「這句應該屬於外層還是內層?」
換行錯放在內層會令每個字元都換行。把它移到外層末尾,輸出就會重新變成每行一行。
- for:常錯在 range 上界、或把 row/col 寫反。
- while:常錯在忘記更新/重置變量,或更新語句縮排錯導致無限循環。
若你只是把輸出字元由 * 改成 #,但走格子的次序仍錯,題目仍會錯。請先確保走格子(外層/內層/換行)正確,再調整輸出內容。
以下 20 題已設計為「每題只修正一行」。你可以把它當成模擬考的快速操練:每題先跑一次看錯誤,再用縮排與追蹤思維定位問題。