الثلاثاء، 15 مايو 2012

تعلم لغة البرمجة Go بالعربي ( الدرس الثامن )

بسم الله الرحمن الرحيم

في هذا الدرس سوف نتعرف على المصفوفات و الشرائح (slices) و هو نوع من البيانات أكثر مرونة من المصفوفات و أخيرا الخرائط (map) و هي بديلة للهاش (Hash) و المعاجم (Dictionary) .

1. المصفوفات ( Arrays ) :
    المصفوفات هي عبارة عن مجموعة من المتغيرات المتشابه في النوع و المتجاورة في المواقع في الذاكرة , و يتعدد أستخدمها في البرمجة مثل التصنيف ( Sorting ) و السلاسل النصية ( Strings) .
    •  التصريح عن مصفوفة :
    لتصريح عن مصفوفة جديدة في لغة Go يوجد أكثر من طريقة , و هذا مثال على الطرق المستخدمة :

    var list [4]int

    list1 := [4]int{ 12, 43 ,35 ,2}


    في السطر الأول قمنا بالتصريح عن المصفوفة بأسم "list" بأستخدام var ثم أنهينا التصريح بحجم المصفوفة متبوعا بنوع المصفوفة [4] بعد ذلك النوع int .

    أما في السطر الثاني فأستخدمنا الطريقة المختصرة بأستخدام " =: " ثم بعد ذلك حجم المصفوفة ونوعها , الفرق في هذا التصريح بأنه من اللازم تحديد عناصر المصفوفة :


    list1 := [4]int{ 12, 43 ,35 ,2}

    في حالة عدم رغبتك في التصريح عن المصفوفة بهذه الطريقة بدون تحديد القيم يجب كتابة التصريح بهذه الطريقة


    list1 := [4]int{}



    حيث يجب كتابة القوسين بدون أي قيم , كما تستطيع في التصريح عن المصفوفات الأعتماد على المترجم لتحديد عدد العناصر كالتالي :

    list2 :=[...]int{ 12, 43 ,35 ,2}

    النقاط الثلاث الموجودة في المكان المخصص لحجم المصفوفة تخبر المترجم بأن يقوم بحساب الحجم , و سوف أتطرق في نهاية هذه النقطة لكيفية التصريح عن المصفوفات متعددة الأبعاد فبكل بساطة قم بكتابة التصريح كالتالي:

    a := [2][2]int{ {1,2}, {3,4} }

    • أعطاء قيمة لعناصر المصفوفة :
    في المصفوفات يتم ترقيم العناصر المنتمية للمصفوفة بداية بالرقم صفر كما في الصورة :






    لنبدأ بالمصفوفة list الذي قمنا بالتصريح عنها في بداية الدرس , و سنقوم بأعطاء العناصر القيم :

    var list [4]int

    list[0]=12

    list[1]=13

    list[2]=14

    list[3]=15

    بكل بساطة كل ما تحتاجه هو كتابة أسم المصفوفة متبوعا برقم العنصر بين قوسين [ ] ثم كتابة القيمة بعد القوسين.

    • الوصول لعناصر المصفوفة وقيمها :
    للوصول لقيم العناصر يتم استخدام الحلقة التكرارية for كما في هذا المثال :
    package main

    import "fmt"

    func main() {

    list := [4]int{ 4,6,7,1}

    for counter := 0;counter<4 ;counter++ {

    fmt.Println(list[counter])

    }

               }

    أو بأستخدام الكلمة المفتاحية range كما في هذا المثال :

    package main

    import "fmt"

    func main() {

    list := [4]int{3,4,5,6}

    for a,k := range(list){fmt.Printf("Element[%d]=%d\n",a,k)}

    }


    في السطر الخامس قمنا بأستخدام الحلقة التكرارية for , ثم أنشأنا المتغيران a و k الذين سيتم حفظ القيم التي تعيدها الدالة range .

    تعيد الدالة range قيمتين , تكون الأولى خاصه بترتيب العنصر في المصفوفة , و الثانية بالقيمة المحفوظة في العنصر .

    و الأن لمثال لأستخدام range في حساب مجموع القيم الموجودة في المصفوفة :

    package main

    import "fmt"

    func main() {

    list := [4]int{3,4,5,6}

    var total int

    for _ ,k := range(list){total += k}

    fmt.Println("Total=",total)

    }


    تلاحظ أننا في السطر الخاص بالحلقة التكرارية for قمنا بأستخدام المتغير الخاص "_" الذي يتم تجاهله وقت الترجم , حيث يعتبر التصريح عن متغير بدون أستخدامه فعلياً في النص البرمجي خطأ في لغة Go .



    2 . الشرائح ( Slices ) :

    هنالك تشابه كبير بين الشرائح و المصفوفات , بل أن الشرائح في الحقيقة هي عبارة عن مؤشر ( pointer ) لمصفوفة أو جزء من المصفوفة (underlying array)  و بذلك فهي تأخذ خصائص المؤشرات مما يعني توفير الذاكرة فيتم التعامل مع مؤشر للمصفوفة بدل من نسخ المصفوفة بكامل بياناتها    , و لكن تتميز عن المؤشرات بوجود حماية من أخطاء فيض الذاكرة ( Overflow buffer ) و قصر الذاكرة ( underflow buffer ) حيث أن حدوث أي نوع من هذه الأخطاء يولد رسائل خطأ (exceptions) .

     كما تتميز الشرائح بأمكانية تحديد مدى العناصر التي يراد الوصول إليها من المصفوفة (underlying Array) , مما يعد مفيداً جداً في برمجة "subsets" , و ممن ميزات الشرائح "Slices" هو أنه يمكن التحكم في حجم الشريحة في وقت التشغيل "Runtime"  , و يمكن تعديله في أي نقطة من البرنامج .

    • التصريح عن Slices :
    يتشابه التصريح عن Slices مع الطريقة المستخدمة في المصفوفات , و سوف نعيد كتابة مثالنا الأول لنصرح عن شرائح "Slices" بدل المصفوفات "Arrays" :


    var list []int

    list1 := []int{ 1243 ,35 ,2}



    لاحظ أن الأختلاف الوحيد هو أننا لم نحدد حجم الشريحة "Slice" فقط ( يجب أن أنبه أنه في المثال الأول سيظهر خطأ من نوع (index out of range) في حالة أضافة قيمة للشريحة , سوف نتطرق لماذا في الجزء التالي ) , كما يمكنك أستخدام الدالة make لأنشاء شريحة جديدة كالتالي:

    list := make([]int,5)

    و تكون معاملات الدالة make بالترتيب هو نوع الشريحة الجديد ( int[] ) , ثم حجم الشريحة , و هنالك معامل أختياري و هو حجم المصفوفة ( underlying array)  التي تضم الشريحة و في حالة عدم تحديد حجم المصفوفة سيكون حجمها مساوي للشريحة (Slice) :

    list := make([]int,5,7)


    • إنشاء شريحة كجزء من مصفوفة ( Re-sliced ) :
    تتميز الشرائح ( Slices ) بأمكانية إنشاءها كجزء من مصفوفة أو من شريحة أخرى أو ما يسمى بـ"Re-sliced" , كما في هذا المثال :
    package main

    import "fmt"

    func main() {

    list :=[4]int{ 4,1,2,3 }

    s1 := list[1:4

    s2 := s1[:2]

    s3 := list[2:]

    fmt.Println(s1)

    fmt.Println(s2)

    fmt.Println(s3)

    }

     الصيغة العامة لعملية ( re-sliced ) هي :

    Array1[lo:hi]

    حيث lo هي القيمة برقم العنصر الذي سيكون القيمة الأولى في الشريحة الجديدة , و في حالة كتابة هذه القيمة لوحدها :

    Array1[lo:]

    سيتم تضمين العنصر الذي يحمل الرقم lo و جميع العناصر التي تليه , و لنفترض أن هذه القيمة مساوية لرقم 2 :






    سيتم تضمين العناصر 2 و 3 فقط , و يجب أن نتبه أن العناصر في المصفوفة أو الشريحة تبدأ قيمتها من الصفر  و لزيادة التوضيح لنعد لمثالنا العملي :

    list :=[4]int{ 4,1,2,3 }

    s3 := list[2:] // ستكون العناصر مساوية ل 2 و 3 فقط

    في هذا المثال ستحتوي الشريحة s3 على العناصر 2 و 3 اللتان تحملان القيمة الدليلة 2 و 3  , أما بالنسبة للقيمة hi في عملية re-sliced فهي تقوم على نفس المبدأ مع أختلاف بسيط :

    list :=[4]int{ 4,1,2,3 }

    s2 := s1[:2]

    سيتم عرض العناصر التي تقع قبل العنصر ( hi-1 ) أي في هذه الحالة العناصر التي تحمل القيمة الدليلة 0 و 1 , لذا يجب أن نتبه بأن القيمة العليا لعملية re-sliced تدل على القيمة الدليلة ( hi -1 ) ,  فلنفترض بأننا عدلنا المثال للتالي:

    list :=[4]int{ 4,1,2,3 }

    s1 := list[:1

    سيتم عرض القيمة ( hi - 1 ) أي في هذه الحالة العنصر الذي يحمل العنوان الدليلي 0 و قيمته مساوية 4 , و هذا نهاية شرح عملية re-sliced .


    • الحصول على حجم الشريحة و المصفوفة :

    توفر لغة Go الدالة len للحصول على حجم الشريحة ( و تستخدم أيضاً للحصول على حجم المصفوفات ) و الدالة cap التي تستخدم للحصول على حجم المصفوفة المرافقة للشريحة ( underlying array) , و يتم أستخدمها كما في المثال :

    package main

    import "fmt"

    func main() {

    s1 := make([]int,4,12)
    fmt.Printf("Cap == %d\nLen == %d\n",cap(s1),len(s1))

    }



    • توسعة الشريحة :
    توفر لغة Go الوظيفة البرمجة append التي تقوم بأضافة عناصر لشريحة و تنشأ شريحة جديدة , كما في هذا المثال:
    s1 := []int{56,2,3}

    s2 := append(s1,12,5}


    سيتم أضافة 12 و 5 إلى عناصر الشريحة s1 و إنشاء الشريحة s2 , كما يمكن أضافة شريحة إلى  أخرى :

    s1 := []int{56,2,3}

    s2 := append(s1,12,5}
    s3 := []int{0,0,13}
    s4 := append(s2,s3...}

    لاحظ الثلاث نقاط في السطر الثالث حيث دمجنا الشريحة s2 و s3 , حيث يتوجب كتباتها عند دمج الشرائح .

    كما تتواجد الوظيفة البرمجية copy و التي تقوم بنسخ شريحة من مصفوفة لشريحة أخرى وتعيد عدد العناصر الذي تم نسخه و لفهم هذه الوظيفة قم بتطبيق النص البرمجي التالي :

    package main

    import "fmt"

    func main() {

    a := [...]int{1,2,3,4}

    s := make([]int,3)

    n1 := copy(s,a[1:])

    fmt.Println(n1)

    fmt.Println(s)

    }

    في السطر الذي أستخدمنا فيه الوظيفة copy ستلاحظ باننا نسخنا شريحة من المصفوفة a إلى الشريحة s و قمنا بحفظ عدد العناصر التي تم نسخها في المتغير n1 :

    n1 := copy(s,a[1:])

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




    3 . الخرائط ( maps ) :


    تتشابه الخرائط مع المصفوفات و الشرائح في معظم الخصائص و مع أختلاف بسيط و هو أن القيمة الدليلة لا تكون رقمية و لكن يتاح للمبرمج أختيار أي قيمة من أي نوع متاح في اللغة .


    • أنشاء الخريطة :
    تستخدم الوظيفة  البرمجة map لأنشاء الخرائط في لغة Go , و لأنشاء خريطة لا تحتوي على أي قيمة يجب أستخدام الوظيفة البرمجية make :

    m1 := make(map[string]int)

    تلاحظ أننا بعد الوظيفة البرمجية map قمنا بوضع النوع string بين القوسين [ ] , و لذا سوف تكون القيمة الدليلة في هذه الخريطة نصية و القيمة المتبوعة في هذا المثال عبارة عن أعداد صحيحة (int)  , و للتوصح الفكرة أكثر سوف ننشأ خريطة تحتوي على أسماء الشهور و عدد الأيام :

    monthdays := map[string]int{

    "Jan": 31, "Feb": 28, "Mar": 31, // الفاصلة بين القيم أجبارية

    "Apr": 30, "May": 31, "Jun": 30,

    "Jul": 31, "Aug": 31, "Sep": 30,

    "Oct": 31, "Nov": 30, "Dec": 31,  

    }

    يتم كتابة عناصر الخريطة بالترتيب الذي تم الأعلان عنه في الوظيفة البرمجية map و يفصل بينها بالحرف " : " , حيث في مثالنا هذا يتم كتابة أسم الشهر ثم عدد الأيام :


    "Jan"31


    و يفصل بين القيم المتعدد للخريطة بأستخدام  " , " الفاصلة , و أريد أن أشير أنه في خريطة أيام الشهور لم نستخدم الوظيفة البرمجية make لأحتواء الخريطة على عناصر عن التصريح عنها .


    • أضافة قيم للخريطة و تحديث الموجودة حالياً :
    لتحديث أي عنصر في الخريطة يجب كتابة القيمة الدليلة , و في حالة مثالنا أسم الشهر ومن ثم علامة يساوي متبوعة بالقيمة الجديدة :

    monthdays["Feb"] = 29

    سيتم تغير قيمة الشهر فبراير من 28 يوماً إلى  29 يوماً , و في حالة إنشاء قيمة جديدة يتم كتابة أسم الخريطة ثم القيمة الدليلة الجديد في القوسين متبوعه بالقيمة الجديدة :


    monthdays["abgoor"] = 29

     تم أنشاء عنصر جديد قميته الدليلة تساوي "abgoor" و عدد أيامه 29 , من يدري يمكن ينشأون شهر جديد بأسمي ;-)  .


    • أختبار أذا كانت العنصر موجود :
    لأختبار أذا كانت العنصر موجود في الخريطة المنشأه تستخدم هذه الصيغة :

    value,present := monthdays["Jan"]

    في حالة وجود العنصر ستكون قيمة المتغير present الذي سيكون من النوع bool مساوية للقيمة true , و في حالة عدم وجود العنصر سيكون مساوي للقيمة false .

    • الوصول لجميع عناصر الخريطة :
    تستطيع الوصول لجميع عناصر الخريطة بواسطة الوظيفة البرمجية range كالتالي :

    for month, days := range monthdays {  

    fmt.Println("Month:",month," Days:",days)

    }

    و لأنني قمت بشرح الوظيفة البرمجية range فأعتقد بأن النص البرمجي مفهوم , و لا يحتاج شرح , و يجب أن أنبه أنه في يجب أن لا تعتمد على  ترتيب العناصر في الخريطة عند أستخدام الوظيفة البرمجية range .


    • حذف عنصر من الخريطة :
    لحذف أي عنصر من الدالة أستخدم هذه الصيغة  :

    delete(monthdays,"MAR")

    و في الأصدارة الأقدم من اللغة :

    monthdays["Mar"] = 0, false

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












    ليست هناك تعليقات:

    إرسال تعليق