الاستدعاء (برمجة الحاسوب)

غالباً ما تكون عملية إعادة الاتصال على مستوى المتصل الأصلي.

في برمجة الحاسوب ، تُعدّ الاستدعاءات نمطًا برمجيًا يتم فيه تمرير مرجع دالة من سياق (المستهلك) إلى سياق آخر (المُزوّد) بحيث يستطيع المُزوّد ​​استدعاء تلك الدالة. إذا كانت الدالة تصل إلى حالة أو وظيفة المستهلك، فإن الاستدعاء يعود إلى المستهلك - عكس مسار التحكم الطبيعي الذي يستدعي فيه المستهلك المُزوّد.

قد تُصمَّم دالة تقبل مُعامل رد نداء بحيث تستدعي الدالة قبل إعادتها إلى المُستدعي. ولكن، في الغالب، يخزن المُزوِّد مرجع رد النداء ليتمكن من استدعاء الدالة لاحقًا (كدالة مؤجلة ). إذا استدعى المُزوِّد رد النداء على نفس سلسلة العمليات التي يستخدمها المُستهلِك، فإن الاستدعاء يكون حظرًا ، أي متزامنًا . أما إذا استدعى المُزوِّد رد النداء على سلسلة عمليات مختلفة، فإن الاستدعاء يكون غير حظر ، أي غير متزامن .

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

قد يكون الفرق بين مرجع الدالة العامة ودالة الاستدعاء دقيقًا، ويستخدم البعض المصطلحين بشكل متبادل، لكن التمييز بينهما يعتمد عمومًا على الغرض البرمجي. فإذا كان الغرض مشابهًا لرد الاتصال الهاتفي - أي أن الطرف المتصل به أصلاً يتواصل مع المتصل الأصلي - فهو في هذه الحالة دالة استدعاء.

يستخدم

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

إدارة الفعاليات

يمكن استخدام دالة رد النداء لمعالجة الأحداث. غالبًا ما يقوم الكود المستخدم بتسجيل دالة رد نداء لنوع معين من الأحداث. عند وقوع هذا الحدث، يتم استدعاء دالة رد النداء. تُستخدم دوال رد النداء عادةً لبرمجة واجهة المستخدم الرسومية (GUI) لبرنامج يعمل ضمن نظام نوافذ . يوفر التطبيق مرجعًا إلى دالة رد نداء مخصصة ليقوم نظام النوافذ باستدعائها. يستدعي نظام النوافذ هذه الدالة لإعلام التطبيق بأحداث مثل نقرات الماوس وضغطات المفاتيح .

إجراء غير متزامن

يمكن استخدام دالة رد الاتصال لتنفيذ المعالجة غير المتزامنة.

يطلب المتصل إجراءً ويقدم رد اتصال ليتم استدعاؤه عند اكتمال الإجراء، والذي قد يكون بعد فترة طويلة من تقديم الطلب.

تعدد الأشكال

يمكن استخدام دالة رد نداء لتطبيق تعدد الأشكال . في الشفرة الزائفة التالية، say_hiيمكن أن تأخذ إما write_statusأو write_error.

من typing استورد Callabledef write_status ( message : str ) -> None : write ( stdout , message )def write_error ( message : str ) -> None : write ( stderr , message )def say_hi ( write : Callable [[ str ], None ]) -> None : write ( "Hello world" )

تطبيق

يتم تطبيق تقنية الاستدعاء بشكل مختلف باختلاف لغة البرمجة .

