函數式編程中的列表簡介
你好!歡迎來到函數式編程之旅中最重要的章節之一。在 Haskell 這類語言中,列表 (lists) 是我們處理數據的「家常便飯」。不同於你在 Python 或 Java 中可能見過的陣列 (arrays),函數式編程中的列表擁有一個非常特別且優雅的結構,這一切都是基於遞迴 (recursion) 的概念。別擔心這些聽起來有點專業,我們將透過像火車和迴紋針這樣簡單的類比,把它拆解開來逐一擊破!
什麼是列表?
在函數式範式中,列表是一組相同類型的元素集合。然而,我們觀察它們的角度非常特別。我們不只是把它看作一個裝載項目的「容器」,而是將其視為由頭 (head) 和尾 (tail) 連接而成的結構。
空列表
最簡單的列表就是空列表 (empty list)。在大多數函數式語言中,它用方括號表示:[]。你可以把它想像成一個空盒子。它是所有列表中最基礎的起點!
頭與尾:火車類比
每個非空列表都可以拆解成兩個部分:
1. 頭 (Head): 這是列表中第一個元素。重點是,頭一定是一個單一的項目(例如一個整數或一個字元)。
2. 尾 (Tail): 這是列表中除頭部以外的所有剩餘部分。至關重要的一點是,尾本身永遠是一個列表。
類比: 想像一列火車。頭就是最前面的火車頭。尾則是掛在後面的一連串車廂。如果你把火車頭拆下來,「剩下的部分」仍然是一個火車結構!
構造運算子 (:)
在 Haskell 中,我們使用冒號 :(通常稱為 "cons" 運算子)將頭連接到尾上。
例如,列表 [4, 3, 5] 實際上是這樣構造出來的:
\( 4 : [3, 5] \)
如果我們進一步拆解,它看起來會是這樣:
\( 4 : (3 : (5 : [])) \)
快速回顧:
- 頭 (Head): 第一個項目(例如 4)。
- 尾 (Tail): 剩下的列表(例如 [3, 5])。
- []: 位於所有構造末端的空列表。
重點總結: 列表要麼是空的 [],要麼是一個頭通過 : 運算子連接到一個尾上。
必要的列表操作
AQA 教學大綱要求你理解並運用七種特定的操作。讓我們逐一來看,別擔心這些剛開始看起來會有點難,它們就像工具箱裡的工具一樣好用!
1. 返回頭部 (Return Head)
此操作會取出第一個元素。
例子: [10, 20, 30] 的頭是 10。
2. 返回尾部 (Return Tail)
此操作會取出除第一個元素以外的所有內容。
例子: [10, 20, 30] 的尾是 [20, 30]。
3. 檢查空列表 (Test for Empty List)
這會檢查列表是否為 []。如果列表沒有任何項目則返回 True,否則返回 False。這對於遞迴函數來說至關重要,這樣電腦才知道何時該停止處理!
4. 返回列表長度 (Return Length of List)
這會計算列表中有多少個元素。
步驟拆解: 若要計算 [A, B, C] 的長度,電腦會思考:「這等於 1(代表 A)加上尾部 [B, C] 的長度。」它會不斷重複這個過程,直到遇到空列表為止。
5. 構造一個空列表 (Construct an Empty List)
簡單地創建一個 [] 作為起點。
6. 在列表前端添加項目 (Prepend)
這意味著在列表的最前面添加一個項目。在函數式編程中,這非常快速且高效!
例子: 將 1 添加到 [2, 3] 的前端會得到 [1, 2, 3]。
7. 在列表末端添加項目 (Append)
這意味著在列表的最後面添加一個項目。
例子: 將 4 添加到 [1, 2, 3] 的末端會得到 [1, 2, 3, 4]。
注意: 在許多函數式語言中,Append 比 Prepend 要慢,因為電腦必須遍歷整列「火車」才能找到最後一節車廂!
你知道嗎? 因為函數式編程中的數據是不可變的 (immutable)(不能被修改),所以當你執行 "Prepend" 操作時,你並沒有真正改變舊列表。你只是創建了一個全新的「火車頭」,並將它掛載到現有的「火車」上!
重點總結: 熟練運用頭和尾的操作是解決考試中幾乎所有列表相關問題的秘訣。
要避免的常見錯誤
1. 「列表 vs 元素」陷阱: 學生常以為 [5, 4] 的尾是 4。其實不是!尾是 [4](一個包含 4 的列表)。頭才是 5(實際的數字)。
2. 空列表錯誤: 你不能向空列表 [] 要求頭或尾。這就像試圖把火車頭從一列不存在的火車上拆下來——這會導致錯誤!
3. 對 Append/Prepend 的混淆: 記住:Prepend 是在 Priority(優先/前端)位置。Append 是在 After(最後/後端)位置。
記憶輔助:迴紋針鏈
想像一條迴紋針組成的鏈子。
- 頭 (Head) 是你手中拿著的那枚迴紋針。
- 尾 (Tail) 是垂在下面剩下的鏈子。
- 如果你想找長度,你就數手中那一枚,然後移動到下一個「頭」,直到沒有迴紋針剩下為止(也就是空列表)。
總結複習
基本結構: 列表由 head:tail 構成。
基本情況 (Base Case): 每個列表最終都會以空列表 [] 結束。
表示法: [1, 2, 3] 是 \( 1 : (2 : (3 : [])) \) 的簡寫。
效率: Prepend(添加到前端)是函數式編程中增長列表的首選方式。
順利完成這些筆記,做得好!函數式編程確實需要一點「思維轉變」,但一旦你能將列表看作頭和尾,你就已經半隻腳踏入高手之列了。繼續練習那些拆解頭部和尾部的操作吧!