البرمجة

ما الدالة في البرمجة

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

  • الدرس 7 من 14
  • مبتدئ
  • مجاني، دون تسجيل

القطع الأربع التي تصنع دالة

  • المدخل

    الشيء الذي كان يختلف بين النسخ المكررة. وإن لم يختلف شيء فلا حاجة إلى معامل.

  • المنطق

    ما تفعله الدالة، وهو ما ينبغي أن يقوله اسمها. فإن اضطررت لقول «و» في وصفها، فعندك دالتان.

  • المخرج

    ما يُسلَّم بـreturn. والدالة التي تطبع فقط لا مخرَج لها ولا يمكن استعمالها في الحساب التالي.

  • إعادة الاستعمال

    كل قرار اتخذته داخل الدالة صار يعيش في موضع واحد ويتغير في موضع واحد.

القطعة الرابعة هي ما يفصل الدالة عن بضعة أسطر عادية. والشفرة اللازمة مرة واحدة في موضع واحد لا يلزم أن تصير دالة.

آخر مراجعة: تُراجع الحقائق وأسماء الأدوات في هذا الدرس مقابل مصادرها في هذا التاريخ.

ما الدالة بالضبط، وبم تختلف عن بضعة أسطر عادية؟

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

def display_name(user):
    name = user["name"].strip()
    if name == "":
        return "guest"
    return name

هنا يحدث أمران منفصلان يخلط بينهما المبتدئون عادة. السطر الأول هو التعريف: يقول إن هذا الشيء موجود وهذا عمله. وهو لا يُنفَّذ بل يُسجَّل فحسب. أما الاستدعاء فهو في موضع آخر، حين تكتب display_name(user_a)، وعندها فقط تُنفَّذ تلك الأسطر الخمسة.

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

وأفضل تعريف للدالة ليس أنها تقلل التكرار؛ بل أنها تعطي اسما لفكرة. فحين تكتب display_name، لم يعد قارئ شفرتك مضطرا لقراءة أربعة أسطر ليفهم ما يجري. والاسم الجيد يحذف أربعة أسطر من الشرح، وهذا أكبر ما تمنحك إياه الدالة.

ما الفرق بين المعامل والوسيط؟

هاتان الكلمتان تنظران إلى شيء واحد من جهتين، ولهذا بالذات يُخلط بينهما. المعامل هو الاسم الذي تكتبه عند تعريف الدالة، أي user في السطر الأول من المثال أعلاه. والوسيط هو القيمة الحقيقية التي ترسلها عند الاستدعاء، أي user_a.

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

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

وعادة تسهل حياتك: سمِّ معاملاتك بحيث تقول بنفسها ما تتوقعه. فـdef send(to, subject, body) تُقرأ بلا أي شرح، بينما def send(a, b, c) تجبرك على العودة إلى داخل الدالة في كل مرة، وهو بالضبط ما كانت الدالة ستوفره عليك.

المعامل والوسيط وما يعود

التعريف: المعامل

يُكتب مرة واحدة

  • فراغ له اسم
  • ينبغي أن يقول اسمه ما يتوقعه
يقترنان عند الاستدعاء

الاستدعاء: الوسيط

يمكن أن يختلف كل مرة

  • القيمة الحقيقية التي تملأ الفراغ
  • دالة واحدة، خمسون استدعاء، خمسون وسيطا

ويعود شيء واحد

القيمة المرجَعة

ما تسلّمه return لا ما يُطبَع

  • بلا return تُرجع الدالة شيئا فارغا
  • return تنهي الدالة في الحال

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

ماذا تعني return ولماذا لا يحل print محلها؟

هنا يتعثر أكبر عدد من المبتدئين، وجذر المسألة سوء فهم بسيط: print يعرض شيئا على الشاشة، أما return فتسلّم قيمة إلى من استدعى الدالة. وهذان ليسا العمل نفسه إطلاقا.

def double(n):
    print(n * 2)