في لغات التجميع ، وC ، وC++ ، وباسكال ، وموديولا-2، وغيرها، تُخزَّن دالة الاستدعاء داخليًا كمؤشر دالة [ 1 ] . يتيح استخدام نفس التخزين للغات المختلفة مشاركة دوال الاستدعاء مباشرةً دون الحاجة إلى طبقة توافقية في وقت التصميم أو وقت التشغيل . على سبيل المثال، يمكن الوصول إلى واجهة برمجة تطبيقات ويندوز (Windows API) عبر لغات ومترجمات ومجمعات متعددة. كما تسمح لغة C++ للكائنات بتوفير تنفيذ لعملية استدعاء الدالة. تقبل مكتبة القوالب القياسية هذه الكائنات (المسماة بالدوال ) كمعاملات. تسمح العديد من اللغات الديناميكية ، مثل جافا سكريبت ، ولوا ، وبايثون ، وبيرل [ 2 ] [ 3 ] ، وبي إتش بي ، بتمرير كائن دالة. توفر لغات واجهة سطر الأوامر (CLI)، مثل C# وفيجوال بيسك (.NET) (VB.NET)، مرجع دالة مغلفًا آمنًا من حيث النوع يُعرف باسم المندوب (delegate) . توفر الأحداث ومعالجات الأحداث ، كما هو مستخدم في لغات .NET ، دوال الاستدعاء. تدعم لغات البرمجة الوظيفية بشكل عام الدوال من الدرجة الأولى ، والتي يمكن تمريرها كدوال رد نداء إلى دوال أخرى، أو تخزينها كبيانات أو إرجاعها من الدوال.

تدعم العديد من لغات البرمجة، بما في ذلك بيرل، وبايثون، وروبي ، وسمول توك ، وسي++ (الإصدار 11 وما بعده)، وسي#، وفي بي.نت (الإصدارات الحديثة)، ومعظم لغات البرمجة الوظيفية، تعابير لامدا ، وهي دوال غير مُسماة ذات بنية مضمنة، تعمل عادةً كدوال رد نداء. في بعض اللغات، مثل سكيم ، وإم إل ، وجافا سكريبت، وبيرل، وبايثون، وسمول توك، وبي إتش بي (منذ الإصدار 5.3.0)، وسي ++ (الإصدار 11 وما بعده)، وجافا (منذ الإصدار 8 ) ، وغيرها الكثير، يمكن أن تكون لامدا عبارة عن إغلاق ، أي يمكنها الوصول إلى متغيرات مُعرَّفة محليًا في السياق الذي عُرِّفت فيه. في لغات البرمجة كائنية التوجه، مثل إصدارات جافا التي سبقت الوسائط ذات القيم الدالة، يمكن تحقيق سلوك دالة رد النداء بتمرير كائن يُنفِّذ واجهة. وتُعدّ توابع هذا الكائن دوال رد نداء. في لغتي PL/I و ALGOL 60 ، قد يحتاج إجراء الاستدعاء إلى الوصول إلى المتغيرات المحلية في الكتل الحاوية، لذلك يتم استدعاؤه من خلال متغير إدخال يحتوي على كل من نقطة الدخول ومعلومات السياق. [ 6 ]

مثال على التعليمات البرمجية

ج

تُستخدم وظائف الاستدعاء على نطاق واسع في مجموعة متنوعة من الأغراض، على سبيل المثال في الإشارة إلى الأخطاء: قد لا يرغب برنامج Unix في الإنهاء فورًا عند تلقيه إشارة SIGTERM ، لذلك للتأكد من معالجة إنهاء البرنامج بشكل صحيح، فإنه سيقوم بتسجيل وظيفة التنظيف كوظيفة استدعاء.

#include <signal.h> #include <stdlib.h>#include <fcntl.h> #include <sys/types>struct Data { // ... };static struct Data data = { // تهيئة المحتويات هنا }; static volatile sigatomic_t flag = 0 ;void cleanupHandler ( int signum ) { flag = 1 ; int saveFile = open ( "savefile.dat" , O_WRONLY | O_CREATO , S_IWUSR ); write ( saveFile , data , sizeof data ); close ( saveFile ); }int main ( void ) { signal ( cleanupHandler , SIGTERM ); doStuff (); }

يُستخدم رد الاتصال في لغة C بشكل شائع مع دوال مكتبة C القياسية المستخدمة في الفرز ( qsort()) والبحث ( lsearch(), bsearch()) حيث يتم تمرير دالة مقارنة كوسيط للروتين لتحديد ترتيب الفرز. [ 7 ]

