پیمایشگر‌ها (Iterators) در ++C

Iterators in C Plus Plus

Iterators-in-C-Plus-Plus

یک پیمایشگر (iterator) یک شیء (مانند یک اشاره‌گر)  است که به یک عنصر درون نگه‌دارنده (container) اشاره می‌کند. می‌توان از پیمایشگر‌ها (iterators) برای انتقال کامل محتوای نگه‌دارنده استفاده کرد. آن‌ها می‌توانند به عنوان چیزی شبیه به یک اشاره‌گر که اشاره می‌کنند به تعدادی مکان تجسم شوند و می‌توان با استفاده از آن‌ها به محتوا در آن مکان خاص دسترسی داشت.

پیمایشگر‌ها نقشی حیاتی در ارتباط الگوریتم‌ها با نگه‌دارنده‌ها همراه با دستکاری داده‌‌ی ذخیره‌شده داخل نگه‌دارنده‌ها بازی می‌کنند. واضح‌ترین شکل یک پیمایشگر (iterator‌) یک اشاره‌گر (pointer) است. یک اشاره‌گر می‌تواند به عناصر در یک آرایه اشاره‌ کند و می‌توان تمام عناصر آرایه را با استفاده از اُپریتور افزایش (‌increment operator (++)) پیمایش کرد. اما همه‌ی پیمایشگر‌ها قابلیتی مشابه اشاره‌گر‌ها را ندارند.

بسته به قابلیت پیمایشگر‌ها آن‌ها می‌توانند به 5 دسته، طبقه‌بندی شوند. همان‌طور که در دیاگرام پایین نشان داده شده است، خارجی‌ترین آن‌ها، قدرتمند‌ترین آن‌ها است و در نتیجه درونی‌ترین آن‌ها کمترین قدرت را از نظر قابلیت دارند.

باید توجه داشت که هر کدام این پیمایشگر‌ها توسط همه‌ی نگه‌دارنده‌ها در STL پشیبانی‌شده نیستند. تفاوت نگه‌دارنده‌ها پشتیبانی از پیمایشگر‌های مختلف است. مانند vector که از Random-access iterators پشتیبانی می‌کند، در حالی که lists از bidirectional iterators پشتیبانی می‌کند. لیست کامل در زیر ارائه‌شده است:

انواع پیمایشگر‌ها

براساس قابلیت پیمایشگر‌ها (iterators)، آن‌ها می‌توانند در 5 دسته‌بندی مهم طبقه‌بندی شوند:

پیمایشگر‌ها ورودی (Input Iterators)

آن‌ها ضعیف‌ترین ‌همه‌ی پیمایشگر‌ها هستند و قابلیت‌ بسیار محدودی دارند. آن‌ها تنها می‌توانند در یک الگوریتم  single-pass  استفاده شوند. به عنوان مثال، الگوریتم‌هایی که به ترتیب نگه‌دارنده را پردازش می‌کنند، به طوری که هیچ عنصری بیش از یک‌بار مورد دسترسی قرار نمی‌گیرد.

پیمایشگر‌ها خروجی (Output Iterators)

دقیقا مانند input iterators، آن‌ها نیز در قابلیت‌شان بسیار محدود هستند و تنها می‌توانند در الگوریتم‌ها  single-pass استفاده شوند. اما نه برای دسترسی به عناصر، بلکه برای عناصر اختصاص داده‌شده.

پیمایشگر‌های رو به جلو (Forward Iterator)

آن‌ها در سلسه‌مراتب نسبت به  input و output iterators، بالاتر هستند و شامل همه‌ی ویژگی‌ها ارائه شده در این 2 پیمایشگر است. اما، همان‌طور که از نامش پیدا است، آن‌ها نیز تنها می‌توانند در یک جهت رو به جلو و همچنین یک گام در هر بار حرکت کنند.

پیمایشگر‌های دوطرفه (Bidirectional Iterators)

آن‌ها همه‌ی ویژگی‌های forward iterators همراه با این حقیقت که آن‌ها مشکل forward iterators را حل کرده‌اند. به طوری که آن‌ها می‌توانند در هر دو جهت حرکت کنند. به همین خاطر است که نام آن‌ها دوطرفه یا bidirectional است.

پیمایشگر‌ها با دسترسی تصادفی (Random-Access Iterators)

آن‌ها قدرتمندترین پیمایشگر‌ها هستند. آن‌‌ها محدوده به حرکت پی‌درپی نیستند. همان‌طور که از نامش مشخص است، آن‌ها می‌توانند به طور تصادفی به هر عنصر در داخل نگه‌دارنده دسترسی داشته باشند. آن‌ها مواردی از قابلیت‌ها هستند که شبیه به اشاره‌گر‌ها هستند.

در دیاگرام زیر تفاوت در قابلیت‌هایشان با توجه به عملیات‌های متفاوتی که آن‌ها می‌توانند انجام دهند، نشان داده شده است:

مزایای پیمایشگر‌ها

مطمئنن راه‌های بسیار زیاد وجود دارد که نشان داد که پیمایشگر‌ها بی‌نهایت برای ما مفید هستند و ما را برای به شدت برای استفاده از آن‌ها تشویق کند. برخی از مزایای استفاده از پیمایشگر‌ها در زیر لیست شده‌اند:

راحتی در برنامه‌نویسی

