Skill

Collections এবং Iterators (কলেকশনস এবং ইটারেটরস)

রাস্ট (Rust) - Computer Programming

227

Collections (কলেকশনস)

রাস্টে Collections হল ডেটার গ্রুপ যা একসাথে একাধিক ভ্যালু ধারণ করে। রাস্টে মূলত ৩ ধরনের গুরুত্বপূর্ণ ডেটা স্ট্রাকচার রয়েছে, যেগুলো কালেকশন হিসেবে কাজ করে: Vectors, HashMaps, এবং Strings। রাস্টের কলেকশনস অত্যন্ত কার্যকর এবং তাদের ব্যবহার করা সহজ।

১.১. Vectors (ভেক্টর)

ভেক্টর হলো একটি ডায়নামিক অ্যারে যা একই টাইপের একাধিক ভ্যালু ধারণ করে। ভেক্টর এক ধরনের অর্ডারড কালেকশন, যেখানে আপনি নির্দিষ্ট ইনডেক্সের মাধ্যমে প্রতিটি উপাদান অ্যাক্সেস করতে পারেন।

উদাহরণ:

fn main() {
    let mut vec = Vec::new();  // নতুন ভেক্টর তৈরি
    vec.push(1);  // ভেক্টরে মান যোগ
    vec.push(2);

    // ভেক্টরের উপাদান প্রিন্ট করা
    for v in &vec {
        println!("{}", v);
    }
}

ব্যাখ্যা:

  • Vec::new() দিয়ে একটি নতুন ভেক্টর তৈরি করা হয়।
  • vec.push(1) এবং vec.push(2) ভেক্টরে মান যোগ করে।
  • for v in &vec দ্বারা ভেক্টরের প্রতিটি উপাদান iterate করা হয়।

১.২. HashMap (হ্যাশম্যাপ)

হ্যাশম্যাপ একটি অর্ডারলেস কালেকশন যেখানে কীগুলোর সাথে মানের (key-value) যুক্তি তৈরি করা হয়। এটি একটি দুর্দান্ত ডেটা স্ট্রাকচার যেখানে দ্রুত কনস্ট্যান্ট টাইম (O(1)) সার্চ, ইনসার্ট এবং রিমুভ সম্ভব।

উদাহরণ:

use std::collections::HashMap;

fn main() {
    let mut map = HashMap::new();
    map.insert("key1", 10);
    map.insert("key2", 20);

    // হ্যাশম্যাপের মান বের করা
    for (key, value) in &map {
        println!("{}: {}", key, value);
    }
}

ব্যাখ্যা:

  • HashMap::new() দিয়ে একটি নতুন হ্যাশম্যাপ তৈরি করা হয়।
  • map.insert("key1", 10) কীগুলোর সাথে মান যুক্ত করে।
  • for (key, value) দ্বারা হ্যাশম্যাপের প্রতিটি (key, value) জোড়া iterate করা হয়।

১.৩. Strings (স্ট্রিংস)

রাস্টে স্ট্রিং হলো একটি ডাইনামিক কালেকশন, যা ইউটিএফ-৮ এনকোডেড চিহ্ন ধারণ করে। এটি String এবং &str এই দুই ধরনের স্ট্রিং টাইপে বিভক্ত।

উদাহরণ:

fn main() {
    let mut s = String::new();
    s.push_str("Hello, ");
    s.push_str("World!");

    println!("{}", s);
}

ব্যাখ্যা:

  • String::new() দিয়ে একটি নতুন স্ট্রিং তৈরি করা হয়।
  • s.push_str("Hello, ") দ্বারা স্ট্রিংয়ে মান যোগ করা হয়।

Iterators (ইটারেটরস)

ইটারেটরস রাস্টের অন্যতম শক্তিশালী বৈশিষ্ট্য। এটি একটি অবজেক্ট যা সিকোয়েন্সের মধ্যে এক এক করে উপাদান বের করে। রাস্টের ইটারেটরস এর সাথে অনেক কার্যকরী ফাংশন রয়েছে, যা আপনাকে ডেটার উপর বিভিন্ন অপারেশন করতে সাহায্য করে।

২.১. ইটারেটরের মৌলিক কাজ

