Trampolining
استدعاء ذاتي آمن للمكدس
Trampolining درس مجاني في Scala for Backend Engineering & Functional Programming على CoddyKit. هذا هو الدرس 4 من أصل 4. يمكنك قراءة الدرس كاملاً أدناه مجاناً — ثم تمرن عليه مباشرة في المتصفح باستخدام محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7. هذا الدرس جزء من مسار التعلم في Scala for Backend Engineering & Functional Programming، وتقدمك يتزامن عبر الويب وتطبيق CoddyKit. تتضمن دورة Scala for Backend Engineering & Functional Programming 4 دروس في المجموع.
حدود @tailrec
تحسّن @tailrec دالة تستدعي نفسها مباشرة فقط. ولا يمكنها المساعدة في الاستدعاء الذاتي المتبادل (استدعاء دالتين إحداهما الأخرى)، إذ يواصل ذلك زيادة حجم المكدس. ويحل الترامبولين هذه المشكلة.
مشكلة الاستدعاء الذاتي المتبادل
لنفترض أن isEven وisOdd معرفتان بدلالة كل منهما الأخرى. بالنسبة إلى عدد كبير، يؤدي ذلك إلى تجاوز المكدس، ولا يمكن وضع @tailrec على أي منهما.
object Main {
def isEven(n: Int): Boolean = if (n == 0) true else isOdd(n - 1)
def isOdd(n: Int): Boolean = if (n == 0) false else isEven(n - 1)
def main(args: Array[String]): Unit = {
println(isEven(10))
}
}ما هو الترامبولين؟
يحوّل الترامبولين الاستدعاءات الذاتية إلى بيانات. فبدلًا من استدعاء الدالة لنفسها، تعيد وصفًا للخطوة التالية. ثم تنفذ حلقة تشغيل هذه الخطوات مرارًا، مع إبقاء المكدس ثابتًا.
TailRec في المكتبة القياسية
يوفر Scala النوع scala.util.control.TailCalls مع النوع TailRec. استخدم done(x) للنتيجة النهائية، وtailcall(...) لتأجيل الاستدعاء التالي.
import scala.util.control.TailCalls._
object Main {
def isEven(n: Int): TailRec[Boolean] =
if (n == 0) done(true) else tailcall(isOdd(n - 1))
def isOdd(n: Int): TailRec[Boolean] =
if (n == 0) done(false) else tailcall(isEven(n - 1))
def main(args: Array[String]): Unit = {
println(isEven(100000).result)
}
}done وtailcall
المكوّنان الأساسيان:
- تغلف
done(value)إجابة نهائية. - تؤجل
tailcall(expr)استدعاءً يعيدTailRec.
ويشغّل استدعاء .result حلقة الترامبولين وينتج القيمة.
سلامة المكدس
بما أن كل tailcall يعيد التحكم إلى حلقة التشغيل بدلًا من تداخل استدعاء Java، فلا ينمو مكدس JVM مع عمق الاستدعاء الذاتي. ويتعامل المثال أعلاه مع 100,000 خطوة من دون تجاوز المكدس.
استخدام الترامبولين مع الاستدعاء الذاتي للنفس
تعمل الترامبولينات أيضًا مع الاستدعاء الذاتي العميق العادي عندما يتعذر استخدام مُجمِّع بسهولة. ويبقى العد التنازلي العميق هنا آمنًا للمكدس.
import scala.util.control.TailCalls._
object Main {
def countDown(n: Int): TailRec[Int] =
if (n == 0) done(0) else tailcall(countDown(n - 1))
def main(args: Array[String]): Unit = {
println(countDown(500000).result)
}
}دمج النتائج باستخدام flatMap
يدعم TailRec الدالتين map وflatMap، لذا يمكنك تنفيذ العمل بعد الاستدعاء المؤجل مع الحفاظ على سلامة المكدس.
import scala.util.control.TailCalls._
object Main {
def sum(n: Int): TailRec[Int] =
if (n == 0) done(0)
else tailcall(sum(n - 1)).map(_ + n)
def main(args: Array[String]): Unit = {
println(sum(100000).result)
}
}آلية عمل حلقة التشغيل
من الناحية المفاهيمية، ينفذ .result حلقة: يأخذ الخطوة الحالية؛ فإذا كانت done يعيد قيمتها؛ وإذا كانت استدعاءً مؤجلًا، يقيّم طبقة واحدة ويتابع. وكل ذلك باستخدام مساحة مكدس ثابتة.
الترامبولين في مكتبات التأثيرات
تستخدم مكتبات مثل Cats Effect وZIO الترامبولين داخليًا لسلاسل flatMap الخاصة بها، ولذلك يمكنك بناء برامج تأثيرات متداخلة بعمق من دون تجاوز المكدس. ويُعد الترامبولين أساس التأثيرات الوظيفية الآمنة للمكدس.
متى تستخدم الترامبولين
استخدم trampoline عندما:
- تكون لديك استدعاءات عودية متبادلة لا يمكن تحويلها إلى دالة واحدة عودية ذيلية.
- يكون الاستدعاء العودي عميقًا جدًا بالنسبة إلى المكدس، ولا يناسبه استخدام مُجمِّع.
بالنسبة إلى الاستدعاء العودي الذاتي البسيط، فضّل استخدام @tailrec مع مُجمِّع أولًا.
تحقّق سريع
اختبر مدى فهمك لـ trampoline.
مراجعة
لقد تعلّمت مفهوم trampolining:
- يجعل الاستدعاءات العودية المتبادلة والعميقة جدًا آمنة بالنسبة إلى المكدس.
- استخدم
TailCalls: done(x)وtailcall(...)، ثم.result. - يدعم
TailRecالدالتينmapوflatMap. - وهو أساس مكتبات التأثيرات الآمنة بالنسبة إلى المكدس.
الأسئلة الشائعة
هل درس «Trampolining» مجاني؟
نعم — نص درس «Trampolining» كامل متاح مجاناً هنا على الويب. لتمرينه بشكل تفاعلي (محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7) وفتح باقي دورة Scala for Backend Engineering & Functional Programming، انتقل إلى CoddyKit PRO. تتضمن دورة Scala for Backend Engineering & Functional Programming 4 دروس في المجموع.
ماذا ستتعلم في «Trampolining»؟
استدعاء ذاتي آمن للمكدس تتمرن على Scala for Backend Engineering & Functional Programming مع أكواد عملية تشغلها مباشرة في المتصفح، ومدرس ذكاء اصطناعي متاح 24/7 يجيب على أسئلتك أثناء عملك.
هل أحتاج إلى خبرة سابقة لأبدأ Scala for Backend Engineering & Functional Programming؟
لا تُشترط خبرة سابقة. Scala for Backend Engineering & Functional Programming على CoddyKit منظم للمبتدئين حتى المتقدمين، لذا يمكنك البدء من هنا أو من البداية والتقدم بسرعتك الخاصة. هذا هو الدرس 4 من أصل 4.
كم من الوقت يستغرق درس «Trampolining»؟
معظم دروس CoddyKit تستغرق حوالي 5–10 دقائق. كل منها موجز وتفاعلي، لذا تحرز تقدماً مستمراً وتستأنف من حيث توقفت عبر الويب والتطبيق.
هل يمكنني كتابة وتشغيل أكواد في درس Scala for Backend Engineering & Functional Programming هذا؟
نعم. كل درس في Scala for Backend Engineering & Functional Programming يتضمن محرر أكواد مدمج، لذا تكتب وتشغل أكواداً حقيقية مباشرة في متصفحك وتحصل على تعليقات فورية من الذكاء الاصطناعي — بدون إعداد محلي.