كيفية استخدام المتغيرات والثوابت في لغة Go

المتغيرات مفهوم مهم في البرمجة يجب إتقانه. هي رموز تمثل قيمة تستخدم في برنامجك.

سيغطي هذا الدرس بعض الأساسيات المتعلقة بالمتغيرات وأفضل الممارسات لاستخدامها داخل برامج Go التي تقوم بإنشائها.

فهم المتغيرات

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

يمكننا التفكير في المتغير كتصنيف له اسم عليه، والذي تربطه بقيمة.

لنقل أن لدينا عدد صحيح، 1032049348، ونريد تخزينه في متغير بدلاً من إعادة كتابة الرقم الطويل مراراً وتكراراً. لتحقيق ذلك، يمكننا استخدام اسم سهل التذكر، مثل المتغير i. لتخزين قيمة في متغير، نستخدم الصيغة التالية:

i := 1032049348

يمكننا التفكير في هذا المتغير كتصنيف مرتبط بالقيمة.

التصنيف يحمل اسم المتغير i، ويرتبط بالقيمة الصحيحة 1032049348.

العبارة i := 1032049348 هي عبارة تعريف وتخصيص تتكون من عدة أجزاء:

  • اسم المتغير (i)
  • تخصيص التصنيف القصير (:=)
  • القيمة المعلومة باسم معلومات الشركة (1032049348)
  • النواتج من نوع الداتا في Go (int)

سيتمناهم أن نرى لاحقا كيفية إختر نوع المعلومة بشكل مخصص في سECTION آخرة.

مع ذلك، تم إنشاء هذا المعلومة باسم معلومات مثل i يساوي القيمة من نوع 整数 1032049348.

بالتوفيق بين هذا القيمة والقيمة المعلومة معلومات مثل i يساوي القيمة من نوع 整数 1032049348.بالمزيد من المعلومات عن كيفية إستخدام المعلومات بشكل مخصص في المستقبلة، سيظهر لنا في السECTION التالية.

بالمزيد من المعلومات عن كيفية إستخدام المعلومات بشكل مخصص في المستقبلة، سيظهر لنا في السECTION التالية.

package main

import "fmt"

func main() {
	i := 1032049348
	fmt.Println(i)
}
Output
1032049348

لكننا سيتم إنشاء هذا المعلومة باسم معلومات مثل i يساوي القيمة من نوع 整数 1032049348.

fmt.Println(i - 813)
Output
1032048535

بالمزيد من المعلومات عن كيفية إستخدام المعلومات بشكل مخصص في المستقبلة، سيظهر لنا في السECTION التالية.

بالمزيد من المعلومات عن كيفية إستخدام المعلومات بشكل مخصص في المستقبلة، سيظهر لنا في السECTION التالية.

x := 76 + 145

بالمزيد من المعلومات عن كيفية إستخدام المعلومات بشكل مخصص في المستقبلة، سيظهر لنا في السECTION التالية.

لكننا سيتم إنشاء هذا المعلومة باسم معلومات مثل x:

package main

import "fmt"

func main() {
	x := 76 + 145
	fmt.Println(x)
}
Output
221

تعادل القيمة التي أعادتها Go هي 221 لأن المتغير x تم تعيينه ليساوي مجموع 76 و 145.

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

s := "Hello, World!"
f := 45.06
b := 5 > 9 // سيعيد القيمة المنطقية إما صواب أو خطأ
array := [4]string{"item_1", "item_2", "item_3", "item_4"}
slice := []string{"one", "two", "three"}
m := map[string]string{"letter": "g", "number": "seven", "symbol": "&"}

إذا طبعت أي من هذه المتغيرات، ستعيد Go ما يعادل ذلك المتغير. لنعمل مع تعيين العبارة لنوع بيانات السلسلة slice:

package main

import "fmt"

func main() {
	slice := []string{"one", "two", "three"}
	fmt.Println(slice)
}
Output
[one two three]

لقد قمنا بتعيين قيمة الشريحة []string{"one", "two", "three"} إلى المتغير slice، ثم استخدمنا دالة fmt.Println لطباعة هذه القيمة عن طريق استدعاء slice.

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

