English | 简体中文 | 繁體中文 | Русский язык | Français | Español | Português | Deutsch | 日本語 | 한국어 | Italiano | بالعربية
في هذا المقال، ستتعلم إنشاء دالة تكرارية. دالة تستدعي نفسها. بالإضافة إلى ذلك، ستتعلم أيضًا دالة التكرار الخلفي.
تدعو نفسهادالةيسمى دالة التكرار. بالإضافة إلى ذلك، يُطلق على هذه التقنية التكرار.
مثال في العالم الفيزيائي هو وضع مرآت متوازية مع بعضها البعض. أي جسم بينها سيتم انعكاسه بشكل تكراري.}
fun main(args: Array<String>) { ... .. .. recurse() ... .. .. {} fun recurse() { ... .. .. recurse() ... .. .. {}
في هذا الحالة، يتم استدعاء دالة recurse() من جسم دالة recurse() نفسها. يعمل هذا البرنامج كما يلي:
في هذا الحالة، تستمر دعوة التكرار بشكل مستمر، مما يؤدي إلى تكرار غير محدود.
لمنع التكرار غير المحدود، يمكن استخدام دعوة التكرار في فرع واحد فقط بينما لا تتم دعوة التكرار في الفرع الآخر.if ... elseأو طريقة مشابهة).
fun main(args: Array<String>) { val number = 4 val result: Long result = عامل(number) println("$number ثنائي = $result") {} fun factorial(n: Int): Long {}} return if (n == 1) n.toLong() else n*عامل(n-1) {}
عند تشغيل هذا البرنامج، الناتج هو:
4 ثنائي = 24
تصور عامل() يوضح دعوات التكرار للدالة:
الخطوات المعنية كالتالي:
عامل(4) // دعوة الدالة الأولى، المعامل: 4 4*عامل(3) // دعوة الدالة الثانية، المعامل: 3 4*(3*عامل(2)) // دعوة الدالة الثالثة، المعامل: 2 4*(3*(2*عامل(1))) // دعوة الدالة الرابعة، المعامل: 1 4*(3*(2*1)) 24
التكرار النهائي ليس خاصية لغة Kotlin، بل هو مفهوم عام. تشمل Kotlin بعض لغات البرمجة التي تستخدمها لتحسين دعوات التكرار، بينما لا تدعمها لغات أخرى (مثل Python).
في التكرار العادي، يتم أولاً تنفيذ جميع الدعوات التكرارية، ثم حساب النتيجة بناءً على القيم التي تعود من الدعوات التكرارية (كما هو موضح في المثال أعلاه). لذلك، لن تحصل على النتيجة قبل تنفيذ جميع الدعوات التكرارية.
في التكرار النهائي، يتم أولاً تنفيذ الحسابات، ثم تنفيذ الدعوة التكرارية (تُنقل دعوة التكرار الحالية نتيجة الخطوة الحالية إلى دعوة التكرار التالية). هذا يجعل دعوة التكرار تساوي الدورات، ويجنب خطر انهيار الذاكرة.
إذا كانت دعوة الدالة الخاصة بها هي العملية الأخيرة التي تنفذها الدالة التكرارية، فإن هذه الدالة التكرارية يمكن أن تقوم بتكرار نهاية التجميع. على سبيل المثال،
مثال 1:لا تتوافق مع شروط التكرار التجميعي لأن دعوة الدالة الخاصة بها n*عامل(n-1) ليست العملية الأخيرة.
fun factorial(n: Int): Long {}} if (n == 1) { return n.toLong() } else { return n*factorial(n - 1) {} {}
مثال 2:يستوفي شرط التكرار الذاتي، لأن الاستدعاء إلى نفس الدالة fibonacci(n-1, a+b, a) هو العملية الأخيرة.
fun fibonacci(n: Int, a: Long, b: Long): Long { return if (n == 0) b else fibonacci(n-1, a+b, a) {}
لإعلام المُعالج بأن التكرار الذاتي يتم في Kotlin، يجب وضع علامة التكرار tailrec على الدالة.
import java.math.BigInteger fun main(args: Array<String>) { val n = 100 val first = BigInteger("0") val second = BigInteger("1") println(fibonacci(n, first, second)) {} tailrec fun fibonacci(n: Int, a: BigInteger, b: BigInteger): BigInteger { return if (n == 0) a else fibonacci(n-1, b, a+b) {}
عند تشغيل هذا البرنامج، الناتج هو:
354224848179261915075
هذا البرنامج يحسب المثلث الأصفر في المكان الـ 100. لأن الناتج يمكن أن يكون عددًا كبيرًا جدًا، لذا قمنا بجلب دالة BigInteger من مكتبة Java القياسية.
في هذا المكان، يتم تسمية الدالة fibonacci() بـ trarec، وهي تتمتع بالاستحقاق للتكرار الذاتي. لذلك، يتم تحسين التكرار في هذه الحالة.
إذا حاولت البحث عن المثلث الأصفر في المكان (أو أي عدد كبير آخر)، سيقوم المُعالج بطرح استثناء java.lang.StackOverflowError.
لكن، برنامجنا السابق يعمل بشكل جيد. هذا لأننا استخدمنا التكرار الذاتي، الذي يستخدم إصدارًا مبتكرًا يعتمد على الدورات، بدلاً من التكرار التقليدي.
في المثال السابق (المثال الأول) الذي يستخدم لحساب ناقص الصفر من الرقم، لا يمكن تحسين التكرار الذاتي. هذا برنامج آخر يقوم بتنفيذ نفس المهمة.
fun main(args: Array<String>) { val number = 5 println("$number ^ 120 = ${factorial(number)}") {} tailrec fun factorial(n: Int, run: Int = 1): Long { return if (n == 1) run.toLong() else factorial(n-1, run*n) {}
عند تشغيل هذا البرنامج، الناتج هو:
5 ^ 120 = 120
يمكن للمعالج أن يتحسين التحقق المكرر في هذا البرنامج، لأن وظائف التحقق المكرر يمكن أن تكون تحققًا مكتملًا، ونحن نستخدم علامة الترقيم التالفة tailrec لتخبر المعالج بتحسين التحقق المكرر.