函数式编程读书精粹
函数式编程读书精粹
函数式编程理论
函数式编程理论Function Programming With C#函数式编程读书精粹œ第一章 函数式编程基础第二章 Expressions && Statements第三章 纯函数与副作用第四章 诚实函数、null 与 Option第五章 错误处理第六章 高阶函数与委托第七章 函子与单子第八章 递归与尾递归第九章 柯里化第十章 管道和组合知乎-如何学习函数式编程函数式编程原则
第一章 函数式编程基础
第一章 函数式编程基础函数式编程的益处
装逼
Yagur Alex - Functional Programming with C - 2024.pdf - p22 - But why should you consider adopting functional programming in your projects? The benefits aremany, including the following
C#对函数式编程的支持
Lambda表达式
LINQ
数据类型不可变
模式匹配
object obj = "Hello, World!";if (obj is string str) // 模式匹配{ Console.WriteLine(str.ToUpper()); // 输出 "HELLO, WORLD!"}
委托与事件
怎么写函数式代码
表达式写法
// 非函数式写法:命令式int sum = 0;foreach (var number in numbers){ ...
第三章 纯函数与副作用
第三章 纯函数与副作用纯函数
纯函数:
输出确定(相同输入->相同输出)
没有副作用
c#实现: immutability, readonly, const, static, [Pure]
示例
public static List<string> GetTitlesOfGenre(List<Book> books, string genre){ return books .Where(b => b.Genre == genre) .Select(b => b.Title) .ToList();}
纯函数的优点
容易测试(不需要模拟环境,只要给输入就行)
可复用性高
容易调试(有问题只有可能是逻辑问题)
示例
private static Dictionary<TowerType, double> _damageModifiers = new(){ {TowerType.Cannon, 0.8}, // Takes 20% less damage ...
第七章 函子与单子
第七章 函子与单子
函子(Functor) :对容器中的值(Map)
应用函子(Applicative Functor) :将包裹在容器中的函数(Apply)包裹在容器中的值
单子(Monad) :链式操作
函子
一个函数,输入输出类型相同的函数
函子需要遵从的定律
恒等律相同输出->相同输出
结合律组合两个函子map box = 两个函子分别map box
函子的好处
错误处理
链式操作
好看
鲁棒
应用函子(Applicative Functors)四大定律
同态律(Homomorphism law)
交换律(Interchange law)
组合律
同态律$$pure(x).map(f)=pure(f(x))$$
交换律$$pure(x).apply(pure(f))=pure(f(x))$$
结合律$$pure(x).apply(pure(g∘f))=pure(x).apply(pure(f)).apply(pure(g))$$
pure: 把一个值装进盒子中
apply:把一个函数装进盒子中
(g∘f): ...
第九章 柯里化
第九章 柯里化什么是柯里化
DDDD
柯里化实践
识别函数:
有多个参数
似乎常只使用部分参数
参数上天然可以分阶段传入
定义函数:
将转化为一系列嵌套的单参数函数。每个函数返回另一个函数,直到所有参数都被传入
柯里化会导致一些繁琐,但在一些情况下,这会非常实用
如配置
public static Func<NotificationType, Func<int, Action<string>>>CurryNotificationConfig(){ return notificationType => maxNotificationsPerMinute => recipientEmail => { Console.WriteLine($"配置 {notificationType} 通知:收件人 {recipientEmail},每分钟最多 {maxNotificationsPerMinute} 条&q ...
第二章 Expressions && Statements
第二章 Expressions && Statements
Expressions: 表达式
Statements: 语句
表达式和语句的区别表达式: 有返回值
语句: 执行操作
// Expression20pagesPerChapterpagesPerChapter * 10Math.Max(a, b)//Statementvar pagesPerChapter = 20;var totalBookPages = pagesPerChapter * 10;Console.WriteLine("Hello");if (x > 0) { ... }for (int i = 0; i < 10; i++) { ... }//每个表达式都可以成为语句(加上分号)x + 2; // 语句形式,但它什么都不做
将语句转换为表达式的技巧
用 LINQ 替代集合操作语句
用三元运算符(?:)代替 if-else
用递归代替循环(for、while、foreach)
Yagur Alex - Funct ...
第五章 错误处理
第五章 错误处理
传统派: 相信后人的智慧
函数式: 承认每一步都会失败
传统异常处理是“出问题 -> 跳出去 -> 另想办法”
函数式错误处理是“每步都承认有可能失败,提前规划好”
传统派 - try catch具体移步-> c#错误处理
Result 类型// 传统派public Product GetProduct(int id){ var product = _productRepository.Get(id); if (product is null) { throw new ProductNotFoundException($"找不到ID为 {id} 的产品。"); } return product;}// Resultpublic Result<Product, string> GetProduct(int id){ var product = _productRepository.Get( ...
第八章 递归与尾递归
第八章 递归与尾递归递归分类
普通递归: 递归调用·不是· 函数的最后一步
尾递归: 递归调用 ·是· 函数的最后一步
尾递归可以被编译器优化,以提高性能,防止栈溢出.
递归应该尽量尾递归
缓解堆栈溢出风险的策略
尾递归
别递了
增加堆栈大小:通过设置 System.Threading.Thread.MaxStackSize 属性
限制递归深度:递归实现时追踪递归深度,太深了直接抛异常
C#递归代码优雅手段局部函数void ProcessAndCountVideosInCategory(Category category){ int videoCount = 0; // 局部函数:递归统计视频数量 void CountVideos(Category cat) { foreach (var subcategory in cat.Subcategories) { CountVideos(subcategory); // 递归调用 } ...
第六章 高阶函数与委托
第六章 高阶函数与委托高阶函数定义:
接受函数作为参数
或 返回函数作为结果
背景知识
Linq
筛选: Where
map: Select
聚合: Aggregation
代码建议
确保传递的函数是无状态的
努力维护不可变性
