5  استدعاء الإجراءات

الإجراء (Procedure) هو جزء محفوظ من البرنامج يتم استدعاؤه مع تمرير عوامل مختلفة.

ويمكن استدعاؤه (Call) بعامل واحد:

وقد يأخذ عاملين:

وقد يأخذ عاملاً واحدًا لكنَّهُ قائمة:

وقد يكون عدد عوامله لا محدودًا:

name = "Adam"
age = 25
print("My name is", name, "and I'm", age, "years old")
My name is Adam and I'm 25 years old

فإن print قبلت خمسة عوامل:

  1. النص: "My name is"
  2. قيمة المتغير: name
  3. النص: "and I'm"
  4. قيمة المتغير: age
  5. النص: "years old"

ولدينا الإجراء help(func) يطلب مساعدة الإجراء المعيَّن، بلا أقواس، هكذا:

help(sum)
Help on built-in function sum in module builtins:

sum(iterable, /, start=0)
    Return the sum of a 'start' value (default: 0) plus an iterable of numbers

    When the iterable is empty, return the start value.
    This function is intended specifically for use with numeric values and may
    reject non-numeric types.

وقد عرفت أن طلب التنفيذ يكون بالقوسين بعد اسمه ()، وتوضَع العوامل فيهما وهي ضربان:

  1. عامل مؤثر
  2. عامل متأثر

وفي كل ما سبق كانت العوامل من النوع المؤثر؛ لأنها كانت تؤثر في النتيجة ولا تتأثر بها.

أما المتأثر فنحو فعل الترتيب من مكتبة القائمة: list.sort(xs) فإن المتأثر به القائمة نفسها، مثل:

xs = [40, 20, 10, 30]
list.sort(xs)

فأما جملة list.sort فإننا حددنا اسم النوع list وأردنا منه الإجراء sort، وأتينا بالقوسين لطلب تنفيذه، ووضعنا المتأثر xs فيه.

ومع أننا لم نعيِّن النتيجة (بجملة التعيين =)، فقد تغيَّرت (تأثرت) القائمة بالإجراء:

print(xs)
[10, 20, 30, 40]

بعكس الترتيب بالإجراء الذي ليس من النطاق العام: sorted(xs) فإنَّ القائمة فيه ليست متأثرًا؛ إذْ يُنتِجُ الإجراءُ قائمةً جديدةً ولا يغير القائمة المُدخلة:

xs = [40, 20, 10, 30]
ys = sorted(xs)
print(xs, "لم يعمل الإجراء في القائمة نفسها")
print(ys, "هي قائمة جديدة")
[40, 20, 10, 30] لم يعمل الإجراء في القائمة نفسها
[10, 20, 30, 40] هي قائمة جديدة

وقد تتساءل ما الفائدة من وجود طريقتين وكلاهما يعمل نفس العمل؟

  1. الطريقة الأولى: list.sort(xs) لا ترجع بشيء بل تعدل نفس القائمة
  2. الطريقة الثانية: sorted(xs) ترجع قائمة جديدة

فأما الطريقة الأولى فأصغر في الذاكرة لأنها لا تُنشئ نُسخة كما تفعل الطريقة الثانية. لكن الطريقة الثانية مفيدة إن أردت أن تُبقي القائمة الأصلية كما هي.

والأمر كذلك في الإجراءين:

  1. list.reverse(xs)
  2. reversed(xs)

ويجتمع المؤثر والمتأثر في نحو:

list.append(xs, 50)

فالإجراء list.append يأخذ القائمة، ويضيف إليها القيمة 50. ولذلك نقول الأوَّل متأثر والثاني مؤثر.

print(xs)
[40, 20, 10, 30, 50]

وقد وضعت اللغات الشيئية (Object-Oriented) مثل بايثون صياغة خاصَّةً: للإجراء المسند إلى الشيء. وبذلك يتحصل لدينا طريقتان كلتاهما تؤديان نفس المعنى:

ومثاله أيضًا في الإجراء list.sort للترتيب وكذلك في list.append للإضافة:

xs = [20, 10, 30, 40]
ys = [20, 10, 30, 40]

list.sort(xs)
ys.sort()
assert xs == ys

list.append(xs, 50)
ys.append(50)
assert xs == ys

وسنفرد للبرمجة الشيئية باباً.

وقد تُعَيَّنُ العوامل بأحد طريقتين:

  1. تعيين بالموضع: نحو: round(3.14159, 2)
  2. تعيين بالاسم: نحو: round(number=3.14159, ndigits=2) فلا يشترط فيه ترتيب العوامل.

ويجوز استعمال الطريقتين معًا في نحو: list.sort(numbers, reverse=True) ويشترط فيه تقدم التعيين بالموضع ليكون في مكانه، ثم يتبعه التعيين بالاسم حيث لا يشترط الترتيب فيه.