result = double(5)
print(result)

تعرض الشاشة 10 أولا ثم None. فالعشرة طبعتها الدالة نفسها، أما result ففارغ لأن هذه الدالة لم تُرجع شيئا. والدالة التي لا return فيها تسلّم None في بايثون، وما يعادلها في اللغات الأخرى.

def double(n):
    return n * 2

result = double(5)
print(result)

الآن لا يُطبَع شيء داخل الدالة وresult يساوي عشرة، أي شيئا تستطيع استعماله في الحساب التالي. والفرق العملي هو هذا: الدالة التي تطبع تبدو صحيحة حين تختبرها يدويا، وتنكسر في اللحظة التي تريد فيها استعمال نتيجتها. ولهذا أيضا يُكتشف هذا الخلل متأخرا عادة.

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

إعادة بناء حقيقية: ثلاث تكرارات ودالة واحدة

افترض أنك كتبت هذه الأسطر الأربعة نفسها في ثلاثة مواضع من برنامج، ولا تختلف إلا بمتغير واحد:

name = user_a["name"].strip()
if name == "":
    name = "guest"
print(name)

name = user_b["name"].strip()
if name == "":
    name = "guest"
print(name)

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

def display_name(user):
    name = user["name"].strip()
    if name == "":
        return "guest"
    return name

print(display_name(user_a))
print(display_name(user_b))

وما رُبح فعلا ليس بضعة أسطر موفَّرة. بل أن كلمة guest صارت مكتوبة في موضع واحد فقط. وغدا، حين تقرر كتابة شيء آخر مكانها، تغير سطرا واحدا وينتهي الأمر.

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

خمس خطوات لاستخراج دالة من شفرة مكررة

  1. 1

    جد التكرار

    مرتان قد تكونان مصادفة، وثلاث مرات ليست كذلك.

  2. 2

    جد الفروق

    كل ما يختلف بين النسخ هو معامل.

  3. 3

    ضع لها اسما

    إن لم تجد اسما، فأنت لا تعرف بعد ما تفعله هذه الشفرة.

  4. 4

    اكتب دالة واحدة

    تصير الفروق مدخلات ويبقى الباقي في الداخل.

  5. 5

    استبدل المواضع الثلاثة

    إن تركت واحدا، فهو الذي سيبقي السلوك القديم شهورا.

الخطوة الثانية هي التي تُتخطى فتفسد إعادة البناء. فإن فاتتك الفروق بين النسخ بنيت دالة تغير بهدوء سلوك إحداها.

أربعة أسطر شفرة تُستدعى ١٨١ مرة

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

function rgb_dg_t( $value, string $lang ): string {
    if ( is_array( $value ) ) {
        return (string) ( $value[ $lang ] ?? $value['fa'] ?? '' );
    }
    return (string) $value;
}

يوم مراجعة هذا الدرس، كانت هذه الدالة مستدعاة ١٨١ مرة عبر ملفات مكتبة المخططات الأربعة. فكل عنوان وكل بطاقة وكل ملاحظة تحت كل شكل تراه في قسم التعلم يمر عبر هذه الأسطر الأربعة.

وانظر الآن إلى ?? 'fa' في وسط السطر الثالث. إنها قرار: إن لم تُكتب ترجمة بطاقة بعد، فاعرض النص الفارسي بدل شكل فارغ. وهذا القرار مكتوب في موضع واحد ويؤثر في ١٨١ نقطة. وإن غيرنا رأينا غدا وفضّلنا ألا نعرض شيئا بدل الفارسية، تغيرت كلمة واحدة.

والوجه الآخر للعملة نفسها، ويجب أن يُقال صراحة: خطأ واحد في هذه الأسطر الأربعة يُفسد أيضا ١٨١ موضعا دفعة واحدة. والدالة بهذه المركزية يجب أن تُقرأ بعناية أكبر من الشفرة التي تستدعيها، لهذا السبب بالذات. فالتركيز يمنحك المنفعة والمخاطرة معا، وهما من الطينة نفسها.

