تعبيرات لامدا في C ++

Lambda Expressions C



لماذا تعبير لامدا؟

ضع في اعتبارك العبارة التالية:

intmyInt= 52؛

هنا ، myInt هو معرف ، قيمة. 52 هو حرفي ، prvalue. اليوم ، من الممكن ترميز وظيفة خاصة ووضعها في موضع 52. تسمى هذه الوظيفة تعبير lambda. ضع في اعتبارك أيضًا البرنامج القصير التالي:







#يشمل

استخدام مساحة الاسمساعات؛

intالجبهة الوطنية(intعبر)

{

intإجابه=عبر+ 3؛

إرجاعإجابه؛

}


intالأساسية()

{

الجبهة الوطنية(5)؛



إرجاع 0؛

}

اليوم ، من الممكن ترميز دالة بشكل خاص ووضعها في موضع الوسيطة 5 ، لاستدعاء الوظيفة ، fn (5). تسمى هذه الوظيفة تعبير لامدا. تعبير lambda (الوظيفة) في هذا الموضع هو prvalue.



أي حرفية ماعدا السلسلة الحرفية هي prvalue. تعبير لامدا هو تصميم خاص لوظيفة من شأنها أن تتلاءم مع الرمز الحرفي. إنها وظيفة مجهولة (غير مسماة). تشرح هذه المقالة التعبير الأساسي الجديد لـ C ++ ، المسمى تعبير lambda. المعرفة الأساسية في C ++ هي شرط لفهم هذه المقالة.



محتوى المادة

رسم توضيحي لتعبير لامدا

في البرنامج التالي ، يتم تخصيص دالة ، وهي عبارة عن تعبير لامدا ، إلى متغير:





#يشمل

استخدام مساحة الاسمساعات؛

تلقاءيالجبهة الوطنية= [](intقف)

{

intإجابه=قف+ 3؛

إرجاعإجابه؛

}؛


intالأساسية()

{

تلقاءيفارياب=الجبهة الوطنية(2)؛

كلفة <<فارياب<< 'ن'؛


إرجاع 0؛

}

الخرج هو:

5

خارج الدالة main () يوجد المتغير fn. نوعه تلقائي. يعني Auto في هذه الحالة أن النوع الفعلي ، مثل int أو float ، يتم تحديده بواسطة المعامل الأيمن لمشغل التعيين (=). يوجد تعبير لامدا على يمين عامل التخصيص. تعبير lambda هو دالة بدون نوع الإرجاع السابق. لاحظ استخدام وموضع الأقواس المربعة ، []. ترجع الدالة 5 ، int ، والتي ستحدد نوع fn.



في الدالة main () ، هناك العبارة:

تلقاءيفارياب=الجبهة الوطنية(2)؛

هذا يعني ، fn خارج main () ، ينتهي به المطاف كمعرّف للدالة. معلماته الضمنية هي تلك الخاصة بتعبير لامدا. نوع varab تلقائي.

لاحظ أن تعبير lambda ينتهي بفاصلة منقوطة ، تمامًا مثل تعريف الفئة أو البنية ، وينتهي بفاصلة منقوطة.

في البرنامج التالي ، الدالة ، وهي تعبير lambda تُرجع القيمة 5 ، هي وسيطة لدالة أخرى:

#يشمل

استخدام مساحة الاسمساعات؛

فارغotherfn(intرقم 1int (*ptr)(int))

{

intno2= (*ptr)(2)؛

كلفة <<رقم 1<< ' <<no2<< 'ن'؛

}


intالأساسية()

{

otherfn(4و[](intقف)

{

intإجابه=قف+ 3؛

إرجاعإجابه؛

})؛


إرجاع 0؛
}

الخرج هو:

أربعة خمسة

هناك وظيفتان هنا ، تعبير lambda ووظيفة otherfn (). تعبير lambda هو الوسيطة الثانية لـ otherfn () ، ويُدعى في main (). لاحظ أن دالة lambda (التعبير) لا تنتهي بفاصلة منقوطة في هذا الاستدعاء لأنها هنا وسيطة (وليست دالة قائمة بذاتها).

