ال

الدوال في Go

1 تمرين

نبذة عن الدوال

تسمح لك الدالة بتجميع الكود في وحدة قابلة لإعادة الاستخدام. وتتكوّن من الكلمة المفتاحية func، واسم الدالة، وقائمة من صفر أو أكثر من المعاملات والأنواع بين الأقواس الهلالية، مفصولة بفواصل.

معاملات الدالة

يجب تحديد نوع كل معامل صراحةً؛ فلا يوجد استنتاج للنوع في المعاملات. ولا توجد قيم افتراضية للمعاملات، لذا فإن جميع معاملات الدالة مطلوبة.

import "fmt"

// No parameters
func PrintHello() {
    fmt.Println("Hello")
}

// One parameter
func PrintHelloName(name string) {
  fmt.Println("Hello " + name)
}

يمكن تعريف المعاملات من النوع نفسه معًا، ويتبعها تعريف نوع واحد.

import "fmt"

func PrintGreetingName(greeting, name string) {
  fmt.Println(greeting + " " + name)
}

المعاملات مقابل الوسائط

لنستعرض سريعًا مصطلحين كثيرًا ما يختلط أحدهما بالآخر: parameters وarguments. معاملات الدالة هي الأسماء المعرَّفة في توقيع الدالة، مثل greeting وname في الدالة PrintGreetingName أعلاه. أما وسائط الدالة فهي القيم الفعلية التي تُمرَّر إلى معاملات الدالة عند استدعاء الدالة. على سبيل المثال، في المثال أدناه، "Hello" و"Katrina" هما الوسيطان المُمرَّران إلى المعاملين greeting وname:

PrintGreetingName("Hello", "Katrina")

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

تتبع معاملات الدالة صفر أو أكثر من القيم المُرجَعة التي يجب أيضًا تحديد نوعها صراحةً. تُترك القيمة المُرجَعة المفردة دون أقواس، أما القيم المُرجَعة المتعددة فتُوضع بين الأقواس الهلالية. وتُرجع الدوال القيم إلى الكود المستدعي باستخدام الكلمة المفتاحية return. ويمكن أن توجد عبارات return متعددة في الدالة. وينتهي تنفيذ الدالة بمجرد وصوله إلى إحدى عبارات return هذه. وإذا أُريد إرجاع قيم متعددة من الدالة، فتُفصل بينها بفواصل. ويمكن العثور على مزيد من المعلومات حول الاستخدام الاصطلاحي لـالقيم المُرجَعة المتعددة في المفهوم المرتبط.

func Hello(name string) string {
  return "Hello " + name
}

func HelloAndGoodbye(name string) (string, string) {
  return "Hello " + name, "Goodbye " + name
}

استدعاء الدوال

يتم استدعاء الدالة بتحديد اسم الدالة وتمرير وسائط لكل معامل من معاملاتها بين الأقواس الهلالية.

import "fmt"

// No parameters, no return value
func PrintHello() {
    fmt.Println("Hello")
}
// Called like this:
PrintHello()

// One parameter, one return value
func Hello(name string) string {
  return "Hello " + name
}
// Called like this:
greeting := Hello("Dave")

// Multiple parameters, multiple return values
func SumAndMultiply(a, b int) (int, int) {
    return a+b, a*b
}
// Called like this:
aplusb, atimesb := SumAndMultiply(a, b)

القيم المُرجَعة المُسمّاة والإرجاع المجرّد

إلى جانب المعاملات، يمكن اختياريًا تسمية القيم المُرجَعة. إذا استُخدمت قيم مُرجَعة مُسمّاة، فإن عبارة return بدون وسائط ستُرجع تلك القيم. ويُعرف هذا بالإرجاع المجرّد.

func SumAndMultiplyThenMinus(a, b, c int) (sum, mult int) {
    sum, mult = a+b, a*b
    sum -= c
    mult -= c
    return
}

التمرير بالقيمة مقابل التمرير بالمرجع

من المهم أيضًا توضيح مفهوم التمرير بالقيمة والتمرير بالمرجع.

أولًا، لنوضّح معنى التمرير بالقيمة. في المثال أدناه، عندما نمرّر المتغير val إلى الدالة MultiplyByTwo، فإننا نمرّر نسخة من val. وبسبب ذلك، تصبح قيمة newVal هي القيمة المحدَّثة 4، بينما لا يزال المتغير الأصلي val مساويًا 2. وفي الخلفية، يُنشئ Go نسخة من القيمة الأصلية بحيث تُعدّل الدالة هذه النسخة فقط، أي v.

val := 2
func MultiplyByTwo(v int) int {
    v = v * 2
    return v
}
newVal := MultiplyByTwo(val)
// newval is 4, val is still 2 because only a copy of its value was passed into the function

بالمعنى الدقيق، تُمرَّر جميع الوسائط بالقيمة في Go، أي تُنشأ نسخة من القيمة أو البيانات المُقدَّمة إلى الدالة. لكن إذا كنت لا تريد إنشاء نسخة من البيانات المُمرَّرة إلى دالة وتريد تغيير البيانات داخل الدالة، فيجب عليك استخدام pointers كوسائط، وهو ما يُعرف بالتمرير بالمرجع.

المؤشرات

نستخدم pointer لتحقيق التمرير بالمرجع. وبتمرير وسائط من نوع pointer إلى دالة، يمكننا تعديل البيانات الأساسية المُمرَّرة إلى الدالة بدلًا من التعامل مع نسخة منها فقط.

يكفي في الوقت الحالي أن تعرف أن أنواع المؤشرات يمكن تمييزها بعلامة * التي تسبق النوع في توقيع الدالة.

func HandlePointers(x, y *int) {
    // Some logic to handle integer values referenced by pointers x and y
}

إذا كان مفهوم pointer محيّرًا، فلا تقلق. لدينا قسم مخصص لاحقًا في المنهج لمساعدتك على إتقان المؤشرات.

الاستثناءات

لاحظ أن slices وmaps استثناءان من القاعدة المذكورة أعلاه. عندما نمرّر slice أو map كوسائط إلى دالة، فإنها تُعامَل كأنواع مؤشرات حتى لو لم توجد علامة * صريحة في النوع. وهذا يعني أنه إذا مرّرنا slice أو map إلى دالة وعدّلنا بياناتهما الأساسية، فستنعكس التغييرات على slice أو map الأصليَّين.

أنواع أخرى من الدوال

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

تعديل عبر GitHub يفتح الرابط في نافذة أو علامة تبويب جديدة

تعلّم الدوال