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

التعامل مع تعليمة التبديل Switch في لغة جو Go


هدى جبور

تمنح التعليمات الشرطية المبرمجين القدرة على التحكم بسير عمل برامجهم وتفريعها وتوجيهها لاتخاذ بعض الإجراءات إذا كان الشرط المحدَّد صحيحًا وإجراء آخر إذا كان الشرط خاطئًا.

تتوفَّر عدة تعليمات للتفريع branching statements بلغة جو، فقد تناولنا تعليمة if و else و else if والتعليمات الشرطية المتداخلة في المقال السابق كيفية كتابة التعليمات الشرطية if في لغة جو Go، وسننتقل الآن إلى تعليمة تفريع أخرى تدعى switch والتي يُعدّ استخدامها أقل شيوعًا من التعليمات السابقة، ولكن مع ذلك فهي مفيدة أحيانًا للتعبير عن نوع معيّن من التفريع المُتعدِّد multiway branches عندما نريد مثلًا مقارنة قيمة متغير (أو دخل من المستخدِم) بعدة قيم محتملة واتخاذ إجراء محدد بناءً على ذلك.

إن أفضل مثال عملي على ذلك هو برنامج الآلة الحاسبة الذي يُجد ناتج عملية حسابية لعددين، بحيث يكون الدخل هو عملية حسابية -وهذا يكافئ عدة قيم محتملة + أو - أو * أو /- وعددين x و y.، وسيعتمد هنا الإجراء المُتخذ على نوع العملية التي يُدخلها المستخدِم، فإذا أدخل + فسيكون الإجراء x+y، وإذا أدخل - فسيكون x-y، وهكذا.

يمكن تحقيق التفريع المتعدد باستخدام التعليمات الشرطية التي تعرفنا عليها في المقال السابق، لكن يمكن تحقيقه أيضًا باستخدام تعليمة switch كما سنرى في هذا المقال.

بنية التعليمة Switch

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

package main

import "fmt"

func main() {
    flavors := []string{"chocolate", "vanilla", "strawberry", "banana"}

    for _, flav := range flavors {
        if flav == "strawberry" {
            fmt.Println(flav, "is my favorite!")
            continue
        }

        if flav == "vanilla" {
            fmt.Println(flav, "is great!")
            continue
        }

        if flav == "chocolate" {
            fmt.Println(flav, "is great!")
            continue
        }

        fmt.Println("I've never tried", flav, "before")
    }
}

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

chocolate is great!
vanilla is great!
strawberry is my favorite!
I've never tried banana before

نُعرّف بداخل الدالة main شريحةً slice من نكهات المثلجات ثم نستخدِم حلقة for للتكرار على عناصرها ثم نستخدِم ثلاث تعليمات if لطباعة رسائل مختلفة تشير إلى تفضيلات نكهات المثلجات المختلفة، إذ يجب على كل تعليمة if أن تتضمن تعليمة Continue لإيقاف التكرار الحالي في الحلقة لكي لا تُطبَع الرسالة الافتراضية في نهاية كتلة الحلقة.

لاحظ أنه كلما أضفنا نكهةً جديدةً إلى الشريحة السابقة، فسيتعين علينا إضافة التفضيل المقابل لها، وبالتالي الاستمرار في كتابة تعليمات if في كل مرة نضيف فيها عنصرًا جديدًا إلى الشريحة، كما يجب أيضًا تكرار تعليمات if مع الرسائل المكررة كما في حالة "Vanilla" و "chocolate".

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

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

package main

import "fmt"

func main() {
    flavors := []string{"chocolate", "vanilla", "strawberry", "banana"}

    for _, flav := range flavors {
        switch flav {
        case "strawberry":
            fmt.Println(flav, "is my favorite!")
        case "vanilla", "chocolate":
            fmt.Println(flav, "is great!")
        default:
            fmt.Println("I've never tried", flav, "before")
        }
    }
}

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