تعتبر معلمة دالة lambda في تعريف وظيفة otherfn () مؤشرًا لوظيفة. المؤشر له الاسم ، ptr. يستخدم الاسم ptr في تعريف otherfn () لاستدعاء وظيفة lambda.

البيان،

intno2= (*ptr)(2)؛

في تعريف otherfn () ، يستدعي دالة lambda مع وسيطة من 2. يتم تعيين القيمة المعادة للاستدعاء ، '(* ptr) (2)' من دالة lambda ، إلى no2.

يوضح البرنامج أعلاه أيضًا كيف يمكن استخدام وظيفة lambda في مخطط وظيفة رد الاتصال C ++.

أجزاء من تعبير لامدا

أجزاء دالة لامدا النموذجية هي كما يلي:

[] () {}
  • [] هو شرط الالتقاط. يمكن أن تحتوي على عناصر.
  • () لقائمة المعلمات.
  • {} لجسم الوظيفة. إذا كانت الوظيفة قائمة بمفردها ، فيجب أن تنتهي بفاصلة منقوطة.

يلتقط

يمكن تعيين تعريف دالة lambda إلى متغير أو استخدامه كوسيطة لاستدعاء دالة مختلفة. يجب أن يكون تعريف استدعاء الوظيفة كمعامل ، مؤشر إلى وظيفة ، يتوافق مع تعريف دالة lambda.

يختلف تعريف دالة lambda عن تعريف الوظيفة العادية. يمكن إسنادها إلى متغير في النطاق العالمي ؛ يمكن أيضًا ترميز هذه الوظيفة المخصصة للمتغير داخل دالة أخرى. عند تعيينه إلى متغير نطاق عالمي ، يمكن لجسمه رؤية متغيرات أخرى في النطاق العام. عند تعيينه إلى متغير داخل تعريف دالة عادية ، يمكن لجسمه رؤية متغيرات أخرى في نطاق الوظيفة فقط بمساعدة عبارة الالتقاط ، [].

تسمح عبارة الالتقاط [] ، المعروفة أيضًا باسم مقدم lambda ، بإرسال المتغيرات من النطاق المحيط (الوظيفة) إلى جسم وظيفة تعبير lambda. يُقال أن الجسم الوظيفي لتعبير لامدا يلتقط المتغير عندما يستقبل الكائن. بدون عبارة الالتقاط [] ، لا يمكن إرسال متغير من النطاق المحيط إلى جسم وظيفة تعبير lambda. يوضح البرنامج التالي هذا ، مع نطاق الوظيفة () الرئيسي ، باعتباره النطاق المحيط:

#يشمل

استخدام مساحة الاسمساعات؛

intالأساسية()

{

intهوية شخصية= 5؛


تلقاءيالجبهة الوطنية= [هوية شخصية]()

{

كلفة <<هوية شخصية<< 'ن'؛

}؛

الجبهة الوطنية()؛


إرجاع 0؛

}

الإخراج 5 . بدون الاسم ، id ، داخل [] ، لن يرى تعبير lambda المعرف المتغير لنطاق الوظيفة الرئيسية ().

الالتقاط بالمرجع

يتم التقاط المثال السابق لعبارة الالتقاط حسب القيمة (انظر التفاصيل أدناه). عند الالتقاط بالإشارة ، يتم توفير موقع (تخزين) المتغير ، على سبيل المثال ، المعرف أعلاه ، للنطاق المحيط ، داخل جسم وظيفة lambda. لذا ، فإن تغيير قيمة المتغير داخل جسم دالة لامدا سيغير قيمة نفس المتغير في النطاق المحيط. كل متغير يتكرر في جملة الالتقاط مسبوق بعلامة العطف (&) لتحقيق ذلك. البرنامج التالي يوضح هذا:

#يشمل

