Functional Programming in C++ 读书笔记

基本信息

  • 作者:Ivan Čukić(KDE核心开发者,贝尔格莱德大学数学系现代C++讲师)
  • 出版社:Manning, 2019
  • 页数:321页

核心主题

本书是C++函数式编程的权威实践指南,面向有2年以上C++经验的开发者。核心观点:C++不止是面向对象语言,还支持过程式、泛型和函数式编程范式。每个新标准都增加了更多函数式特性。

章节结构与关键知识点

第一部分:函数式编程惯用法

第1章 函数式编程导论

  • 命令式 vs 声明式编程对比
  • 纯函数与引用透明性
  • 函数式编程四大收益:代码简洁可读、并发安全、持续优化、不变数据
  • C++作为函数式语言的演进历程

第2章 初探函数式编程

  • 高阶函数:接受函数或返回函数的函数
  • STL中的高阶函数:accumulate、for_each、transform、filter、partition
  • 自己实现高阶函数:通过循环、递归、fold实现
  • 尾递归优化和fold实现两种模式

第3章 函数对象

  • C++中”函数”的四种形式:普通函数、函数指针、函数对象、lambda
  • Lambda语法与闭包:捕获列表、通用lambda
  • std::function类型擦除包装器
  • 运算符函数对象(Boost.Phoenix风格)

第4章 从旧函数创建新函数

  • 偏函数应用:将二元函数转为单目函数
  • 柯里化:逐参数绑定
  • std::bind vs lambda实现
  • 函数组合:f(g(x))管道模式
  • 函数提升:将普通函数提升为作用于容器/optional的函数

第5章 纯度:避免可变状态

  • 可变状态的问题:难以推理、并发竞争
  • 纯函数与引用透明性
  • 使用const的正确姿势:逻辑const vs 内部const
  • 临时对象优化成员函数

第6章 惰性求值

  • 惰性作为优化技术:排序、UI视图、记忆化
  • 动态规划作为惰性形式
  • 通用记忆化实现
  • 表达式模板与惰性字符串拼接
  • lazy_val模板类实现

第7章 Ranges(范围)

  • range-v3库的核心概念
  • 只读视图:filter、transform、take、skip、reverse
  • 惰性求值:管道操作 |
  • 无限range与哨兵值
  • 实际应用:词频统计

第8章 函数式数据结构

  • 不可变链表:头尾中间增删
  • 位图向量trie(Bitmapped Vector Trie):持久化vector的实现
    • 元素查找、追加、更新、删除
    • 共享节点实现COPY-ON-WRITE
  • 内存管理与垃圾回收

第二部分:高级函数式设计

第9章 代数数据类型与模式匹配

  • Sum类型实现:继承 vs std::variant
  • 具体状态模式实现
  • Optional值作为Sum类型特例
  • 错误处理的Sum类型
  • 模式匹配:重载函数对象实现visit
  • Mach7库:C++中的强大模式匹配
  • 领域建模:自顶向下设计

第10章 Monad(单子)

  • Functor基础:fmap
  • Monad三定律:左单位、右单位、结合律
  • std::optional作为Monad
  • expected<T,E>错误处理Monad
  • Try Monad:异常处理
  • 状态Monad
  • 未来/Future作为Monad
  • Monad组合

第11章 模板元编程

  • 编译期类型操作:decltype、std::decay_t等
  • 调试推导类型
  • 编译期模式匹配
  • 可调用对象与SFINAE
  • DSL构建块

第12章 并发系统的函数式设计

  • Actor模型:组件化思维
  • 响应式流作为Monad
  • Continuation Monad构建响应式系统
  • 带可变状态的Actor
  • 分布式系统实现

第13章 测试与调试

  • 纯函数的单元测试优势
  • 属性测试(Property-based testing):QuickCheck风格
  • 比较测试
  • Monad化并发系统的测试技巧:切换同步/异步
  • Fuzzing模糊测试

核心设计模式

  1. Fold模式:统一循环、递归、并行计算
  2. 管道组合:|运算符链式调用
  3. 类型类风格:通过模板约束实现类似Haskell的类型类
  4. Actor消息传递:响应式流封装
  5. Monad错误处理:expected<T,E>替代异常

关键代码示例

// Lambda捕获与通用lambda
auto transform = [](auto f) { 
    return view::transform(lift(f)); 
};
 
// 管道操作
auto pipeline = source | transform(trim) 
                  | filter(pred) 
                  | sink(consume);
 
// 不可变数据结构
struct ImmutableList<T> {
    T head;
    ImmutableList<T> tail;
    // 无mut方法,只返回新实例
};

与其他知识的关联

可行动点

  1. 在项目中使用std::optional替代裸指针返回错误状态
  2. 用Lambda管道重构复杂循环逻辑
  3. 尝试属性测试替代手工编写边界用例
  4. 学习range-v3库实现惰性数据处理