রাস্টের ইটারেটরগুলি সাধারণত .iter() মেথডের মাধ্যমে তৈরি করা হয়। এটি একটি ইটারেটর ফেরত দেয় যা সেই কালেকশনকে পর্যায়ক্রমে ট্রাভার্স করে।

উদাহরণ:

fn main() {
    let vec = vec![1, 2, 3, 4, 5];

    // ইটারেটর ব্যবহার করে ভেক্টরের উপাদান প্রিন্ট করা
    for v in vec.iter() {
        println!("{}", v);
    }
}

ব্যাখ্যা:

  • vec.iter() দিয়ে একটি ইটারেটর তৈরি করা হয়, যা ভেক্টরের প্রতিটি উপাদান আউটপুট করবে।
  • for v in vec.iter() দ্বারা ভেক্টরের প্রতিটি উপাদান প্রিন্ট করা হয়।

২.২. ইটারেটর মেথড (Iterator Methods)

ইটারেটরসের সাথে বিভিন্ন কার্যকরী মেথড ব্যবহার করা যেতে পারে। কিছু সাধারণ ইটারেটর মেথডের মধ্যে রয়েছে:

  • map: একটি ফাংশন প্রয়োগ করে প্রতিটি উপাদান পরিবর্তন করা।
  • filter: একটি শর্ত অনুযায়ী উপাদান ফিল্টার করা।
  • collect: একটি ইটারেটরকে ভেক্টর বা অন্য কোনো কালেকশনে রূপান্তরিত করা।

উদাহরণ:

fn main() {
    let nums = vec![1, 2, 3, 4, 5];

    // map: প্রতিটি উপাদানকে দ্বিগুণ করা
    let doubled: Vec<i32> = nums.iter().map(|x| x * 2).collect();

    // filter: শুধু বিজোড় সংখ্যা ফিল্টার করা
    let odd: Vec<i32> = nums.iter().filter(|&&x| x % 2 != 0).collect();

    println!("{:?}", doubled); // [2, 4, 6, 8, 10]
    println!("{:?}", odd); // [1, 3, 5]
}

ব্যাখ্যা:

  • map(|x| x * 2) দিয়ে ভেক্টরের প্রতিটি উপাদানকে দ্বিগুণ করা হয়।
  • filter(|&&x| x % 2 != 0) দিয়ে শুধু বিজোড় সংখ্যাগুলি ফিল্টার করা হয়।
  • .collect() মেথডের মাধ্যমে ইটারেটরের উপাদানগুলিকে একটি নতুন ভেক্টরে রূপান্তরিত করা হয়।

সারাংশ

রাস্টে Collections যেমন ভেক্টর, হ্যাশম্যাপ এবং স্ট্রিংস দিয়ে আপনি ডেটার গ্রুপ সংরক্ষণ করতে পারেন। Iterators এর মাধ্যমে আপনি ঐ ডেটার উপাদানগুলো খুব সহজেই ট্রাভার্স এবং প্রক্রিয়া করতে পারেন। রাস্টের শক্তিশালী ইটারেটর মেথডগুলি যেমন map, filter, এবং collect আপনাকে ডেটার উপর আরও জটিল অপারেশন করার সুযোগ দেয়, যা খুবই কার্যকরী।

Content added By

Arrays (অ্যারেই)

রাস্টে arrays হল একটি সংগ্রহ যা একধরনের ডেটা একত্রে রাখে। অ্যারেতে সব উপাদানের ধরন একই ধরনের হয় এবং আকার (length) স্থির থাকে, অর্থাৎ একবার অ্যারে তৈরি হলে তার আকার পরিবর্তন করা যায় না।

অ্যারের মৌলিক গঠন:

  • ফিক্সড আকার (Fixed-size): অ্যারের আকার কোডের মাধ্যমে নির্ধারণ করা হয় এবং তা পরিবর্তনযোগ্য নয়।
  • ডেটা টাইপ: সব উপাদানের ডেটা টাইপ একই হতে হবে।

অ্যারের উদাহরণ:

fn main() {
    let numbers = [1, 2, 3, 4, 5];  // একটি অ্যারে যার মধ্যে ৫টি সংখ্যা আছে

    // অ্যারে থেকে একটি উপাদান অ্যাক্সেস করা
    println!("The first number is: {}", numbers[0]);

    // অ্যারের আকার বের করা
    println!("The length of the array is: {}", numbers.len());
}

