البرمجة الوظيفية في لغة رست: المستوى المتقدم
رست هي لغة متعددة النماذج تجمع بسلاسة بين المنهجين الأمرّي والوظيفي. للمطورين الملمين بالفعل بأساسيات رست، ينفتح عالم من التقنيات الوظيفية المتقدمة، مما يتيح لك كتابة كود أكثر إيجازًا وأمانًا وتعبيرًا. في هذه المقالة، سنستكشف المفاهيم الأساسية للبرمجة الوظيفية على مستوى متقدم: من الاستخدام العميق للإغلاقات والمكررات إلى تركيب الدوال ومحاكاة المونادات.
1. الإغلاقات المتقدمة: الملكية، الاقتراض، والسمات
الإغلاقات في رست ليست مجرد دوال مجهولة. يقوم المحول البرمجي تلقائيًا بتطبيق إحدى السمات عليها: Fn أو FnMut أو FnOnce. فهم كيفية التقاط الإغلاق للمتغيرات من بيئته أمر بالغ الأهمية لكتابة كود فعال.
إغلاق النقل يُستخدم عندما يحتاج الإغلاق إلى امتلاك البيانات الملتقطة، على سبيل المثال، عند تمريرها إلى مسار تنفيذ جديد:
use std::thread;
let data = vec![1, 2, 3];
let handle = thread::spawn(move || { println!("{:?}", data); // تم نقل البيانات إلى الإغلاق});
handle.join().unwrap();الإغلاقات كوسيطات للدوال تسمح بإنشاء واجهات برمجية مرنة. استخدم الأنواع العامة مع قيود السمات:
fn apply_twice<F>(f: F, x: i32) -> i32where F: Fn(i32) -> i32,{ f(f(x))}
let square = |x| x * x;println!("{}", apply_twice(square, 2)); // المخرجات: 16إرجاع الإغلاقات مهمة معقدة بسبب الحجم غير المعروف لنوع الإغلاق. استخدم Box<dyn Fn> أو الكلمة المفتاحية impl:
fn make_adder(x: i32) -> impl Fn(i32) -> i32 { move |y| x + y}
let add_five = make_adder(5);println!("{}", add_five(10)); // 152. المكررات: الكسل، الدوال الجامعة، والأداء
المكررات في رست أداة وظيفية قوية. إنها كسولة: لا تحدث العمليات الحسابية إلا عند استدعاء طريقة نهائية (مثل collect() أو for_each()).
سلاسل الدوال الجامعة تسمح ببناء تحويلات بيانات معقدة دون حلقات:
let numbers = vec![1, 2, 3, 4, 5, 6];
let result: Vec<i32> = numbers .iter() .filter(|&&x| x % 2 == 0) // الاحتفاظ بالأعداد الزوجية .map(|&x| x * 2) // الضرب في 2 .take(2) // أخذ أول اثنين .collect();
println!("{:?}", result); // [4, 8]المكررات المخصصة يتم إنشاؤها عن طريق تطبيق سمة Iterator. على سبيل المثال، مكرر أرقام فيبوناتشي:
struct Fibonacci { curr: u64, next: u64,}
impl Fibonacci { fn new() -> Self { Fibonacci { curr: 0, next: 1 } }}
impl Iterator for Fibonacci { type Item = u64;
fn next(&mut self) -> Option<Self::Item> { let new_next = self.curr.checked_add(self.next)?; self.curr = self.next; self.next = new_next; Some(self.curr) }}
let fib: Vec<u64> = Fibonacci::new().take(10).collect();println!("{:?}", fib); // [1, 1, 2, 3, 5, 8, 13, 21, 34, 55]أداء المكررات غالبًا ما يكون مماثلاً للحلقات اليدوية، وفي بعض الحالات يولد المحول البرمجي LLVM كودًا أكثر أمثلية.
3. تركيب الدوال والأسلوب المونادي
لا تحتوي رست على تركيب دوال مدمج، ولكن من السهل تنفيذه. التركيب يسمح ببناء دوال جديدة من دوال موجودة.
التركيب البسيط باستخدام الإغلاقات:
fn compose<F, G, A, B, C>(f: F, g: G) -> impl Fn(A) -> Cwhere F: Fn(B) -> C, G: Fn(A) -> B,{ move |x| f(g(x))}
let add_o