5  الفعل المتسلسل

الفعل المتسلسل: يحتوي على جملة يطلب فيها نفسه. ويجب أن تؤول سلسلة الطلبات هذه إلى جملة تنهي التسلسل.

تنبيه: هذا مبحثٌ شيِّق، وقد يكون فيه شيءٌ من الصعوبة، لكنّ فيه أدوات فعَّالة في استعمال المنطق البرمجي بأقصى مدى.

5.1 مثال: المضروب

فمثلا: تعرف الرياضيات مضروب العدد

\[ !n = n(n-1)(n-2)\cdots(1) \]

فهي عملية ضرب لكل عدد مع الذي قبله حتى ينتهي للواحد. ومن أمثلته:

\[ !5 = (5)(4)(3)(2)(1) \]

ولك أن تصف نفس العملية هكذا:

\[ !n = n !(n-1) \]

أي أن مضروب العدد هو ضربُ هذا العدد في مضروب العدد الذي قبله. وذلك يتسلسل على النحو التالي:

\[ !5 = (5)!(4) = (5)(4!(3)) = (5)(4(3!(2))) = (5)(4(3(2!(1)))) = (5)(4(3(2(1)))) = (5)(4)(3)(2)(1) \]

إذا نخلص بالمعادلة التالية:

\[ !n = n(n-1)(n-2)\cdots(1) = n!(n-1) \]

وهكذا يكون تعريفها في بايثون:

def factorial(n: int) -> int:
    # Recursive case
    if n > 0:
        recursive_result = factorial(n - 1)
        return n * recursive_result
    # Terminal case
    return 1

factorial(5)
120

حيث لدينا حالتان:

  • عندما تكون n > 0 يتم الطلب الذاتي : recursive_result = factorial(n - 1) إذْ هي جملة متسلسلة تكدِّس طلبات فوق طلبات؛ لكنها تؤول في النهاية إلى الجملة التي تُنهي التسلسل
  • return 1 هي الجملة التي تنهي التسلسل

وهنا قطعة كود نستعملها لتصور تسلسل الطلبات:

الكود
def factorial(n: int, depth: int = 0) -> int:

    # Recursive case
    print(f"{'  ' * depth}Call factorial({n})")
    if n > 0:
        result = n * factorial(n - 1, depth + 1)
        print(f"{'  ' * depth}Return {result} from factorial({n})")
        return result
    
    # Terminal case
    print(f"{'  ' * depth}Return 1 from factorial({n})")
    return 1

factorial(5)
Call factorial(5)
  Call factorial(4)
    Call factorial(3)
      Call factorial(2)
        Call factorial(1)
          Call factorial(0)
          Return 1 from factorial(0)
        Return 1 from factorial(1)
      Return 2 from factorial(2)
    Return 6 from factorial(3)
  Return 24 from factorial(4)
Return 120 from factorial(5)
120
  • فكل طلب يُنشأ له ظرف تنفيذ جديد تكون بالنسبة له قيمة n هي المعيَّنة له وقت النداء.
  • وهكذا يتم تكديس الطلبات حتى ينتهي التسلسل عند الطلب factorial(0) الذي يؤول لنتيجة مباشرة: return 1 فيخلَّى هذا الظرف من الذاكرة وتعود نتيجته إلى الظرف المباشر الذي استدعاه وهو ظرف factorial(1).
  • فتتعين القيمة recursive_result = 1 وينتقل إلى الجملة التي بعدها وهي جملة الرجوع بنتيجة return n * recursive_result وهُما معيَّنان، أي تكون الجملة في واقع الظرف: return 1 * 1.
  • وهذه النتيجة تعود للظرف الذي استدعاه وهو factorial(2) … إلخ.

طلب الفعل المتسلسل يؤدي إلى ظروف متداخلة تؤول إلى ظرف واحد في النهاية.

يستعمل الفعل المتسلسل وكذلك هياكل البيانات المتسلسلة بشكل كبير في الخوارزميات الفعالة.

يسهل كتابة بعض الخوارزميات باستعمال الفعل المتسلسل. لكن قد تكون (أحياًنا) أقل أداءً من استعمال الحلقات.