البرمجة الوظيفية في لغة C++: المستوى المتقدم
البرمجة الوظيفية (FP) هي نموذج يتم فيه تمثيل العمليات الحسابية كتقييم للدوال الرياضية، مع تجنب تغييرات الحالة والبيانات القابلة للتغيير. على الرغم من أن لغة C++ كانت في الأصل لغة كائنية التوجه وإجرائية، إلا أنها بدءًا من معيار C++11 اكتسبت أدوات قوية للبرمجة الوظيفية: تعبيرات لامدا، std::function، std::bind، وأضاف معيار C++17 دعمًا لـ std::optional و std::variant، مما يسمح بتطبيق أنماط أحادية. في هذه المقالة، سننظر إلى تقنيات البرمجة الوظيفية المتقدمة في لغة C++: تركيب الدوال، الكيرينج، العمل مع الأنماط الأحادية، وإنشاء الدوال النقية باستخدام ميزات اللغة الحديثة.
1. تعبيرات لامدا والدوال النقية
تعبيرات لامدا هي أساس الأسلوب الوظيفي في لغة C++. فهي تسمح لك بإنشاء دوال مجهولة في الحال، وهو مثالي للخوارزميات من <algorithm>. ومع ذلك، من أجل البرمجة الوظيفية الحقيقية، من المهم أن تكون تعبيرات لامدا نقية — أي أنه ليس لها آثار جانبية ولا تعتمد على حالة خارجية قابلة للتغيير.
#include <iostream>#include <vector>#include <algorithm>
int main() { std::vector<int> numbers = {1, 2, 3, 4, 5};
// لامدا نقية: بيانات إدخال فقط، مخرجات فقط auto square = [](int x) { return x * x; };
// تطبيق التحويل بدون آثار جانبية std::vector<int> squared; std::transform(numbers.begin(), numbers.end(), std::back_inserter(squared), square);
for (int n : squared) std::cout << n << " "; // 1 4 9 16 25 return 0;}للاستخدام المتقدم، من الجدير تجنب الالتقاط بالمرجع ([&]) إلا إذا كان ضروريًا. الالتقاط بالقيمة ([=]) أو التعداد الصريح للالتقاطات يجعل اللامدا أكثر قابلية للتنبؤ والاختبار.
2. تركيب الدوال باستخدام std::function والعوامل
التركيب هو دمج دالتين أو أكثر في دالة واحدة. في لغة C++، يمكن تنفيذ التركيب باستخدام القوالب و std::function. دعنا نلقي نظرة على أداة تركيب معممة:
#include <functional>#include <iostream>
template <typename F, typename G>auto compose(F f, G g) { return [f, g](auto x) { return f(g(x)); };}
int main() { auto addOne = [](int x) { return x + 1; }; auto multiplyByTwo = [](int x) { return x * 2; };
auto addThenMultiply = compose(multiplyByTwo, addOne); std::cout << addThenMultiply(5) << std::endl; // (5+1)*2 = 12
// تركيب دوال متعددة auto triple = compose(multiplyByTwo, compose(addOne, addOne)); std::cout << triple(5) << std::endl; // ((5+1)+1)*2 = 14 return 0;}للسلاسل الأكثر تعقيدًا، يمكنك استخدام عامل خط أنابيب (مشابه لـ |> في اللغات الأخرى). في C++20، أصبح من الممكن تحميل operator| بشكل زائد لإنشاء بناء جملة مناسب:
#include <functional>#include <iostream>
template <typename F>class Pipeable { F f;public: Pipeable(F func) : f(func) {} template <typename T> auto operator()(T x) const { return f(x); }};
template <typename T, typename F>auto operator|(T x, Pipeable<F> p) { return p(x);}
int main() { auto addOne = Pipeable([](int x) { return x + 1; }); auto square = Pipeable([](int x) { return x * x; });
int result = 5 | addOne | square; // (5+1)^2 = 36 std::cout << result << std::endl; return 0;}هذا الأسلوب يجعل الكود تصريحيًا وسهل القراءة.
3. الكيرينج والتطبيق الجزئي
الكيرينج هو تحويل دالة ذات عدة وسائط إلى