اذهب إلى المحتوى

تُعَدّ الدالة funtion كتلةً من التعليمات التي تنفِّذ إجراءً ما، ويمكن بعد تعريفها إعادة استخدامها في أكثر من موضع. تجعل الدوال الشيفرة تركيبية modular، مما يسمح بتقسيم الشيفرة إلى أجزاء صغيرة سهلة الفهم واستخدامها مرارًا وتكرارًا. تضم لغة جو مكتبةً قياسيةً تدعى fmt، إذ تحتوي على عدد من الدوال المُضمّنة التي قد تكون شائعة بالنسبة لك مثل:

  • الدالة ()fmt.Println تُستخدَم للطباعة على شاشة الخرج القياسية المُستخدَمة.
  • الدالة ()fmt.Printf تُستخدَم للطباعة مع إمكانية تنسيق الخرج.

تتضمن أسماء الدوال الأقواس وقد تتضمن معامِلات أيضًا، وسنتعلم في هذا المقال كيفية تعريف الدوال وكيفية استخدامها في البرامج.

تعريف الدالة

لنبدأ بتحويل برنامج "Hello, World!‎" إلى دالة، لذا أنشئ ملفًا نصيًا جديدًا وافتحه في محرر النصوص المفضل عندك ثم استدع البرنامج ‎hello.go.

تُعرَّف الدالة باستخدام الكلمة المفتاحية func متبوعة باسم من اختيارك ثم قوسين يمكن أن يَحتويا المعامِلات التي ستأخذها الدالة ثم ينتهي التعريف بنقطتين، وسنعرّف هنا دالةً باسم ‎‎hello()‎‎:

func hello() {}

أعددنا في الشفرة أعلاه تعليمة التهيئة لإنشاء الدالة، وبعد ذلك سنضيف سطرًا ثانيًا مُزاحًا بأربع مسافات بيضاء ثم سنكتب التعليمات التي ستنفّذها الدالة، وفي هذه الحالة سنطبع العبارة !Hello, World في الطرفية:

func hello() {
    fmt.Println("Hello, World!")
}

أتممنا تعريف دالتنا ولكن إذا نَفَّذنا البرنامج الآن، فلن يحدث أيّ شيء لأننا لم نستدع الدالة، لذلك سنستدعي الدالة ‎hello()‎ ضمن الدالة ()main كما يلي:

package main

import "fmt"

func main() {
    hello()
}

func hello() {
    fmt.Println("Hello, World!")
}

لننفّذ البرنامج الآن كما يلي:

$ go run hello.go

يجب أن تحصل على الخرج التالي:

Hello, World!

الدالة ()main هي دالة خاصة تخبر المُصرّف أنّ هذا هو المكان الذي يجب أن يبدأ منه البرنامج، فأيّ برنامج تريده أن يكون قابلاً للتنفيذ (برنامج يمكن تشغيله من الطرفية)، فستحتاج إلى دالة ()main، كما يجب أن تظهر الدالة ()main مرةً واحدةً فقط وأن تكون في الحزمة main ولا تستقبل أو تعيد أيّ وسائط، وهذا يسمح بتنفيذ البرنامج في أيّ برنامج جو آخر حسب المثال التالي:

package main

import "fmt"

func main() {
    fmt.Println("this is the main section of the program")
}

بعض الدوال أكثر تعقيدًا بكثير من الدالة ‎hello()‎ التي عرّفناها أعلاه، إذ يمكننا على سبيل المثال استخدام حلقة for والتعليمات الشرطية وغيرها داخل كتلة الدالة، فالدالة المُعرّفة أدناه على سبيل المثال تستخدِم تعليمةً شرطيةً للتحقق مما إذا كانت المدخلات الممرّرة إلى المتغير ‎name‎ تحتوي على حرف صوتي vowel، ثم تستخدِم الحلقة ‎for‎ للتكرار على الحروف الموجودة في السلسلة النصية ‎name‎.

package main

import (
    "fmt"
    "strings"
)

func main() {
    names()
}

func names() {
    fmt.Println("Enter your name:")

    var name string
    fmt.Scanln(&name)
    // Check whether name has a vowel
    for _, v := range strings.ToLower(name) {
        if v == 'a' || v == 'e' || v == 'i' || v == 'o' || v == 'u' {
            fmt.Println("Your name contains a vowel.")
            return
        }
    }
    fmt.Println("Your name does not contain a vowel.")
}

