# 讀函數式編程思維
## 總的說下
這本書總得講了下函數式思維,以及用各種語言來掩飾了下函數式思維。。。后面我用 swift 來演示吧
## 第一章 為什么要用函數式思維
函數式思維是編程范式的轉換,最重要的是我們要正確的掌握語言給我們提供的特性,才能把一個語言用好
隨著計算機性能提升以及編譯器的不斷優化,可以把一些控制權(比如內存管理,多線程等瑣碎的語言細節)交給運行時來處理,用更好和更簡潔的抽象來解決我們的問題
## 第二章 轉變思維
命令式編程是按照“程序是一系列改變狀態的命令”來建模的一種編程風格。
比如 `for` 循環處理數組,我們平時使用的時候,是通過確定初始狀態,然后每次迭代都執行循環的命令,在循環的命令里面我們完成業務邏輯
函數式方法,講程序描述為表達式和變換,以數學方程的形式建立模型,并盡可能避免可變狀態
通過將數據集,進行 filter,map,reduce 等轉換,得到我們想要的結果,以一種定義或是描述式的方式來處理
所有的函數式語言里面都會以不同的形式提供 filter(過濾),map(映射),reduce(折疊或者化約)
## 第三章 責權讓渡
函數式思維的好處之一,是能夠將低層次細節(如垃圾收集)的控制權移交給運行時,從 而消弭了一大批注定會發生的程序錯誤。開發者們可以一邊熟視無睹地享受著最基本的抽 象,比如內存,一邊卻會對更高層次的抽象感覺突兀。然而不管層次高低,抽象的目的總 是一樣的:讓開發者從繁瑣的運作細節里解脫出來,去解答問題中非重復性的那些方面。
1. 不再使用類似 `for`,`while` 之類的迭代,而是使用高階函數,比如 map
2. 閉包,關注函數執行的上下文,而非通過閉包去控制狀態
3. 柯里化,實現函數工廠,模板方法,隱藏參數
4. 遞歸,遞歸更加優雅,以及更容易讓我們考慮無限的集合
5. Stream 和作業順序重排
## 第四章 用巧不用蠻
記憶,和緩存類似,但是不需要我們來管理,只需要在函數調用后,告訴函數需要記憶此次調用的結果,下次使用相同參數調用的時候,就不需要再次進行計算了
swift 中的參考 [memoize](https://gist.github.com/berkus/8a9e104f8aac5d025eb5#file-memoize-swift)
緩求值,不到逼不得已,不去對函數進行求值。
緩求值可以處理無限長的序列,減少存儲空間占用,緩求值還有利于運行時產生高效代碼
## 第五章 演化的語言
在面向對象的世界里,我們針對具體問題,建立專門的數據結構,以方法的形式將專門的操作關聯在數據結構上。
函數式編程,只使用很少的一組關鍵數據結構(list,set,map)搭配專為這些數據結構深度優化過的操作。
比起在定制的類結構上做文章,把封裝的單元縮小到函數級別,有利于在更基礎的層面上 更細粒度地實施重用。
Clojure 很好地發揮了這方面的優勢,例如在 XML 的解析問題上。 Java 語言的 XML 解析框架數量繁多,每一種都有自己的定制數據結構和方法語義(如 SAX 和 DOM 都是自成一體)。Clojure 的做法相反 ,它不鼓勵使用專門的數據結構,而是 將 XML 解析成標準的 Map 結構。而 Clojure 有極為豐富的工具可以與 map 結構相配合
使用模式匹配來替代長長的 if
要想契合問題域的表達習慣,可以利用運算符重載來改變語言的外貌,不必 創造全新的語言。
函數式數據結構,使用 Either 表示兩種結果的返回值,使用 Option 來表示有為空返回值的類型
## 第六章 模式與重用
函數式語言有函數式語言的設計模式,傳統的對于函數式語言來說,因為語言的特性,讓部分模式變得沒有意義了,但是部分問題還是存在,他們在函數式的世界里面,通過其他的方法來解決了
傳統設計模式在函數式編程的世界中大致有三種歸宿。
- 模式已被吸收成為語言的一部分。
- 模式中描述的解決辦法在函數式范式下依然成立,但實現細節有所變化。
- 由于在新的語言或范式下獲得了原本沒有的能力,產生了新的解決方案(例如很多問題都可以用元編程干凈利落地解決,但 Java 沒有元編程能力可用)。
函數級別的重用
因為函數式編程的特點,重用的最小單位變成了函數,并且程序由多個函數組合而成。
Template Model,通過使用函數變量,進行了化簡,減少耦合
Strategy 模式,更加靈活
Flyweight 模式,使用記憶來實現
Factory 模式和柯里化,柯里化就是產出函數的工廠
以結構為載體的代碼重構需要考慮整個類的關系網,可以很好的減少耦合的情況。
## 第七章 現實應用
Java 8 中的函數式接口,Option 類型,Stream,都是函數式語言在 Java 語言中的應用
函數試的構架,貫徹“值不可變”,學習從值不可變的角度去思考
可變的狀態與測試數量有直接的關聯:可變的狀態越多,要求的測試也越多。
實現一個值不可變的 Java 類,我們需要做到以下事情。
- 把所有的字段都標記為final。Java 要求被標記為 final 的字段,要么在聲明時初始化,要么在構造器中初始化。不要 在意 IDE 大驚小怪地提醒我們字段沒有在聲明位置上初始化,當我們在構造器里寫好 相關的初始化代碼,IDE 就會明白過來。 ?
- 把類標記為final,防止被子類覆蓋。 如果類可以被覆蓋,類中的方法也就有可能被改變行為,因此以防萬一,我們干脆禁止 子類化。Java 的 String 類就采取了這樣的防范策略。 ?
- 不要提供無參數的構造器。 一個值不可變的對象,它的一切狀態都必須通過構造器來設定。假如我們沒有需要設定 的狀態,那建立這么一個對象又有何必要呢?在無狀態的類里面安排幾個靜態方法就足 夠了。所以說,值不可變的類根本不應該出現無參數的構造器。假如我們受到某些框架 的限制,不得不提供無參數的構造器,這時可以考慮能否用一個私有的無參數構造器來 滿足框架的要求(私有的構造器仍然可以通過反射來訪問)。 JavaBeans 的標準規定要有默認構造器,我們摒除無參數構造器違反了這條規定。不過 反正 JavaBeans 里有各種 setXXX 方法存在,本身就不可能是值不可變的。 ?
- 提供至少一個構造器。 構造器是我們在對象里添置狀態的最后機會! ?
- 除了構造器之外,不要提供任何制造變化的方法。我們不但要避免沿襲 JavaBeans 風格的 setXXX 方法,還必須小心防范,不能返回任何 值可變的對象引用。標記了 final 的對象引用并不等于它所指向的一切都不可改變。因 此,我們需要預防性地復制所有通過 getXXX 方法返回的對象引用。 ?
對于 web 框架,函數式語言回很合適,因為整個 web 就是一系列從請求到響應的變換