#include <stdlib.h>struct Person { char name [ 20 ]; int age ; };شخص أشخاص [ 1000 // يجمع الأشخاص حسب العمر int compareAges ( struct Person * p1 , struct Person * p2 ) { return p2 -> age - p1 -> age ; }int main ( void ) { // ... int numberOfPeople = /* بعض الأرقام هنا */ ;qsort ( numberOfPeople , sizeof ( struct Person ), people , compareAges ); // ... }

يمكن أيضًا استخدام دوال الاستدعاء للتحكم في تنفيذ دالة ما: يسمح Xlib بتحديد شروط مخصصة لتحديد ما إذا كان البرنامج يرغب في معالجة حدث ما. في كود C التالي ، printNumber()تستخدم الدالة المعامل getNumberكدالة استدعاء مانعة. printNumber()يتم استدعاء getAnswerToMostImportantQuestion()الدالة التي تعمل كدالة استدعاء. عند التشغيل، تكون الإجابة على السؤال: "القيمة: 42".

#include <stdio.h> #include <stdlib.h>void printNumber ( int ( * getNumber )( void )) { int val = getNumber (); printf ( "القيمة: %d \n " , val ); }دالة ` getAnswerToMostImportantQuestion ( void ) ` تُرجع 42 .int main ( void ) { printNumber ( getAnswerToMostImportantQuestion ); return 0 ; }

لغة سي++

في لغة C++ ، يمكن استخدام الدوال بالإضافة إلى مؤشر الدالة. الدالة هي كائن operator()مُعرَّف. على سبيل المثال، الكائنات في std::viewsهي دوال. هذا مثال على استخدام الدوال في C++:

استيراد std ؛class MyCallback { public : void operator ()( int x ) { std :: println ( "تم استدعاء دالة رد الاتصال بالقيمة: {}" , x ); } };template < typename Callback > void performOperation ( int a , Callback callback ) { std :: println ( "إجراء العملية على: {}" , a ); callback ( a ); }int main () { MyCallback callback ; int value = 10 ; performOperation ( value , callback ); return 0 ; }

std::function<R(Args...)>هو غلاف تم مسح نوعه لأي كائنات قابلة للاستدعاء، تم تقديمه في C++11 :

استيراد std ؛باستخدام std :: function ؛void performOperation ( int a , function < void ( int ) > callback ) { std :: println ( "إجراء العملية على: {}" , a ); callback ( a ); }int main () { int value = 10 ; performOperation ( value , []( int x ) -> void { std :: println ( "تم استدعاء دالة رد الاتصال بالقيمة: {}" , x ); });return 0 ; }

سي شارب

في كود C# التالي ، Helper.PerformActionتستخدم الدالة مُعاملًا callbackكدالة رد نداء مُعطِّلة. Helper.PerformActionيتم استدعاء Logالدالة التي تعمل كدالة رد نداء. عند تشغيلها، يُكتب ما يلي في وحدة التحكم: "دالة رد النداء: Hello world".

namespace Wikipedia.Examples ;باستخدام النظام ؛class Helper { public void PerformAction ( Action < string > callback ) { callback ( "Hello world" ); } }public class Main { static void Log ( string str ) { Console . WriteLine ( $"Callback was: {str}" ); }static void Main ( string [] args ) { Helper helper = new (); helper . PerformAction ( Log ); } }

جافا سكريبت

في كود جافا سكريبت التالي ، calculateتستخدم الدالة المعامل operateكدالة رد نداء مانعة. calculateيتم استدعاء الدالة باستخدام المعاملين multiplyالتاليين، ثم يتم استدعاء الدالة باستخدام sumالمعاملين التاليين اللذين يعملان كدوال رد نداء.