تستخدِم الدالة ‎names() التي عرّفناها أعلاه تعليمةً شرطيةً وحلقة ‎for‎، وهذا توضيح لكيفية تنظيم الشيفرة البرمجية ضمن تعريف الدالة، كما يمكننا أيضًا جعل التعليمة الشرطية والحلقة ‎for‎ دالتين منفصلتين. يجعل تعريف الدوال داخل البرامج الشفرة البرمجية تركيبيةً modular وقابلةً لإعادة الاستخدام، وذلك سيتيح لنا استدعاء الدالة نفسها دون إعادة كتابة شيفرتها في كل مرة.

المعاملات

عرّفنا حتى الآن دالة ذات قوسين فارغين لا تأخذ أيّ وسائط arguments، وسنتعلم في هذا القسم كيفية تعريف المعامِلات parameters وتمرير البيانات إلى الدوال.

يُعَدّ المعامِل كيانًا مُسمًّى يوضَع في تعريف الدالة ويعرِّف وسيطًا يمكن أن تقبله الدالة عند استدعائها، ويجب عليك في لغة Go أن تحدد نوع البيانات data type لكل معامِل

لننشئ برنامجًا يُكرر كلمة عدة مرات، إذ سنحتاج إلى متغير من النوع string سنسميه word ومتغير من النوع int سنسميه reps يُحدد عدد التكرارات.

package main

import "fmt"

func main() {
    repeat("Sammy", 5)
}

func repeat(word string, reps int) {
    for i := 0; i < reps; i++ {
        fmt.Print(word)
    }
}

مرّرنا السلسلة Sammy إلى المتغير word والقيمة 5 إلى المعامِل reps وذلك وفقًا لترتيب المعامِلات في ترويسة الدالة repeat، إذ تتضمّن الدالة حلقة for تطبع قيمة المعامِل word عدة مرات يُحدّدها المعامِل reps، وسيكون الخرج كما يلي:

SammySammySammySammySammy

إذا كانت لديك مجموعة من المعامِلات وجميعها تمتلك القيمة نفسها، فيمكنك تجاهل تحديد النوع من أجل كل متغير كما سنرى، لذا دعنا ننشئ برنامجًا صغيرًا يأخذ ثلاثة معامِلات ‎x‎ و ‎y و ‎z‎ من النوع int، إذ سننشئ دالةً تجمع تلك المعامِلات وفق عدة مجموعات ثم تطبع الدالة حاصل جمعها.

package main

import "fmt"

func main() {
    addNumbers(1, 2, 3)
}

func addNumbers(x, y, z int) {
    a := x + y
    b := x + z
    c := y + z
    fmt.Println(a, b, c)
}

عند تعريف الدالة addNumbers لم نكن بحاجة إلى تحديد نوع كل متغير على حدة، وإنما وضعنا نوع بيانات كل المتغيرات مرةً واحدةً فقط.

مرّرنا العدد ‎1‎ إلى المعامل ‎x‎ والعدد ‎2‎ إلى المعامل ‎y‎ والعدد ‎3‎ إلى المعامل ‎z‎، إذ تتوافق هذه القيم مع المعامِلات المقابلة لها في ترتيب الظهور، ويُجري البرنامج العمليات الحسابية على المعامِلات على النحو التالي:

a = 1 + 2
b = 1 + 3
c = 2 + 3

تطبع الدالة أيضًا ‎a‎ و ‎b‎ و c‎، وبناءً على العمليات الحسابية أعلاه، فستساوي قيمة ‎a العدد ‎3‎ و ‎b العدد ‎4‎ و ‎c‎ العدد ‎5‎، ولننفّذ البرنامج سنكتب ما يلي:

$ go run add_numbers.go

سيكون الخرج كما يلي:

3 4 5

عندما نمرر 1 و 2 و 3 على أساس معامِلات إلى الدالة ()addNumbers، فإننا نتلقى الناتج المتوقع.

تُعَدّ المعامِلات وسائط تُعرَّف عادة على أساس متغيرات ضمن تعريف الدالة، كما يمكن تعيين قيم إليها عند تنفيذ التابع بتمرير وسائط إلى الدالة.

إعادة قيمة

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

استخدمنا حتى الآن الدالة ()fmt.Println‎ بدلاً من التعليمة ‎return‎ في دوالنا لطباعة شيء بدلًا من إعادته، فلننشئ برنامجًا يعيد متغيرًا بدلًا من طباعته مباشرةً، لذا سننشئ برنامجًا في ملف نصي جديد يسمى double.go‎ يحسب ناتج مُضاعفة المعامِل ‎x‎ ويُسند الناتج إلى المتغير ‎y ثم يعيده، كما سنطبع المتغير ‎result‎ والذي يساوي ناتج تنفيذ الدالة double(3).

