البرمجة الوظيفية في C++: من الألف إلى الياء
كثير من المطورين الذين يعرفون Python أو JavaScript اعتادوا على الأدوات القوية للبرمجة الوظيفية: لامدا، map، filter، reduce. لكن هل تعلم أن C++ توفر إمكانيات لا تقل مرونة للأسلوب الوظيفي؟ في هذه المقالة سنشرح البرمجة الوظيفية في C++ من المفاهيم الأساسية إلى التقنيات المتقدمة. ستتعلم كيفية كتابة كود أكثر إيجازًا وأمانًا وتعبيرًا باستخدام إمكانيات C++11/14/17 الحديثة.
1. أساسيات المنهج الوظيفي: تعبيرات لامدا و std::function
تبدأ البرمجة الوظيفية في C++ بتعبيرات لامدا. فهي تسمح بإنشاء دوال مجهولة مباشرة في نقطة الاستخدام. بناء جملة لامدا بسيط:
// لامدا أساسيةauto sum = [](int a, int b) -> int { return a + b; };std::cout << sum(5, 3); // 8
// لامدا مع التقاط المتغيراتint multiplier = 3;auto multiply = [multiplier](int x) { return x * multiplier; };std::cout << multiply(7); // 21الميزة الرئيسية للامدا هي التقاط السياق. يمكنك التقاط المتغيرات بالقيمة ([=])، أو بالمرجع ([&])، أو بطريقة مختلطة. هذا هو أساس الإغلاقات التي تشكل حجر الزاوية للأسلوب الوظيفي.
لتخزين وتمرير لامدا، يُستخدم std::function — وهو غلاف متعدد الأشكال للكائنات القابلة للاستدعاء:
#include <functional>
std::function<int(int, int)> operation;
operation = [](int a, int b) { return a + b; };std::cout << operation(10, 20); // 30
operation = [](int a, int b) { return a * b; };std::cout << operation(10, 20); // 200نصيحة: استخدم auto للامدا إذا لم تكن بحاجة لتخزينها كنوع. std::function يضيف تكاليف إضافية صغيرة، لذا يُفضل استخدامه فقط عند الحاجة لتعدد الأشكال.
2. خوارزميات STL: map، filter، reduce بأيدينا
في C++ لا توجد دوال مدمجة map، filter، reduce كما في Python، لكن STL توفر خوارزميات قوية تحل محلها. دعنا نستعرض الأساسيات:
2.1. Map — تحويل العناصر
بدلاً من map استخدم std::transform:
#include <algorithm>#include <vector>
std::vector<int> numbers = {1, 2, 3, 4, 5};std::vector<int> squares(numbers.size());
std::transform(numbers.begin(), numbers.end(), squares.begin(), [](int n) { return n * n; });// squares: {1, 4, 9, 16, 25}2.2. Filter — التصفية حسب الشرط
نظير filter هو std::copy_if أو std::remove_if (مع الحذف اللاحق):
std::vector<int> numbers = {1, 2, 3, 4, 5, 6};std::vector<int> evens;
std::copy_if(numbers.begin(), numbers.end(), std::back_inserter(evens), [](int n) { return n % 2 == 0; });// evens: {2, 4, 6}2.3. Reduce — طي المجموعة
لـ reduce استخدم std::accumulate من <numeric>:
#include <numeric>
std::vector<int> numbers = {1, 2, 3, 4, 5};int sum = std::accumulate(numbers.begin(), numbers.end(), 0, [](int acc, int n) { return acc + n; });// sum = 15مثال متقدم: دمج map و filter و reduce لمعالجة البيانات:
std::vector<std::string> names = {"Alice", "Bob", "Charlie", "David"};// الحصول على مجموع أطوال الأسماء التي تبدأ بـ 'A' أو 'C'int result = std::accumulate(names.begin(), names.end(), 0, [](int acc, const std::string& name) { if (name[0] == 'A' || name[0] == 'C') { return acc + name.length(); } return acc;