到目前為止,在本系列的每期文章中,我都說明了為什麼理解函數式編程非常重要。但是,有些原因是在 多期文章中進行說明的,只有在綜合思路的更大背景中,才可以完全瞭解這些原因。在本期文章中,我會探討 函數式編程方興未艾的所有原因,並綜合前幾期文章中的一些個人經驗教訓。
在電腦科學短短的發 展曆史中,技術的主流有時會產生分支,包括實用分支和學術分支。20 世紀 90 年代的 4GL(第四代語言) 是一個實用分支,而函數式編程是來自學術界的一個樣本。每隔一段時間,都會有一些分支加入主流,函數式 編程目前也是這種情況。函數式語言不僅在 JVM 上剛剛嶄露頭腳(其中兩個最有趣的新語言是 Scala 和 Clojure),在 .NET 平台上也是才開始得到應用,在 .NET 平台上,F# 是頭等公民。為什麼所有平台都如此 歡迎函數式編程?答案是,隨著時間的推移,隨著運行時都要能夠處理更多的繁忙工作,開發人員已經能夠將 日常任務的更多控制權割讓給它們。
割讓控制權
在 20 世紀 80 年代初,在我上大學的時候, 我們使用一個被稱為 Pecan Pascal 的開發環境。其獨特的特性是,相同的 Pascal 代碼可以在 Apple II 或 IBM PC 上運行。Pecan 工程師使用某個稱為 “位元組碼” 的神秘東西實現了這一壯舉。開發人員將 Pascal 代碼編譯為 “位元組碼”,它可以在每個平台本地編寫的 “虛擬機器” 上運行。這是一個可怕的體驗!所產生 的代碼慢得讓人痛苦,甚至簡單的類賦值也非常緩慢。當時的硬體還沒有準備好迎接這個挑戰。
在發 布 Pecan Pascal 之後的十年,Sun 發布了 Java,Java 使用了相同的架構,對於 20 世紀 90 年代中期的硬 件環境,運行該代碼顯得有些緊張,但最終取得了成功。Java 還增加了其他開發人員友好的特性,如自動垃 圾收集。使用過像 C++ 這樣的語言之後,我再也不想在沒有垃圾收集的語言中編寫代碼。我寧願花將時間花 在更高層次上的抽象上,思考解決複雜業務問題的方法,也不願意在記憶體管理等複雜的管道問題上浪費時間。
Java 緩解了我們與記憶體管理的互動;函數式程式設計語言使我們能夠用高層次的抽象取代其他核心構建塊 ,並更注重結果而不是步驟。
結果比步驟更重要
函數式編程的特點之一是存在強大的抽象,它 隱藏了許多日常操作的細節(比如迭代)。我在本系列文章中一直使用的一個樣本是數字分類:確定某個數字 是 perfect、abundant 還是 deficient(完整的定義參見 第一期文章)。清單 1 中顯示的 Java 實現可以 解決這個問題:
清單 1. 內建緩衝總數的 Java 數字分類器
import static java.lang.Math.sqrt; public class ImpNumberClassifier { private Set<Integer> _factors; private int _number; private int _sum; public ImpNumberClassifier(int number) { _number = number; _factors = new HashSet<Integer>(); _factors.add(1); _factors.add(_number); _sum = 0; } private boolean isFactor(int factor) { return _number % factor == 0; } private void calculateFactors() { for (int i = 1; i <= sqrt(_number) + 1; i++) if (isFactor(i)) addFactor(i); } private void addFactor(int factor) { _factors.add(factor); _factors.add(_number / factor); } private void sumFactors() { calculateFactors(); for (int i : _factors) _sum += i; } private int getSum() { if (_sum == 0) sumFactors(); return _sum; } public boolean isPerfect() { return getSum() - _number == _number; } public boolean isAbundant() { return getSum() - _number > _number; } public boolean isDeficient() { return getSum() - _number < _number; }}
清單 1 中的代碼是典型的 Java 代碼,它使用迭代來確定和匯總係數。在使用函數式程式設計語言時 ,開發人員很少關心細節(比如迭代,由 calculateFactors() 使用)和轉換(比如匯總一個列表,該列表由 sumFactors() 使用),寧願將這些細節留給高階函數和粗粒度抽象。