第一章 函数式编程基础
第一章 函数式编程基础
函数式编程的益处
- 装逼
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)
{
if (number > 0)
{
sum += number;
}
}
// 函数式写法:声明式(表达式)
var sum = numbers.Where(x => x > 0).Sum();纯函数(Pure Function)
// 即永远: 相同输入->相同输出
// 同时 : 没有副作用(不干别的事情,如log,更改外部状态)
// 非纯函数:修改外部状态
int result = 0;
void Add(int a, int b)
{
result = a + b; // 修改外部状态
}
// 纯函数
int Add(int a, int b) => a + b;诚实函数(Honest Functions)
// 诚实函数 = 明确输入 + 明确输出 + 明确失败路径 + 无隐藏副作用
// 诚实函数 是一种 对调用者“诚实”的函数
// 它 不会隐藏信息、不会偷偷修改外部状态、不会意外地抛出异常、也不会返回模糊不清的值
// 诚实函数
int Multiply(int a, int b) => a * b;
// 非诚实函数(隐含使用了外部状态)
int result;
void Multiply(int a, int b) => result = a * b;
// 诚实函数
int Square(int x) => x * x;
// 非诚实函数(有副作用,打印log)
int Square(int x)
{
Console.WriteLine("计算平方");
return x * x;
}
// 诚实函数
string Divide(int numerator, int denominator)
{
if (denominator == 0)
throw new ArgumentException("Denominator cannot be zero.");
return (numerator / denominator).ToString();
}
// 非诚实函数 (未处理错误)
string Divide(int numerator, int denominator)
{
return (numerator / denominator).ToString();
}
// 非诚实函数
// 1. 悄悄访问数据库(副作用)
// 2. 如果 userId 无效可能抛异常
// 3. 签名没有任何表示错误或失败的可能
double CalculateDiscountedPrice(double price, int userId)
{
var user = Database.GetUser(userId); // 隐式依赖外部资源
if (user.IsPremium)
return price * 0.8;
return price;
}高阶函数
// 即接受其他函数作为参数,或者返回函数作为结果
// 非高阶函数:显式的操作
int result;
int a = 5, b = 10;
string operation = "add"; // 假设通过某种方式传入操作类型
if (operation == "add")
{
result = a + b;
}
else if (operation == "multiply")
{
result = a * b;
}
// 高阶函数:接受函数作为参数
Func<int, int, int> Add = (a, b) => a + b;
Func<int, int, int> Multiply = (a, b) => a * b;
Func<int, int, Func<int, int>> ApplyOperation(Func<int, int, int> operation) =>
(a, b) => operation(a, b);
var result = ApplyOperation(Add)(5, 10); // 15函子(Functors)
// 函子 : 一个类型(容器),一个定义了Map操作的类型(容器)
// Map方法: 对内部的值应用函数(映射),同时保持容器结构不变
// 函子满足规则:
// 1. 恒等律: box.Map(x => x) 等价于 box
// 2. 组合律: box.Map(f).Map(g) 等价于 box.Map(x => g(f(x)))
// 示例
public class Box<T>
{
public T Value { get; }
public Box(T value)
{
Value = value;
}
public Box<U> Map<U>(Func<T, U> func)
{
return new Box<U>(func(Value));
}
}
var box = new Box<int>(5);
var newBox = box.Map(x => x * 2); // Box<int>,Value 为 10单子(Monads)
// 单子 : 一个更牛逼的“函子”, 有Return 和 Bind 操作的函子
// Return : 把普通值变成单子
// Bind : 接收一个函数,这个函数会产生另一个单子(Monad),然后把它“展开”并返回
// 从一个容器 M<T> 出发,
// 接收一个函数 T → M<U>,
// 结果是一个新的容器 M<U>。
// Bind 示例
Maybe<int> GetNumber() => new Maybe<int>(3);
Maybe<int> MultiplyByTwo(int x) => new Maybe<int>(x * 2);
var result = GetNumber().Bind(MultiplyByTwo);
// Return示例
public static Maybe<T> Return<T>(T value) => new Maybe<T>(value);
Maybe<int> five = Return(5); // Maybe<int> with value 5
C# 单子实现范例
public class Maybe<T> |
在Linq中, Return 对应了创建的起始容器,Bind对应了SelectMany
var result = |
书中代码示例
Yagur Alex - Functional Programming with C - 2024.pdf - p25 - A practical example – a book publishing system
这个例子展现了函数式编程的几个关键概念:
- 不可变性: 使用 record 类型来定义 Book
- 纯函数(Pure functions) :
IsValid和FormatBook都是纯函数。它们在相同输入下总是返回相同的输出,并且没有副作用- 高阶函数(Higher-order function) :
ProcessBooks是一个高阶函数,它接收两个函数作为参数(一个验证器和一个格式化器- 可组合性(Composability) :在
ProcessBooks函数中,我们将验证和格式化操作进行了组合- 声明式风格(Declarative style) :我们描述了我们想要的结果(合法且已格式化的书籍),而不是一步一步地说明如何实现
- LINQ:我们使用了 LINQ 方法(如
Where和Select),它们非常符合函数式编程的原则
如何结合C#的OOP和函数式编程
- 使用不可变对象
- 使用扩展方法
- 将((20250423205629-fwkf2n6 “高阶函数”))作为类的实例方法
- 依赖注入与组合
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 VanishingBlog!


