Skip to content

متغيرات Go

المتغير هو موقع تخزين لحفظ قيمة، يسمح للقيمة المخزنة بالتغير ديناميكياً أثناء التشغيل. كل إعلان لمتغير يخصص له ذاكرة لتخزين قيمة النوع المقابل، انتقل لـ دليل المرجع - المتغيرات لمزيد من التفاصيل.

التعريف

في Go إعلان النوع يأتي بعد الاسم، تعريف المتغير يستخدم الكلمة المفتاحية var، الصيغة هي var اسم_المتغير اسم_النوع، قواعد تسمية المتغير يجب أن تتبع قواعد تسمية المعرفات:

go
var intNum int
var str string
var char byte

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

go
var numA, numB, numC int

عند تعريف عدة متغيرات من أنواع مختلفة، يمكن استخدام () للتغليف، يمكن وجود عدة ():

go
var (
  name    string
  age     int
  address string
)

var (
  school string
  class int
)

المتغير إذا تم تعريفه فقط دون تعيين قيمة، فالقيمة المخزنة فيه هي القيمة الصفرية للنوع المقابل.

التعيين

التعيين يستخدم العامل =، مثلاً:

go
var name string
name = "jack"

يمكن أيضاً التعيين عند التعريف:

go
var name string = "jack"

أو هكذا أيضاً:

go
var name string
var age int
name, age = "jack", 1

الطريقة الثانية تتطلب تحديد النوع في كل مرة، يمكن استخدام السكر النحوي الذي توفره Go: التهيئة القصيرة للمتغير، يمكن حذف الكلمة المفتاحية var والنوع اللاحق، النوع يُترك للمترجم للاستنتاج:

go
name := "jack" // متغير من نوع سلسلة نصية

رغم إمكانية عدم تحديد النوع، لكن عند التعيين اللاحق، يجب أن يتطابق النوع، الكود التالي لا يمكن ترجمته:

a := 1
a = "1"

أيضاً يجب الانتباه أن التهيئة القصيرة للمتغير لا يمكن استخدام nil، لأن nil لا ينتمي لأي نوع، والمترجم لا يمكنه استنتاج نوعه:

go
name := nil // لا يمكن ترجمته

التعريف القصير يمكنه التهيئة المتغيرات بشكل جماعي:

go
name, age := "jack", 1

طريقة التعريف القصير لا يمكن استخدامها لمتغير موجود مسبقاً، مثل:

go
// مثال خاطئ
var a int
a := 1

// مثال خاطئ
a := 1
a := 2

لكن هناك حالة استثنائية، وهي عند تعيين قيمة لمتغير قديم مع تعريف متغير جديد، مثل:

go
a := 1
a, b := 2, 2

هذا الكود يمكن ترجمته، المتغير a أُعيد تعيينه، و b مُعرَّف حديثاً.

في Go هناك قاعدة، وهي أن جميع المتغيرات داخل الدالة يجب أن تُستخدم، مثل الكود التالي الذي عرَّف المتغير لكنه لم يستخدمه:

go
func main() {
  a := 1
}

عند الترجمة سيكون هناك خطأ، يُذكرك أن هذا المتغير مُعرَّف لكنه غير مستخدم:

a declared and not used

هذه القاعدة تنطبق فقط على المتغيرات داخل الدوال، بالنسبة لمتغيرات مستوى الحزمة خارج الدوال لا يوجد هذا القيد، الكود التالي يمكن ترجمته:

go
var a = 1

func main() {

}

المجهول

استخدام الشرطة السفلية يمكن تمثيل متغير غير مطلوب:

go
Open(name string) (*File, error)

مثلاً دالة os.Open لها قيمتان مُرجعتان، نريد الأولى فقط ولا نريد الثانية، يمكن الكتابة كالتالي:

go
file, _ := os.Open("readme.txt")

المتغير غير المستخدم لا يمكن ترجمته، عندما لا تحتاج متغيراً معيناً، يمكنك استخدام الشرطة السفلية _ كبديل.

التبديل

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

go
num1, num2 := 25, 36
num1, num2 = num2, num1

ثلاثة متغيرات كذلك:

go
num1, num2, num3 := 25, 36, 49
num1, num2, num3  = num3, num2, num1

فكر في الكود التالي، هذا جزء من كود حساب متتالية فيبوناتشي، ما هي قيم المتغيرات الثلاثة بعد الحساب:

go
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 عند إجراء عمليات إسناد متعددة للمتغيرات، الترتيب هو حساب القيم أولاً ثم الإسناد، وليس من اليسار لليمين:

go
a, b, c = b, c, a+b

قد تظن أنه سيُوسَّع لـ:

go
a = b
b = c
c = a + b

لكن في الواقع سيتم حساب قيم a و b و c أولاً ثم إسنادها لهم، وهذا يكافئ الكود التالي:

go
a, b, c = 1, 1, 0+1

عند وجود استدعاءات دوال، هذا التأثير أوضح، لدينا دالة sum يمكنها حساب ناتج جمع رقمين:

go
func sum(a, b int) int {
  return a + b
}

استخدام الدالة لجمع رقمين:

go
a, b, c := 0, 1, 1
a, b, c = b, c, sum(a, b)

النتيجة لم تتغير، عند حساب القيمة المُرجعة لدالة sum، معاملاتها لا تزال 0 و 1:

1 1 1

لذا يجب فصل الكود هكذا:

go
a, b = b, c
c = a + b

المقارنة

مقارنة المتغيرات لها شرط أساسي، وهو أن تكون أنواعها متطابقة، Go لا تحتوي على تحويل ضمني للأنواع، الكود التالي لا يمكن ترجمته:

go
func main() {
  var a uint64
  var b int64
  fmt.Println(a == b)
}

سيخبرك المترجم أن النوعين غير متطابقين:

invalid operation: a == b (mismatched types uint64 and int64)

لذا يجب استخدام التحويل الصريح:

go
func main() {
  var a uint64
  var b int64
  fmt.Println(int64(a) == b)
}

قبل وجود الوراثة، كانت دوال min و max المدمجة في Go تدعم فقط أرقام الفاصلة العائمة، وفي إصدار 1.21، أعادت Go كتابة هاتين الدالتين المدمجتين باستخدام الوراثة، الآن يمكن استخدام دالة min لمقارنة الأصغر:

go
minVal := min(1, 2, -1, 1.2)

استخدام دالة max لمقارنة الأكبر:

go
maxVal := max(100, 22, -1, 1.12)

معاملاتهما تدعم جميع الأنواع القابلة للمقارنة، الأنواع القابلة للمقارنة في Go هي:

  • منطقي
  • أرقام
  • سلاسل نصية
  • مؤشرات
  • قنوات (تدعم فقط التحقق من المساواة)
  • مصفوفات بعناصر من أنواع قابلة للمقارنة (الشرائح غير قابلة للمقارنة) (تدعم فقط التحقق من المساواة) (تدعم فقط المقارنة بين مصفوفات بنفس الطول، لأن طول المصفوفة جزء من النوع، والأنواع المختلفة لا يمكن مقارنتها)
  • هياكل بحقول من أنواع قابلة للمقارنة (تدعم فقط التحقق من المساواة)

بالإضافة لذلك، يمكن استيراد المكتبة القياسية cmp للمقارنة، لكنها تدعم فقط معاملات الأنواع المرتبة، والأنواع المرتبة المدمجة في Go هي الأرقام والسلاسل فقط:

go
import "cmp"

func main() {
  cmp.Compare(1, 2)
  cmp.Less(1, 2)
}

كتل الكود

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

go
func main() {
  a := 1

  {
    a := 2
    fmt.Println(a)
  }

  {
    a := 3
    fmt.Println(a)
  }
  fmt.Println(a)
}

مخرجاته:

2
3
1

المتغيرات بين الكتل مستقلة، لا تتأثر ببعضها، لا يمكن الوصول إليها، لكنها تتأثر بالكتلة الأب:

go
func main() {
  a := 1

  {
    a := 2
    fmt.Println(a)
  }

  {
    fmt.Println(a)
  }
  fmt.Println(a)
}

مخرجاته:

2
1
1

Golang تم تحريره بواسطة www.golangdev.cn