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;
}اختبر فهمك