تعريف المتغيرات

في Go، هناك عدة طرق لتعريف متغير، وفي بعض الحالات، أكثر من طريقة لتعريف نفس المتغير والقيمة بالضبط.

يمكننا تعريف متغير يسمى i من نوع بيانات int دون تهيئة. هذا يعني أننا سنعرف مساحة لوضع قيمة، لكننا لن نعطيها قيمة أولية:

var i int

هذا ينشئ متغيرًا تم تعريفه كـ i من نوع بيانات int.

يمكننا تهيئة القيمة باستخدام المشغل (=) كما في المثال التالي:

var i int = 1

في لغة Go، كلا هذين الأشكال من الإعلانات تُسمى إعلانات متقدمة للمتغيرات.

يمكننا أيضًا استخدام إعلان مختصر للمتغير:

i := 1

في هذه الحالة، لدينا متغير يُدعى i، ونوع بيانات int. عندما لا نحدد نوع بيانات، سيقوم Go بتحديد نوع البيانات.

مع الطريقة الثلاثة لإعلان المتغيرات، اتبعت مجتمع Go التالي الأمثلة:

  • استخدم الشكل الطويل فقط، var i int، عندما لا تقوم بتهيئة المتغير.

  • استخدم الشكل القصير، i := 1، عند الإعلان والتهيئة.

  • إذا لم تكن ترغب في تحديد Go لنوع بياناتك، ومع ذلك ترغب في استخدام إعلان مختصر للمتغير، يمكنك ضم القيمة في النوع المرغوب فيه بالسياق التالي:

i := int64(1)

لا يُعتبر استخدام شكل إعلان متقدم للمتغيرات إذا كنا نقوم بتهيئة القيمة هو شيء امثلي في Go:

var i int = 1

من الجيد أن نتبع الطريقة التي تُتبعها مجتمع Go عادةً في إعلان المتغيرات حتى يتمكن الآخرون من قراءة برامجك بسلاسة.

القيم الصفرية

جميع الأنواع المدمجة لها قيمة صفرية. أي متغير مخصص قابل للاستخدام حتى لو لم يتم تعيين قيمة له أبداً. يمكننا رؤية القيم الصفرية للأنواع التالية:

package main

import "fmt"

func main() {
	var a int
	var b string
	var c float64
	var d bool

	fmt.Printf("var a %T = %+v\n", a, a)
	fmt.Printf("var b %T = %q\n", b, b)
	fmt.Printf("var c %T = %+v\n", c, c)
	fmt.Printf("var d %T = %+v\n\n", d, d)
}
Output
var a int = 0 var b string = "" var c float64 = 0 var d bool = false

استخدمنا الفعل %T في عبارة fmt.Printf. هذا يخبر الدالة بطباعة نوع البيانات للمتغير.

في Go، لأن جميع القيم لها قيمة صفرية، لا يمكننا الحصول على قيم غير معرفة مثل بعض اللغات الأخرى. على سبيل المثال، منطقي في بعض اللغات يمكن أن يكون غير معرف، صحيح، أو خطأ، مما يسمح بـ ثلاث حالات للمتغير. في Go، لا يمكننا الحصول على أكثر من حالتين لقيمة منطقية.

تسمية المتغيرات: القواعد والأسلوب

تسمية المتغيرات مرنة للغاية، ولكن هناك بعض القواعد التي يجب مراعاتها:

  • يجب أن تكون أسماء المتغيرات كلمة واحدة فقط (أي بدون مسافات).
  • يجب أن تتكون أسماء المتغيرات من حروف وأرقام وشرطات سفلية (_) فقط.
  • لا يمكن أن تبدأ أسماء المتغيرات برقم.

اتبعًا هذه القواعد، دعونا ننظر إلى أسماء متغيرات صالحة وغير صالحة:

Valid Invalid Why Invalid
userName user-name Hyphens are not permitted
name4 4name Cannot begin with a number
user $user Cannot use symbols
userName user name Cannot be more than one word

