Skip to content

🚀 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

cpp
// 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)

cpp
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

cpp
// 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

cpp
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:

cpp
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:

cpp
// 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:

cpp
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

cpp
#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 VND

Ví dụ 2: Xử lý dữ liệu với Custom Predicates

cpp
#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

cpp
#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:

cpp
// 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án

Bà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:

cpp
struct ValidationRule {
    function<bool(const string&)> validator;
    string errorMessage;
};

// Thêm rules: email format, password strength, phone number

Lời giải chi tiết

Bài tập 1 - Calculator:

cpp
#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:

cpp
[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ả!

Khóa học C++ miễn phí