package main

import "fmt"

func main() {
    result := double(3)
    fmt.Println(result)
}

func double(x int) int {
    y := x * 2
    return y
}

لننفّذ البرنامج كما يلي:

$ go run double.go

سيكون الخرج كما يلي:

6

خرج البرنامج هو العدد الصحيح ‎6 الذي أعادته الدالة وهو ما نتوقعه إذا طلبنا من جو حساب ناتج ضرب 2 بالعدد 3.

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

package main

import "fmt"

func main() {
    result := double(3)
    fmt.Println(result)
}

func double(x int) int {
    y := x * 2
    // return y
}

لنحاول تنفيذ البرنامج:

$ go run double.go

سيُشير الخرج إلى خطأ لأنه لم يجد تعليمة الإعادة return:

./double.go:13:1: missing return at end of function

لا يمكن تصريف البرنامج بدون تعليمة الإعادة هذه، فعندما تصل الدالة إلى تعليمة return فإنها ستُنهي تنفيذ الدالة حتى إذا كان هناك تعليمات تالية ضمنها:

package main

import "fmt"

func main() {
    loopFive()
}

func loopFive() {
    for i := 0; i < 25; i++ {
        fmt.Print(i)
        if i == 5 {
            //i == 5 أوقف الدالة عندما 
          return
        }
    }
    fmt.Println("This line will not execute.") // هذا السطر لن ينفَّذ
}

نستخدِم هنا حلقة for تُؤدي عملية تكرار 25 مرة وبداخلها تعليمة if تتحقق مما إذا كانت قيمة i تساوي العدد 5، فإذا كانت كذلك، فسيكون لدينا تعليمة return تُنهي تنفيذ الحلقة وتُنهي تنفيذ الدالة أيضًا، وهذا يعني أنّ باقي التكرارات لن تُنفّذ والسطر الأخير من الدالة This line will not execute لن يُنفّذ.

يؤدي استخدام التعليمة ‎return‎ داخل الحلقة ‎for‎ إلى إنهاء الدالة، وبالتالي لن يُنفَّذ السطر الموجود خارج الحلقة، فإذا استخدمنا بدلًا من ذلك التعليمة break، فسيُنفّذ السطر fmt.Println() الأخير من المثال السابق.

نعيد التذكير أنَّ التعليمة ‎return‎ تنهي عمل الدالة وقد تعيد قيمةً إذا أعقبها تعبير وكان ذلك محدد في تعريف الدالة.

إعادة عدة قيم

يمكن للدوال أن تُعيد أكثر من قيمة، إذ سنجعل برنامج repeat.go يُعيد قيمتين بحيث تكون القيمة الأولى القيمة المُكررة والثانية خطأً في حال كانت قيمة المعامِل reps أصغر أو تساوي 0.

package main

import "fmt"

func main() {
    val, err := repeat("Sammy", -1)
    if err != nil {
        fmt.Println(err)
        return
    }
    fmt.Println(val)
}

func repeat(word string, reps int) (string, error) {
    if reps <= 0 {
        return "", fmt.Errorf("invalid value of %d provided for reps. value must be greater than 0.", reps)
    }
    var value string
    for i := 0; i < reps; i++ {
        value = value + word
    }
    return value, nil
}

تتحقق الدالة repeat بدايةً من أن الوسيط reps صالح، فأيّ قيمة أقل أو تساوي الصفر ستؤدي إلى خطأ. بما أننا مررنا القيمة 1- إلى reps فهذا سيؤدي إلى تحقق شرط حدوث خطأ، وبالتالي ستُعاد قيمتين الأولى هي سلسلة فارغة "" والثانية هي رسالة خطأ، وبالطبع يجب علينا إعادة قيمتين دومًا تبعًا للتعريف الذي وضعناه في ترويسة الدالة، إذ حددنا أنّ هناك قيمتان مُعادتان من الدالة؛ فالأولى هي سلسلة والثانية هي خطأ، ولهذا السبب أعدنا سلسلةً فارغةً في حال ظهور خطأ.

نستدعي الدالة repeat بداخل الدالة main ونُسند القيم المُعادة إلى متغيرين هما value و err، وبما أنّ هناك احتمال لحدوث خطأ، فسنتحقق في الأسطر التالية من وجوده، وفي حال وجوده سنطبع رسالةً تشير إلى الخطأ وسنستخدِم التعليمة return للخروج من الدالة main والبرنامج، وبتنفيذ البرنامج سنحصل على ما يلي:

invalid value of -1 provided for reps. value must be greater than 0.

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

الدوال المرنة Variadic

الدالة المرنة Variadic هي دالة لا تقبل أي قيمة أو تقبل قيمةً واحدةً أو قيمتين أو أكثر على أساس وسيط واحد، والدوال المرنة ليست شائعة الاستخدام لكنها تجعل الشيفرة أنظف وأكثر قابليةً للقراءة، وأحد الأمثلة على هذا النوع من الدوال هو الدالة Println من الحزمة fmt:

func Println(a ...interface{}) (n int, err error)

نسمي الدالة التي تتضمن مُعامِلًا مُلحقًا بثلاثة نقاط ... كما في الشيفرة أعلاه بالدالة المرنة، إذ تشير النقاط الثلاث إلى أنّ هذا المعامِل يمكن أن يكون صفر قيمة أو قيمةً واحدةً أو قيمتين أو عدة قيم، وبالتالي تُعَدّ الدالة fmt.Println دالةً مرنةً لأنها تتضمن مُعامِلًا مرنًا يسمى a.

سنستخدِم في المثال التالي الدالة المرنة السابقة وسنبيّن كيف أنه من الممكن أن نمرر لها عددًا غير مُحدد من الوسائط:

package main

import "fmt"

func main() {
    fmt.Println()
    fmt.Println("one")
    fmt.Println("one", "two")
    fmt.Println("one", "two", "three")
}

كما تُلاحظ فإننا نستدعيها أول مرة بدون تمرير أيّ وسيط ثم نستدعيها مع تمرير وسيط واحد هو السلسلة one ثم نستدعيها مع وسيطين ثم نستدعيها مع ثلاثة وسائط، ولننفذ البرنامج الآن:

$ go run print.go

سيكون الخرج كما يلي:

one
one two
one two three

لاحظ أنّ السطر الأول من الخرج فارغ لأننا استدعينا تعليمة الطباعة بدون تمرير أيّ متغير ثم السطر الثاني يحتوي على one لأن دالة الطباعة التي استدعيناها في المرة الثانية تتضمنها، ثم one two لأننا مررناهما إلى دالة الطباعة في المرة الثالثة، وأخيرًا السطر الأخير one two three لأننا مررنا هذه الكلمات إلى دالة الطباعة في الشيفرة السابقة.

سنتحدّث الآن عن كيفية تعريف هذه الدوال بعد أن اطلعنا على كيفية استخدام الدوال المرنة.

تعريف الدوال المرنة

كما ذكرنا سابقًا فإن الدوال المرنة تُعرّف من خلال وضع ثلاث نقاط ... بعد اسم أحد المعامِلات فيها، وفي المثال التالي سنعرّف دالةً نمرر لها أسماءً لكي تُحَييهم:

package main

import "fmt"

func main() {
    sayHello()
    sayHello("Sammy")
    sayHello("Sammy", "Jessica", "Drew", "Jamie")
}

func sayHello(names ...string) {
    for _, n := range names {
        fmt.Printf("Hello %s\n", n)
    }
}

تتضمّن الدالة sayHello معامِلًا اسمه names من النوع string، وكما نلاحظ فإنه توجد ثلاث نقاط بعد اسمه، وبالتالي هو معامل مرن أي يقبل عدد غير مُحدد من الوسائط، وبالتالي تُعَدّ الدالة sayHello هي دالةً مرنةً.

تُعامِل هذه الدالة المعامِل names على أنه شريحة من الأسماء، أي أنها تعامله على أساس شريحة من السلاسل النصية string[]، وبالتالي يمكننا التكرار عليه بحلقة for من خلال استخدام العامِل range.

ستحصل عند تنفيذ هذه الشيفرة على ما يلي:

Hello Sammy
Hello Sammy
Hello Jessica
Hello Drew
Hello Jamie

لاحظ أنه في المرة الأولى التي استدعينا فيها الدالة sayHello لم يُطبع أيّ شيء، وذلك لأننا لم نمرر لها أيّ قيمة، أي عمليًّا هي شريحة فارغة، وبالتالي لا تتضمن أي قيمة ليُكرر عليها، والآن سنُعدّل الشيفرة السابقة بحيث تطبع عبارةً مُحددةً عندما لا تُمرّر أيّ قيمة:

package main

import "fmt"

func main() {
    sayHello()
    sayHello("Sammy")
    sayHello("Sammy", "Jessica", "Drew", "Jamie")
}

func sayHello(names ...string) {
    if len(names) == 0 {
        fmt.Println("nobody to greet")
        return
    }
    for _, n := range names {
        fmt.Printf("Hello %s\n", n)
    }
}

أضفنا تعليمة if تتحقق مما إذا كانت الشريحة فارغةً وهذا يُكافئ أن يكون طولها 0، وفي هذه الحالة سنطبع nobody to greet:

nobody to greet
Hello Sammy
Hello Sammy
Hello Jessica
Hello Drew
Hello Jamie

يجعل استخدام الدوال والمتغيرات المرنة شيفرتك أكثر قابليةً للقراءة، وسنُنشئ الآن دالةً مرنةً تربط الكلمات اعتمادًا على رمز مُحدّد، لكن سنكتب أولًا دالةً ليست مرنة لنُبيّن الفرق:

package main

import "fmt"

func main() {
    var line string

    line = join(",", []string{"Sammy", "Jessica", "Drew", "Jamie"})
    fmt.Println(line)

    line = join(",", []string{"Sammy", "Jessica"})
    fmt.Println(line)

    line = join(",", []string{"Sammy"})
    fmt.Println(line)
}

func join(del string, values []string) string {
    var line string
    for i, v := range values {
        line = line + v
        if i != len(values)-1 {
            line = line + del
        }
    }
    return line
}

مرّرنا هنا الفاصلة , إلى الدالة join لكي يُنجز الربط اعتمادًا عليها، ثم مرّرنا شريحةً من الكلمات لكي تُربط، فكان الخرج كما يلي:

Sammy,Jessica,Drew,Jamie
Sammy,Jessica
Sammy

لاحظ هنا أنّ تعريف شريحة في كل مرة نستدعي فيها هذه الدالة قد يكون مُملًا وأكثر صعوبةً في القراءة، لذا سنُعرّف الآن الشيفرة نفسها لكن مع دالة مرنة:

package main

import "fmt"

func main() {
    var line string

    line = join(",", "Sammy", "Jessica", "Drew", "Jamie")
    fmt.Println(line)

    line = join(",", "Sammy", "Jessica")
    fmt.Println(line)

    line = join(",", "Sammy")
    fmt.Println(line)
}

func join(del string, values ...string) string {
    var line string
    for i, v := range values {
        line = line + v
        if i != len(values)-1 {
            line = line + del
        }
    }
    return line
}

سنحصل عند تشغيل البرنامج على خرج المثال السابق نفسه:

Sammy,Jessica,Drew,Jamie
Sammy,Jessica
Sammy

يمكن بسهولة ملاحظة أنّ استخدام مفهوم الدالة المرنة قد جعل من الدالة join أكثر قابليةً للقراءة.

ترتيب الوسائط المرنة

يمكنك تعريف معامِل مرن واحدة فقط في الدالة ويجب أن يكون هو المعامِل الأخير في ترويسة الدالة، فإذا عرّفتَ أكثر من معامِل مرن أو وضعته قبل المعامِلات العادية، فسيظهر لك خطأ وقت التصريف compilation error.

package main

import "fmt"

func main() {
    var line string

    line = join(",", "Sammy", "Jessica", "Drew", "Jamie")
    fmt.Println(line)

    line = join(",", "Sammy", "Jessica")
    fmt.Println(line)

    line = join(",", "Sammy")
    fmt.Println(line)
}

func join(values ...string, del string) string {
    var line string
    for i, v := range values {
        line = line + v
        if i != len(values)-1 {
            line = line + del
        }
    }
    return line
}

وضعنا هنا المعامِل المرن values أولًا ثم وضعنا المعامِل العادي del، وبالتالي خالفنا الشرط المذكور سلفًا، وبالتالي سنحصل على الخطأ التالي:

./join_error.go:18:11: syntax error: cannot use ... with non-final parameter values

عند تعريف دالة مرنة لا يمكن أن يكون المعامِل الأخير إلا معاملًا مرنًا.

تفكيك الوسائط

رأينا كيف أنّ المعامل المرن سيسمح لنا بتمرير 0 قيمة أو قيمة واحدة أو أكثر من قيمة إلى الدالة، لكن هناك حالات سيكون لدينا فيها شريحة من القيم نريد تمريرها إلى الدالة المرنة، لذا دعونا نرى الدالة join التي بنيناها مؤخرًا لرؤية ما يحدث:

package main

import "fmt"

func main() {
    var line string

    names := []string{"Sammy", "Jessica", "Drew", "Jamie"}

    line = join(",", names)
    fmt.Println(line)
}

func join(del string, values ...string) string {
    var line string
    for i, v := range values {
        line = line + v
        if i != len(values)-1 {
            line = line + del
        }
    }
    return line
}

سنحصل عند تشغيل البرنامج على خطأ وقت التصريف:

./join-error.go:10:14: cannot use names (type []string) as type string in argument to join

على الرغم من أن الدالة المرنة ستحوّل المعامِل values ...string إلى شريحة من السلاسل النصية string[]، إلا أنّ هذا لا يعني أنه بإمكاننا تمرير شريحة من السلاسل على أساس وسيط، فالمُصرّف يتوقع وسائط منفصلةً من نوع سلسلة نصية.

يمكننا لحل هذه المشكلة تفكيك قيم الشريحة -أي فصلها عمليًّا- من خلال وضع ثلاثة نقط بعد اسم الشريحة عندما نُمررها لدالة مرنة.

package main

import "fmt"

func main() {
    var line string

    names := []string{"Sammy", "Jessica", "Drew", "Jamie"}

    line = join(",", names...)
    fmt.Println(line)
}

func join(del string, values ...string) string {
    var line string
    for i, v := range values {
        line = line + v
        if i != len(values)-1 {
            line = line + del
        }
    }
    return line
}

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

Sammy,Jessica,Drew,Jamie

لاحظ أنه مازال بإمكاننا عدم تمرير أيّ شيء أو تمرير أيّ عدد نريده من القيم، ويبيّن المثال التالي كل الحالات:

package main

import "fmt"

func main() {
    var line string

    line = join(",", []string{"Sammy", "Jessica", "Drew", "Jamie"}...)
    fmt.Println(line)

    line = join(",", "Sammy", "Jessica", "Drew", "Jamie")
    fmt.Println(line)

    line = join(",", "Sammy", "Jessica")
    fmt.Println(line)

    line = join(",", "Sammy")
    fmt.Println(line)

}

func join(del string, values ...string) string {
    var line string
    for i, v := range values {
        line = line + v
        if i != len(values)-1 {
            line = line + del
        }
    }
    return line
}

سيكون الخرج كما يلي:

Sammy,Jessica,Drew,Jamie
Sammy,Jessica,Drew,Jamie
Sammy,Jessica
Sammy

أصبحنا الآن نعرف كيف نمرِّر 0 قيمة أو أكثر إلى دالة مرنة، كما أصبحنا نعرف كيف يمكننا تمرير شريحة إلى دالة مرنة.

غالبًا ما تكون الدوال المرنة مفيدةً في الحالات التالية:

  • عندما تكون بحاجة إلى تعريف شريحة مؤقتًا من أجل تمريرها إلى دالة.
  • لجعل الشيفرة أكثر قابليةً للقراءة.
  • عندما يكون عدد الوسائط غير معروف أو متغير مثل دالة الطباعة.

الخاتمة

تُعَدّ الدوال كتلًا من التعليمات البرمجية التي تُنفِّذ إجراءات معيّنة داخل البرنامج، كما تساعد على جعل الشفرة تركيبيةً وقابلةً لإعادة الاستخدام، بالإضافة إلى أنها تنظمها وتسهل من قراءتها، كما تجعل الدوال المرنة الشيفرة أكثر قابليةً للقراءة، إلا أنها ليست شائعة الاستخدام، ولتتعلم كيف تجعل برنامجك تركيبيًّا أكثر، فيمكنك قراءة مقال كيفية تعريف الحزم في لغة جو Go.

ترجمة -وبتصرف- للمقالَين How To Define and Call Functions in Go وللمقال How To Use Variadic Functions in Go لصاحبه Gopher Guides.

اقرأ أيضًا


تفاعل الأعضاء

أفضل التعليقات

لا توجد أية تعليقات بعد



انضم إلى النقاش

يمكنك أن تنشر الآن وتسجل لاحقًا. إذا كان لديك حساب، فسجل الدخول الآن لتنشر باسم حسابك.

زائر
أضف تعليق

×   لقد أضفت محتوى بخط أو تنسيق مختلف.   Restore formatting

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   جرى استعادة المحتوى السابق..   امسح المحرر

×   You cannot paste images directly. Upload or insert images from URL.


×
×
  • أضف...