Kodax

Lambda Expressions — C++ المتقدم

تعبيرات Lambda

lambda دالة صغيرة بلا اسم تكتبها في مكان استخدامها. أكثر استخدام لها: تمرير سلوك إلى خوارزميات STL (الدرس التالي).

الصيغة

[الالتقاط](المعاملات) -> نوع_الإرجاع { الجسم }

نوع الإرجاع اختياري ويُستنتج غالباً.

#include <iostream>
using namespace std;

int main() {
    auto square = [](int x) { return x * x; };
    cout << square(5) << '\n';            // 25

    auto greet = [] { cout << "مرحباً\n"; };   // بلا معاملات: الأقواس اختيارية
    greet();
}

الالتقاط (Capture)

الـ lambda لا ترى المتغيرات المحلية المحيطة بها إلا إذا التقطتها:

| الصيغة | المعنى |

|---|---|

| [] | لا شيء |

| [x] | x بالقيمة (نسخة وقت إنشاء الـ lambda) |

| [&x] | x بالمرجع (الأصل) |

| [=] | كل ما يُستخدم بالقيمة |

| [&] | كل ما يُستخدم بالمرجع |

| [this] | أعضاء الكائن الحالي |

| [y = x * 2] | متغير جديد مهيأ (C++14) |

#include <iostream>
using namespace std;

int main() {
    int bonus = 10;
    auto byValue = [bonus](int s) { return s + bonus; };
    auto byRef   = [&bonus](int s) { return s + bonus; };
    bonus = 50;
    cout << byValue(70) << '\n';   // 80  — النسخة التُقطت عندما كانت 10
    cout << byRef(70) << '\n';     // 120 — ترى القيمة الحالية
}

mutable

النسخ الملتقطة بالقيمة للقراءة فقط افتراضياً. mutable تسمح بتعديل النسخة (لا الأصل):

#include <iostream>
using namespace std;

int main() {
    int counter = 0;
    auto next = [counter]() mutable { return ++counter; };
    cout << next() << next() << next() << '\n';   // 123
    cout << counter << '\n';                       // 0 — الأصل لم يتغير
}

⚠️ خطر: مرجع معلّق

الالتقاط بالمرجع لا يطيل عمر المتغير. إذا عاشت الـ lambda أطول منه (أُعيدت من دالة، أو خُزّنت، أو أُرسلت لخيط)، فالمرجع يشير إلى ذاكرة ميتة:

#include <functional>

std::function<int()> makeCounter() {
    int count = 0;
    // return [&count] { return ++count; };   // ❌ count يموت عند نهاية الدالة
    return [count]() mutable { return ++count; };   // ✅ نسخة تملكها الـ lambda
}

القاعدة: [&] للاستخدام الفوري (مع خوارزمية مثلاً)، والقيمة لما يُخزَّن.

lambda عامة

auto في المعاملات تجعلها تعمل مع أي نوع (مثل القالب):

auto add = [](auto a, auto b) { return a + b; };
// add(1, 2) → 3     add(1.5, 2.0) → 3.5

تخزين lambda

- auto f = [...]{...}; — الأسرع؛ لكل lambda نوع فريد لا اسم له.

- std::function<int(int)> — حاوية لأي شيء قابل للاستدعاء بنفس التوقيع؛ مفيدة لتخزين lambdas مختلفة في vector أو كعضو في صنف، مع تكلفة بسيطة.

#include <functional>
#include <map>
#include <string>
using namespace std;

map<string, function<double(double, double)>> ops = {
    {"+", [](double a, double b) { return a + b; }},
    {"*", [](double a, double b) { return a * b; }},
};
// ops["*"](3, 4) → 12

تمرين

اكتب دالة makeMultiplier(int k) تعيد lambda تضرب أي عدد في k. أنشئ twice و triple وجرّبهما. لماذا يجب الالتقاط بالقيمة هنا؟

#include <iostream>
#include <vector>
#include <algorithm>
#include <functional>
using namespace std;

int main() {
    // ===== أساسيات Lambda =====
    cout << "=== أساسيات ===" << endl;
    
    // Lambda بسيطة
    auto hello = []() { cout << "مرحباً!" << endl; };
    hello();
    
    // Lambda مع معاملات
    auto add = [](int a, int b) { return a + b; };
    cout << "5 + 3 = " << add(5, 3) << endl;
    
    // Lambda مع نوع الإرجاع صريح
    auto divide = [](double a, double b) -> double {
        if (b == 0) return 0;
        return a / b;
    };
    cout << "10 / 3 = " << divide(10, 3) << endl;
    
    // ===== Capture =====
    cout << "\n=== Capture ===" << endl;
    
    int x = 10;
    int y = 20;
    
    // Capture by value
    auto byValue = [x, y]() {
        cout << "x = " << x << ", y = " << y << endl;
        // x = 50;  // خطأ! نسخة ثابتة
    };
    byValue();
    
    // Capture by reference
    auto byRef = [&x, &y]() {
        x *= 2;
        y *= 2;
    };
    byRef();
    cout << "بعد التعديل: x = " << x << ", y = " << y << endl;
    
    // Capture all by value
    auto allByValue = [=]() {
        return x + y;
    };
    cout << "المجموع: " << allByValue() << endl;
    
    // Capture all by reference
    int counter = 0;
    auto increment = [&]() { counter++; };
    increment();
    increment();
    increment();
    cout << "العداد: " << counter << endl;
    
    // مزيج
    int a = 5, b = 10;
    auto mixed = [a, &b]() {
        b += a;  // b بالمرجع، a بالقيمة
    };
    mixed();
    cout << "b = " << b << endl;
    
    // ===== مع STL =====
    cout << "\n=== مع STL ===" << endl;
    
    vector<int> nums = {5, 2, 8, 1, 9, 3, 7, 4, 6};
    
    // sort مخصص
    sort(nums.begin(), nums.end(), [](int a, int b) {
        return a > b;  // تنازلي
    });
    cout << "تنازلي: ";
    for (int n : nums) cout << n << " ";
    cout << endl;
    
    // filter (remove_if + erase)
    nums.erase(
        remove_if(nums.begin(), nums.end(), [](int x) { return x < 5; }),
        nums.end()
    );
    cout << "أكبر من 5: ";
    for (int n : nums) cout << n << " ";
    cout << endl;
    
    // for_each
    cout << "المربعات: ";
    for_each(nums.begin(), nums.end(), [](int x) {
        cout << x * x << " ";
    });
    cout << endl;
    
    // ===== Lambda متقدمة =====
    cout << "\n=== متقدم ===" << endl;
    
    // Mutable lambda
    int count = 0;
    auto mutableLambda = [count]() mutable {
        count++;  // يعمل لأن mutable
        return count;
    };
    cout << mutableLambda() << " ";  // 1
    cout << mutableLambda() << " ";  // 2
    cout << mutableLambda() << endl; // 3
    cout << "الأصلي: " << count << endl;  // 0 (لم يتغير)
    
    // Generic lambda (C++14)
    auto genericAdd = [](auto a, auto b) { return a + b; };
    cout << genericAdd(5, 3) << endl;      // int
    cout << genericAdd(2.5, 1.5) << endl;  // double
    cout << genericAdd(string("Hello "), string("World")) << endl;
    
    // Lambda كـ callback
    function<void(int)> callback = [](int result) {
        cout << "النتيجة: " << result << endl;
    };
    
    auto compute = [](int x, function<void(int)> cb) {
        cb(x * x);
    };
    compute(5, callback);
    
    return 0;
}
اختبر فهمك