دالة حساب ( أ ، ب ، عملية ) { إرجاع عملية ( أ ، ب } دالة ضرب ( أ ، ب ) { إرجاع أ * ب ؛ } دالة جمع ( أ ، ب ) { إرجاع أ + ب ؛ } // الناتج 20 تنبيه ( حساب ( 10 ، 2 ، ضرب ))؛ // الناتج 12 تنبيه ( حساب ( 10 ، 2 ، جمع ))؛

تستخدم دالة التجميع .each()في مكتبة jQuery الدالة المُمرَّرة إليها كدالة رد نداء مُعطِّلة. وتستدعي هذه الدالة لكل عنصر من عناصر المجموعة. على سبيل المثال:

$ ( "li" ). each ( function ( index ) { console.log ( index + ": " + $ ( this ) .text ( )); } );

تُستخدم عمليات الاستدعاء المؤجلة بشكل شائع لمعالجة الأحداث الواردة من المستخدم والعميل والمؤقتات. يمكن العثور على أمثلة addEventListenerفي Ajax و [ 8 ]XMLHttpRequest .

بالإضافة إلى استخدام ردود الاتصال في كود مصدر جافا سكريبت، يتم دعم دوال C التي تأخذ دالة عبر js-ctypes. [ 9 ]

جوليا

في كود جوليا التالي ، calculateتقبل الدالة مُعاملًا operateيُستخدم كدالة رد نداء مانعة. calculateيتم استدعاء squareالدالة التي تعمل كدالة رد نداء.

julia> square ( val ) = val ^ 2 square ( دالة عامة بطريقة واحدة) julia> calculate ( operat , val ) = operat ( val ) calculate ( دالة عامة بطريقة واحدة) julia> calculate ( square , 5 ) 25

كوتلين

في كود Kotlin التالي ، askAndAnswerتستخدم الدالة مُعاملًا getAnswerكدالة رد نداء مُعطِّلة. askAndAnswerيتم استدعاء getAnswerToMostImportantQuestionالدالة التي تعمل كدالة رد نداء. عند تشغيلها، تكون الإجابة على السؤال: "42".

fun main () { print ( "أدخل السؤال الأكثر أهمية: " ) val question = readLine () askAndAnswer ( question , :: getAnswerToMostImportantQuestion ) }دالة getAnswerToMostImportantQuestion (): Int { return 42 }دالة ` askAndAnswer` ( السؤال : سلسلة نصية؟، الإجابة : () -> عدد صحيح ) { طباعة ( "السؤال: $ السؤال " ) طباعة ( "الإجابة: ${ الإجابة () } " ) }

لغة لوا

في كود Luacalculate هذا، تقبل الدالة operationالمعامل الذي يُستخدم كدالة رد نداء مانعة. calculateيتم استدعاء الدالة مع كل من addو multiply، ثم تستخدم دالة مجهولة لإجراء القسمة.

دالة حساب ( أ ، ب ، العملية ) تُرجع العملية ( أ ، ب ) نهايةدالة الضرب ( أ ، ب ) تُرجع أ * ب نهايةدالة الجمع ( أ ، ب ) تُرجع أ + ب نهايةprint ( calculate ( 10 , 20 , multiply )) -- الناتج 200 print ( calculate ( 10 , 20 , add )) -- الناتج 30 -- مثال على دالة رد نداء تستخدم دالة مجهولة print ( calculate ( 10 , 20 , function ( a , b ) return a / b) -- الناتج 0.5 end ))

بايثون

في كود بايثون التالي ، calculateتقبل الدالة مُعاملًا operateيُستخدم كدالة رد نداء مانعة. calculateيتم استدعاء squareالدالة التي تعمل كدالة رد نداء.