এখানে numbers অ্যারে ৫টি ইন্টিজার ধারণ করে। অ্যারের উপাদানগুলির ধরন একটাই এবং অ্যারের আকারও নির্দিষ্ট, যা পরিবর্তন করা যায় না।

অ্যারের বৈশিষ্ট্য:

  • স্থির আকার: অ্যারের আকার শুরুতেই নির্ধারিত হয় এবং পরে সেটি পরিবর্তন করা যায় না।
  • অ্যারের উপাদান অ্যাক্সেস: অ্যারের উপাদানগুলি নির্দিষ্ট ইনডেক্সের মাধ্যমে অ্যাক্সেস করা হয় (যেমন numbers[0]).
  • নির্দিষ্ট টাইপ: অ্যারের সব উপাদানের টাইপ একই হতে হবে।

Vectors (ভেক্টর)

রাস্টে vectors হল একটি ডায়নামিক ডেটা স্ট্রাকচার, যা resizable array হিসেবে কাজ করে। ভেক্টর ডেটা সংরক্ষণের জন্য অ্যারে থেকেও বেশি নমনীয় এবং এর আকার রানটাইমে পরিবর্তন করা যায়। ভেক্টরের উপাদানগুলিও একই ধরনের হতে হবে।

ভেক্টরের মৌলিক গঠন:

  • ডায়নামিক আকার (Dynamic-size): ভেক্টরের আকার সময়ের সাথে সাথে পরিবর্তন করা যায়।
  • ডেটা টাইপ: ভেক্টরের সব উপাদান একই ধরনের হতে হবে।
  • উপরি স্তুপ (Heap allocation): ভেক্টর মেমোরি heap এ সংরক্ষিত হয়, যা অ্যারে থেকে আলাদা।

ভেক্টরের উদাহরণ:

fn main() {
    let mut numbers = vec![1, 2, 3];  // একটি ভেক্টর তৈরি করা

    // ভেক্টরের একটি উপাদান অ্যাক্সেস করা
    println!("The first number is: {}", numbers[0]);

    // ভেক্টরে নতুন উপাদান যোগ করা
    numbers.push(4);
    println!("After adding an element, the vector is: {:?}", numbers);

    // ভেক্টরের আকার বের করা
    println!("The length of the vector is: {}", numbers.len());
}

এখানে numbers একটি ভেক্টর যা প্রথমে তিনটি উপাদান ধারণ করে, এবং push ফাংশনের মাধ্যমে একটি নতুন উপাদান যোগ করা হয়।

ভেক্টরের বৈশিষ্ট্য:

  • ডায়নামিক আকার: ভেক্টরের আকার রানটাইমে পরিবর্তিত হতে পারে।
  • উপরি স্তুপে মেমোরি বরাদ্দ: ভেক্টরের ডেটা heap মেমোরিতে সঞ্চিত থাকে, যা অ্যারের তুলনায় বেশি নমনীয়।
  • অ্যারে থেকে বেশি নমনীয়: ভেক্টরে নতুন উপাদান যোগ বা বিদায় করা সম্ভব।

Vectors এবং Arrays এর মধ্যে পার্থক্য

বৈশিষ্ট্যঅ্যারে (Array)ভেক্টর (Vector)
আকারস্থির আকার (Fixed-size)ডায়নামিক আকার (Resizable)
মেমোরি বরাদ্দস্ট্যাক (Stack)heap (Heap)
ধরনসব উপাদান একই টাইপ এবং স্থির আকারসব উপাদান একই টাইপ, তবে আকার পরিবর্তনশীল
ধরা হয় কিভাবেনির্দিষ্ট ইনডেক্স দিয়েইনডেক্স বা অন্যান্য ফাংশন দিয়ে
উপাদান যোগ/হটানোসম্ভব নয় (ফিক্সড আকার)সম্ভব (push, pop, insert)
পারফরম্যান্সবেশি (ফিক্সড আকারের জন্য)কম (dynamic resizing এর জন্য)

Vectors এবং Arrays এর ব্যবহার

অ্যারে ব্যবহার:

অ্যারেগুলি তখন ব্যবহার করা হয় যখন:

  • ফিক্সড আকার প্রয়োজন: যখন উপাদানগুলির সংখ্যা জানি এবং তা পরিবর্তন হবে না।
  • কম্পিউটার সিস্টেমে যেখানে স্ট্যাটিক মেমোরি ম্যানেজমেন্ট গুরুত্বপূর্ণ।

উদাহরণ:

fn main() {
    let days = ["Monday", "Tuesday", "Wednesday"];
    println!("The first day is: {}", days[0]);
}

ভেক্টর ব্যবহার:

ভেক্টর তখন ব্যবহার করা হয় যখন:

  • ডায়নামিক আকার প্রয়োজন: যখন আপনি জানেন না যে কতগুলি উপাদান সংগ্রহ করতে হবে এবং কোড চলাকালীন সংখ্যাটি পরিবর্তিত হতে পারে।
  • একাধিক উপাদান যোগ বা মুছে ফেলা প্রয়োজন

উদাহরণ:

fn main() {
    let mut numbers = vec![10, 20, 30];
    numbers.push(40);  // নতুন উপাদান যোগ করা
    numbers.push(50);  // আরও একটি উপাদান যোগ করা
    println!("The vector is: {:?}", numbers);
}

সারাংশ

রাস্টে arrays এবং vectors দুটি গুরুত্বপূর্ণ ডেটা স্ট্রাকচার। অ্যারে ফিক্সড আকারের স্ট্যাটিক ডেটা সংগ্রহ করার জন্য ব্যবহৃত হয়, যেখানে ভেক্টর ডায়নামিক ডেটা সংগ্রহের জন্য ব্যবহৃত হয় এবং তার আকার পরিবর্তনশীল। অ্যারে স্ট্যাক মেমোরিতে সঞ্চিত হয়, কিন্তু ভেক্টর heap মেমোরিতে সঞ্চিত থাকে এবং এটি অতিরিক্ত নমনীয়তার সুবিধা দেয়।

Content added By

HashMap

HashMap একটি key-value pair ডেটা স্ট্রাকচার, যেখানে প্রতিটি key এর জন্য একটি corresponding value থাকে। এটি unordered collection এবং unique keys ব্যবহার করে, অর্থাৎ একাধিক identical key থাকতে পারে না, তবে value গুলি পুনরাবৃত্তি হতে পারে। রাস্টে, HashMap ব্যবহারের জন্য std::collections::HashMap লাইব্রেরি ইম্পোর্ট করতে হয়।

HashMap এর বৈশিষ্ট্য:

  • Key-value pair: প্রতিটি key এর সাথে একটি value সম্পর্কিত থাকে।
  • Unordered: HashMap এর এলিমেন্টগুলো অর্ডার অনুসরণ করে না।
  • Unique keys: প্রতিটি key একবারই থাকতে পারে।
  • Efficient Lookup: HashMap এর ভিতরে একটি key দিয়ে দ্রুত মান (value) খুঁজে পাওয়া যায়।

HashMap ব্যবহার:

use std::collections::HashMap;

fn main() {
    // একটি নতুন HashMap তৈরি করা হচ্ছে
    let mut scores = HashMap::new();
    
    // key-value পেয়ার যুক্ত করা হচ্ছে
    scores.insert("Alice", 50);
    scores.insert("Bob", 40);
    
    // একটি key এর মাধ্যমে value অ্যাক্সেস করা হচ্ছে
    println!("Alice's score: {}", scores.get("Alice").unwrap());
    
    // সমস্ত key-value পেয়ার প্রিন্ট করা হচ্ছে
    for (key, value) in &scores {
        println!("{}: {}", key, value);
    }
}

এখানে:

  • scores.insert("Alice", 50) দ্বারা একটি key-value পেয়ার অ্যাড করা হচ্ছে।
  • scores.get("Alice") দিয়ে Alice এর স্কোর অ্যাক্সেস করা হচ্ছে।

Set

Set হল একটি collection যা unique elements ধারণ করে, তবে এতে কোন নির্দিষ্ট অর্ডার থাকে না। রাস্টে Set এর জন্য সাধারণত HashSet ব্যবহার করা হয়, যা std::collections::HashSet লাইব্রেরি দ্বারা প্রদান করা হয়। Set কোন ডুপ্লিকেট মান গ্রহণ করে না এবং এটির প্রধান কাজ হল ইউনিক মান সংগ্রহ করা।

