歡迎來到函數式程式設計(Functional Programming)的世界!

在本章中,我們將探索一種全新的電腦指令編寫方式。你之前接觸過的絕大多數程式設計方式,很可能都是指令式(Imperative)——也就是向電腦提供一系列逐步執行的指令。而函數式程式設計(Functional Programming)則大不相同!它就像使用一套數學公式,將數據轉換成你想要的結果。

如果剛開始覺得這有點像「數學課」,請別擔心。一旦你看懂了當中的邏輯模式,你就會發現這是一種非常簡潔且強大的寫碼方式,而且能大幅減少程式碼中的錯誤(bugs)!

1. 函數式 vs. 指令式程式設計

要理解函數式程式設計,我們首先要看看它「不是」什麼。

指令式程式設計:你透過改變程式的「狀態」(使用隨時間改變值的變數)來告訴電腦「如何」做某事。比喻:就像食譜,你需要改變食材的狀態(切碎洋蔥、炒牛肉)。
函數式程式設計:這屬於宣告式(Declarative)。你透過應用函數來告訴電腦你「想要」什麼結果。你不改變原始數據,而是創建新的數據。比喻:就像數學公式 \( f(x) = x + 2 \)。如果 \( x \) 是 5,答案永遠是 7。它不會改變 5 本身,只是為你產生一個 7。

函數式程式設計的核心特性

無狀態(Statelessness):在函數式程式設計中,我們避免改變「狀態」。這意味著我們不會使用隨程式執行而改變數值的全域變數。
無副作用(Side-Effect Free):函數應該只做一件事:接收輸入並返回輸出。它不應該「干預」或改變其他東西,例如將結果印在螢幕上或更新資料庫。
引用透明(Referential Transparency):這是一個專業術語,意思就是如果你用相同的輸入呼叫一個函數,你「永遠」會得到相同的輸出。這讓程式變得非常可預測且可靠!

快速回顧:
指令式:告訴電腦「如何」做(逐步執行,改變變數)。
函數式:告訴電腦「做什麼」(使用函數,無副作用)。
引用透明:相同的輸入 = 每次都得到相同的輸出。

2. 函數作為一等物件(First-Class Objects)

在 Haskell 或 Python(以函數式風格使用時)等語言中,函數被視為一等物件(First-Class Objects)。這意味著你可以像對待整數(integer)或字串(string)等數據一樣對待函數。

你可以做到:
1. 將函數作為參數(argument)傳遞給另一個函數。
2. 將函數作為另一個函數的結果返回。
3. 將函數指派給一個變數

比喻:把函數想像成「工具」。在程序式程式設計中,工具被鎖在棚子裡;而在函數式程式設計中,你可以把工具放進盒子裡、交給朋友,甚至用一個工具來製造另一個工具!

重點總結:如果你能像對待變數一樣對待函數,那麼它就是一個一等物件

3. 函數應用(Function Application)

在函數式程式設計中,我們使用函數應用。這只是將參數提供給函數以產生結果的過程。

我們通常將其寫作 \( f \, x \),其中 \( f \) 是函數,\( x \) 是參數。
範例:如果我們有一個名為 square 的函數,那麼 square 5 的結果就是 25。

冷知識:在函數式語言中,我們通常不會在呼叫函數時使用括號!與其寫 square(5),我們直接寫 square 5 即可。

4. 高階函數(Higher-Order Functions)

這就是最精彩的部分了!高階函數是指能接收另一個函數作為參數,或將函數作為結果返回的函數。在你的教學大綱中,你需要掌握以下三個主要的函數:

A. Map

Map 接收一個函數和一個列表(list),它會將該函數應用於列表中的「每一個項目」,並返回一個新的列表。
範例:將 map 與「雙倍(Double)」函數搭配列表 [1, 2, 3] 使用,即可得到 [2, 4, 6]。

B. Filter

Filter 接收一個條件(一個回傳 True 或 False 的函數)和一個列表。它會檢查列表中的每個項目,並只保留符合條件的項目。
範例:將 filter 與「判斷偶數(IsEven)」函數搭配 [1, 2, 3, 4] 使用,即可得到 [2, 4]。

C. Reduce(或稱 Fold)

Reduce 接收一個列表,並透過重複應用函數將其「縮減」為單一數值。
範例:將 reduce 與「加法(Addition)」函數搭配 [1, 2, 3, 4] 使用,即可得到 10(因為 1+2+3+4 = 10)。

記憶小撇步:廚房比喻
Map:處理籃子裡「所有」蔬菜的切菜動作。
Filter:只從籃子裡挑出「成熟的」番茄。
Reduce:把所有蔬菜丟進鍋子裡燉成「一鍋」湯。

5. 操作列表(Lists)

在函數式程式設計中,列表非常重要。我們通常將其拆分為三個部分:

1. 首項(Head):列表中的第一個元素。
2. 尾項(Tail):除了首項之外,「所有其餘項目」組成的列表。
3. 空列表(Empty List):什麼都沒有的列表,通常表示為 [ ]。

範例:在列表 [10, 20, 30, 40] 中:
首項(Head)是 10。
尾項(Tail)是 [20, 30, 40]。

常見錯誤:學生常以為「尾項」只是最後一個項目(40)。並非如此!尾項是「列表剩下的部分」。如果你取出尾項 [20, 30, 40] 的首項,你會得到 20!

重點總結:每個非空列表都有一個首項(一個元素)和一個尾項(另一個列表)

總結與建議

• 函數式程式設計是宣告式的,並且避免了副作用
一等物件意味著函數可以像數據一樣被傳遞。
高階函數(Map, Filter, Reduce)讓處理列表變得輕而易舉。
• 列表由首項尾項組成。

考試小撇步:如果考題問到為什麼函數式程式設計對於多核心處理器很有用,請記得:因為它沒有副作用且沒有共享狀態,所以程式的不同部分可以同時在不同核心上執行,而互不干擾!