الفريق العربي للبرمجةأرشيف المنتديات · 2000 – 2023
نسخة أرشيفية للقراءة فقط — التسجيل والمشاركة مغلقان، والمحتوى محفوظ كما كان.

لمحة على استخدام for_each في السي++

مغلق
بدأه هاني الأتاسي في 7 يونيو 2002 · 3 رد · 508 مشاهدة · في ارشيف قسم C/C++
مشاركة: واتساب X فيسبوك تيليجرام
#1 صاحب الموضوع

اخترت أن أشرح هذا التابع الموجود في مكتبة algorithm في ال STL لأهميته ولأوضح بعض الأمر المهمة في كيفية استخدامه للاستفادة التامة منه .

طبعا المكتبة algorithm تحتوي على العديد من التوابع الهامة والمفيدة والتي يجب على كل مبرمج السي++ استخدامها .

من اسم التابع السابق : هو عبارة عن حلقة من أجل كل عنصر في ال container يقوم بتنفيذ أمر معين . مثلا طباعة العناصر في ال list أو في ال vector أو تنفيذ أي شئ آخر نحدده نحن .

أولا التابع يحتوي على البارامترات التالية :

template
    Fun for_each(InIt first, InIt last, Fun f);

التابع قد تم تعريفه كقالب أو template وممكن أن يقبل أي نوع من أنواع البارامترات ولكن لا يذهب تفكيرنا بعيدا حتى نرى كود التابع السابق . من أسماء البارامترات ممكن أن نستنتج :

first و last عبارة عن Input Iterator أي ممكن أن تأخذ أي iterator بالإضافة إلى standard input iterators . وهي تحدد المجال الذي نريد أن ننفذ الحلقة عليه وهو دائما [first, last) أي أن last تكون بعد آخر عنصر ، وهذا هو الافتراضي دوما في ال stl .

f عبارة عن التابع الذي سوف ينفذ من أجل كل عنصر .

كود هذا التابع موجود في المكتبة algorithm وهو كالتالي :

// TEMPLATE FUNCTION for_each
template< class InIt, class Fun> inline
    Fun for_each(InIt _F, InIt _L, Fun _Op)
    {
         for (; _F != _L; ++_F)
                _Op(*_F);
        return (_Op); 
    }

من الكود السابق نلاحظ أنه يقوم بتنفيذ حلقة عادية مثل التي نكتبها عندما نريد أن نمشي في ال list أو ال vector . طبعا من الكود نجد أنه لا يمكن أن نمرر أي نوع كان في البارمترات لأن النوع InIt يجب أن يقبل المعاملين ++ و != وأيضا يجب أن يقبل المعامل * لأن طريقة الاستدعاء هي كما نلاحظ _Op(*_F); . بعد الانتهاء من الحلقة نلاحظ أن التابع for_each يرجع قيمة التابع الذي مررناه وسوف نرى استخدام لهذا لاحقا .

الآن بكل بساطة ممكن أن نرى المثال التالي :

#include 
#include 
#include 
using namespace std;

void PrintInt(int obj)
{
    cout << obj << endl;
}

void main()
{
    vector v1;

    v1.push_back(1);
    v1.push_back(2);
    v1.push_back(3);

    for_each(v1.begin(), v1.end(), PrintInt);
}

الكود السابق يقوم بطباعة كل من الأعداد 1 و 2 و 3 .. لاحظ أنه قمنا بتعريف التابع PrintInt من أجل ذلك . طبعا هذا غير عملي أبدا وإذا أردنا أن نطبع من مصفوفة char مثلا لأردنا كتابة التابع PrintChar الذي يكون تماما مثل التابع PrintInt . الحل لهذا هو استخدام ال template في تعريف ال تابع PrintInt .. ونلاحظ الكود التالي :

#include 
#include 
#include 
using namespace std;

template
void PrintObject(T obj)
{
    cout << obj << endl;
}

void main()
{
    vector v1;

    v1.push_back(1);
    v1.push_back(2);
    v1.push_back(3);

    for_each(v1.begin(), v1.end(), PrintObject);
}

الكود السابق صحيح من ناحية الترجمة والمعنى اللغوي ولكن عند الربط فإن مترجم الفجوال سي++ لم يقوم بوضع كود لـ PrintObject وذلك لاعتقاده أننا لم نستخدمها . وبالتالي أخفق الكود السابق في مرجة ال link .

ممكن أن نتحايل ونضع تعريف التابع داخل struct ونستخدم المعامل () من أجل ذلك كالتالي :

#include 
#include 
#include 
using namespace std;

template
struct PrintObject {
    void operator()(const T obj) const 
    {
        cout << obj << endl;
    }
};

void main()
{
    vector v1;

    v1.push_back(1);
    v1.push_back(2);
    v1.push_back(3);

    for_each(v1.begin(), v1.end(), PrintObject());
}

لاحظ أننا مررنا المعامل () في البارامتر الأخير في التابع for_each .

الكود السابق يعمل تماما وهكذا يمكن استخدام المعامل في الstruct PrintObject مع أي نوع بيحث يقبل معامل الاخراج إلى الشاشة .

لاحظ أننا يجب أن نحدد نوع الكائن الذي نمرره في PrintObject . طبعا بتعديل بسيط على الكود السابق ممكن أن نحسن فيه بأن نجعل المترجم يكتشف نوع الكائن بشكل آلي إذا قمنا بادخال تعريف ال template إلى داخل ال struct كالتالي :

#include 
#include 
#include 
using namespace std;

struct PrintObject {
    template
    void operator()(const T obj) const 
    {
        cout << obj << endl;
    }
};

void main()
{
    vector v1;

    v1.push_back(1);
    v1.push_back(2);
    v1.push_back(3);

    for_each(v1.begin(), v1.end(), PrintObject());
}

وهكذا ليس من الداعي تحديد النوع في PrintObject ويمكن استخدام ال PrintObject مع for_each من أجل طباعة أي container لأي نوع في ال STL .

بما أن التابع for_each يرجع البارامتر الثالث فبالامكان أن ننفذ عنصر نحدده نحن بعد for_each كالتالي :

    for_each(v1.begin(), v1.end(), PrintObject())(500);

التعديل السابق يعنني أننا سوف نطبع عناصر ال v1 كالعادة وبعدها سوف نطبع القيمة 500 ..

طبعا ممكن استخدام for_each ليس فقط مع ال iterators .. مثلا ممكن أن نستخدمه في طباعة محتويات مصفوفة عناصرها من نوع معين وليكن int . الكود التالي يوضح ذلك :

    int array[3] = {1, 2, 3};
    for_each(&array[0], &array[3], PrintObject());

Coding on the Cloud and for the Cloud!

My Blog

#2

شكراً اخي هاني

موضوع جميل جداً

شكراً

If you think yourself that you are nobody, you should go somewhere to find yourself

#3

الله يعطيك الف عافية :)

#4

أتم الله عليك وحفظ لك عقلك

جزاك الله خير على الدروس اللي مفضل علينا فيها

هذا الموضوع مغلق.

مواضيع مشابهة