استخدام مساحة الاسمساعات؛

intالأساسية()

{

intهوية شخصية= 5؛ تطفوقدم= 2.3؛ شارالفصل= 'إلى'؛

تلقاءيالجبهة الوطنية= [&هوية شخصية،&قدم&الفصل]()

{

هوية شخصية= 6؛قدم= 3.4؛الفصل= 'ب'؛

}؛

الجبهة الوطنية()؛

كلفة <<هوية شخصية<< '،' <<قدم<< '،' <<الفصل<< 'ن'؛

إرجاع 0؛

}

الخرج هو:

6 ، 3.4 ، ب

التأكيد على أن أسماء المتغيرات داخل الجسم الوظيفي لتعبير lambda هي لنفس المتغيرات خارج تعبير lambda.

التقاط بالقيمة

عند الالتقاط بالقيمة ، يتم توفير نسخة من موقع المتغير ، من النطاق المحيط ، داخل جسم وظيفة لامدا. على الرغم من أن المتغير داخل جسم وظيفة لامدا هو نسخة ، فلا يمكن تغيير قيمته داخل الجسم حتى الآن. لتحقيق الالتقاط بالقيمة ، لا يسبق كل متغير متكرر في جملة الالتقاط أي شيء. البرنامج التالي يوضح هذا:

#يشمل

استخدام مساحة الاسمساعات؛

intالأساسية()

{

intهوية شخصية= 5؛ تطفوقدم= 2.3؛ شارالفصل= 'إلى'؛

تلقاءيالجبهة الوطنية= [معرف ، قدم ، الفصل]()

{

// معرف = 6 ؛ قدم = 3.4 ؛ الفصل = 'ب' ؛

كلفة <<هوية شخصية<< '،' <<قدم<< '،' <<الفصل<< 'ن'؛

}؛

الجبهة الوطنية()؛

هوية شخصية= 6؛قدم= 3.4؛الفصل= 'ب'؛

كلفة <<هوية شخصية<< '،' <<قدم<< '،' <<الفصل<< 'ن'؛

إرجاع 0؛

}

الخرج هو:

5 ، 2.3 ، أ

6 ، 3.4 ، ب

إذا تمت إزالة مؤشر التعليق ، فلن يقوم البرنامج بالتجميع. سيصدر المترجم رسالة خطأ مفادها أن المتغيرات الموجودة داخل تعريف جسم الوظيفة لتعبير lambda لا يمكن تغييرها. على الرغم من أنه لا يمكن تغيير المتغيرات داخل وظيفة lambda ، إلا أنه يمكن تغييرها خارج وظيفة lambda ، كما يظهر مخرجات البرنامج أعلاه.

خلط يلتقط

يمكن الجمع بين الالتقاط بالإشارة والتقاط القيمة ، كما يوضح البرنامج التالي:

#يشمل

استخدام مساحة الاسمساعات؛

intالأساسية()

{

intهوية شخصية= 5؛ تطفوقدم= 2.3؛ شارالفصل= 'إلى'؛ منطقيbl= حقيقية؛


تلقاءيالجبهة الوطنية= [معرف ، قدم ،&الفصل ،&bl]()

{

الفصل= 'ب'؛bl= خاطئة؛

كلفة <<هوية شخصية<< '،' <<قدم<< '،' <<الفصل<< '،' <<bl<< 'ن'؛

}؛

الجبهة الوطنية()؛


إرجاع 0؛

}

الخرج هو:

5 ، 2.3 ، ب ، 0

عندما يتم التقاط جميع الصور ، يتم الرجوع إليها:

إذا تم التقاط جميع المتغيرات المراد التقاطها بالرجوع إليها ، فسيكون واحد فقط كافياً في جملة الالتقاط. البرنامج التالي يوضح هذا:

#يشمل

استخدام مساحة الاسمساعات؛

intالأساسية()

