بسم الله الرحمن الرحيم
في هذا الدرس سوف نتعرف على المصفوفات و الشرائح (slices) و هو نوع من البيانات أكثر مرونة من المصفوفات و أخيرا الخرائط (map) و هي بديلة للهاش (Hash) و المعاجم (Dictionary) .
- التصريح عن مصفوفة :
لتصريح عن مصفوفة جديدة في لغة Go يوجد أكثر من طريقة , و هذا مثال على الطرق المستخدمة :
var list [4]int
list1 := [4]int{ 12, 43 ,35 ,2}
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
list[0]=12
list[1]=13
list[2]=14
list[3]=15
بكل بساطة كل ما تحتاجه هو كتابة أسم المصفوفة متبوعا برقم العنصر بين قوسين [ ] ثم كتابة القيمة بعد القوسين.
- الوصول لعناصر المصفوفة وقيمها :
package main
import "fmt"
func main() {
list := [4]int{ 4,6,7,1}
for counter := 0;counter<4 ;counter++ {
fmt.Println(list[counter])
}
}
import "fmt"
func main() {
list := [4]int{ 4,6,7,1}
for counter := 0;counter<4 ;counter++ {
fmt.Println(list[counter])
}
}
أو بأستخدام الكلمة المفتاحية range كما في هذا المثال :
في السطر الخامس قمنا بأستخدام الحلقة التكرارية for , ثم أنشأنا المتغيران a و k الذين سيتم حفظ القيم التي تعيدها الدالة range .
تعيد الدالة range قيمتين , تكون الأولى خاصه بترتيب العنصر في المصفوفة , و الثانية بالقيمة المحفوظة في العنصر .
و الأن لمثال لأستخدام range في حساب مجموع القيم الموجودة في المصفوفة :
تلاحظ أننا في السطر الخاص بالحلقة التكرارية for قمنا بأستخدام المتغير الخاص "_" الذي يتم تجاهله وقت الترجم , حيث يعتبر التصريح عن متغير بدون أستخدامه فعلياً في النص البرمجي خطأ في لغة Go .
2 . الشرائح ( Slices ) :
var list []int
list1 := []int{ 12, 43 ,35 ,2}
الصيغة العامة لعملية ( re-sliced ) هي :
حيث lo هي القيمة برقم العنصر الذي سيكون القيمة الأولى في الشريحة الجديدة , و في حالة كتابة هذه القيمة لوحدها :
سيتم تضمين العنصر الذي يحمل الرقم lo و جميع العناصر التي تليه , و لنفترض أن هذه القيمة مساوية لرقم 2 :
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)}
}
import "fmt"
func main() {
list := [4]int{3,4,5,6}
for a,k := range(list){fmt.Printf("Element[%d]=%d\n",a,k)}
}
تعيد الدالة 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)
}
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" , و يمكن تعديله في أي نقطة من البرنامج .
كما تتميز الشرائح بأمكانية تحديد مدى العناصر التي يراد الوصول إليها من المصفوفة (underlying Array) , مما يعد مفيداً جداً في برمجة "subsets" , و ممن ميزات الشرائح "Slices" هو أنه يمكن التحكم في حجم الشريحة في وقت التشغيل "Runtime" , و يمكن تعديله في أي نقطة من البرنامج .
- التصريح عن Slices :
var list []int
list1 := []int{ 12, 43 ,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)
}
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 := list[2:] // ستكون العناصر مساوية ل 2 و 3 فقط
في هذا المثال ستحتوي الشريحة s3 على العناصر 2 و 3 اللتان تحملان القيمة الدليلة 2 و 3 , أما بالنسبة للقيمة hi في عملية re-sliced فهي تقوم على نفس المبدأ مع أختلاف بسيط :
list :=[4]int{ 4,1,2,3 }
s2 := s1[:2]
s2 := s1[:2]
سيتم عرض العناصر التي تقع قبل العنصر ( hi-1 ) أي في هذه الحالة العناصر التي تحمل القيمة الدليلة 0 و 1 , لذا يجب أن نتبه بأن القيمة العليا لعملية re-sliced تدل على القيمة الدليلة ( hi -1 ) , فلنفترض بأننا عدلنا المثال للتالي:
list :=[4]int{ 4,1,2,3 }
s1 := list[:1]
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))
}
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}
s2 := append(s1,12,5}
سيتم أضافة 12 و 5 إلى عناصر الشريحة s1 و إنشاء الشريحة s2 , كما يمكن أضافة شريحة إلى أخرى :
لاحظ الثلاث نقاط في السطر الثالث حيث دمجنا الشريحة s2 و s3 , حيث يتوجب كتباتها عند دمج الشرائح .
كما تتواجد الوظيفة البرمجية copy و التي تقوم بنسخ شريحة من مصفوفة لشريحة أخرى وتعيد عدد العناصر الذي تم نسخه و لفهم هذه الوظيفة قم بتطبيق النص البرمجي التالي :
3 . الخرائط ( maps ) :
تتشابه الخرائط مع المصفوفات و الشرائح في معظم الخصائص و مع أختلاف بسيط و هو أن القيمة الدليلة لا تكون رقمية و لكن يتاح للمبرمج أختيار أي قيمة من أي نوع متاح في اللغة .
تلاحظ أننا بعد الوظيفة البرمجية map قمنا بوضع النوع string بين القوسين [ ] , و لذا سوف تكون القيمة الدليلة في هذه الخريطة نصية و القيمة المتبوعة في هذا المثال عبارة عن أعداد صحيحة (int) , و للتوصح الفكرة أكثر سوف ننشأ خريطة تحتوي على أسماء الشهور و عدد الأيام :
يتم كتابة عناصر الخريطة بالترتيب الذي تم الأعلان عنه في الوظيفة البرمجية map و يفصل بينها بالحرف " : " , حيث في مثالنا هذا يتم كتابة أسم الشهر ثم عدد الأيام :
و يفصل بين القيم المتعدد للخريطة بأستخدام " , " الفاصلة , و أريد أن أشير أنه في خريطة أيام الشهور لم نستخدم الوظيفة البرمجية make لأحتواء الخريطة على عناصر عن التصريح عنها .
سيتم تغير قيمة الشهر فبراير من 28 يوماً إلى 29 يوماً , و في حالة إنشاء قيمة جديدة يتم كتابة أسم الخريطة ثم القيمة الدليلة الجديد في القوسين متبوعه بالقيمة الجديدة :
في حالة وجود العنصر ستكون قيمة المتغير present الذي سيكون من النوع bool مساوية للقيمة true , و في حالة عدم وجود العنصر سيكون مساوي للقيمة false .
و لأنني قمت بشرح الوظيفة البرمجية range فأعتقد بأن النص البرمجي مفهوم , و لا يحتاج شرح , و يجب أن أنبه أنه في يجب أن لا تعتمد على ترتيب العناصر في الخريطة عند أستخدام الوظيفة البرمجية range .
s1 := []int{56,2,3}
s2 := append(s1,12,5}
s3 := []int{0,0,13}
s4 := append(s2,s3...}
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)
}
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 ) :
تتشابه الخرائط مع المصفوفات و الشرائح في معظم الخصائص و مع أختلاف بسيط و هو أن القيمة الدليلة لا تكون رقمية و لكن يتاح للمبرمج أختيار أي قيمة من أي نوع متاح في اللغة .
- أنشاء الخريطة :
m1 := make(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,
}
"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
monthdays["abgoor"] = 29
تم أنشاء عنصر جديد قميته الدليلة تساوي "abgoor" و عدد أيامه 29 , من يدري يمكن ينشأون شهر جديد بأسمي ;-) .
- أختبار أذا كانت العنصر موجود :
value,present := monthdays["Jan"]
- الوصول لجميع عناصر الخريطة :
for month, days := range monthdays {
fmt.Println("Month:",month," Days:",days)
}
fmt.Println("Month:",month," Days:",days)
}
- حذف عنصر من الخريطة :
لحذف أي عنصر من الدالة أستخدم هذه الصيغة :
و في الأصدارة الأقدم من اللغة :
delete(monthdays,"MAR")
و في الأصدارة الأقدم من اللغة :
monthdays["Mar"] = 0, false
هذا هو ختام الدرس الثامن , و أن شاء الله أكون سهلت الموضوع بحيث توصل لكم الفكرة , و أي تعديل أو أقتراح و خاصة بخصوص ترجمة المصطحات فأنا بنتظار أقتراحتكم .
ليست هناك تعليقات:
إرسال تعليق