Functional Programming
语法
transform
#include <algorithm>
#include <vector>
std::vector<int> v{1,2,3};
std::vector<int> out(v.size());
std::transform(v.begin(), v.end(), out.begin(),
[](int x){ return x * 2; });
// out = {2,4,6}
copy_if
#include <algorithm>
#include <vector>
std::vector<int> v{1,2,3,4,5};
std::vector<int> odds;
std::copy_if(v.begin(), v.end(), std::back_inserter(odds),
[](int x){ return x % 2 == 1; });
// odds = {1,3,5}
accumulate
#include <numeric>
#include <vector>
std::vector<int> v{1,2,3,4};
int sum = std::accumulate(v.begin(), v.end(), 0,
[](int a,int b){ return a + b; });
views
#include <ranges>
#include <vector>
#include <iostream>
std::vector<int> v{1,2,3,4,5,6};
auto view = v
| std::views::filter([](int x){ return x % 2 == 0; })
| std::views::transform([](int x){ return x * x; });
for (int x : view) std::cout << x << " "; // 4 16 36
-
views::filter(pred):过滤 -
views::transform(f):映射 -
views::take(n)/views::drop(n):取前 n / 跳过前 n -
views::take_while(pred)/views::drop_while(pred):按条件截断 -
views::reverse:反向 -
views::split(delim):按分隔符拆分(字符串处理很常用)
#include <ranges>
#include <string_view>
#include <iostream>
#include <string>
int main() {
std::string_view s = "a,b,cc";
auto parts = s | std::views::split(',');
for (auto sub : parts) {
std::string token(sub.begin(), sub.end()); // sub 是一段字符 range
std::cout << "[" << token << "] ";
}
// [a] [b] [cc]
}
-
views::join:把 range-of-ranges 拉平 -
views::iota(a, b):生成序列(像 Python 的 range) -
views::enumerate:C++23 才有(有的库实现已提供)用于(index, value)风格 -
sort
#include <ranges>
std::ranges::sort(v);
练习
Week 1:FP 基础(map / filter / reduce)
- Map:把一串整数映射为平方(保序)
- Filter:筛出素数(写纯
is_prime(int)) - Reduce:求和、求最大值、求乘积(空输入要处理)
- 组合:先 filter 偶数再 map 平方再 sum(用 views pipeline)
- 词频统计:给定字符串列表,统计单词出现次数(注意纯化:分词函数独立)
- Top-K:词频 top 10(尽量用 ranges + partial_sort / nth_element)
- 去重:稳定去重(保留首次出现)与非稳定去重(排序+unique)对比
- 函数组合:写一个
compose(f,g)让compose(f,g)(x)=f(g(x))(模板+完美转发)
Week 2:Ranges 进阶(惰性视图 + 管道表达)
- 解析→过滤→变换:从一行 CSV(逗号分隔)解析数字,过滤非法/空,转成 int,再统计均值
- 滑动窗口:计算 moving average(窗口大小 N,输出序列)
- 相邻差分:输出
a[i+1]-a[i](空/单元素处理) - Run-length encoding:把
aaabcc编码成a3b1c2(尽量用 ranges 思路组织) - 区间合并:输入若干
[l,r]合并重叠区间(排序后 fold) - “惰性”练习:实现一个 view/pipeline:读入序列后只在最终消费时才计算(用 views,不要提前 materialize)
- zip-like:用索引实现“带下标 map”(
(i,x)->...),体验“enumerate”风格
Week 3:函数式“错误处理”(optional / variant / expected)
(这周重点是把“失败”当值传递,避免到处 throw 或返回魔法值)
16) parse_int:返回 std::optional<int>,支持前后空格、符号、溢出检测
17) 链式处理:optional 的管道:解析→校验范围→映射(你自己写 and_then / transform / or_else 小工具)
18) 表达多种错误:把 parse_int 改成 std::expected<int, ParseError>(C++23)
19) 组合 expected:实现 map_expected / and_then_expected,让多步解析可以一行链起来
20) 配置加载:输入多行 key=value,解析成 map;遇到重复 key 或非法行,用 expected 返回详细错误
21) 业务规则评估:一组规则函数 Rule: T->expected<Score,Err>,把它们组合成总评分(fold)
22) variant 分派:写一个表达式 AST(数字/加/乘/括号),用 std::variant + std::visit 做纯求值
Week 4:更“函数式”的工程化写法(性能+可维护)
- 无
std::function的高阶函数:写一个apply_all(fns, x),其中fns是一组 callable(模板参数/auto),返回依次应用结果 - 策略组合:排序策略(按姓名/按年龄/按多字段),用函数对象组合比较器(
then_by风格) - 不可变数据建模:实现一个
struct(只读字段)+ 一组with_xxx返回修改后的副本(练“持久化”思路) - 数据流水线项目:日志分析:输入多行文本 → 解析字段 → filter → group by → top-K(把每一步写成独立纯函数)
-
性能练习:同一题写 2 版:
-
A:全 ranges/views 组合
-
B:手写循环 用基准测试对比(理解“抽象成本”与何时该退回循环)
-
并行友好 reduce:写一个结合律友好的聚合(比如计数/求和/最值),对比不结合的例子(理解并行 reduce 的坑)
- 边界层隔离:把上面的“日志分析”做成:
parse -> compute -> render三层,只有 render 做 IO - 小库封装:把你写过的
compose/and_then/transform整理成一个头文件fp.hpp,给每个函数补 5 行文档注释 + 单测