{

intهوية شخصية= 5؛ تطفوقدم= 2.3؛ شارالفصل= 'إلى'؛ منطقيbl= حقيقية؛


تلقاءيالجبهة الوطنية= [&]()

{

هوية شخصية= 6؛قدم= 3.4؛الفصل= 'ب'؛bl= خاطئة؛

}؛

الجبهة الوطنية()؛

كلفة <<هوية شخصية<< '،' <<قدم<< '،' <<الفصل<< '،' <<bl<< 'ن'؛


إرجاع 0؛

}

الخرج هو:

6 ، 3.4 ، ب ، 0

إذا كان سيتم التقاط بعض المتغيرات بالرجوع والبعض الآخر بالقيمة ، فإن واحد & سوف يمثل جميع المراجع ، والباقي لن يسبق كل منها أي شيء ، كما يوضح البرنامج التالي:

استخدام مساحة الاسمساعات؛

intالأساسية()

{

intهوية شخصية= 5؛ تطفوقدم= 2.3؛ شارالفصل= 'إلى'؛ منطقيbl= حقيقية؛


تلقاءيالجبهة الوطنية= [&، معرف ، قدم]()

{

الفصل= 'ب'؛bl= خاطئة؛

كلفة <<هوية شخصية<< '،' <<قدم<< '،' <<الفصل<< '،' <<bl<< 'ن'؛

}؛

الجبهة الوطنية()؛


إرجاع 0؛

}

الخرج هو:

5 ، 2.3 ، ب ، 0

لاحظ أنه يجب أن يكون الحرف الأول في عبارة الالتقاط & (بمعنى ، & لا يتبعه معرّف).

عندما يتم التقاط كل شيء بالقيمة:

إذا تم التقاط جميع المتغيرات المراد التقاطها بالقيمة ، فسيكون واحد فقط = كافياً في جملة الالتقاط. البرنامج التالي يوضح هذا:

#يشمل

استخدام مساحة الاسمساعات؛

intالأساسية()
{

intهوية شخصية= 5؛ تطفوقدم= 2.3؛ شارالفصل= 'إلى'؛ منطقيbl= حقيقية؛


تلقاءيالجبهة الوطنية= [=]()

{

كلفة <<هوية شخصية<< '،' <<قدم<< '،' <<الفصل<< '،' <<bl<< 'ن'؛

}؛

الجبهة الوطنية()؛


إرجاع 0؛


}

الخرج هو:

5 ، 2.3 ، أ ، 1

ملحوظة : = للقراءة فقط ، اعتبارًا من الآن.

إذا تم التقاط بعض المتغيرات بالقيمة والبعض الآخر بالرجوع إليها ، فإن واحد = سيمثل جميع المتغيرات المنسوخة للقراءة فقط ، والباقي سيكون لكل متغير & ، كما يوضح البرنامج التالي:

#يشمل

استخدام مساحة الاسمساعات؛

intالأساسية()

{

intهوية شخصية= 5؛ تطفوقدم= 2.3؛ شارالفصل= 'إلى'؛ منطقيbl= حقيقية؛


تلقاءيالجبهة الوطنية= [=و&الفصل ،&bl]()

{

الفصل= 'ب'؛bl= خاطئة؛

كلفة <<هوية شخصية<< '،' <<قدم<< '،' <<الفصل<< '،' <<bl<< 'ن'؛

}؛

الجبهة الوطنية()؛


إرجاع 0؛

}

الخرج هو:

5 ، 2.3 ، ب ، 0

لاحظ أن = وحده يجب أن يكون الحرف الأول في جملة الالتقاط.

نظام وظيفة رد الاتصال الكلاسيكي مع تعبير لامدا

يوضح البرنامج التالي كيف يمكن تنفيذ مخطط وظيفة رد الاتصال الكلاسيكي باستخدام تعبير lambda:

#يشمل

استخدام مساحة الاسمساعات؛

شار *انتاج؛


تلقاءيcba= [](شارخارج[])

{

انتاج=خارج؛

}؛



فارغرئيسي(شارإدخال[]وفارغ (*ل)(شار[]))