Set এর বৈশিষ্ট্য:

  • Unique elements: একাধিক identical এলিমেন্ট থাকতে পারে না।
  • Unordered: Set এর মধ্যে এলিমেন্টগুলো কোন নির্দিষ্ট অর্ডারে থাকে না।
  • Efficient operations: Set সাধারণত দ্রুততার সাথে মানের উপস্থিতি চেক করতে পারে।

HashSet ব্যবহার:

use std::collections::HashSet;

fn main() {
    let mut numbers = HashSet::new();
    
    // এলিমেন্ট অ্যাড করা হচ্ছে
    numbers.insert(1);
    numbers.insert(2);
    numbers.insert(3);
    
    // এলিমেন্ট চেক করা হচ্ছে
    if numbers.contains(&2) {
        println!("The set contains 2");
    }
    
    // সব এলিমেন্ট প্রিন্ট করা হচ্ছে
    for number in &numbers {
        println!("{}", number);
    }
}

এখানে:

  • numbers.insert(1) দ্বারা একটি এলিমেন্ট অ্যাড করা হচ্ছে।
  • numbers.contains(&2) দিয়ে চেক করা হচ্ছে যে ২টি এলিমেন্ট সেটে রয়েছে কিনা।

HashMap এবং Set এর মধ্যে পার্থক্য:

বৈশিষ্ট্যHashMapSet
উদ্দেশ্যkey-value pair storingশুধুমাত্র unique values storing
ডেটা সঞ্চয়key-value pairশুধু values (এলিমেন্ট)
ডুপ্লিকেটএকই key একাধিক হতে পারে নাডুপ্লিকেট ভ্যালু থাকতে পারে না
অর্ডারঅর্ডার ঠিক থাকে নাঅর্ডার ঠিক থাকে না
সার্চ অপারেশনkey দ্বারা value দ্রুত অ্যাক্সেসশুধু উপাদান চেক করা

সারাংশ

HashMap এবং Set রাস্টের দুটি গুরুত্বপূর্ণ ডেটা স্ট্রাকচার। HashMap key-value pair সংরক্ষণ করে, যেখানে প্রতিটি key এর সাথে একটি value যুক্ত থাকে, এবং Set শুধুমাত্র ইউনিক উপাদান ধারণ করে। HashMap এলিমেন্ট অনুসন্ধানে খুবই দ্রুত এবং Set শুধুমাত্র ইউনিক মান সংরক্ষণে উপযোগী।

Content added By

Iterators (ইটারেটরস)

ইটারেটর হচ্ছে একটি অবজেক্ট যা একটি কালেকশন (যেমন অ্যারে, ভেক্টর, ইত্যাদি) বা সিকোয়েন্সের উপাদান এক এক করে পর্যালোচনা করতে সাহায্য করে। রাস্টে, ইটারেটর হল এমন একটি ট্রেইট (trait) যা next() মেথডের মাধ্যমে পরবর্তী উপাদানটি রিটার্ন করে, যতক্ষণ না সব উপাদান পর্যালোচনা হয়ে যায়।

ইটারেটরের মূল কাজ:

  • পর্যায়ক্রমে উপাদান অ্যাক্সেস করা: ইটারেটর আপনাকে একে একে সব উপাদান অ্যাক্সেস করতে সাহায্য করে।
  • অবজেক্টের উপর লুপ চালানো: ইটারেটর আপনাকে একটি সিকোয়েন্সের উপর সহজে লুপ চালানোর সুযোগ দেয়।

সিনট্যাক্স:

let iterator = collection.iter();

ইটারেটর এর উদাহরণ:

fn main() {
    let nums = vec![1, 2, 3, 4, 5];
    let mut iterator = nums.iter();  // ইটারেটর তৈরি

    println!("{}", iterator.next().unwrap()); // 1
    println!("{}", iterator.next().unwrap()); // 2
    println!("{}", iterator.next().unwrap()); // 3
}

এখানে:

  • nums.iter() হল একটি ইটারেটর যা nums ভেক্টরের উপাদানগুলো একে একে রিটার্ন করবে।
  • next() মেথড দ্বারা পরবর্তী উপাদান রিটার্ন হয়, এবং যদি কোন উপাদান না থাকে, তাহলে None ফেরত দেয়।

