歡迎來到模組化程式編寫:程序與函數!
你有沒有試過用樂高積木(Lego)砌一座巨大又複雜的城堡?如果你嘗試用一整塊完整的塑膠直接雕刻出整座城堡,萬一做錯了,就很難修改或修理。相反,我們會用一粒粒較小、獨立的積木把它們拼砌在一起。要是其中一粒積木顏色錯了,你只需更換那一粒就可以了!
編寫電腦程式的原理也是完全一樣的。程式員不會一口氣寫出數千行冗長又混亂的程式碼,而是會將程式碼拆解成較小、可重複使用且易於處理的模組。這就叫做模組化程式編寫(Modular Programming)。
如果一開始覺得有點陌生或困難,千萬不用擔心!讀完這份學習筆記後,你就能掌握如何將問題拆解,並學會使用程序(Procedures)和函數(Functions)來建立屬於你自己的迷你可重用程式。
---1. 甚麼是模組化程式編寫?
模組化程式編寫是一種軟件設計技術,它將一個大型電腦程式拆分成較小、獨立且易於管理的區塊,這些區塊稱為模組(Modules)或子程序(Subroutines)。
子程序是一段擁有特定名稱、獨立且用於執行特定工作的程式碼區塊。子程序一旦寫好,你就可以在主程式的任何地方隨時執行(或稱「呼叫 / Call」)它。
為甚麼我們要使用模組化程式編寫?
將程式拆解成子程序,能為程式員帶來四大超能力:
• 問題分解(Decomposition):這代表將一個龐大複雜的問題拆解成較小、更容易解決的細小部分。編寫和解決五個小任務,比起一口氣解決一個巨大的難題容易得多。
• 程式碼重用性(Code Reusability):子程序只需編寫一次,便能重複使用多次。你不需要浪費時間把相同的程式碼重複輸入十次!
• 易於維護與除錯(Maintainability and Debugging):如果程式碼出現 Bug(錯誤),模組化程式編寫能讓你輕鬆找出並修正問題。你只需測試該特定子程序,而不需要在數千行程式碼中大海撈針。
• 抽象化(Abstraction):主程式只需要知道該子程序的作用以及如何呼叫它,完全不需要理會子程序內部的複雜運作細節。
重點提示:子程序就像程式的積木一樣。它們能幫你節省時間、避免重複編寫相同的程式碼,並讓除錯變得更輕鬆。
---2. 兩大類子程序:程序 vs 函數
你需要認識兩大類子程序:程序(Procedures)和函數(Functions)。它們之間的分別,主要在於完成工作後會否「交回結果」!
A. 程序(Procedures)
程序是執行一系列指令來完成特定動作的子程序,但它不會傳回任何數值給呼叫它的那行程式碼。
生活化比喻:想像一下按按鈕開燈。開關執行了動作(燈開了),但開關並不會遞回一個計算好的數字或物件給你。
B. 函數(Functions)
函數是執行指令、計算出答案,並將單一數值傳回給呼叫它的程式碼的子程序(使用 return 關鍵字)。
生活化比喻:想像在計算機輸入 \(7 \times 8\) 然後按下等號。計算機會接收數字、算出答案,並將數值 \(56\) 直接傳回到螢幕上,讓你可以用這個結果繼續進行下一個計算。
快速比較:
• 程序(Procedure):執行動作。不會向程式傳回任何數值。
• 函數(Function):進行計算或處理。會使用 return 陳述式向程式傳回一個數值。
重點提示:如果子程序會將數值交回給程式儲存到變數中,它就是函數。如果它只是單純執行動作而不傳回數值,它就是程序。
---3. 子程序的結構:定義與呼叫
在像 Python 這樣的文字式程式語言中使用子程序,必須遵循兩個明確步驟:
1. 定義(Define)(編寫子程序的指令內容)。
2. 呼叫(Call)(指示電腦實際執行這些指令)。
步驟 1:定義子程序
在 Python 中,我們使用特別關鍵字 def(即 define 的縮寫),後面加上子程序的名稱、括號 () 和冒號 :。屬於該子程序的所有程式碼都必須向內縮排(Indented)。
定義程序的範例:
def say_hello():
print("Hello! Welcome to programming.")
步驟 2:呼叫子程序
單單編寫 `def say_hello():` 只是寫好了食譜——它並不會替你煮出美食!要真正執行子程序內的程式碼,你必須輸入它的名稱連同括號來呼叫它:
呼叫程序:
say_hello()
4. 參數與引數(傳入數據)
當你可以將數據傳入子程序進行處理時,子程序的功能就會變得更加強大。這就是參數(Parameters)與引數(Arguments)派上用場的時候。
參數(佔位符 / Placeholders)
參數是列在子程序定義標頭內的特殊變數。它就像一個空的佔位符,等待接收傳入的數值。
引數(實際數值 / Actual Values)
引數是在呼叫子程序時,實際傳入其中的真實數值、變數或數據。
記憶小技巧:
• Parameter(參數)= Placeholder(在 `def` 行定義的佔位符)。
• Argument(引數)= Actual value(在呼叫時傳入的實際數值)。
綜合應用:逐步拆解函數範例
讓我們來看一個計算矩形面積的函數:
程式碼:
def calculate_area(width, height):
area = width * height
return area
# 呼叫函數並儲存傳回的數值:
room_area = calculate_area(5, 10)
print(room_area)
背後運作流程:
1. `width` 和 `height` 是參數(定義中的佔位符)。
2. 我們用 `5` 和 `10` 來呼叫函數。這兩個數值就是引數(實際數值)。
3. 函數計算面積:\(5 \times 10 = 50\)。
4. return 關鍵字將數值 \(50\) 傳送回去。
5. 變數 `room_area` 接收了傳回的數值,並把 \(50\) 儲存起來!
重點提示:參數是 `def` 標頭中的佔位符;引數則是呼叫子程序時傳送進去的真實數值。
---5. 變數作用域:局部變數 vs 全域變數
當你在程式中建立變數時,變數在哪裡能被讀取和使用?這就稱為作用域(Scope)。
局部變數(Local Variables)
局部變數是在子程序內部建立的。它的作用域僅限於該特定子程序。
• 它在子程序開始執行時產生。
• 當子程序執行完畢後就會立即銷毀。
• 它不能在該子程序以外的主程式中被存取或使用!
生活化比喻:想像局部變數就像你私人筆袋裏的文具。只有當你坐在自己的座位上時,才可以伸手拿那支鉛筆使用;其他課室的同學是看不到也用不到的。
全域變數(Global Variables)
全域變數是在程式的主體部分(即任何子程序之外)宣告的。
• 通常在整個程式檔案的任何位置都可以讀取和存取它。
生活化比喻:想像全域變數就像學校走廊牆上的掛鐘。任何課室裏的任何人都可以抬頭查看時間。
重點提示:局部變數只屬於建立它的子程序;全域變數則存在於整個程式中。
---6. 常見錯誤大避忌
就算是經驗豐富的程式員有時也會犯錯!初學者要特別留意以下幾個常見陷阱:
• 陷阱 1:混淆 `print()` 與 `return`
錯誤觀念:以為 `print()` 會將數值傳回給程式。
修正方法:`print()` 只是在螢幕上顯示文字供人類閱讀。電腦程式本身無法將僅僅印出的內容儲存或用作進一步計算。要將數據傳回記憶體以便用變數儲存,你必須使用 return!
• 陷阱 2:定義了子程序卻忘記呼叫
錯誤觀念:寫好了 `def` 區塊,按執行後卻奇怪為甚麼甚麼事都沒發生。
修正方法:子程序會一直靜止待命,直到你明確使用它的名稱連同括號來呼叫它(例如 `my_subroutine()`)為止。
• 陷阱 3:嘗試在子程序外部存取局部變數
錯誤觀念:在主程式中寫了 `print(total)`,但 `total` 實際上是在函數內部建立的。
修正方法:電腦會發出「變數未定義(variable not defined)」的錯誤訊息,因為局部變數在子程序結束時就消失了。請改用 `return` 將數值傳送出來!
7. 本章快速溫習
試用以下重點檢測自己的理解:
• 模組化程式編寫(Modular Programming):將程式碼拆分成較小、獨立的子程序。
• 問題分解(Decomposition):將大問題拆解成易於管理的細小部分。
• 程序(Procedure):執行動作但不傳回數值的子程序。
• 函數(Function):執行任務並使用 return 傳回數值的子程序。
• `def`:Python 中用來定義程序和函數的關鍵字。
• 參數(Parameters):子程序定義中的佔位符變數。
• 引數(Arguments):在呼叫時傳入子程序的實際數值。
• 局部作用域(Local Scope):在子程序內部建立的變數,無法從外部讀取。