متغيرات Go
المتغير هو موقع تخزين لحفظ قيمة، يسمح للقيمة المخزنة بالتغير ديناميكياً أثناء التشغيل. كل إعلان لمتغير يخصص له ذاكرة لتخزين قيمة النوع المقابل، انتقل لـ دليل المرجع - المتغيرات لمزيد من التفاصيل.
التعريف
في Go إعلان النوع يأتي بعد الاسم، تعريف المتغير يستخدم الكلمة المفتاحية var، الصيغة هي var اسم_المتغير اسم_النوع، قواعد تسمية المتغير يجب أن تتبع قواعد تسمية المعرفات:
var intNum int
var str string
var char byteعند تعريف عدة متغيرات من نفس النوع، يمكن كتابة النوع مرة واحدة:
var numA, numB, numC intعند تعريف عدة متغيرات من أنواع مختلفة، يمكن استخدام () للتغليف، يمكن وجود عدة ():
var (
name string
age int
address string
)
var (
school string
class int
)المتغير إذا تم تعريفه فقط دون تعيين قيمة، فالقيمة المخزنة فيه هي القيمة الصفرية للنوع المقابل.
التعيين
التعيين يستخدم العامل =، مثلاً:
var name string
name = "jack"يمكن أيضاً التعيين عند التعريف:
var name string = "jack"أو هكذا أيضاً:
var name string
var age int
name, age = "jack", 1الطريقة الثانية تتطلب تحديد النوع في كل مرة، يمكن استخدام السكر النحوي الذي توفره Go: التهيئة القصيرة للمتغير، يمكن حذف الكلمة المفتاحية var والنوع اللاحق، النوع يُترك للمترجم للاستنتاج:
name := "jack" // متغير من نوع سلسلة نصيةرغم إمكانية عدم تحديد النوع، لكن عند التعيين اللاحق، يجب أن يتطابق النوع، الكود التالي لا يمكن ترجمته:
a := 1
a = "1"أيضاً يجب الانتباه أن التهيئة القصيرة للمتغير لا يمكن استخدام nil، لأن nil لا ينتمي لأي نوع، والمترجم لا يمكنه استنتاج نوعه:
name := nil // لا يمكن ترجمتهالتعريف القصير يمكنه التهيئة المتغيرات بشكل جماعي:
name, age := "jack", 1طريقة التعريف القصير لا يمكن استخدامها لمتغير موجود مسبقاً، مثل:
// مثال خاطئ
var a int
a := 1
// مثال خاطئ
a := 1
a := 2لكن هناك حالة استثنائية، وهي عند تعيين قيمة لمتغير قديم مع تعريف متغير جديد، مثل:
a := 1
a, b := 2, 2هذا الكود يمكن ترجمته، المتغير a أُعيد تعيينه، و b مُعرَّف حديثاً.
في Go هناك قاعدة، وهي أن جميع المتغيرات داخل الدالة يجب أن تُستخدم، مثل الكود التالي الذي عرَّف المتغير لكنه لم يستخدمه:
func main() {
a := 1
}عند الترجمة سيكون هناك خطأ، يُذكرك أن هذا المتغير مُعرَّف لكنه غير مستخدم:
a declared and not usedهذه القاعدة تنطبق فقط على المتغيرات داخل الدوال، بالنسبة لمتغيرات مستوى الحزمة خارج الدوال لا يوجد هذا القيد، الكود التالي يمكن ترجمته:
var a = 1
func main() {
}المجهول
استخدام الشرطة السفلية يمكن تمثيل متغير غير مطلوب:
Open(name string) (*File, error)مثلاً دالة os.Open لها قيمتان مُرجعتان، نريد الأولى فقط ولا نريد الثانية، يمكن الكتابة كالتالي:
file, _ := os.Open("readme.txt")المتغير غير المستخدم لا يمكن ترجمته، عندما لا تحتاج متغيراً معيناً، يمكنك استخدام الشرطة السفلية _ كبديل.
التبديل
في Go، إذا أردت تبديل قيم متغيرين، لا تحتاج لاستخدام المؤشرات، يمكنك استخدام عامل الإسناد للتبديل مباشرة، نحوياً يبدو بديهياً جداً، مثال:
num1, num2 := 25, 36
num1, num2 = num2, num1ثلاثة متغيرات كذلك:
num1, num2, num3 := 25, 36, 49
num1, num2, num3 = num3, num2, num1فكر في الكود التالي، هذا جزء من كود حساب متتالية فيبوناتشي، ما هي قيم المتغيرات الثلاثة بعد الحساب:
a, b, c := 0, 1, 1
a, b, c = b, c, a+bالجواب:
1 1 1قد تتساءل لماذا ليس:
1 1 2أليس a أُسندت له قيمة b، لماذا ناتج a+b لا يزال 1؟ في Go عند إجراء عمليات إسناد متعددة للمتغيرات، الترتيب هو حساب القيم أولاً ثم الإسناد، وليس من اليسار لليمين:
a, b, c = b, c, a+bقد تظن أنه سيُوسَّع لـ:
a = b
b = c
c = a + bلكن في الواقع سيتم حساب قيم a و b و c أولاً ثم إسنادها لهم، وهذا يكافئ الكود التالي:
a, b, c = 1, 1, 0+1عند وجود استدعاءات دوال، هذا التأثير أوضح، لدينا دالة sum يمكنها حساب ناتج جمع رقمين:
func sum(a, b int) int {
return a + b
}استخدام الدالة لجمع رقمين:
a, b, c := 0, 1, 1
a, b, c = b, c, sum(a, b)النتيجة لم تتغير، عند حساب القيمة المُرجعة لدالة sum، معاملاتها لا تزال 0 و 1:
1 1 1لذا يجب فصل الكود هكذا:
a, b = b, c
c = a + bالمقارنة
مقارنة المتغيرات لها شرط أساسي، وهو أن تكون أنواعها متطابقة، Go لا تحتوي على تحويل ضمني للأنواع، الكود التالي لا يمكن ترجمته:
func main() {
var a uint64
var b int64
fmt.Println(a == b)
}سيخبرك المترجم أن النوعين غير متطابقين:
invalid operation: a == b (mismatched types uint64 and int64)لذا يجب استخدام التحويل الصريح:
func main() {
var a uint64
var b int64
fmt.Println(int64(a) == b)
}قبل وجود الوراثة، كانت دوال min و max المدمجة في Go تدعم فقط أرقام الفاصلة العائمة، وفي إصدار 1.21، أعادت Go كتابة هاتين الدالتين المدمجتين باستخدام الوراثة، الآن يمكن استخدام دالة min لمقارنة الأصغر:
minVal := min(1, 2, -1, 1.2)استخدام دالة max لمقارنة الأكبر:
maxVal := max(100, 22, -1, 1.12)معاملاتهما تدعم جميع الأنواع القابلة للمقارنة، الأنواع القابلة للمقارنة في Go هي:
- منطقي
- أرقام
- سلاسل نصية
- مؤشرات
- قنوات (تدعم فقط التحقق من المساواة)
- مصفوفات بعناصر من أنواع قابلة للمقارنة (الشرائح غير قابلة للمقارنة) (تدعم فقط التحقق من المساواة) (تدعم فقط المقارنة بين مصفوفات بنفس الطول، لأن طول المصفوفة جزء من النوع، والأنواع المختلفة لا يمكن مقارنتها)
- هياكل بحقول من أنواع قابلة للمقارنة (تدعم فقط التحقق من المساواة)
بالإضافة لذلك، يمكن استيراد المكتبة القياسية cmp للمقارنة، لكنها تدعم فقط معاملات الأنواع المرتبة، والأنواع المرتبة المدمجة في Go هي الأرقام والسلاسل فقط:
import "cmp"
func main() {
cmp.Compare(1, 2)
cmp.Less(1, 2)
}كتل الكود
داخل الدالة، يمكن إنشاء كتلة كود باستخدام الأقواس المعقوفة، متغيرات كتل الكود المتجاورة مستقلة في نطاقها. مثلاً الكود التالي:
func main() {
a := 1
{
a := 2
fmt.Println(a)
}
{
a := 3
fmt.Println(a)
}
fmt.Println(a)
}مخرجاته:
2
3
1المتغيرات بين الكتل مستقلة، لا تتأثر ببعضها، لا يمكن الوصول إليها، لكنها تتأثر بالكتلة الأب:
func main() {
a := 1
{
a := 2
fmt.Println(a)
}
{
fmt.Println(a)
}
fmt.Println(a)
}مخرجاته:
2
1
1