Lazy Evaluation (লেজি ইভালুয়েশন)

Lazy Evaluation একটি কৌশল যেখানে একটি ফলাফল তখনই গণনা করা হয়, যখন সেটি প্রয়োজন হয়। অর্থাৎ, কোনো ক্যালকুলেশন বা প্রক্রিয়া তখনই সম্পাদিত হয় যখন সেটির রেজাল্ট প্রয়োজন হয়, পূর্বে থেকে এটি গণনা করা হয় না।

রাস্টের ইটারেটরগুলি lazy evaluation পদ্ধতিতে কাজ করে। এর মানে হলো, ইটারেটরগুলি যতটুকু প্রয়োজন, ততটুকুই কাজ করে, এবং পুরো সিকোয়েন্সটি একসাথে প্রক্রিয়া করা হয় না, বরং এক এক করে শুধুমাত্র যে উপাদানটি প্রয়োজন সেটিই রিটার্ন করা হয়।

উদাহরণ:

fn main() {
    let nums = vec![1, 2, 3, 4, 5];

    let sum: i32 = nums.iter()
        .map(|x| x * 2)  // প্রতিটি উপাদানকে ২ দিয়ে গুণ করা
        .filter(|x| x > &5)  // ৫ এর চেয়ে বড় মানগুলো ফিল্টার করা
        .sum();  // সব মান যোগ করা

    println!("The sum is: {}", sum);
}

এখানে:

  • .map(|x| x * 2) প্রতিটি উপাদানকে ২ দিয়ে গুণ করছে।
  • .filter(|x| x > &5) ফিল্টার করছে, কেবলমাত্র ৫ এর চেয়ে বড় উপাদানগুলো রাখতে।
  • .sum() সমস্ত মান যোগ করছে।

Lazy Evaluation:

  • Lazy evaluation দ্বারা শুধুমাত্র প্রয়োজনীয় কাজই ঘটবে। উদাহরণস্বরূপ, .map() এবং .filter() শুধু তখনই কার্যকর হবে যখন .sum() মেথড কল হবে। এখানে পুরো সিকোয়েন্স একসাথে প্রক্রিয়া করা হয় না, বরং এক এক করে উপাদানগুলো পর্যালোচনা করা হয়।

ইটারেটর ট্রেইট এবং লেইজি ইভালুয়েশন

রাস্টের ইটারেটরগুলি Lazy Evaluation কে ব্যবহার করে, অর্থাৎ যখন আপনি কোনো ইটারেটরের উপর অপারেশন করবেন, তখন তা তখনই কার্যকর হবে যখন আপনি ফলাফলটি ব্যবহার করবেন। কিছু সাধারণ ইটারেটর মেথড যেগুলি লেইজি ইভালুয়েশন অনুসরণ করে:

  • map(): প্রতিটি উপাদানকে একটি নির্দিষ্ট ফাংশনের মাধ্যমে পরিবর্তন করে।
  • filter(): একটি শর্ত অনুযায়ী উপাদানগুলো ফিল্টার করে।
  • take(): একটি নির্দিষ্ট সংখ্যক উপাদান নেয়।
  • skip(): প্রথম কিছু উপাদান স্কিপ করে।

উদাহরণ:

fn main() {
    let nums = vec![1, 2, 3, 4, 5];

    // লেইজি ইভালুয়েশন: এখানে ইটারেটরগুলি পরে এক এক করে কাজ করবে
    let result: Vec<i32> = nums.iter()
        .filter(|&x| x % 2 == 0)  // ২ দিয়ে ভাগযোগ্য উপাদান ফিল্টার
        .map(|x| x * 2)           // পরবর্তী সব মানকে গুণে ২ দিয়ে বাড়ানো
        .collect();               // ফলাফলকে একটি ভেক্টরে সংগ্রহ করা

    println!("{:?}", result);  // [4, 8]
}

এখানে:

  • Lazy Evaluation চালানোর সময়, .filter() এবং .map() একে একে উপাদানগুলোর উপর কাজ করবে। সম্পূর্ণ সিকোয়েন্সটি প্রক্রিয়া করা হয় না, বরং শুধুমাত্র ফলাফল প্রাপ্তির সময়ই অপারেশনগুলি কার্যকর হবে।

