تمرير الدوال في Go: من المبتدئ إلى المحترف في 15 دقيقة

مدرب بايثون أونلاين للمبتدئين

تعلم Python بسهولة دون إرهاق نظري. حل مهام عملية مع التحقق التلقائي واكتب الكود مباشرة في المتصفح.

ابدأ الدورة

هل يمكن تمرير دالة كمعامل؟ نعم، وهذا يغير كل شيء

\n\n

هل تساءلت يومًا ما إذا كان بإمكانك تمرير دالة داخل دالة أخرى في Go، تمامًا مثل متغير عادي؟ إذا كنت قادمًا من عالم Python أو JavaScript، فهذا نمط مألوف لك. لكن في Go، على الرغم من صرامته، الدوال كمعاملات ليست مجرد "ممكنة"، بل هي واحدة من أقوى أدوات اللغة.

\n\n

أتذكر أول مرة تعرفت فيها على هذا المفهوم: كنت أكتب أداة لمعالجة السجلات (logs) وأدركت أنني أكرر نفس الحلقة البرمجية بمنطق مختلف داخلها. جاء الحل عندما أدركت أنه يمكنني ببساطة تمرير الدالة المطلوبة كوسيطة. أصبح الكود أنظف وأقصر وأوضح. في هذا المقال، سأريكم كيفية إتقان تمرير دالة إلى دالة في Go — من الأمثلة البسيطة إلى الأنماط العملية التي يمكنكم استخدامها غدًا في العمل.

\n\n

بالمناسبة، نحن في PythonLib نرى غالبًا أسئلة مثل "هل يمكن تمرير الدوال كمعاملات؟" على Stack Overflow بآلاف المشاهدات. دعنا ننهي هذا الموضوع مرة واحدة وإلى الأبد باستخدام Go.

\n\n

ما هي الدوال كمعاملات في Go؟

\n\n

في Go، الدوال هي كائنات من الدرجة الأولى. هذا يعني أنه يمكن:

\n
    \n
  • تخصيصها للمتغيرات
  • \n
  • تمريرها إلى دوال أخرى
  • \n
  • إرجاعها من دوال
  • \n
  • تخزينها في الهياكل (structs) والشرائح (slices)
  • \n
\n\n

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

\n\n

الصيغة: كيفية تعريف دالة تقبل دالة أخرى

\n\n

في Go، يجب تحديد نوع الدالة المُستقبلة بشكل صريح. يبدو الأمر كالتالي:

\n\n
// تعريف نوع الدالة\ntype Transformer func(string) string\n\n// دالة تستقبل دالة أخرى\nfunc processText(text string, transform Transformer) string {\n    return transform(text)\n}
\n\n

أو يمكن تعريف النوع مباشرة في التوقيع:

\n\n
func processText(text string, transform func(string) string) string {\n    return transform(text)\n}
\n\n

كلا الخيارين صحيحان. الأول أفضل إذا كنت تخطط لإعادة استخدام النوع. الثاني للحلول السريعة.

\n\n

مثال بسيط: تمرير دالة ترحيب

\n\n

لنكتب شيئًا عمليًا. تخيل أن لديك دالة تقوم بتنسيق اسم المستخدم، وتريد استخدام تحيات مختلفة:

\n\n
package main\n\nimport "fmt"\n\n// نوع لدالة الترحيب\ntype Greeter func(name string) string\n\n// دالة تستقبل التحية\nfunc greetUser(name string, greeter Greeter) {\n    message := greeter(name)\n    fmt.Println(message)\n}\n\nfunc main() {\n    // تحية عادية\n    normalGreet := func(name string) string {\n        return "مرحبًا، " + name + "!"\n    }\n\n    // تحية رسمية\n    formalGreet := func(name string) string {\n        return "عزيزي/عزيزتي " + name + "، مساء الخير."\n    }\n\n    greetUser("سارة", normalGreet)\n    greetUser("سارة", formalGreet)\n}
\n\n

النتيجة:

\n
مرحبًا، سارة!\nعزيزي/عزيزتي سارة، مساء الخير.
\n\n

أرأيت؟ لقد مررنا دوال مختلفة إلى نفس الدالة greetUser. هذا هو تمرير دالة كوسيطة عمليًا.

\n\n

دوال الاستدعاء العكسي (Callback): عندما تحتاج لمعالجة النتيجة

\n\n

أحد أكثر السيناريوهات شيوعًا لاستخدام الدوال كمعاملات هو الاستدعاء العكسي (callback). تقوم بتنفيذ عملية ما ثم تستدعي الدالة المُمررة عند اكتمال العملية.

\n\n
package main\n\nimport (\n    "fmt"\n    "time"\n)\n\n// دالة تحاكي تحميل بيانات طويل\nfunc fetchData(callback func(string)) {\n    fmt.Println("جارٍ تحميل البيانات...")\n    time.Sleep(2 * time.Second) // محاكاة تأخير\n    data := "بيانات مهمة من قاعدة البيانات"\n    callback(data)                

المدونات

توصيات الكتب