این بهتر است از پیمایشگر‌ها برای پیمایش محتوای کامل نگه‌دارنده استفاده کنیم. مانند این‌که ما از یک پیمایشگر و دسترسی به عناصر با استفاده از اُپریتور [ ] استفاده نخواهیم کرد که بعد باید همیشه درمورد اندازه‌ی نگه‌دارنده نگران باشیم. در حقیقت با پیمایشگر‌ها می‌توان به سادگی از تابع عضو ()end استفاده کرد و محتوای کامل را بدون نگه داشتن چیزی در ذهن، پیمایش کرد.

// C++ program to demonstrate iterators
  
#include <iostream>
#include <vector>
using namespace std;
int main()
{
    // Declaring a vector
    vector<int> v = { 1, 2, 3 };
  
    // Declaring an iterator
    vector<int>::iterator i;
  
    int j;
  
    cout << "Without iterators = ";
      
    // Accessing the elements without using iterators
    for (j = 0; j < 3; ++j) 
    {
        cout << v[j] << " ";
    }
  
    cout << "\nWith iterators = ";
      
    // Accessing the elements using iterators
    for (i = v.begin(); i != v.end(); ++i)
    {
        cout << *i << " ";
    }
  
    // Adding one more element to vector
    v.push_back(4);
  
    cout << "\nWithout iterators = ";
      
    // Accessing the elements without using iterators
    for (j = 0; j < 4; ++j) 
    {
        cout << v[j] << " ";
    }
  
    cout << "\nWith iterators = ";
      
    // Accessing the elements using iterators
    for (i = v.begin(); i != v.end(); ++i) 
    {
        cout << *i << " ";
    }
  
    return 0;
}

خروجی قطعه‌کُد بالا به این صورت است:

Without iterators = 1 2 3
With iterators = 1 2 3
Without iterators = 1 2 3 4
With iterators = 1 2 3 4

توضیح قطعه‌کُد بالا

همان‌طور که در قطعه‌کُد بالا مشاهده کردید، بدون استفاده از پیمایش‌گرها،‌ باید تعداد کل عناصر را در نگه‌دارنده، دنبال کنیم. در ابتدا تنها 3 عنصر وجود دارد، اما بعد 1 عنصر بیشتر به داخل آن وارد شده است. به این ترتیب حلقه‌ی for هم باید اصلاح شود. اما با استفاده از پیمایشگر‌ها، هر دو بار حلقه بدون اصلاح باقی می‌ماند. بنابراین پیمایشگر‌ها کار ما را راحت‌تر کرده‌اند.

قابلیت استفاده‌ی مجدد کُد (Code reusability)

حال در نظر بگیرید اگر v را یک لیست (list) به جای vector در برنامه‌ی بالا تعریف کنیم، او اگر از پیمایشگر‌ها برای دسترسی به عناصر استفاده نکنیم و تنها از اُپریتور [] استفاده کنیم، که در آن‌ صورت این روش دسترسی برای لیست قابل استفاده نیست (همان‌طور که آن‌ها از  random-access iterators پشتیبانی نمی‌کنند). در این صورت، اگر از پیمایشگر‌ها برای vector برای دسترسی به عناصر  استفاده کنیم، تنها کافی است vector را  به لیست در اعلامیه‌ی پیمایشگر تغییر دهیم تا همان کار را انجام دهد، بدون انجام کاری دیگر. بنابراین پیمایشگر‌ها از قابلیت استفاده‌ی مجدد کُد پشتیبانی می‌کنند، به طوری که آن‌ها می‌توانند برای دسترسی به عناصر هر نگه‌دارنده استفاده شوند.

پردازش نگه‌دارنده به صورت پویا (Dynamic processing of the container)

پیمایشگر‌ها ما را قادر می‌سازند که هر زمان که بخواهیم، به راحتی، به طور پویا عناصر را به نگه‌دارنده حذف یا اضافه کنیم:

// C++ program to demonstrate iterators
  
#include <iostream>
#include <vector>
using namespace std;
int main()
{
    // Declaring a vector
    vector<int> v = { 1, 2, 3 };
  
    // Declaring an iterator
    vector<int>::iterator i;
  
    int j;
  
    // Inserting element using iterators
    for (i = v.begin(); i != v.end(); ++i) {
        if (i == v.begin()) {
            i = v.insert(i, 5);
            // inserting 5 at the beginning of v
        }
    }
      
    // v contains 5 1 2 3
  
    // Deleting a element using iterators
    for (i = v.begin(); i != v.end(); ++i) {
        if (i == v.begin() + 1) {
            i = v.erase(i);
            // i now points to the element after the
            // deleted element
        }
    }
      
    // v contains 5 2 3
  
    // Accessing the elements using iterators
    for (i = v.begin(); i != v.end(); ++i) {
        cout << *i << " ";
    }
  
    return 0;
}

خروجی قطعه‌کُد بالا به این صورت است:

5 2 3

توضیح قطعه‌کُد بالا: همان‌طور که در قطعه‌کُد بالا مشاهده کردید، می‌توان به راحتی و به طور پویا، عناصر را با استفاده از پیمایشگر به نگه‌دارنده اضافه یا حذف کرد، با این حال، انجام همین کار بدون استفاده از پیمایشگر‌ها، می‌تواند بسیار خسته‌کننده باشد به طوری که لازم است عناصر را هر بار قبل از درج‌شدن و بعد از حذف‌شدن تغییر مکان داد.


منبع: وب سایت geeksforgeeks

نویسنده شوید

دیدگاه‌های شما

در این قسمت، به پرسش‌های تخصصی شما درباره‌ی محتوای مقاله پاسخ داده نمی‌شود. سوالات خود را اینجا بپرسید.