دالة مربعة ( القيمة : عدد صحيح ) -> عدد صحيح : تُرجع القيمة ** 2دالة حسابية ( عملية : دالة قابلة للاستدعاء [[ عدد صحيح عدد صحيح قيمة : عدد صحيح ) -> عدد صحيح : إرجاع عملية ( قيمة )عدد مرات الطباعة: 25 اطبع ( احسب ( مربع ، 5 ))

ريبول، أحمر

يوضح الكود التالي في Rebol و Red استخدام الاستدعاءات.

  • بما أن التنبيه يتطلب سلسلة نصية، فإن النموذج يُنتج سلسلة نصية من نتيجة الحساب.
  • تؤدي قيم get-word! (أي :calc-product و :calc-sum) إلى قيام المفسر بإرجاع رمز الدالة بدلاً من التقييم باستخدام الدالة.
  • تُقيّد مراجع أنواع البيانات في الكتلة [float! integer!] نوع القيم التي يتم تمريرها كوسائط.
أحمر [ العنوان: "مثال على رد الاتصال" ]حساب: دالة [ رقم1 [ رقم! ] رقم2 [ رقم! ] دالة رد نداء [ دالة! ] ][ دالة رد نداء رقم 1 رقم2 ]دالة حساب الضرب: دالة [ رقم1 [ رقم! ] رقم2 [ رقم! ] ][ رقم1 * رقم2 ]دالة حساب المجموع: دالة [ رقم1 [ رقم! ] رقم2 [ رقم! ] ][ رقم1 + رقم2 ]تنبيهات 75، ناتج ضرب 5 و15، نموذج التنبيه : حساب 5 15 :calc-productتنبيهات 20، نموذج تنبيه مجموع 5 و15، حساب 5 15 :calc-sum

الصدأ

تتمتع لغة Rust بالخصائص التالية: Fn[ 10 ]FnMut و [10 ] .FnOnce

fn call_with_one < F > ( func : F ) -> usize where F : Fn ( usize ) -> usize { func ( 1 ) }let double = | x | x * 2 ; assert_eq! ( call_with_one ( double ), 2 );

انظر أيضاً

مراجع

  1. جالابا، ك.، مصباح، أ.، وبشاستنيخ، إ. (2015). لا تتصل بنا، سنتصل بك: توصيف وظائف الاستدعاء في جافا سكريبت. ندوة ACM/IEEE الدولية لعام 2015 حول هندسة البرمجيات التجريبية والقياس (ESEM) . https://doi.org/10.1109/esem.2015.7321196
  2. "كتاب طبخ بيرل - 11.4. أخذ المراجع إلى الدوال" . 2 يوليو 1999. تم الاسترجاع في 3 مارس 2008 .
  3. "برمجة بيرل المتقدمة - 4.2 استخدام مراجع الروتينات الفرعية" . 2 يوليو 1999. تم الاطلاع عليه بتاريخ 3 مارس 2008 .
  4. "مرجع لغة PHP - الدوال المجهولة" . تم الاطلاع عليه بتاريخ 2011-06-08 .
  5. "ما الجديد في JDK 8" . oracle.com .
  6. بيلزر، جاك؛ هولزمان، ألبرت ج؛ كينت، ألين، محرران. (1979). موسوعة علوم وتكنولوجيا الحاسوب: المجلد 12. مارسيل ديكر، ص 164. ISBN  0-8247-2262-0تم الاطلاع عليه بتاريخ 28 يناير 2024 .
  7. برينز، بيتر؛ كروفورد، توني (12 ديسمبر 2015). C باختصار ( الطبعة الثانية). سيباستوبال، كاليفورنيا: أورايلي ميديا ​​إنك . الصفحات 538-540 ، 374-377 . ISBN   978-1-491-90475-6.
  8. "إنشاء دوال رد نداء جافا سكريبت في المكونات" . أرشيف. وثائق UDN على الويب (صفحة التوثيق). sec. دوال جافا سكريبت كدوال رد نداء. مؤرشف من الأصل بتاريخ 16-12-2021 . تم الاسترجاع بتاريخ 16-12-2021 .
  9. هولي، بوبي؛ شيبارد، إريك (محرران). "الإعلان عن دوال الاستدعاء واستخدامها" . وثائق. شبكة مطوري موزيلا (صفحة الوثائق). مؤرشف من الأصل بتاريخ 17 يناير 2019. تم الاطلاع عليه بتاريخ 16 ديسمبر 2021 .
  10. "Fn in std::ops - Rust" . doc.rust-lang.org . تم الاطلاع عليه بتاريخ 18 يناير 2025 .