chocolate is great!
vanilla is great!
strawberry is my favorite!
I've never tried banana before

نُعرّف بداخل الدالة main كما في المرة السابقة شريحةً slice من نكهات المثلجات ثم نستخدِم حلقة for للتكرار على عناصرها، لكن سنستخدِم في هذه المرة تعليمة التبديل وسنلغي استخدام التعليمة if، إذ وضعنا المتغير flav بعد الكلمة switch وستُختبر قيمة هذا المتغير وستُنفّذ إجراءات محددة بناءً عليها:

  • الحالة الأولى سيطبع رسالةً محددةً عندما تكون قيمة هذا المتغير تساوي strawberry.
  • الحالة الثانية سيطبع رسالةً محددةً عندما تكون قيمة هذا المتغير تساوي vanilla أو chocolate، ولاحظ أنه في المثال السابق اضطررنا لكتابة تعليمتَي if.
  • الحالة الأخيرة هي الحالة الافتراضية وتسمى default حيث أنه إذا لم تتحقق أيّ من الحالات السابقة، فستُنفّذ هذه التعليمة ويطبع الرسالة المحددة، لكن إذا تحققت حالة ما من الحالات السابقة، فلن تُنفّذ هذه التعليمة إطلاقًا.

ملاحظة: لم نحتاج إلى استخدام continue لأن تعليمة التبديل لا تُنفّذ إلا حالةً واحدةً فقط تلقائيًا.

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

تعليمات التبديل العامة

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

package main

import (
    "fmt"
    "math/rand"
    "time"
)

func main() {
    rand.Seed(time.Now().UnixNano())
    target := rand.Intn(100)

    for {
        var guess int
        fmt.Print("Enter a guess: ")
        _, err := fmt.Scanf("%d", &guess)
        if err != nil {
            fmt.Println("Invalid guess: err:", err)
            continue
        }

        if guess > target {
            fmt.Println("Too high!")
            continue
        }

        if guess < target {
            fmt.Println("Too low!")
            continue
        }

        fmt.Println("You win!")
        break
    }
}

سيختلف الخرج بناءً على العدد العشوائي المحدد ومدى جودة لعبك، وفيما يلي ناتج دورة لعب واحدة:

Enter a guess: 10
Too low!
Enter a guess: 15
Too low!
Enter a guess: 18
Too high!
Enter a guess: 17
You win!

تحتاج لعبة التخمين إلى عدد عشوائي لمقارنة التخمينات به، لذا سنستخدِم الدالة rand.Intn من الحزمة math/rand لتوليده، كما سنستخدِم rand.Seed مولّد الأعداد العشوائية بناءً على الوقت الحالي لضمان حصولنا على قيم مختلفة للمتغير target في كل مرة نلعب فيها، ولاحظ أننا مررنا القيمة 100 إلى الدالة rand.Intn وبالتالي ستكون الأعداد المولَّدة ضمن المجال من 0 إلى 100، وأخيرًا سنستخدِم حلقة for لبدء جمع التخمينات من اللاعب.

تعطينا الدالة fmt.Scanf إمكانية قراءة مدخلات المستخدِم وتخزينها في متغير من اختيارنا، وهنا نريد تخزين القيمة المُدخَلة في المتغير guess، كما نريد أن تكون قيمة هذا المتغير من النوع الصحيح int، لذا سنمرر لهذه الدالة العنصر النائب d% على أساس وسيط أول والذي يشير إلى وجود قيمة من النوع الصحيح، ويكون الوسيط الثاني هو عنوان المتغير guess الذي نريد حفظ القيمة المدخَلة به.