ومن المهم أيضًا أن تتذكر، عند تسمية المتغيرات، أنها حساسة بالحالة. هذه الأسماء userName، USERNAME، UserName، وuSERnAME هي جميعها متغيرات مختلفة تمامًا. من أفضل الممارسات تجنب استخدام أسماء متغيرات متشابهة ضمن برنامج لضمان أنك ومساهميك الحاليين والمستقبليين تتمكنون من تتبع متغيراتك.

بينما تكون المتغيرات حساسة بالحالة، فإن حالة الحرف الأول لمتغير لها معنى خاص في لغة Go. إذا بدأت المتغيرة بحرف أولي كبير، فإن تلك المتغيرة قابلة للوصول من خارج الحزمة التي تم إعلانها فيها (أو exported). إذا بدأت المتغيرة بحرف صغير، فإنها متاحة فقط داخل الحزمة التي تم إعلانها فيها.

var Email string
var password string

Email تبدأ بحرف أولي كبير ويمكن الوصول إليها من قبل الحزمات الأخرى. password تبدأ بحرف صغير، ويمكن الوصول إليها فقط داخل الحزمة التي تم إعلانها فيها.

من العادة في Go استخدام أسماء متغيرات قصيرة جدًا. من بين الخيارين استخدام userName وuser لمتغير، فإن الاختيار الأدبي سيكون user.

يلعب النطاق أيضًا دورًا في قصارة اسم المتغير. القاعدة هي أن كلما كان نطاق المتغير أصغر، كان اسم المتغير أصغر:

names := []string{"Mary", "John", "Bob", "Anna"}
for i, n := range names {
	fmt.Printf("index: %d = %q\n", i, n)
}

نستخدم المتغير `names` في نطاق أكبر، لذا من الشائع إعطاؤه اسمًا أكثر معنى للمساعدة في تذكر ما يعنيه في البرنامج. ومع ذلك، نستخدم المتغيرات `i` و `n` فورًا في السطر التالي من الكود، ثم لا نستخدمها مرة أخرى… وبسبب ذلك، لن يخلط ذلك شخصًا يقرأ الكود حول مكان استخدام المتغيرات، أو ما تعنيه.

التالي، دعونا نغطي بعض الملاحظات حول نمط المتغير. النمط هو استخدام `MixedCaps` أو `mixedCaps` بدلاً من الشرطات السفلية للأسماء متعددة الكلمات.

Conventional Style Unconventional Style Why Unconventional
userName user_name Underscores are not conventional
i index prefer i over index as it is shorter
serveHTTP serveHttp acronyms should be capitalized

الأهم من ذلك هو أن تكون متسقًا، وأن يتفق الفريق الذي تعمل معه على النمط.

إعادة تعيين المتغيرات

كما يوحي الكلمة “متغير”، يمكننا تغيير متغيرات Go بسهولة. هذا يعني أنه يمكننا ربط قيمة مختلفة بمتغير تم تعيينه سابقًا من خلال إعادة التعيين. أن تكون قادرًا على إعادة التعيين مفيد لأنه طوال مسار البرنامج قد نحتاج إلى قبول قيم جيدة التوليد في متغيرات تم تهيئتها بالفعل. قد نحتاج أيضًا إلى تغيير التعيين إلى شيء تم تعريفه مسبقًا.

معرفة أنه يمكننا إعادة تعيين متغير بسهولة يمكن أن يكون مفيدًا عند العمل على برنامج كبير كتبه شخص آخر، ولا يتضح ما هي المتغيرات التي تم تعريفها بالفعل.

لنُعَرِّف قيمة 76 لمتغير يُسمى i من نوع int، ثم نُعَدِّله ليصبح قيمته 42:

package main

import "fmt"

func main() {
	i := 76
	fmt.Println(i)

	i = 42
	fmt.Println(i)
}
Output
76 42

يوضح هذا المثال أنه يمكننا تعيين المتغير i بقيمة عدد صحيح أولاً، ثم إعادة تعيين المتغير i هذه المرة بقيمة 42.

ملاحظة: عندما تعلن و تهيئة متغير، يمكنك استخدام :=، ولكن عندما تريد فقط تغيير قيمة متغير معلن بالفعل، فأنت تحتاج فقط إلى استخدام عامل التساوي (=).

