《C++函數(shù)式編程》介紹了C++的面向函數(shù)式編程。面向函數(shù)式編程是繼面向?qū)ο缶幊讨笥忠痪幊谭妒剑鉀Q了命令式(過程式)編程與面向?qū)ο缶幊讨谐霈F(xiàn)的問題,是一種極具潛力的編程方式,值得研究學習。主要講解了函數(shù)對象、純潔性(Purity)、惰性求值、range、函數(shù)式數(shù)據(jù)結(jié)構(gòu)、代數(shù)類型及模式匹配、monad、模板元編程、并發(fā)系統(tǒng)的函數(shù)式設計,以及測試與調(diào)試等有關(guān)內(nèi)容,還介紹了使用原有函數(shù)創(chuàng)建新函數(shù)的知識。
《C++函數(shù)式編程》不僅可以作為C++程序員、編程愛好者以及軟件工程師學習函數(shù)式編程的參考書,還可以作為高等院校C++編程語言課的高級教材
《C++函數(shù)式編程》不是一本教授C++編程的書,是講解如何將函數(shù)式編程適用于C++。
《C++函數(shù)式編程》線性編排,每一章的內(nèi)容都建立在前一章的基礎上,循序漸進。
《C++函數(shù)式編程》通過豐富的案例和圖解,詳細闡述了C++面向函數(shù)式編程,每個小知識點跟隨實戰(zhàn)練習,講解透徹,且免費提供所有源碼。
編程是一門罕見的學科,通過它可以從無到有地創(chuàng)建一些東西。編程可以根據(jù)自己的意志創(chuàng)建想要的東西,唯一需要的就是一臺電腦。
我在上學的時候,大部分的編程課集中于命令式編程——首先是面向過程的C 語言,然后是面向?qū)ο缶幊痰腃++和Java。在我的大學里情況也沒有太大的改變——主要的編程思想還是面向?qū)ο蟮木幊蹋∣OP)。
在這段時間,幾乎使我認為所有的語言在概念上都是相同的——只不過語法不同,在學習了某種語言的基礎之后,如循環(huán)和分支,通過很小的調(diào)整就可以編寫其他程序。
第一次接觸函數(shù)式編程語言是在大學中,在課堂上學習了Lisp 語言。我的直覺反應是使用Lisp 來模擬if-then-else 語句和for 循環(huán),這樣可以真正使它變得有用。不是使我的認識適合語言,卻決定使語言適合我的想法,以便用C 的方式編程。我只想說那時候,沒有看到函數(shù)式編程的任何意義——Lisp 可以做的,用C 語言就可以實現(xiàn),而且更加簡單。
經(jīng)過相當長的時間我才開始研究函數(shù)式編程。這么做的根本原因是,在某些項目中使用的語言發(fā)展太慢了。語言中添加了for-each 循環(huán),就像什么了不起的東西:只需要下載新的編譯器,就可以使編程生涯更輕松了。
這讓我陷入深思。為了得到諸如for-each 循環(huán)的新語言結(jié)構(gòu),必須等待語言的新版本和新的編譯器。但在Lisp 中,卻可以使用一個簡單的函數(shù)實現(xiàn)for 循環(huán)相同的功能,根本不需要升級編譯器。
這正是我學習函數(shù)式編程的原因:無須改變編譯器就可以擴展編程語言的能力。編程思想仍然是“面向?qū)ο蟆钡,但卻學著用函數(shù)式風格的結(jié)構(gòu)簡化面向?qū)ο蟠a的工作。
我開始投入大量的時間研究函數(shù)式編程語言,如Haskell、Scala 和Erlang。我驚奇地發(fā)現(xiàn),使面向?qū)ο蟪绦騿T頭痛不已的問題,換個角度——以函數(shù)式風格思考——就迎刃而解了。
我的工作主要使用C++,所以必須尋找一種方式用C++進行函數(shù)式編程。事實證明,這樣的人并非只有我一個,世界上到處都是具有類似想法的人。很榮幸在各大會議上遇到他們。這是交流思想、學習新的東西、交流C++函數(shù)式編程經(jīng)驗的絕佳機會。
大多數(shù)這樣的會議結(jié)束的時候都有一個共同的結(jié)論:如果有人寫一本C++函數(shù)式編程的書,那就太棒了。但問題是,所有人都讓別人來寫,因為我們都需要從實際項目中尋找思想的源泉。
當Manning 出版社找到我寫這本書時,我起初是猶豫的——我寧愿讀這樣的一本書,而不愿意去寫。但我意識到,如果每個人都是這樣的想法,將不會有C++函數(shù)式編程的書了。我決定接受邀請,并開始了著書的旅程,于是就有了你看到的這本書。
Ivan Čukić 在貝爾格萊德數(shù)學系教授現(xiàn)代C++技術(shù)和函數(shù)式編程。他從1998 年開始使用C++。在以前和攻讀博士學位時研究函數(shù)式編程,他應用函數(shù)式編程技術(shù)編寫了全球數(shù)億人使用的真實項目。Ivan是KDE 的核心開發(fā)人員,KDE 是*大的開源C++項目。
目錄
譯者序
致謝
前言
關(guān)于本書
關(guān)于作者
第1 章 函數(shù)式編程簡介
1.1 什么是函數(shù)式編程?
1.1.1 與面向?qū)ο缶幊痰年P(guān)系
1.1.2 命令式與聲明式編程的比較
1.2 純函數(shù)(Pure functions)
1.2.1 避免可變狀態(tài)
1.3 以函數(shù)方式思考問題 ·
1.4 函數(shù)式編程的優(yōu)點
1.4.1 代碼簡潔易讀
1.4.2 并發(fā)和同步
1.4.3 持續(xù)優(yōu)化
1.5 C++作為函數(shù)式編程語言的進化
1.6 將會學到什么
總結(jié)
第2 章 函數(shù)式編程之旅
2.1 函數(shù)使用函數(shù)?
2.2 STL 實例
2.2.1 求平均值
2.2.2 折疊(Folding)
2.2.3 刪除字符串空白符
2.2.4 基于謂詞分割集合
2.2.5 過濾(Filtering)和轉(zhuǎn)換(Transforming)
2.3 STL 算法的可組合性
2.4 編寫自己的高階函數(shù)
2.4.1 接收函數(shù)作為參數(shù)
2.4.2 用循環(huán)實現(xiàn)
2.4.3 遞歸(Recursion)和尾調(diào)用優(yōu)化(Tail-call optimization)
2.4.4 使用折疊實現(xiàn)
總結(jié)
第3 章 函數(shù)對象
3.1 函數(shù)和函數(shù)對象
3.1.1 自動推斷返回值類型
3.1.2 函數(shù)指針
3.1.3 調(diào)用操作符重載
3.1.4 創(chuàng)建通用函數(shù)對象
3.2 lambda 和閉包(Closure)
3.2.1 lambda 語法
3.2.2 lambda 詳解
3.2.3 在lambda 中創(chuàng)建任意成員變量
3.2.4 通用lambda 表達式
3.3 編寫比lambda 更簡潔的函數(shù)對象
3.3.1 STL 中的操作符函數(shù)對象
3.3.2 其他庫中的操作符函數(shù)對象
3.4 用std::function 包裝函數(shù)對象
總結(jié)
第4 章 以舊函數(shù)創(chuàng)建新函數(shù)
4.1 偏函數(shù)應用
4.1.1 把二元函數(shù)轉(zhuǎn)換成一元函數(shù)的通用方法
4.1.2 使用std::bind 綁定值到特定的函數(shù)參數(shù)
4.1.3 二元函數(shù)參數(shù)的反轉(zhuǎn)
4.1.4 對多參數(shù)函數(shù)使用std::bind
4.1.5 使用lambda 替代std::bind
4.2 柯里化(Currying):看待函數(shù)不同的方式
4.2.1 創(chuàng)建柯里化函數(shù)的簡單方法
4.2.2 數(shù)據(jù)庫訪問柯里化
4.2.3 柯里化與偏函數(shù)應用
4.3 函數(shù)組合
4.4 函數(shù)提升(復習)
4.4.1 鍵值對列表反轉(zhuǎn)
總結(jié)
第5 章 純潔性:避免可變狀態(tài)
5.1 可變狀態(tài)帶來的問題
5.2 純函數(shù)和引用透明
5.3 無副作用編程
5.4 并發(fā)環(huán)境中的可變狀態(tài)與不可變狀態(tài)
5.5 const 的重要性
5.5.1 邏輯const 與內(nèi)部const
5.5.2 對于臨時值優(yōu)化成員函數(shù)
5.5.3 const 的缺陷
總結(jié)
第6 章 惰性求值
6.1 C++的惰性
6.2 惰性作為一種優(yōu)化技術(shù)
6.2.1 集合惰性排序
6.2.2 用戶接口中的列表視圖
6.2.3 通過緩存函數(shù)結(jié)果修剪遞歸樹
6.2.4 動態(tài)編程作為惰性形式
6.3 通用記憶化(Generalized memoization)
6.4 表達式模板與惰性字符串拼接
6.4.1 純潔性與表達式模板
總結(jié)
第7 章 range
7.1 range 簡介
7.2 創(chuàng)建數(shù)據(jù)的只讀視圖
7.2.1 range 的filter 函數(shù)
7.2.2 range 的transform 函數(shù)
7.2.3 range 惰性求值
7.3 修改range 中的值
7.4 定界rang 和無限r(nóng)ange
7.4.1 用定界range 優(yōu)化用于輸入的range
7.4.2 用哨兵創(chuàng)建無限r(nóng)ange
7.5 用range 統(tǒng)計詞頻
總結(jié)
第8 章 函數(shù)式數(shù)據(jù)結(jié)構(gòu)
8.1 不可變鏈表(Immutable linked lists)
8.1.1 在表頭添加和刪除元素
8.1.2 在鏈表末尾添加和刪除元素
8.1.3 在鏈表中間添加和刪除元素
8.1.4 內(nèi)存管理
8.2 不可變類向量結(jié)構(gòu)
8.2.1 位圖向量樹中的元素查找
8.2.2 向位圖向量樹追加元素
8.2.3 位圖向量樹的修改
8.2.4 在位圖向量樹的末尾刪除元素
8.2.5 其他操作和位圖向量樹的整體效率
總結(jié)
第9 章 代數(shù)數(shù)據(jù)類型及模式匹配
9.1 代數(shù)數(shù)據(jù)類型
9.1.1 通過繼承實現(xiàn)和類型
9.1.2 通過union 和std::variant 實現(xiàn)和類型
9.1.3 特定狀態(tài)的實現(xiàn)
9.1.4 特殊的和類型:Optional
9.1.5 和類型用于錯誤處理
9.2 使用代數(shù)數(shù)據(jù)類型進行域建模
9.2.1 原始的方法及其缺點
9.2.2 更復雜的方法:自上而下的設計
9.3 使用模式匹配更好地處理代數(shù)數(shù)據(jù)類型
9.4 Mach7 的強大匹配功能
總結(jié)
第10 章 monad
10.1 仿函數(shù)并不是以前的仿函數(shù)
10.1.1 處理可選值
10.2 monad:更強大的仿函數(shù)
10.3 基本的例子
10.4 range 與monad 的嵌套使用
10.5 錯誤處理
10.5.1 std::optional作為monad
10.5.2 expected作為monad
10.5.3 try monad
10.6 monad 狀態(tài)處理
10.7 并發(fā)和延續(xù)monad
10.7.1 future 作為monad
10.7.2 future 的實現(xiàn)
10.8 monad 組合
總結(jié)
第11 章 模板元編程
11.1 編譯時操作類型
11.1.1 推斷類型調(diào)試
11.1.2 編譯時的模式匹配
11.1.3 提供類型的元信息
11.2 編譯時檢查類型的屬性
11.3 構(gòu)造科里化函數(shù)
11.3.1 調(diào)用所有可調(diào)用的
11.4 DSL 構(gòu)建塊
總結(jié)
第12 章 并發(fā)系統(tǒng)的函數(shù)式設計
12.1 Actor 模型:組件思想
12.2 創(chuàng)建簡單的消息源
12.3 將反應流建模為monad
12.3.1 創(chuàng)建宿(Sink)接收消息
12.3.2 轉(zhuǎn)換反應流
12.3.3 創(chuàng)建給定值的流
12.3.4 連接流
12.4 過濾反應流
12.5 反應流的錯誤處理
12.6 響應客戶端
12.7 創(chuàng)建狀態(tài)可修改的Actor
12.8 用Actor 編寫分布式系統(tǒng)
總結(jié)
第13 章 測試與調(diào)試
13.1 程序編譯正確嗎?
13.2 單元測試與純函數(shù)
13.3 自動產(chǎn)生測試