نختبر بعد ذلك فيما إذا كان هناك دخل خاطئ من المستخدِم مثل إدخال نص بدلًا من عدد صحيح ثم نكتب تعليمتَي if بحيث تختبر الأولى فيما إذا كان التخمين الذي أدخله المستخدِم أكبر من قيمة العدد المولَّد ليطبع Too high!‎ وتختبر الثانية فيما إذا كان العدد أصغر ليطبع Too low!‎، وسيكون التخمين في الحالتين خاطئًا، أي القيمة المولّدة لا تتطابق مع التخمين وبالتالي يخسر، طبعًا لاننسى كتابة تعليمة continue بعد كل تعليمة if كما ذكرنا سابقًا، وأخيرًا إذا لم تتحقق أي من الشروط السابقة فإنه يطبع You win!‎ دلالةً إلى أنّ تخمينه صحيح وتتوقف الحلقة لوجود تعليمة break؛ أما إذا لم يكن تخمينه صحيح، فستتكرر الحلقة مرةً أخرى.

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

package main

import (
    "fmt"
    "math/rand"
)

func main() {
    target := rand.Intn(100)

    for {
        var guess int
        fmt.Print("Enter a guess: ")
        _, err := fmt.Scanf("%d", &guess)
        if err != nil {
            fmt.Println("Invalid guess: err:", err)
            continue
        }

        switch {
        case guess > target:
            fmt.Println("Too high!")
        case guess < target:
            fmt.Println("Too low!")
        default:
            fmt.Println("You win!")
            return
        }
    }
}

سيكون الخرج مُشابهًا لما يلي:

Enter a guess: 25
Too low!
Enter a guess: 28
Too high!
Enter a guess: 27
You win!

استبدلنا هنا كل تعليمات if بتعليمة التبديل switch، ولاحظ أننا لم نذكر بعد تعليمة التبديل أيّ متغير كما فعلنا في أول مثال من المقال لأن هدفنا هو تجميع الشروط معًا كما ذكرنا، وتتضمن تعليمة التبديل في هذا المثال ثلاث حالات:

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

ملاحظة: لا حاجة إلى استخدام continue لأن تعليمة التبديل لاتُنفّذ إلا حالة واحدة فقط تلقائيًا.

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

التعليمة fallthrough: نفذ الخطوة التالية أيضا

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

package main

import "fmt"

func main() {
    flavors := []string{"chocolate", "vanilla", "strawberry", "banana"}

    for _, flav := range flavors {
        switch flav {
        case "strawberry":
            fmt.Println(flav, "is my favorite!")
            fallthrough
        case "vanilla", "chocolate":
            fmt.Println(flav, "is great!")
        default:
            fmt.Println("I've never tried", flav, "before")
        }
    }
}

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

chocolate is great!
vanilla is great!
strawberry is my favorite!
strawberry is great!
I've never tried banana before

نُعرّف داخل الدالة main شريحةً slice من نكهات المثلجات ثم نستخدِم حلقة for للتكرار على عناصرها ثم نستخدِم تعليمة التبديل ونضع المتغير flav بعد الكلمة switch بحيث تُختبر قيمته وتُنفَّذ إجراءات محددة بناءً عليها:

  • الحالة الأولى سيطبع رسالةً محددةً عندما تكون قيمة هذا المتغير تساوي vanilla أو chocolate.
  • الحالة الثانية سيطبع strawberry is my favorite!‎ عندما تكون قيمة هذا المتغير تساوي strawberry ثم سيُنفّذ التعليمة fallthrough التي تؤدي إلى تنفيذ شيفرة الحالة التالية لها، وبالتالي سيطبع strawberry is great!‎ أيضًا.
  • الحالة الأخيرة هي الحالة الافتراضية default.

لا يستخدِم المطورون تعليمة fallthrough في لغة جو كثيرًا لأنه من الممكن الاستغناء عنها بتعريف دالة تؤدي الغرض واستدعائها ببساطة، وعمومًا لا يُنصَح باستخدامها.

الخاتمة

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

ترجمة -وبتصرف- للمقال How To Construct For Loops 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.


×
×
  • أضف...