نظرًا لأن Go هي لغة مُصنَّفة (typed)، لا يمكننا تعيين نوع إلى آخر. على سبيل المثال، لا يمكننا تعيين القيمة "Sammy" إلى متغير من نوع int:

i := 72
i = "Sammy"

محاولة تعيين أنواع مختلفة لبعضها البعض ستؤدي إلى خطأ في وقت التحويل (compile-time error):

Output
cannot use "Sammy" (type string) as type int in assignment

لن تسمح لنا Go باستخدام اسم متغير أكثر من مرة:

var s string
var s string
Output
s redeclared in this block

إذا حاولنا استخدام تعريف المتغير المختصر أكثر من مرة لنفس اسم المتغير، سنتلقى أيضًا خطأ تحويل. قد يحدث هذا عن طريق الخطأ، لذا فهم ما تعنيه رسالة الخطأ مفيد:

i := 5
i := 10
Output
no new variables on left side of :=

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

التعيين المتعدد

يسمح لنا Go أيضًا بتعيين عدة قيم لعدة متغيرات في نفس السطر. يمكن أن تكون كل هذه القيم من نوع بيانات مختلف:

j, k, l := "shark", 2.05, 15
fmt.Println(j)
fmt.Println(k)
fmt.Println(l)
Output
shark 2.05 15

في هذا المثال، تم تعيين المتغير j إلى السلسلة "shark"، وتم تعيين المتغير k إلى العدد العشري 2.05، وتم تعيين المتغير l إلى العدد الصحيح 15.

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

المتغيرات العامة والمحلية

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

توجد المتغيرات العامة خارج الدوال. توجد المتغيرات المحلية داخل الدوال.

دعونا نلقي نظرة على المتغيرات العامة والمحلية في العمل:

package main

import "fmt"


var g = "global"

func printLocal() {
	l := "local"
	fmt.Println(l)
}

func main() {
	printLocal()
	fmt.Println(g)
}
Output
local global

نستخدم var g = "global" هنا لإنشاء متغير عالمي خارج المétodo. ومن ثم نحن نحدد المétodo printLocal(). داخل المétodo تم تعيين متغير محلي يدعى l ومن ثم نسرده. يتنتهي البرنامج بهذه المدعومة printLocal() ومن ثم نسرد المتغير العالمي g.

لأن g متغير عالمي يمكننا أن نشير إليه في printLocal(). دعونا نغير البرنامج السابق للقيام بذلك:

package main

import "fmt"


var g = "global"

func printLocal() {
	l := "local"
	fmt.Println(l)
	fmt.Println(g)
}

func main() {
	printLocal()
	fmt.Println(g)
}
Output
local global global

نبدأ بالتعيين لمتغير عالمي g, var g = "global". في المétodo main نطلب المétodo printLocal, الذي يدعوم متغير محلي l ونسرده, fmt.Println(l). ثم ينسرد printLocal المتغير العالمي g, fmt.Println(g). 即使 g لم يتم تعيينه في printLocal, يمكن الوصول إليه لأنه تم تعيينه في نطاق عالمي. وأخيرًا ينسرد المétodo main g أيضًا.

الآن دعونا نحاول إنجاز متغير محلي خارج المétodo:

package main

import "fmt"

var g = "global"

func printLocal() {
	l := "local"
	fmt.Println(l)
}

func main() {
	fmt.Println(l)
}

Output
undefined: l

لا يمكننا استخدام متغير محلي خارج المétodo الذي يمكن تعيينه فيه. إذا حاولت فعل ذلك سوف تحصل على خطأ undefined عند التجهيز.

دعونا ننظر إلى مثال آخر حيث نستخدم نفس الاسم لمتغير عالمي ومتغير محلي:

package main

import "fmt"

var num1 = 5

func printNumbers() {
	num1 := 10
	num2 := 7  

	fmt.Println(num1)
	fmt.Println(num2)
}

func main() {
	printNumbers()
	fmt.Println(num1)
}
Output
10 7 5