Lazy Evaluation এর সুবিধা

  1. দ্রুততা বৃদ্ধি: শুধুমাত্র যে উপাদানটি প্রয়োজন তা প্রক্রিয়া করা হয়, ফলে অপারেশন দ্রুত হয়।
  2. মেমোরি সাশ্রয়ী: লেইজি ইভালুয়েশন কম মেমোরি ব্যবহার করে, কারণ পুরো সিকোয়েন্স একবারে মেমোরিতে রাখা হয় না।
  3. কমপ্লেক্স প্রোগ্রামিং: লেইজি ইভালুয়েশন উচ্চ স্তরের অ্যাবস্ট্রাকশন ব্যবহারের মাধ্যমে কমপ্লেক্স সমস্যা সমাধান করা সহজ করে তোলে।

সারাংশ

Iterators এবং Lazy Evaluation রাস্টের দুটি শক্তিশালী বৈশিষ্ট্য। ইটারেটরগুলি একটি সিকোয়েন্সের উপাদান এক এক করে পর্যালোচনা করতে সাহায্য করে, এবং Lazy Evaluation নিশ্চিত করে যে কোডটি শুধুমাত্র প্রয়োজনীয় সময়েই কাজ করে, ফলে এটি মেমোরি এবং কার্যক্ষমতা সাশ্রয়ী হয়ে থাকে। এই দুটি বৈশিষ্ট্য একত্রে কোডকে আরও দক্ষ, দ্রুত এবং সহজে ব্যবহৃত করতে সহায়তা করে।

Content added By

রাস্টে iterators হল একটি গুরুত্বপূর্ণ বৈশিষ্ট্য যা ডেটা সংগ্রহ বা কাস্টম কালেকশনগুলোর উপাদানগুলো একে একে প্রক্রিয়া করার জন্য ব্যবহার করা হয়। এটি মূলত একটি অবজেক্ট হিসেবে কাজ করে যা পরবর্তী উপাদানটি আনতে পারে এবং সেই উপাদানটি প্রক্রিয়া করার জন্য একটি অ্যাক্সেস প্রদান করে। এর মাধ্যমে কাস্টম কালেকশন বা ডেটা স্ট্রাকচারের উপাদানগুলোতে সহজেই ট্রাভার্স করা যায়।

এই টিউটোরিয়ালে, আমরা কীভাবে একটি কাস্টম কালেকশন (যেমন একটি কাস্টম struct) তৈরি করতে পারি এবং তার উপাদানগুলোকে iterators ব্যবহার করে ট্রাভার্স (অনুসন্ধান) করতে পারি, তা দেখবো।


Custom Collection তৈরি করা

প্রথমে আমরা একটি কাস্টম কালেকশন তৈরি করব যা Book নামক একটি struct ব্যবহার করবে। এর মধ্যে একটি ভেক্টর (vector) থাকবে যা বইয়ের নামগুলি ধারণ করবে।

struct BookCollection {
    books: Vec<String>,
}

impl BookCollection {
    fn new() -> Self {
        BookCollection { books: Vec::new() }
    }

    fn add_book(&mut self, book: String) {
        self.books.push(book);
    }
}

এখানে, BookCollection একটি কাস্টম কালেকশন যা বইয়ের নামগুলো ধারণ করে। add_book মেথডটি নতুন বই যোগ করার জন্য ব্যবহার করা হয়।


Iterator Trait ইমপ্লিমেন্ট করা

একটি কালেকশনের উপাদানগুলোতে ট্রাভার্স করতে আমরা রাস্টের Iterator trait ইমপ্লিমেন্ট করব। এর মাধ্যমে, আমরা একটি next() মেথড প্রদান করতে পারব যা পরবর্তী উপাদানটি ফেরত দেবে।

impl Iterator for BookCollection {
    type Item = String;

    fn next(&mut self) -> Option<Self::Item> {
        self.books.pop()
    }
}

এখানে, Iterator trait এর মাধ্যমে next() মেথডে বইয়ের তালিকা থেকে একটি বই পপ করা হবে। এটি একটি Option টাইপ ফেরত দেবে। Some(book) যদি কোনো বই থাকে এবং None যদি তালিকা ফাঁকা হয়।


