البرمجة الوظيفية في C++: تقنيات متقدمة وأمثلة

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

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

ابدأ الدورة

البرمجة الوظيفية في C++: مستوى متقدم

\n\n

البرمجة الوظيفية (FP) هي نموذج يعتبر العمليات الحسابية كحساب للدوال الرياضية ويتجنب الحالة القابلة للتغيير والآثار الجانبية. على الرغم من أن C++ يرتبط تقليديًا بالبرمجة الكائنية والإجرائية، إلا أن المعايير الحديثة (C++11 وما بعده) أدخلت أدوات قوية لكتابة كود وظيفي. في هذه المقالة، سنتجاوز أساسيات لامدا البسيطة وننظر في التقنيات المتقدمة: من تركيب الدوال إلى محاكاة المونادات.

\n\n

1. تركيب الدوال وخطوط الأنابيب

\n\n

تركيب الدوال هو عملية دمج دالتين أو أكثر لإنشاء دالة جديدة. في C++، من الملائم استخدام تعبيرات لامدا والقوالب لهذا الغرض.

\n\n
#include <iostream>\n#include <functional>\n#include <vector>\n#include <algorithm>\n\n// قالب لتركيب دالتين\ntemplate<typename F, typename G>\nauto compose(F f, G g) {\n    return [f, g](auto x) {\n        return f(g(x));\n    };\n}\n\n// مثال على الاستخدام\nint main() {\n    auto addTwo = [](int x) { return x + 2; };\n    auto multiplyByThree = [](int x) { return x * 3; };\n\n    // التركيب: أولاً multiplyByThree، ثم addTwo\n    auto composed = compose(addTwo, multiplyByThree);\n\n    std::cout << composed(5) << std::endl; // (5*3) + 2 = 17\n\n    // معالجة خط الأنابيب (pipe) باستخدام std::ranges (C++20)\n    std::vector<int> numbers = {1, 2, 3, 4, 5};\n    auto result = numbers\n        | std::views::filter([](int n) { return n % 2 == 0; })\n        | std::views::transform([](int n) { return n * n; });\n\n    for (int n : result) {\n        std::cout << n << " "; // 4 16\n    }\n    return 0;\n}\n
\n\n

تسمح خطوط الأنابيب مع std::views (C++20) ببناء سلاسل من التحويلات دون إنشاء حاويات وسيطة، وهو نهج وظيفي كلاسيكي.

\n\n

2. الكاري (Currying) والتطبيق الجزئي

\n\n

الكاري هو تحويل دالة تأخذ عدة وسائط إلى سلسلة من الدوال، كل منها تأخذ وسيطًا واحدًا. التطبيق الجزئي يثبت جزءًا من الوسائط.

\n\n
#include <iostream>\n#include <functional>\n\n// دالة جمع مُكَرّاة\nauto curriedAdd = [](int a) {\n    return [a](int b) {\n        return [a, b](int c) {\n            return a + b + c;\n        };\n    };\n};\n\n// التطبيق الجزئي باستخدام std::bind\nint multiply(int a, int b, int c) {\n    return a * b * c;\n}\n\nint main() {\n    // استخدام الكاري\n    auto add5 = curriedAdd(5);\n    auto add5And3 = add5(3);\n    std::cout << add5And3(2) << std::endl; // 10\n\n    // التطبيق الجزئي عبر std::bind\n    using namespace std::placeholders;\n    auto multiplyBy2 = std::bind(multiply, _1, _2, 2);\n    std::cout << multiplyBy2(3, 4) << std::endl; // 3*4*2 = 24\n\n    // التطبيق الجزئي عبر لامدا (أكثر اصطلاحية في C++14+)\n    auto multiplyBy3 = [](int a, int b) { return multiply(a, b, 3); };\n    std::cout << multiplyBy3(5, 6) << std::endl; // 5*6*3 = 90\n\n    return 0;\n}\n
\n\n

الكاري مفيد بشكل خاص عند العمل مع دوال ذات ترتيب أعلى، مثل std::transform أو std::accumulate، مما يسمح بإنشاء إصدارات متخصصة من الدوال "أثناء التنفيذ".

\n\n

3. الفونكتورات، التطبيقات، ومحاكاة المونادات

\n\n

في Haskell، المونادات هي نمط قوي للتعامل مع الآثار الجانبية. في C++، يمكننا محاكاتها باستخدام قوالب الفئات. لننظر في مونادة Maybe البسيطة (مشابهة لـ std::optional)، ولكن مع عمليات مونادية.

\n\n
#include <iostream>\n#include <functional>\n#include <optional>\n\n// مونادة Maybe (غلاف لـ std::optional)\ntemplate<typename T>\nclass Maybe {\n    s                

المدونات

توصيات الكتب