متى لا نكتب دالة؟ وأين كررنا الشفرة عن قصد

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

ثلاث علامات على أن الدالة لم تكن لازمة: أنها تُستدعى من موضع واحد وستبقى كذلك على الأرجح؛ وأن اسمها هو عمليا الشفرة التي بداخلها، كدالة اسمها add_one تضيف واحدا؛ وأن كتابتها تحتاج ستة معاملات. والأخيرة أهمها: فكثرة المعاملات تعني عادة أن الدالة تؤدي أكثر من عمل واحد. والاختبار البسيط أن تحاول قول ما تفعله في عبارة قصيرة؛ فإن اضطررت لقول «و»، فعندك دالتان.

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

هذا التكرار ليس خطأ بل قرار. فكل إضافة يجب أن تعمل وحدها، حتى لو كانت الأخرى مطفأة. ولو استعملت إحداهما دالة الأخرى، لأدى تعطيل إضافة إلى كسر صفحات الإضافة الثانية. والثمن الذي ندفعه مقابل هذا الاستقلال حقيقي ولا نخفيه: أي تغيير في طريقة عرض السعر يجب أن يُجرى في موضعين، وعلينا أن نتذكر أنهما موضعان. ونقول هذا لتعرف أنه لا يوجد دائما طريق ثالث؛ فأحيانا عليك أن تختار بين كلفتين ثم تكتب ذلك الاختيار في مكان ما كي لا يظن من يأتي بعدك أنك نسيت.

المسار السريع مع الذكاء الاصطناعي

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

  1. انسخ النسخ الثلاث كاملة، لا واحدة منها مع ملاحظة أن «البقية شبيهة». فكلمة «شبيهة» هي بالضبط موضع انكسار إعادة البناء.
  2. أرسل الوصفة أدناه واقرأ أولا قائمة الفروق وحدها قبل أن تنظر إلى الشفرة المقترحة. فإن وجدت في تلك القائمة شيئا لم تكن تعلمه، فتوقف هناك وقرر أولا أي سلوك هو الصحيح.
  3. خذ الاسم المقترح للدالة على محمل الجد لكن لا تقبله بلا تمحيص. فإن اضطرك شرح الاسم إلى قول «و»، فالدالة تؤدي عملين وينبغي أن تصير اثنتين.
  4. بعد الاستبدال، افتح المواضع الثلاثة من جديد وتأكد ألا يكون أحدها قد بقي على حاله. فترك واحد من الثلاثة أشيع أخطاء هذا العمل ولا ينتج عنه أي خطأ.

وصفة جاهزة للنسخ

هذه الكتل الثلاث من الشفرة مكررة في برنامجي وأريد استخراج دالة واحدة منها.

{الكتلة الأولى}

{الكتلة الثانية}

{الكتلة الثالثة}

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

وبعد ذلك:
٣. اقترح دالة واحدة تأخذ الفروق الحقيقية معاملات، باسم يقول ما تفعله.
٤. الاستدعاءات الثلاثة التي تحل محل الكتل الثلاث.
٥. قل كيف تتصرف الدالة المقترحة على مدخل فارغ أو غير موجود.

وإن احتاج أي من ذلك شفرة لم أعطك إياها، فقل أيها ولماذا؛ ولا تخمّن.

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

الذكاء الاصطناعي في هذا العمل

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

