البرمجة الوظيفية في C++: من لامبدا إلى الخوارزميات

مدرب بايثون أونلاين للمبتدئين

تعلم Python بسهولة دون إرهاق نظري. حل مهام عملية مع التحقق التلقائي واكتب الكود مباشرة في المتصفح.

ابدأ الدورة

البرمجة الوظيفية في C++: من التعبيرات اللامبدا إلى التركيب



البرمجة الوظيفية (FP) هي نموذج حيث تُبنى الحسابات على تطبيق دوال نقية، وثبات البيانات، وغياب التأثيرات الجانبية. على الرغم من أن لغة C++ كانت في الأصل لغة متعددة النماذج، إلا أن معاييرها الحديثة (C++11/14/17/20) قدمت أدوات قوية للبرمجة الوظيفية: تعبيرات لامبدا، std::function، خوارزميات STL، الروتينات المتزامنة (coroutines)، وحتى دعم أنماط الموناد. في هذه المقالة، سنستكشف كيفية كتابة كود وظيفي في C++ دون فقدان الأداء.



1. أساسيات الأسلوب الوظيفي: لامبدا و std::function



تعبيرات لامبدا هي قلب البرمجة الوظيفية في C++. إنها تسمح لك بإنشاء دوال مجهولة "أثناء التنفيذ"، مع التقاط السياق.



#include <iostream>#include <vector>#include <algorithm>

int main() { std::vector<int> nums = {1, 2, 3, 4, 5}; // لامبدا لتصفية الأعداد الزوجية auto is_even = [](int x) { return x % 2 == 0; }; // الاستخدام مع خوارزمية std::copy_if std::vector<int> evens; std::copy_if(nums.begin(), nums.end(), std::back_inserter(evens), is_even); for (int n : evens) std::cout << n << " "; // 2 4 return 0;}


نقاط رئيسية حول لامبدا:

  • الالتقاط — [=] (بالقيمة)، [&] (بالمرجع)، [this] (لدوال الفئة).
  • النوع — كل لامبدا لها نوع فريد، لذلك يُستخدم std::function أو القوالب لتمريرها إلى الدوال.
  • قيمة الإرجاع — تُستنتج تلقائياً ما لم يُحدد خلاف ذلك.


std::function — غلاف للكائنات القابلة للاستدعاء (الدوال، لامبدا، الكائنات الدالية). إنه مناسب للتخزين والتمرير لكنه يضيف عبئاً (استدعاء افتراضي).



#include <functional>

void apply_twice(std::function<int(int)> f, int x) { std::cout << f(f(x)) << std::endl; // التركيب}

int main() { auto square = [](int x) { return x * x; }; apply_twice(square, 3); // (3^2)^2 = 81 return 0;}


2. خوارزميات STL كأساس للسلاسل الوظيفية



خوارزميات STL (std::transform، std::copy_if، std::accumulate، إلخ) تتناسب تماماً مع الأسلوب الوظيفي. هي لا تعدل الحاويات مباشرة بل تأخذ مكررات وتعيد النتائج.



مثال: التصفية والتحويل (map/filter)



#include <numeric>#include <ranges> // C++20

int main() { std::vector<int> data = {1, 2, 3, 4, 5, 6}; // الطريقة التقليدية (C++11) std::vector<int> filtered; std::copy_if(data.begin(), data.end(), std::back_inserter(filtered), [](int x) { return x > 3; }); std::vector<int> result; std::transform(filtered.begin(), filtered.end(), std::back_inserter(result), [](int x) { return x * 2; }); // الطريقة الحديثة مع النطاقات (C++20) auto view = data | std::views::filter([](int x) { return x > 3; }) | std::views::transform([](int x) { return x * 2; }); for (int v : view) std::cout << v << " "; // 8 10 12 return 0;}


الطي باستخدام std::accumulate



#include <numeric>

int main() { std::vector<int> nums = {1, 2, 3, 4}; // المجموع بقيمة ابتدائية 0 int sum = std::accumulate(nums.begin(), nums.end(), 0, [](int a, int b) { return a + b; }); // حاصل الضرب بقيمة ابتدائية 1 int prod = std::accumulate

المدونات

توصيات الكتب