في هذا البرنامج، قمنا بتعريف المتغير `num1` مرتين. أولاً، قمنا بتعريف `num1` على المستوى العام، `var num1 = 5`، ومرة أخرى ضمن النطاق المحلي للدالة `printNumbers`، `num1 := 10`. عندما نطبع `num1` من برنامج `main`، نرى القيمة `5` مطبوعة. وذلك لأن `main` يرى فقط تعريف المتغير العام. ومع ذلك، عندما نطبع `num1` من دالة `printNumbers`، فإنها ترى التعريف المحلي، وستطبع القيمة `10`. على الرغم من أن `printNumbers` تنشئ متغيرًا جديدًا يسمى `num1` وتعينه القيمة `10`، إلا أن ذلك لا يؤثر على المثيل العام لـ `num1` بالقيمة `5`.

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

الثوابت

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

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

لإعلان مستمر، يمكننا استخدام الصيغة التالية:

const shark = "Sammy"
fmt.Println(shark)
Output
Sammy

إذا حاولنا تعديل مستمر بعد تعليمته، سنحصل على خطأ في وقت التركيب:

Output
cannot assign to shark

المستمرات يمكن أن تكون غير مصفوفة. هذا يمكن أن يكون مفيدًا عند العمل مع أرقام مثل بيانات النوع الصحيح. إذا كان المستمر غير مصفوف، فإنه يتم تحويله بوضوح، بينما المستمرات المصفوفة لا. دعونا نرى كيف يمكننا استخدام المستمرات:

package main

import "fmt"

const (
	year     = 365
	leapYear = int32(366)
)

func main() {
	hours := 24
	minutes := int32(60)
	fmt.Println(hours * year)    
	fmt.Println(minutes * year)   
	fmt.Println(minutes * leapYear)
}
Output
8760 21900 21960

إذا قمت بإعلان مستمر بنوع، فسيكون ذلك النوع المحدد بالضبط. هنا عندما نعلن المستمر leapYear، نحدده كنوع بيانات int32. لذلك، هو مستمر مصفوف، مما يعني أنه يمكنه التشغيل فقط مع أنواع بيانات int32. المستمر year الذي نعلنه بدون نوع، لذلك يعتبر غير مصفوف. وبسبب هذا، يمكنك استخدامه مع أي نوع بيانات صحيح.

عندما تم تعريف hours، تم تصديق أنها من نوع int لأننا لم نعطيها نوعًا بوضوح، hours := 24. عندما علنا minutes، قمنا بتعريفها بوضوح كـ int32، minutes := int32(60).

الآن دعونا نتحدث عن كل حساب ولماذا يعمل:

hours * year

في هذه الحالة، hours هو int، وyears هو غير محدد النوع. عندما يتم ترجمة البرنامج، يتم تحويل years صراحة إلى int، مما يسمح بنجاح عملية الضرب.

minutes * year

في هذه الحالة، minutes هو int32، وyear هو غير محدد النوع. عندما يتم ترجمة البرنامج، يتم تحويل years صراحة إلى int32، مما يسمح بنجاح عملية الضرب.

minutes * leapYear

في هذه الحالة، minutes هو int32، وleapYear هو ثابت محدد النوع من int32. لا يوجد شيء يجب على المترجم القيام به هذه المرة لأن كلا المتغيرين من نفس النوع بالفعل.

إذا حاولنا ضرب نوعين ليسا محددين النوع ولا يتوافقان، لن يتم ترجمة البرنامج:

fmt.Println(hours * leapYear)
Output
invalid operation: hours * leapYear (mismatched types int and int32)

في هذه الحالة، تم استنتاج hours كint، وتم الإعلان عن leapYear بشكل صريح كint32. نظرًا لأن Go هي لغة محددة النوع، فإن int وint32 ليسا متوافقين للعمليات الرياضية. لضربهما، ستحتاج لتحويل أحدهما إلى int32 أو int.

الخاتمة

في هذا الدرس راجعنا بعض الحالات الاستخدامية الشائعة للمتغيرات داخل Go. المتغيرات هي عنصر بناء مهم في البرمجة، حيث تعمل كرموز تمثل قيمةً لنوع البيانات الذي نستخدمه في برنامج.

Source:
https://www.digitalocean.com/community/tutorials/how-to-use-variables-and-constants-in-go