دالة متداخلة

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

يختلف دعم لغات البرمجة للدوال المتداخلة. ففي لغات البرمجة الهيكلية ، تدعمها بعض اللغات القديمة مثل ALGOL و Simula 67 و Pascal ، بالإضافة إلى لغة JavaScript الشائعة الاستخدام . كما تدعمها لغات البرمجة الديناميكية والوظيفية . مع ذلك، لا تدعمها بعض اللغات الشائعة الاستخدام، بما في ذلك لغتي C و C++ القياسيتين .

توفر تقنيات البرمجة الأخرى فوائد مماثلة. على سبيل المثال، تسمح دالة لامدا بتعريف دالة داخل دالة أخرى (وكذلك في أي مكان آخر)، وتتيح إخفاء البيانات وتغليفها بشكل مشابه. والجدير بالذكر أن دالة لامدا ليس لها اسم (فهي مجهولة الهوية)، وبالتالي لا يمكن استدعاؤها بالاسم، كما أنها لا تتمتع بأي مستوى من الرؤية.

صفات

نطاق الدالة المتداخلة هو الكتلة التي تحتويها ، سواء كانت كتلة دالة مستقلة أو كتلة داخل جسم دالة. وهي غير مرئية (لا يمكن استدعاؤها بالاسم) خارج الكتلة التي تحتويها.

يمكن للدالة المتداخلة استخدام المعرفات (أي اسم الدوال والمتغيرات والأنواع والفئات) المعلنة في أي كتلة محيطة، باستثناء عندما يتم إخفاؤها بواسطة إعلانات داخلية بنفس الأسماء.

يمكن تعريف دالة متداخلة داخل دالة متداخلة أخرى، بشكل متكرر، لتكوين بنية متداخلة عميقة. وتستطيع الدالة المتداخلة العميقة الوصول إلى المعرفات المُعرَّفة في جميع كتلها المحيطة، بما في ذلك الدوال المحيطة.

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

قيمة

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

يسمح نطاق الكتلة للوظائف بمشاركة حالة الكتل المحيطة (بما في ذلك الوظائف المحيطة) دون تمرير المعلمات أو استخدام المتغيرات العامة . [ 1 ]

الاستخدامات

المساعد

عادةً ما تعمل الدالة المتداخلة كدالة مساعدة أو دالة تكرارية .

تدفق التحكم

يمكن استخدام الدوال المتداخلة للتحكم غير المنظم في تدفق البرنامج ، وذلك باستخدام عبارة الإرجاع للتحكم غير المنظم العام. يتيح هذا التحكم الدقيق مقارنةً بالميزات المدمجة الأخرى في اللغة، فعلى سبيل المثال، يسمح بإنهاء حلقة for مبكرًا في حال عدم توفر متغير، أو إنهاء حلقة forbreak متداخلة مبكرًا في حال عدم توفر متغير متعدد المستويات أو استثناءات.break

الدوال ذات الرتبة العليا

في بعض لغات البرمجة، يُمكن إنشاء دالة متداخلة تصل إلى مجموعة من المعاملات من الدالة الخارجية، أي دالة مغلقة ، وتكون هذه الدالة هي القيمة المُعادة من الدالة الخارجية. وبالتالي، يُمكن إعادة دالة مُصممة لأداء مهمة مُحددة مع عدد قليل من المعاملات الإضافية أو بدونها، مما يُحسّن الأداء بشكل ملحوظ. [ 2 ]

أمثلة

مثال بسيط

مثال بسيط بلغة باسكال:

دالة E ( x : عدد حقيقي ) : عدد حقيقي ; دالة F ( y : عدد حقيقي ) : عدد حقيقي ; بداية F := x + y نهاية ; بداية E := F ( 3 ) + F ( 4 ) نهاية ;

الدالة Fمتداخلة داخل E. لاحظ أن Eمُعامل 's xمرئي أيضًا في F(لأنه Fجزء من E) بينما كل من xو yغير مرئيين خارج Eو Fعلى التوالي.

وبالمثل، في لغة البرمجة القياسية (Standard ML) :

fun e ( x : real ) = let fun f y = x + y in f 3 + f 4 end ;

في لغة هاسكل :

e :: Float -> Float e x = f 3 + f 4 حيث f y = x + y

في اللغة الإنجليزية :

هـ: الإجراء (س) يُرجع (عدد عشري)؛ أعلن x كعدد عشري؛ f: procedure(y) returns(float); أعلن y كعدد عشري؛ أعد x + y نهاية؛ أعد f(3.0) + f(4.0)؛ نهاية؛ 

في لغة بايثون :

def e ( x : float ) -> float : def f ( y : float ) -> float : return x + y return f ( 3.0 ) + f ( 4.0 )

في لغة GNU C [ 3 ] التي توسع لغة C القياسية بوظائف متداخلة:

float e ( float x ) { float f ( float y ) { return x + y ; } return f ( 3.0f ) + f ( 4.0f ); }

فرز سريع