أدوات تساعد فعلا

  • Claude Code أفضل ما فيه لهذا الموضوع أنه يرى المستودع، فحين تسأل أين تُستدعى هذه الدالة يذهب ويعد بدل أن يخمن. وهو العمل نفسه الذي أنجزناه في هذا الدرس على تلك الدالة ذات الأسطر الأربعة. يُثبَّت مجانا لكنه لا يعمل دون اشتراك كلود أو حساب في كونسول أنثروبيك، وإيران ليست في قائمة الدول المدعومة.
  • Claude جيد لنقد عقد الدالة: اسأل هل تؤدي أكثر من عمل واحد، وهل اسمها دقيق. وإيران ليست في أي من قائمتي الدول المدعومة لدى أنثروبيك؛ قرأنا ذلك في صفحة أنثروبيك نفسها لا عبر اختبار شبكة.
  • Gemini وضع الدراسة فيه، Guided Learning، يطرح أسئلة بدل تسليم الأجوبة، وهو أنسب لترسيخ الفرق بين return وprint، وهو أصعب أجزاء هذا الدرس، من الحصول على جواب مباشر. وصفحة غوغل نفسها تقول إن تطبيق جيميناي على الويب يعمل في أكثر من ٢٣٠ دولة وإقليما، وإيران ليست في تلك القائمة.

اين ينقلب ضدك

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

المصادر: Anthropic: reduce hallucinations Anthropic: supported countries Claude Code: overview and install Google: where the Gemini web app is available

حدود هذه النصيحة

أمثلة هذه الصفحة بايثون وواحد منها PHP؛ والفكرة واحدة في كل اللغات لكن الكتابة تختلف. وتركنا عمدا ثلاثة مواضيع لأن كل واحد منها يحتاج درسا خاصا به: نطاق المتغيرات وما يُرى داخل الدالة، والقيم الافتراضية والمدخلات الاختيارية، والفرق بين الدالة والطريقة التي تُستدعى على كائن. وسؤال جوابه الصادق أنه لا قاعدة له: كم سطرا ينبغي أن تكون الدالة. فأي رقم يُعطى لك لم يأت من مكان؛ والاختبار الحقيقي هو أن تستطيع قول عملها في عبارة قصيرة، وهذا الاختبار لا علاقة له بعدد الأسطر.

من عملنا نحن

الرقم الوارد في هذا الدرس ليس تخمينا. يوم مراجعة هذه الصفحة عددنا استدعاءات rgb_dg_t عبر ملفات مكتبة المخططات الأربعة في هذا الموقع نفسه: ٢١ في diagram.php، و٨٦ في diagram-lib.php، و٦٢ في diagram-lib2.php، و١٢ في diagram-chart.php، أي ١٨١ استدعاء لدالة جسمها أربعة أسطر. وما يظهره هذا الرقم، وما يهم في هذا الدرس، ليس توفير الكتابة: فسياسة تعدد اللغات كلها في رسوم هذا الموقع مضغوطة في عبارة واحدة داخل تلك الدالة، هي ?? $value['fa']، وتلك العبارة وحدها تقرر ما يُعرض حين لا تكون ترجمة بطاقة قد كُتبت. فتغيير تلك السياسة يعني تغيير كلمة واحدة وراءها ١٨١ نقطة؛ والخطأ فيها يعني انكسار تلك النقاط الـ١٨١ معا.

اسئلة متابعة حقيقية

ما الفرق بين الدالة والطريقة؟

الطريقة دالة ملتصقة بكائن، وتعمل عادة على بيانات ذلك الكائن. وفي أمثلة هذه الصفحة تُعد strip() طريقة لأنها تُستدعى على السلسلة نفسها؛ أما display_name() فدالة مستقلة. وما دمت لم تنتقل إلى البرمجة الكائنية، فالفرق بينهما في شكل الكتابة فحسب.

لماذا تُرجع دالتي None؟

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

كيف أعرف أن مقطع شفرة ينبغي أن يصير دالة؟

علامتان تكفيان. الأولى أنه تكرر ثلاث مرات؛ فمرتان قد تكونان مصادفة وثلاث مرات ليست كذلك. والثانية أن تستطيع قول ما يفعله في عبارة قصيرة؛ فإن وجدت تلك العبارة فقد وجدت اسم الدالة أيضا، وإن لم تجدها فأنت لا تعرف بعد ما تفعله تلك الشفرة.