{

(*ل)(إدخال)؛

كلفة<<'للوظيفة الرئيسية'<<'ن'؛

}


فارغالجبهة الوطنية()

{

كلفة<<'حاليا'<<'ن'؛

}


intالأساسية()

{

شارإدخال[] = 'لوظيفة رد الاتصال'؛

رئيسي(المدخلات ، cba)؛

الجبهة الوطنية()؛

كلفة<<انتاج<<'ن'؛



إرجاع 0؛

}

الخرج هو:

للوظيفة الرئيسية

حاليا

لوظيفة رد الاتصال

تذكر أنه عند تعيين تعريف تعبير lambda إلى متغير في النطاق العام ، يمكن لهيئة وظيفتها رؤية المتغيرات العامة دون استخدام عبارة الالتقاط.

نوع العودة الزائدة

نوع الإرجاع لتعبير لامدا هو تلقائي ، بمعنى أن المترجم يحدد نوع الإرجاع من تعبير الإرجاع (إن وجد). إذا كان المبرمج يريد حقًا الإشارة إلى نوع الإرجاع ، فسيقوم بذلك كما في البرنامج التالي:

#يشمل

استخدام مساحة الاسمساعات؛

تلقاءيالجبهة الوطنية= [](intقف) -> int

{

intإجابه=قف+ 3؛

إرجاعإجابه؛

}؛


intالأساسية()

{

تلقاءيفارياب=الجبهة الوطنية(2)؛

كلفة <<فارياب<< 'ن'؛


إرجاع 0؛

}

الناتج هو 5. بعد قائمة المعلمات ، يتم كتابة عامل تشغيل السهم. ويتبع ذلك نوع الإرجاع (int في هذه الحالة).

إنهاء

ضع في اعتبارك مقطع الكود التالي:

هيكلكلا

{

intهوية شخصية= 5؛

شارالفصل= 'إلى'؛

}obj1 ، obj2؛

هنا ، Cla هو اسم فئة الهيكل. Obj1 و obj2 هما كائنان سيتم إنشاء مثيل لهما من فئة البنية. تعبير Lambda مشابه في التنفيذ. تعريف دالة لامدا هو نوع من الصنف. عندما يتم استدعاء (استدعاء) دالة lambda ، يتم إنشاء مثيل لكائن من تعريفه. هذا الكائن يسمى الإغلاق. إنه الإغلاق الذي يقوم بالعمل الذي من المتوقع أن تقوم به لامدا.

ومع ذلك ، فإن ترميز تعبير lambda مثل البنية أعلاه سوف يتم استبدال obj1 و obj2 بحجج المعلمات المقابلة. البرنامج التالي يوضح هذا:

#يشمل

استخدام مساحة الاسمساعات؛

تلقاءيالجبهة الوطنية= [](intبارام 1 ،intبارام 2)

{

intإجابه=بارام 1+بارام 2؛

إرجاعإجابه؛

} (2و3)؛


intالأساسية()

{

تلقاءيأين=الجبهة الوطنية؛

كلفة <<أين<< 'ن'؛


إرجاع 0؛

}

الناتج هو 5. الوسيطات 2 و 3 بين قوسين. لاحظ أن استدعاء دالة lambda ، fn ، لا يأخذ أي وسيطة حيث تم بالفعل ترميز الوسيطات في نهاية تعريف دالة lambda.

استنتاج

تعبير لامدا هو دالة مجهولة. يتألف من جزأين: فئة وكائن. تعريفه هو نوع من الطبقة. عندما يتم استدعاء التعبير ، يتم تكوين كائن من التعريف. هذا الكائن يسمى الإغلاق. إنه الإغلاق الذي يقوم بالعمل الذي من المتوقع أن تقوم به لامدا.

لكي يتلقى تعبير lambda متغيرًا من نطاق وظيفة خارجية ، فإنه يحتاج إلى عبارة التقاط غير فارغة في جسم وظيفته.