Custom Collection এর Iterator ব্যবহার করা

এখন আমরা আমাদের কাস্টম কালেকশনের উপাদানগুলো ট্রাভার্স (অনুসন্ধান) করতে একটি for লুপ ব্যবহার করব, যেটি iterator এর মাধ্যমে বইয়ের নামগুলো একে একে প্রদর্শন করবে।

fn main() {
    let mut collection = BookCollection::new();

    collection.add_book("The Rust Programming Language".to_string());
    collection.add_book("The Pragmatic Programmer".to_string());
    collection.add_book("Clean Code".to_string());

    // Iterating over the collection using a for loop
    for book in collection {
        println!("{}", book);
    }
}

এখানে for লুপটি collection নামক কাস্টম কালেকশনের উপর কাজ করে, যা Iterator trait ইমপ্লিমেন্ট করেছে। এটি একটি একে একে বইয়ের নামগুলো প্রিন্ট করবে যতক্ষণ না তালিকা ফাঁকা হয়।


ব্যবহারকারীর নিজের Iterator মেথড তৈরি করা

রাস্টে আপনি Iterator trait ইমপ্লিমেন্ট করার সময় নিজের মেথডও তৈরি করতে পারেন, যেগুলি কাস্টম কালেকশনগুলির উপর ট্রাভার্স চালাতে সাহায্য করবে। নিচে একটি কাস্টম filter_books মেথড দেখানো হচ্ছে, যা একটি নির্দিষ্ট শব্দ দিয়ে বই ফিল্টার করবে।

impl BookCollection {
    fn filter_books<'a>(&'a self, keyword: &'a str) -> impl Iterator<Item = &str> {
        self.books.iter().filter(move |&book| book.contains(keyword))
    }
}

এখানে, filter_books মেথডটি একটি Iterator ফেরত দেয় যা শুধুমাত্র সেই বইগুলোকে নির্বাচিত করে যেগুলোর নামের মধ্যে নির্দিষ্ট keyword রয়েছে।


অগ্রসর Iterator ব্যবহার করা

রাস্টের Iterator ট্রেইটগুলির মধ্যে বিভিন্ন ধরনের ইনবিল্ট মেথড রয়েছে, যেগুলি ব্যবহার করে বিভিন্ন ধরনের ট্রাভার্সাল কাজ করা সম্ভব। কিছু উদাহরণ:

  • map: প্রতি উপাদানের উপর কোনো ফাংশন প্রয়োগ করে।
  • filter: একটি শর্তে উপাদানগুলো ফিল্টার করে।
  • collect: সব উপাদানগুলোকে নতুন একটি কালেকশনে সংগ্রহ করে।
fn main() {
    let mut collection = BookCollection::new();

    collection.add_book("The Rust Programming Language".to_string());
    collection.add_book("The Pragmatic Programmer".to_string());
    collection.add_book("Clean Code".to_string());

    // Using map to modify the data while iterating
    let uppercase_books: Vec<String> = collection
        .books
        .iter()
        .map(|book| book.to_uppercase())
        .collect();

    for book in uppercase_books {
        println!("{}", book);
    }
}

এখানে, map মেথডের মাধ্যমে আমরা প্রতিটি বইয়ের নামকে বড় হাতের অক্ষরে পরিণত করেছি এবং তারপর collect মেথড দিয়ে তা একটি নতুন Vec<String> কালেকশনে রূপান্তরিত করেছি।


সারাংশ

রাস্টে Iterators ব্যবহারের মাধ্যমে কাস্টম কালেকশনগুলোর উপাদানগুলো খুব সহজে ট্রাভার্স (অনুসন্ধান) করা যায়। আপনি Iterator trait ইমপ্লিমেন্ট করে আপনার কাস্টম কালেকশনের উপাদানগুলো অ্যাক্সেস করতে পারেন এবং রাস্টের বিভিন্ন iterator method ব্যবহার করে একাধিক কার্যক্রম করতে পারেন, যেমন ফিল্টার, ম্যাপ, এবং সংগ্রহ। এটা একটি খুবই শক্তিশালী এবং সুশৃঙ্খল পদ্ধতি ডেটা পরিচালনার জন্য।

Content added By
Promotion

Are you sure to start over?

Loading...