Rust में कार्यात्मक प्रोग्रामिंग: उन्नत स्तर
Rust एक बहु-प्रतिमान भाषा है जो अनिवार्य और कार्यात्मक दृष्टिकोणों को सहजता से जोड़ती है। Rust की मूल बातों से पहले से परिचित डेवलपर्स के लिए, उन्नत कार्यात्मक तकनीकों की दुनिया खुलती है, जो अधिक संक्षिप्त, सुरक्षित और अभिव्यंजक कोड लिखने में सक्षम बनाती है। इस लेख में, हम उन्नत स्तर पर कार्यात्मक प्रोग्रामिंग (FP) की प्रमुख अवधारणाओं का पता लगाएंगे: क्लोजर और इटरेटर के गहन उपयोग से लेकर फंक्शन कम्पोज़िशन और मोनाड के अनुकरण तक।
1. उन्नत क्लोजर: स्वामित्व, उधार और ट्रेट्स
Rust में क्लोजर केवल अनाम फलन नहीं हैं। कंपाइलर स्वचालित रूप से उनके लिए एक ट्रेट लागू करता है: Fn, FnMut या FnOnce। यह समझना कि क्लोजर पर्यावरण से चर कैसे कैप्चर करता है, कुशल कोड लिखने के लिए महत्वपूर्ण है।
स्थानांतरण के साथ कैप्चर (move closure) का उपयोग तब किया जाता है जब क्लोजर को कैप्चर किए गए डेटा का स्वामित्व लेना होता है, उदाहरण के लिए, किसी नए थ्रेड में भेजते समय:
use std::thread;
let data = vec![1, 2, 3];
let handle = thread::spawn(move || { println!("{:?}", data); // data क्लोजर में स्थानांतरित हो गया});
handle.join().unwrap();फलन तर्कों के रूप में क्लोजर लचीले API बनाने की अनुमति देते हैं। ट्रेट-बाधाओं के साथ सामान्यीकृत प्रकारों का उपयोग करें:
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. इटरेटर: आलस्य, कॉम्बिनेटर और प्रदर्शन
Rust में इटरेटर एक शक्तिशाली कार्यात्मक उपकरण हैं। वे आलसी होते हैं: गणना केवल तब होती है जब कोई टर्मिनल विधि (जैसे, 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. फंक्शन कम्पोज़िशन और मोनैडिक शैली
Rust में बिल्ट-इन फंक्शन कम्पोज़िशन नहीं है, लेकिन इसे आसानी से लागू किया जा सकता है। कम्पोज़िशन मौजूदा फलनों से नए फलन बनाने की अनुमति देता है।
क्लोजर के साथ सरल कम्पोज़िशन:
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