فيما يلي تطبيق لخوارزمية الفرز السريع : [ 4 ]

دالة الفرز ( int * items , int size ) { دالة الفرز السريع ( int first , int last ) { دالة التبديل ( int p , int q ) { int tmp = items [ p ]; items [ p ] = items [ q ]; items [ q ] = tmp ; } دالة التقسيم () { int pivot = items [ first ]; int index = first ; swap ( index , last ); for ( int i = first ; i < last ; i ++ ) { if ( items [ i ] < pivot ) { swap ( index ++ , i ); } } swap ( index , last ); return index ; }إذا كان ( الأول < الأخير ) { int pivotIndex = partition (); quickSort ( first , pivotIndex - 1 ); quickSort ( pivotIndex + 1 , الأخير ); } } quickSort ( 0 , size - 1 ); }

فيما يلي تطبيق لخوارزمية الفرز السريع القائمة على تقسيم هوار باستخدام صيغة تعبير لامدا في لغة C++11، وهي تقنية بديلة تسمح أيضًا بإخفاء دالة داخل دالة أخرى:

// يمثل Iter قالب مُكرِّر وصول عشوائي < typename Iter > void sort ( Iter begin , Iter end ) { auto partition = [ & ]() -> Iter { // مخطط تقسيم هوار Iter & pivot = * begin ; Iter forwardCursor = begin ; Iter backwardCursor = end - 1 ; Iter partitionPositionFound = false ;auto locatePartitionPosition = [ & ]() -> void { while ( * forwardCursor < pivot ) { ++ forwardCursor ; } while ( pivot < * backwardCursor ) { -- backwardCursor ; } if ( forwardCursor >= backwardCursor ) { partitionPositionFound = true ; } else swap ( * forwardCursor , * backwardCursor ); } };// دالة مساعدة بسيطة auto moveOnAndTryAgain = [ & ]() -> void { ++ forwardCursor ; -- backwardCursor ; };// ملخص موجز لعملية التقسيم الفعلية while ( true ) { locatePartitionPosition (); if ( partitionPositionFound ) return backwardCursor + 1 ; else { moveOnAndTryAgain (); } } };// ملخص موجز لخوارزمية الفرز السريع إذا ( بداية < نهاية - 1 ) { تكرار موضع التقسيم = تقسيم (); فرز ( بداية ، موضع التقسيم ); فرز ( موضع التقسيم ، نهاية ); } }

اللغات

تشمل اللغات البارزة التي تدعم الدوال المتداخلة ما يلي:

اللغات الوظيفية

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

البدائل

يمكن استخدام تقنيات بديلة متنوعة لتحقيق نتائج برمجة مماثلة لتلك التي يتم الحصول عليها عبر الدوال المتداخلة.

نمطية التصميم

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

في لغة C، يمكن تحقيق ذلك بتعريف الدوال والمتغيرات كـ static لإخفائها عن التعليمات البرمجية خارج الملف. [ 10 ] يتيح هذا إخفاء البيانات وتغليفها وتفكيكها، ولكن بمستوى دقة مختلف عن الدوال المتداخلة. لا تدعم هذه النمطية أكثر من مستوى واحد من التداخل.

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

حدود

لتنفيذ إخفاء البيانات، يمكن للدوال تمرير البيانات المشتركة كمعاملات، لكن هذا يزيد من تعقيد استدعاءات الدوال. [ 1 ]

في لغة C، يتم تنفيذ ذلك عمومًا عن طريق تمرير مؤشر إلى بنية تحتوي على البيانات المشتركة. [ 10 ]

لامدا

في لغة PHP وغيرها من اللغات، تُعدّ الدوال المجهولة (lambda) بديلاً. تُعرَّف هذه الدوال في عبارة برمجية بدلاً من تعريفها باستخدام صيغة الدوال المعتادة. ليس لها اسم، ولكن يمكن استدعاؤها عبر مرجع دالة . يمكن تعريف هذه الدوال داخل دالة أخرى، وكذلك في نطاقات أخرى. لاستخدام متغيرات محلية في الدالة المجهولة، استخدم الإغلاق (closure) .

البدائل حسب اللغة

توفر اللغات التالية ميزات مشابهة للوظائف المتداخلة:

  • في لغة C++ ، تسمح الفئات بإخفاء البيانات وتغليفها بشكل مماثل؛ كما أن تعريف فئة داخل فئة أخرى يوفر بنية مماثلة (انظر كائن الدالة في C++ ).
  • C++11 والإصدارات الأحدث عبر تعبيرات لامدا (انظر مثال الفرز السريع أعلاه) [ 11 ]
  • لغة إيفل تمنع صراحةً تداخل الإجراءات للحفاظ على بساطة اللغة؛ لكنها تسمح باستخدام متغير خاص، Result ، للدلالة على نتيجة دالة (تُرجع قيمة).

تطبيق

