歡迎來到函數式列表的世界!
哈囉!今天我們要一起探索列表 (Lists),但這裡的列表跟你以前在 Python 或 Java 中見到的不太一樣。在函數式程式設計 (Functional Programming) 中,列表是儲存資料最重要的手段。
如果起初覺得這些概念有點「數學化」或陌生,別擔心!在指令式程式設計中,我們會告訴電腦「如何」一步步執行任務;而在函數式程式設計中,我們則是描述事物「是什麼」。理解列表是掌握這種新思維模式的關鍵!
1. 什麼是函數式列表?
在函數式程式設計中,列表是一組相同資料類型的元素集合。然而,與標準陣列不同的是,函數式列表是遞迴 (recursive) 的。這意味著列表是透過自身來定義的!
一個列表總是以下兩者之一:
1. 空列表 (Empty List)(通常記作 \( [ ] \))。
2. 頭部 (Head)(第一個元素)連接到一個尾部 (Tail)(尾部本身也是一個列表)。
「火車」的比喻
想像一列玩具火車。
- 頭部 (Head) 就是最前端的火車頭。
- 尾部 (Tail) 就是後面連接的所有車廂。
- 如果你把車頭拆下來,剩下的車廂看起來依然像是一列較短的火車!如果你一直拆下去,最後什麼都不會剩下——這就是空列表。
重點複習:每個非空列表都有且僅有一個頭部和一個尾部(即使尾部只是一個空列表)。
2. 頭部與尾部
這是你在 9645 課程中處理任何列表時,最核心的兩個「部分」。
頭部 (Head)
頭部是列表的第一個元素。它是一個單一的項目(例如整數或字元)。
範例:在列表 \( [5, 8, 2, 1] \) 中,頭部是 \( 5 \)。
尾部 (Tail)
尾部是頭部之後的所有其他內容。關鍵在於,尾部始終本身也是一個列表。
範例:在列表 \( [5, 8, 2, 1] \) 中,尾部是 \( [8, 2, 1] \)。
記憶小撇步:頭與尾的技巧
- Head(頭部)就像 Hat(帽子,戴在最上面/最前面)。
- Tail(尾部)就像 Train(火車,跟在後面的整串車廂)。
3. 列表的標記法與建構
在考試中,你可能會看到幾種列表的寫法。最常見的是使用 Cons 運算子(這是「construct」,即建構的縮寫)。
Cons 運算子 \( (:) \) 或 \( (|) \)
我們用它將頭部接上尾部來構成一個新列表。
語法通常看起來像這樣:\( Head : Tail \)。
範例解析:
我們如何建立列表 \( [10, 20, 30] \)?
1. 從空列表開始:\( [ ] \)
2. 加入 30:\( 30 : [ ] \) 變成 \( [30] \)
3. 再加入 20:\( 20 : [30] \) 變成 \( [20, 30] \)
4. 再加入 10:\( 10 : [20, 30] \) 變成 \( [10, 20, 30] \)
常見錯誤:學生常以為 \( [5] \) 的尾部什麼都沒有。實際上,\( [5] \) 的尾部是空列表 \( [ ] \)。切記:尾部必須永遠是一個列表!
4. 列表的運算
由於函數式程式設計不使用「迴圈」(如 for 或 while),我們改用列表的特性來處理資料。
列表是否為空?
函數式語言有內建方法來檢查列表是否為 \( [ ] \)。這非常重要,因為它告訴我們的函數何時該停止(這是基本案例/基底情況,Base Case)。
提取數值
我們使用模式匹配 (pattern matching) 來拆解列表。
如果我們有一個列表 \( L = [x : xs] \):
- \( x \) 代表頭部。
- \( xs \) 代表尾部(字母 's' 代表複數,意指剩餘的更多個 x)。
重點總結:
\( Head([1, 2, 3]) = 1 \)
\( Tail([1, 2, 3]) = [2, 3] \)
5. 為什麼要在函數式程式設計中使用列表?
你知道嗎? 在函數式程式設計中,列表是不可變的 (immutable)。這意味著一旦列表被建立,就無法修改。如果你想「新增」一個項目,你實際上是透過將新的頭部「Cons」到舊列表上,來建立一個全新的列表。
這使得程式更加可預測,並有助於防止資料在意外中被更動而產生的臭蟲 (bugs)!
總結檢查表
在繼續學習之前,請確保你能:
- 識別任何列表的頭部(它是一個元素)。
- 識別任何列表的尾部(它是一個列表)。
- 解釋空列表表示為 \( [ ] \)。
- 理解 \( 1 : [2, 3] \) 與 \( [1, 2, 3] \) 是相同的。
- 認知到單一項目列表的尾部就是空列表。
如果覺得這些概念有點抽象,別擔心!只要多練習「拆解」和「組合」這些列表,一切都會變得自然順手。你做得很好!