🚀 Lambda Expressions trong C++
📖 Giới thiệu
Lambda expressions (biểu thức lambda) là một tính năng mạnh mẽ được giới thiệu trong C++11, cho phép tạo ra các hàm ẩn danh (anonymous functions) ngay tại chỗ sử dụng. Lambda giúp code trở nên ngắn gọn, dễ đọc và hiệu quả hơn, đặc biệt khi làm việc với STL algorithms.
Tại sao cần Lambda?
- Hàm ngắn gọn: Không cần định nghĩa hàm riêng cho logic đơn giản
- Code tại chỗ: Viết logic ngay tại nơi sử dụng
- Functional programming: Hỗ trợ phong cách lập trình hàm
- STL integration: Kết hợp hoàn hảo với
std::sort,std::find_if,std::for_each
Ứng dụng thực tiễn:
- Sắp xếp dữ liệu với tiêu chí tùy chỉnh
- Lọc và biến đổi container
- Event handling trong GUI applications
- Callback functions trong asynchronous programming
🔧 Cú pháp
Cú pháp cơ bản
// Cú pháp tổng quát
[capture list](parameter list) -> return type {
// function body
}
// Ví dụ đơn giản
auto lambda = []() {
cout << "Hello Lambda!" << endl;
};
// Gọi lambda
lambda(); // Output: Hello Lambda!Capture List (Danh sách bắt)
int x = 10, y = 20;
// Không capture gì
auto lambda1 = []() { /* không thể truy cập x, y */ };
// Capture by value
auto lambda2 = [x, y]() {
return x + y; // Copy giá trị x, y
};
// Capture by reference
auto lambda3 = [&x, &y]() {
x++; // Thay đổi x gốc
y++; // Thay đổi y gốc
};
// Capture all by value
auto lambda4 = [=]() {
return x + y; // Copy tất cả biến
};
// Capture all by reference
auto lambda5 = [&]() {
x++; y++; // Tham chiếu tất cả biến
};
// Mixed capture
auto lambda6 = [=, &x]() {
x++; // x by reference
return y; // y by value
};Parameter List và Return Type
// Có tham số
auto add = [](int a, int b) {
return a + b;
};
// Chỉ định kiểu trả về rõ ràng
auto divide = [](double a, double b) -> double {
return (b != 0) ? a / b : 0.0;
};
// Lambda với tham số template (C++14)
auto generic = [](auto a, auto b) {
return a + b;
};Mutable Lambda
int x = 10;
// Lambda mutable - có thể thay đổi captured values
auto lambda = [x]() mutable {
x++; // OK: có thể thay đổi copy của x
return x;
};
cout << lambda() << endl; // 11
cout << x << endl; // 10 (x gốc không đổi)🔬 Phân tích & Giải thích chi tiết
Hiểu về Capture List
Capture by Value [=]:
- Tạo copy của biến vào lambda
- Thay đổi trong lambda không ảnh hưởng biến gốc
- An toàn nhưng tốn memory nếu object lớn
Capture by Reference [&]:
- Lambda truy cập trực tiếp biến gốc
- Tiết kiệm memory nhưng cần cẩn thận về lifetime
- Có thể thay đổi giá trị biến gốc
Mixed Capture:
int x = 1, y = 2, z = 3;
auto lambda = [x, &y, z = z * 2](int a) {
// x: capture by value
// y: capture by reference
// z: capture by value với khởi tạo
return x + y + z + a;
};Lambda vs Function Objects vs Function Pointers
Lambda có ưu điểm:
- Syntax ngắn gọn
- Có thể capture local variables
- Compiler tối ưu hóa tốt
- Type-safe
So sánh:
// Function pointer
bool (*func_ptr)(int) = [](int x) { return x > 5; };
// Function object (functor)
struct GreaterThan {
int threshold;
GreaterThan(int t) : threshold(t) {}
bool operator()(int x) const { return x > threshold; }
};
// Lambda (recommended)
int threshold = 5;
auto lambda = [threshold](int x) { return x > threshold; };Lambda và STL Algorithms
Lambda kết hợp hoàn hảo với STL algorithms:
vector<int> vec = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
// std::find_if
auto it = find_if(vec.begin(), vec.end(),
[](int x) { return x > 5; });
// std::sort
sort(vec.begin(), vec.end(),
[](int a, int b) { return a > b; }); // Descending
// std::for_each
for_each(vec.begin(), vec.end(),
[](int& x) { x *= 2; }); // Double each element💻 Ví dụ minh họa
Ví dụ 1: Hệ thống quản lý nhân viên với Lambda
#include <iostream>
#include <vector>
#include <algorithm>
#include <string>
#include <iomanip>
using namespace std;
struct Employee {
string name;
int age;
double salary;
string department;
// Constructor
Employee(string n, int a, double s, string d)
: name(n), age(a), salary(s), department(d) {}
};
class EmployeeManager {
private:
vector<Employee> employees;
public:
void addEmployee(const Employee& emp) {
employees.push_back(emp);
}
// Sử dụng lambda để lọc và sắp xếp
void demonstrateLambda() {
cout << "=== DEMO LAMBDA EXPRESSIONS ===" << endl;
// 1. Tìm nhân viên có lương > 15 triệu
cout << "\n1. Nhân viên lương > 15 triệu:" << endl;
for_each(employees.begin(), employees.end(),
[](const Employee& emp) {
if (emp.salary > 15000000) {
cout << "- " << emp.name << ": "
<< emp.salary << " VND" << endl;
}
});
// 2. Đếm nhân viên theo độ tuổi
int young = count_if(employees.begin(), employees.end(),
[](const Employee& emp) {
return emp.age < 30;
});
cout << "\n2. Số nhân viên dưới 30 tuổi: " << young << endl;
// 3. Sắp xếp theo lương giảm dần
vector<Employee> sortedBySalary = employees;
sort(sortedBySalary.begin(), sortedBySalary.end(),
[](const Employee& a, const Employee& b) {
return a.salary > b.salary;
});
cout << "\n3. Top 3 nhân viên lương cao nhất:" << endl;
for (int i = 0; i < min(3, (int)sortedBySalary.size()); i++) {
cout << (i + 1) << ". " << sortedBySalary[i].name
<< " - " << sortedBySalary[i].salary << " VND" << endl;
}
// 4. Tìm nhân viên theo phòng ban
string searchDept = "IT";
cout << "\n4. Nhân viên phòng " << searchDept << ":" << endl;
auto it = find_if(employees.begin(), employees.end(),
[searchDept](const Employee& emp) {
return emp.department == searchDept;
});
while (it != employees.end()) {
cout << "- " << it->name << " (" << it->age << " tuổi)" << endl;
it = find_if(it + 1, employees.end(),
[searchDept](const Employee& emp) {
return emp.department == searchDept;
});
}
// 5. Tính lương trung bình theo phòng ban
vector<string> departments = {"IT", "HR", "Sales"};
cout << "\n5. Lương trung bình theo phòng ban:" << endl;
for (const string& dept : departments) {
double totalSalary = 0;
int count = 0;
for_each(employees.begin(), employees.end(),
[&](const Employee& emp) {
if (emp.department == dept) {
totalSalary += emp.salary;
count++;
}
});
if (count > 0) {
cout << "- " << dept << ": "
<< fixed << setprecision(0)
<< (totalSalary / count) << " VND" << endl;
}
}
// 6. Transform: Tăng lương 10% cho tất cả
cout << "\n6. Sau khi tăng lương 10%:" << endl;
transform(employees.begin(), employees.end(), employees.begin(),
[](Employee emp) {
emp.salary *= 1.1;
return emp;
});
// Hiển thị 3 nhân viên đầu
for (int i = 0; i < min(3, (int)employees.size()); i++) {
cout << "- " << employees[i].name << ": "
<< employees[i].salary << " VND" << endl;
}
}
};
int main() {
EmployeeManager manager;
// Thêm dữ liệu mẫu
manager.addEmployee(Employee("Nguyễn Văn An", 28, 20000000, "IT"));
manager.addEmployee(Employee("Trần Thị Bình", 32, 18000000, "IT"));
manager.addEmployee(Employee("Lê Văn Cường", 25, 12000000, "HR"));
manager.addEmployee(Employee("Phạm Thị Dung", 29, 16000000, "Sales"));
manager.addEmployee(Employee("Hoàng Văn Em", 35, 22000000, "IT"));
manager.demonstrateLambda();
return 0;
}Kết quả chạy:
=== DEMO LAMBDA EXPRESSIONS ===
1. Nhân viên lương > 15 triệu:
- Nguyễn Văn An: 20000000 VND
- Trần Thị Bình: 18000000 VND
- Phạm Thị Dung: 16000000 VND
- Hoàng Văn Em: 22000000 VND
2. Số nhân viên dưới 30 tuổi: 3
3. Top 3 nhân viên lương cao nhất:
1. Hoàng Văn Em - 22000000 VND
2. Nguyễn Văn An - 20000000 VND
3. Trần Thị Bình - 18000000 VNDVí dụ 2: Xử lý dữ liệu với Custom Predicates
#include <iostream>
#include <vector>
#include <algorithm>
#include <numeric>
#include <functional>
using namespace std;
class DataProcessor {
private:
vector<int> data;
public:
DataProcessor(vector<int> input) : data(input) {}
// Filter dữ liệu với lambda predicate
vector<int> filter(function<bool(int)> predicate) {
vector<int> result;
copy_if(data.begin(), data.end(), back_inserter(result), predicate);
return result;
}
// Transform dữ liệu
vector<int> transform(function<int(int)> transformer) {
vector<int> result;
::transform(data.begin(), data.end(), back_inserter(result), transformer);
return result;
}
// Reduce (accumulate) dữ liệu
int reduce(function<int(int, int)> reducer, int initial = 0) {
return accumulate(data.begin(), data.end(), initial, reducer);
}
void printData(const string& title = "Data") {
cout << title << ": ";
for (int x : data) cout << x << " ";
cout << endl;
}
static void printVector(const vector<int>& vec, const string& title) {
cout << title << ": ";
for (int x : vec) cout << x << " ";
cout << endl;
}
void demonstrateAdvancedLambda() {
cout << "=== ADVANCED LAMBDA DEMO ===" << endl;
printData("Original data");
// 1. Lọc số chẵn
auto evenNumbers = filter([](int x) { return x % 2 == 0; });
printVector(evenNumbers, "Even numbers");
// 2. Lọc số trong khoảng [10, 50]
auto inRange = filter([](int x) { return x >= 10 && x <= 50; });
printVector(inRange, "Numbers in [10, 50]");
// 3. Transform: Bình phương
auto squares = transform([](int x) { return x * x; });
printVector(squares, "Squares");
// 4. Transform: Conditional transformation
auto conditionalTransform = transform([](int x) {
return (x > 20) ? x * 2 : x / 2;
});
printVector(conditionalTransform, "Conditional transform");
// 5. Reduce: Tổng
int sum = reduce([](int acc, int x) { return acc + x; });
cout << "Sum: " << sum << endl;
// 6. Reduce: Tích các số dương
int product = reduce([](int acc, int x) {
return (x > 0) ? acc * x : acc;
}, 1);
cout << "Product of positive numbers: " << product << endl;
// 7. Complex lambda với capture
int threshold = 25;
double multiplier = 1.5;
auto complexTransform = transform([threshold, multiplier](int x) {
if (x > threshold) {
return static_cast<int>(x * multiplier);
} else {
return x;
}
});
printVector(complexTransform, "Complex transform");
// 8. Lambda factory - trả về lambda
auto makeMultiplier = [](double factor) {
return [factor](int x) { return static_cast<int>(x * factor); };
};
auto doubler = makeMultiplier(2.0);
auto tripler = makeMultiplier(3.0);
auto doubled = transform(doubler);
auto tripled = transform(tripler);
printVector(doubled, "Doubled");
printVector(tripled, "Tripled");
}
};
int main() {
vector<int> testData = {5, 12, 8, 30, 45, 3, 18, 25, 7, 33};
DataProcessor processor(testData);
processor.demonstrateAdvancedLambda();
return 0;
}Ví dụ 3: Event System với Lambda Callbacks
#include <iostream>
#include <vector>
#include <functional>
#include <string>
#include <map>
using namespace std;
// Event System sử dụng lambda callbacks
class EventSystem {
private:
map<string, vector<function<void(const string&)>>> eventHandlers;
public:
// Đăng ký event handler
void subscribe(const string& eventName, function<void(const string&)> handler) {
eventHandlers[eventName].push_back(handler);
}
// Kích hoạt event
void emit(const string& eventName, const string& data = "") {
if (eventHandlers.find(eventName) != eventHandlers.end()) {
for (auto& handler : eventHandlers[eventName]) {
handler(data);
}
}
}
// Xóa tất cả handlers của một event
void unsubscribe(const string& eventName) {
eventHandlers.erase(eventName);
}
};
// Game Player class
class Player {
private:
string name;
int health;
int score;
EventSystem* eventSystem;
public:
Player(string n, EventSystem* es) : name(n), health(100), score(0), eventSystem(es) {
// Đăng ký các event handlers bằng lambda
// Health change handler
eventSystem->subscribe("health_change", [this](const string& data) {
cout << "[" << name << "] Health changed: " << data << endl;
if (health <= 0) {
eventSystem->emit("player_died", name);
}
});
// Score update handler
eventSystem->subscribe("score_update", [this](const string& data) {
cout << "[" << name << "] Score updated: +" << data << " points" << endl;
});
// Level up handler
eventSystem->subscribe("level_up", [this](const string& data) {
cout << "🎉 [" << name << "] LEVEL UP! New level: " << data << endl;
});
// Player died handler
eventSystem->subscribe("player_died", [this](const string& playerName) {
if (playerName == name) {
cout << "💀 [" << name << "] Game Over!" << endl;
} else {
cout << "[" << name << "] " << playerName << " has died." << endl;
}
});
}
void takeDamage(int damage) {
health -= damage;
eventSystem->emit("health_change", "took " + to_string(damage) + " damage. Health: " + to_string(health));
}
void heal(int amount) {
health += amount;
if (health > 100) health = 100;
eventSystem->emit("health_change", "healed " + to_string(amount) + ". Health: " + to_string(health));
}
void addScore(int points) {
score += points;
eventSystem->emit("score_update", to_string(points));
// Check level up (every 100 points)
static int lastLevel = 0;
int currentLevel = score / 100;
if (currentLevel > lastLevel) {
lastLevel = currentLevel;
eventSystem->emit("level_up", to_string(currentLevel));
}
}
string getName() const { return name; }
int getHealth() const { return health; }
int getScore() const { return score; }
};
int main() {
EventSystem eventSystem;
// Tạo players
Player player1("Alice", &eventSystem);
Player player2("Bob", &eventSystem);
// Đăng ký global event handlers
eventSystem.subscribe("player_died", [](const string& playerName) {
cout << "🔔 Global notification: " << playerName << " is no longer in the game." << endl;
});
eventSystem.subscribe("level_up", [](const string& level) {
cout << "🌟 Global celebration: Someone reached level " << level << "!" << endl;
});
// Simulation
cout << "=== GAME SIMULATION ===" << endl;
cout << "\n--- Round 1 ---" << endl;
player1.addScore(50);
player2.addScore(30);
cout << "\n--- Round 2 ---" << endl;
player1.takeDamage(20);
player2.takeDamage(15);
cout << "\n--- Round 3 ---" << endl;
player1.addScore(80); // Should trigger level up
player2.addScore(45);
cout << "\n--- Round 4 ---" << endl;
player1.heal(10);
player2.takeDamage(30);
cout << "\n--- Final Round ---" << endl;
player2.takeDamage(100); // Should trigger death
return 0;
}🏋️ Thực hành
Bài tập 1: Calculator với Lambda Operations
Tạo một máy tính sử dụng lambda để định nghĩa các phép toán:
// Yêu cầu:
map<string, function<double(double, double)>> operations;
// Thêm các phép toán: +, -, *, /, ^, %
// Sử dụng lambda để định nghĩa từng phép toánBài tập 2: Text Processing Pipeline
Viết chương trình xử lý văn bản với pipeline các lambda functions:
- Loại bỏ ký tự đặc biệt
- Chuyển về chữ thường
- Loại bỏ stop words
- Đếm tần suất từ
Bài tập 3: Custom Sort với Lambda
Sắp xếp danh sách sinh viên theo nhiều tiêu chí:
- Ưu tiên: Điểm trung bình
- Thứ hai: Tuổi (trẻ hơn trước)
- Thứ ba: Tên theo alphabet
Bài tập 4: Lambda-based Validator
Tạo hệ thống validation sử dụng lambda:
struct ValidationRule {
function<bool(const string&)> validator;
string errorMessage;
};
// Thêm rules: email format, password strength, phone numberLời giải chi tiết
Bài tập 1 - Calculator:
#include <iostream>
#include <map>
#include <functional>
#include <string>
#include <cmath>
using namespace std;
class LambdaCalculator {
private:
map<string, function<double(double, double)>> operations;
public:
LambdaCalculator() {
// Định nghĩa các phép toán bằng lambda
operations["+"] = [](double a, double b) { return a + b; };
operations["-"] = [](double a, double b) { return a - b; };
operations["*"] = [](double a, double b) { return a * b; };
operations["/"] = [](double a, double b) {
return (b != 0) ? a / b : throw invalid_argument("Division by zero");
};
operations["^"] = [](double a, double b) { return pow(a, b); };
operations["%"] = [](double a, double b) {
return (static_cast<int>(b) != 0) ? static_cast<int>(a) % static_cast<int>(b) : 0;
};
}
double calculate(double a, const string& op, double b) {
if (operations.find(op) != operations.end()) {
return operations[op](a, b);
}
throw invalid_argument("Unknown operation: " + op);
}
void showOperations() {
cout << "Available operations: ";
for (const auto& pair : operations) {
cout << pair.first << " ";
}
cout << endl;
}
};📋 Tóm tắt
Các điểm quan trọng
Cú pháp Lambda:
[capture](parameters) -> return_type { body }Capture modes:
[=]- Capture by value[&]- Capture by reference[x, &y]- Mixed capture[]- No capture
Ưu điểm Lambda:
- Code ngắn gọn và rõ ràng
- Tích hợp tốt với STL algorithms
- Hỗ trợ functional programming
- Performance tốt nhờ compiler optimization
Best Practices
- Sử dụng auto:
auto lambda = [](int x) { return x * 2; }; - Capture cẩn thận: Tránh capture không cần thiết
- Prefer capture by value: An toàn hơn về lifetime
- Keep it simple: Lambda phức tạp nên viết thành function riêng
- Use with STL: Kết hợp với
sort,find_if,transform, etc.
Chuẩn bị cho bài tiếp theo
Bài học tiếp theo sẽ tìm hiểu về Smart Pointers - công cụ quản lý bộ nhớ tự động:
- unique_ptr: Ownership độc quyền
- shared_ptr: Ownership chia sẻ với reference counting
- weak_ptr: Quan sát object mà không sở hữu
- RAII: Resource Acquisition Is Initialization
- Memory safety: Tránh memory leaks và dangling pointers
Lambda expressions là nền tảng của modern C++ và functional programming. Hãy luyện tập nhiều để viết code ngắn gọn và hiệu quả!