lambda表达式
C++
C++中的lambda表达式是C++11引入的重要特性,它允许在代码中定义匿名函数对象,极大地增强了函数式编程能力。
基本语法
cpp
[capture] (parameters) -> return_type { body }
- capture:捕获列表,指定lambda表达式可以访问的外部变量
- parameters:参数列表,与普通函数的参数列表类似
- return_type:返回类型(可省略,编译器会自动推导)
- body:函数体,包含要执行的代码
捕获列表详解
捕获列表决定了lambda表达式如何访问外部作用域的变量:
cpp
// 1. 值捕获(拷贝)
int x = 10;
auto lambda1 = [x] { return x + 5; }; // 捕获x的副本
// 2. 引用捕获
int y = 20;
auto lambda2 = [&y] { y += 5; return y; }; // 捕获y的引用
// 3. 隐式值捕获
auto lambda3 = [=] { return x + y; }; // 按值捕获所有外部变量
// 4. 隐式引用捕获
auto lambda4 = [&] { x++; y++; }; // 按引用捕获所有外部变量
// 5. 混合捕获
auto lambda5 = [=, &y] { return x + y; }; // x按值,y按引用
auto lambda6 = [&, x] { return x + y; }; // x按值,其他按引用
参数与返回类型
cpp
// 完整形式
auto add = [](int a, int b) -> int {
return a + b;
};
// 省略返回类型(编译器自动推导)
auto multiply = [](int a, int b) {
return a * b;
};
// 无参数lambda
auto getAnswer = [] {
return 42;
};
// 可变lambda(可修改按值捕获的变量)
int counter = 0;
auto increment = [counter]() mutable {
return ++counter; // 修改捕获的副本
};
实际应用示例
1. STL算法中的lambda
cpp
#include <vector>
#include <algorithm>
#include <iostream>
int main() {
std::vector<int> numbers = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
// 使用lambda过滤偶数
auto it = std::remove_if(numbers.begin(), numbers.end(),
[](int n) { return n % 2 != 0; });
numbers.erase(it, numbers.end());
// 使用lambda转换元素
std::transform(numbers.begin(), numbers.end(), numbers.begin(),
[](int n) { return n * 2; });
// 使用lambda排序
std::sort(numbers.begin(), numbers.end(),
[](int a, int b) { return a > b; }); // 降序排序
// 使用lambda查找
auto result = std::find_if(numbers.begin(), numbers.end(),
[](int n) { return n > 15; });
return 0;
}
2. 作为函数参数
cpp
#include <functional>
#include <iostream>
void processNumbers(const std::vector<int>& nums,
std::function<void(int)> processor) {
for (int num : nums) {
processor(num);
}
}
int main() {
std::vector<int> numbers = {1, 2, 3, 4, 5};
// 传递lambda作为回调函数
processNumbers(numbers, [](int n) {
std::cout << "Processing: " << n * 2 << std::endl;
});
// 带状态的lambda
int sum = 0;
processNumbers(numbers, [&sum](int n) {
sum += n;
});
std::cout << "Total sum: " << sum << std::endl;
return 0;
}
3. 延迟执行与函数对象
cpp
#include <iostream>
#include <memory>
class TaskScheduler {
public:
void schedule(std::function<void()> task) {
// 存储任务供稍后执行
tasks_.push_back(task);
}
void executeAll() {
for (auto& task : tasks_) {
task();
}
}
private:
std::vector<std::function<void()>> tasks_;
};
int main() {
TaskScheduler scheduler;
// 创建多个lambda任务
for (int i = 0; i < 5; ++i) {
scheduler.scheduler([i]() {
std::cout << "Task " << i << " executed" << std::endl;
});
}
// 稍后执行所有任务
scheduler.executeAll();
return 0;
}
通用lambda(C++14+)
C++14引入了通用lambda,支持auto参数:
cpp
// 通用lambda,支持任意类型参数
auto printPair = [](const auto& a, const auto& b) {
std::cout << a << ", " << b << std::endl;
};
printPair(1, 2); // int, int
printPair(3.14, "Hello"); // double, const char*
printPair(std::string("C++"), true); // string, bool
// 在模板算法中使用
template<typename Container, typename Func>
void processContainer(Container& c, Func f) {
for (auto& item : c) {
f(item);
}
}
std::vector<int> nums = {1, 2, 3};
processContainer(nums, [](auto x) {
std::cout << x * 2 << " ";
});
初始化捕获(C++14+)
C++14允许在捕获列表中初始化变量:
cpp
// 移动语义与lambda
auto ptr = std::make_unique<int>(42);
auto lambda = [p = std::move(ptr)]() {
return *p;
};
// 在lambda内部创建新变量
auto createMultiplier = [factor = 2](int x) {
return x * factor;
};
std::cout << createMultiplier(5) << std::endl; // 输出10
注意事项
- 生命周期管理:引用捕获要注意被引用对象的生命周期
- 性能考虑:小lambda通常被编译器内联,性能接近普通函数
- mutable关键字:按值捕获的变量默认是const,需要mutable才能修改
- 类型推导:复杂返回类型可能需要显式指定
- 与std::function的转换:lambda可隐式转换为std::function,但可能有性能开销
C++20增强
C++20为lambda带来了更多特性:
- 模板lambda参数
- 可构造和可赋值的无状态lambda
- 在未求值上下文中使用lambda
- 捕获结构化绑定
cpp
// C++20: 模板lambda
auto genericLambda = []<typename T>(T a, T b) {
return a + b;
};
// C++20: 捕获结构化绑定
auto [x, y] = std::pair{1, 2};
auto lambda = [=] {
return x + y; // 正确捕获结构化绑定
};
C++的lambda表达式是现代C++编程的核心特性之一,它使得代码更加简洁、表达力更强,特别是在函数式编程和并发编程中发挥着重要作用。
C#
C#中的lambda表达式是匿名函数的一种简洁写法,主要用于创建委托或表达式树类型。
基本语法
csharp
(parameters) => expression
或者
csharp
(parameters) => { statements; }
示例
csharp
// 1. 简单的lambda表达式
Func<int, int> square = x => x * x;
Console.WriteLine(square(5)); // 输出: 25
// 2. 带多个参数的lambda表达式
Func<int, int, int> add = (x, y) => x + y;
Console.WriteLine(add(3, 4)); // 输出: 7
// 3. 带语句块的lambda表达式
Func<int, int> factorial = n => {
int result = 1;
for (int i = 1; i <= n; i++)
result *= i;
return result;
};
Console.WriteLine(factorial(5)); // 输出: 120
// 4. 无参数的lambda表达式
Func<string> getMessage = () => "Hello, World!";
Console.WriteLine(getMessage()); // 输出: Hello, World!
// 5. 使用LINQ的lambda表达式
List<int> numbers = new List<int> { 1, 2, 3, 4, 5 };
var evenNumbers = numbers.Where(n => n % 2 == 0);
foreach (var num in evenNumbers)
Console.WriteLine(num); // 输出: 2, 4
捕获外部变量
C#的lambda表达式可以捕获外部变量,这称为闭包:
csharp
int multiplier = 3;
Func<int, int> multiply = x => x * multiplier;
Console.WriteLine(multiply(5)); // 输出: 15
// 修改捕获的变量
multiplier = 4;
Console.WriteLine(multiply(5)); // 输出: 20
与C++ lambda的对比
| 特性 | C++ | C# |
|---|---|---|
| 语法 | [capture](params) -> ret { body } |
(params) => expression |
| 捕获方式 | 显式捕获列表 [&]、[=]、[var] |
隐式捕获外部变量 |
| 返回类型 | 可显式指定或自动推导 | 自动推导 |
| 主要用途 | 函数对象、算法参数 | 委托、LINQ、事件处理 |
| 类型 | 函数对象 | 委托或表达式树 |
实际应用场景
- LINQ查询:
csharp
var students = new List<Student>();
var topStudents = students.Where(s => s.Score > 90)
.OrderByDescending(s => s.Score)
.Select(s => s.Name);
- 事件处理:
csharp
button.Click += (sender, e) => {
MessageBox.Show("Button clicked!");
};
- 异步编程:
csharp
Task.Run(() => {
// 异步执行的代码
Console.WriteLine("Running in background");
});
- 表达式树:
csharp
Expression<Func<int, bool>> expr = x => x > 5;
注意事项
- 变量捕获的生命周期:捕获的变量在lambda表达式被调用时仍然需要存在
- 性能考虑:频繁创建lambda可能产生垃圾回收压力
- 可读性:复杂的lambda表达式应拆分为命名方法以提高可读性
- 调试:lambda表达式在调试时可能不如命名方法直观