قد يكون تنفيذ الدوال المتداخلة أكثر تعقيدًا مما يبدو، إذ أن الإشارة إلى دالة متداخلة تشير إلى متغيرات غير محلية تُنشئ إغلاقًا . لهذا السبب، لا تدعم بعض اللغات، مثل C وC++ وجافا، الدوال المتداخلة، لأن ذلك يُصعّب عملية تنفيذ المترجمات. [ 10 ] [ 13 ] مع ذلك، تدعمها بعض المترجمات كإضافة خاصة بها. ومن الأمثلة المعروفة على ذلك تطبيق GNU C للغة C، الذي يتشارك الشيفرة مع مترجمات لغات أخرى مثل باسكال وآدا وموديولا.

الوصول إلى الكائنات غير المحلية

توجد عدة طرق لتنفيذ الإجراءات المتداخلة في لغة ذات نطاق معجمي، ولكن الطريقة الكلاسيكية هي كما يلي:

يُمكن الوصول إلى أي كائن غير محلي ، X، عبر روابط الوصول في إطارات التنشيط على مكدس الجهاز. يُساعد المُستدعي، C، الإجراء المُستدعى، P، عن طريق إنشاء رابط مباشر إلى أحدث تنشيط للتغليف المعجمي المباشر لـ P، (P)، قبل الاستدعاء نفسه. وبذلك، يُمكن لـ P العثور بسرعة على التنشيط الصحيح لكائن X مُعين من خلال تتبع عدد ثابت (عمق P - عمق X) من الروابط (عادةً ما يكون عددًا صغيرًا).
يقوم المتصل بإنشاء هذا الرابط المباشر عن طريق (نفسه) اتباع C.depth – P.depth + 1 روابط أقدم، وصولاً إلى أحدث تفعيل لـ (P)، ثم الربط مؤقتًا عبر هذه الروابط برابط مباشر إلى هذا التفعيل؛ يختفي الرابط لاحقًا مع P، حيث يمكن استخدام الروابط الأقدم الموجودة أسفله مرة أخرى.
لاحظ أن P مرئية لـ C، وبالتالي يمكن استدعاؤها بواسطة C إذا كان (P) = C / (C) / ((C)) / إلخ.

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

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

الدوال كقيم

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

مكدسات لا يمكن تنفيذها

يؤدي تطبيق GCC للدوال المتداخلة في لغة C إلى فقدان مكدسات عدم التنفيذ (مكدسات NX). لذلك، غالبًا ما لا تسمح البرامج المصممة باستخدام دورة حياة التطوير الآمنة ( SEP ) باستخدام دوال GCC المتداخلة. [ 15 ] افتراضيًا، يُصدر GCC إشعارًا موجزًا ​​عندما يتطلب ملف كائن مُنشأ مكدسًا تنفيذيًا. للحصول على تحذير واضح عند تجميع كود C، استخدم الخيار المناسب.Wtrampolines

تكمن المشكلة في أن GCC يستخدم "الترامبولين" (كود قابل للتنفيذ على المكدس) للانتقال إلى الدوال المتداخلة. [ 16 ] يمتلك مشروع GCC طريقة بديلة يمكنها توفير الدوال المتداخلة عبر واصفات غير قابلة للتنفيذ، إلا أنها تُستخدم حاليًا فقط في مُصرّف لغة Ada الخاص بـ GCC .

انظر أيضاً

مراجع

  1. 1 2 برايت 2004 .
  2. الدوال العليا وتعبيرات لامدا - لغة برمجة كوتلن
  3. روثويل، تريفيس ج. (2011). دليل مرجعي للغة سي من جنو . مؤسسة البرمجيات الحرة، ص  63.
  4. ردًا على: الدوال المتداخلة - لماذا؟، بافجاي ،١٤ يناير ٢٠١٢
  5. "جولة في لغة دارت" .
  6. "الوظائف | كوتلين" .
  7. "الأساليب المتداخلة" .
  8. "الدوال المتداخلة - استخدام مجموعة مترجمات جنو (GCC)" . مشروع جنو . تم الاسترجاع في 2007-01-06 .
  9. "جولة في لعبة غو" .
  10. 1 2 3 " السؤال 20.24: لماذا لا تحتوي لغة C على دوال متداخلة؟، الأسئلة الشائعة حول لغة C
  11. "الدالة المتداخلة - كود روزيتا" .
  12. "الدالة المتداخلة - كود روزيتا" .
  13. إجابة ديف فاندرفيس، 28 أغسطس 2009 الساعة 17:45، على سؤال " لماذا لا يدعم معيار لغة C الدوال المتداخلة؟ "
  14. يُطلق على هذا المزيج من كود الدالة وبيئتها أحيانًا اسم الإغلاق .
  15. والتون، جيفري. "تحصين سلسلة أدوات C" . مشروع أمان تطبيقات الويب المفتوحة (OWASP) . تم الاطلاع عليه بتاريخ 28 فبراير 2017 .
  16. "الترامبولين" . وثائق GCC الداخلية . جنو . تم الاسترجاع في 12 مايو 2026 .