اذهب إلى المحتوى

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


هدى جبور

تُعَدّ المتغيرات مفهومًا برمجيًا مهمًا يتوجّب عليك فهمه وإتقانه، فهي رموز تدل على القيم التي تستخدِمها في برنامجك، وسنتحدّث في هذا المقال عن أساسيات المتغيرات وأفضل الممارسات عند التعامل معها.

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

تُخصَّص للمتغير من الناحية الفنية مساحة تخزينية في الذاكرة توضَع القيمة المرتبطة به فيها، ويُستخدَم اسم المتغير للإشارة إلى تلك القيمة المُخزَّنة في ذاكرة البرنامج والتي هي جزء من ذاكرة الحاسوب، ويشبه المُتغيِّر بأنه عنوان تُعلِّقه على قيمة مُعيَّنة كما في الصورة التالية، إذ تشير variable name إلى اسم المتغير وvalue إلى قيمته:

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

لنفترض أنه لدينا عددًا صحيحًا يساوي 1032049348 ونريد تخزينه في متغيِّر بدلًا من إعادة كتابة هذا العدد الطويل كل مرة، لذلك سنستخدِم شيئًا يُسهِّل تذكّره مثل المتغير i،

i := 1032049348

إذا نظرنا إليه على أنَّه عنوانٌ مرتبط بقيمة، فسيبدو على النحو التالي:

منح عدد صحيح تسمية متغير سهل لحفظه بالذاكرة

يمثّل i اسم المتغير وتُربَط به القيمة 1032049348 التي هي من نوع عدد صحيح integer، كما تُعَدّ العبارة i := 1032049348 أنها تعليمة إسناد وتتألف من الأجزاء التالية:

  • اسم المتغير i.
  • معامِل تعريف المتغير المختصر =:.
  • القيمة التي أُسنِدَت إلى المتغير 1032049348.

نوع البيانات تكتشفه اللغة تلقائيًا.

تُسند القيمة السابقة من خلال هذه الأجزاء إلى المتغير i، ونكون عند تحديد قيمة المتغير قد هيّئنا أو أنشأنا ذلك المتغير، وبعد ذلك يمكنك استخدام ذلك المتغير بدلًا من القيمة كما في المثال التالي:

package main

import "fmt"

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

ويكون الخرج كما يلي:

1032049348

يسهل استخدام المتغيرات علينا إجراء العمليات الحسابية، إذ سنعتمد في المثال التالي على التعليمة السابقة i = 1032049348 وسنطرح من المتغير i القيمة 813 كما يلي:

fmt.Println(i - 813)

ويكون الخرج كما يلي:

1032048535

يُجري جو Go العملية الحسابية ويطرح 813 من المتغير i ويعيد القيمة 1032048535.

يمكن ضبط المتغيرات وجعلها تساوي ناتج عملية حسابية ما، إذ سنجمع الآن عددين معًا ونخزِّن قيمة المجموع في المتغير x:

x := 76 + 145

يشبه المثال أعلاه إحدى المعادلات التي تراها في كتب الجبر، إذ تُستخدَم المحارف والرموز لتمثيل الأعداد والكميات داخل الصيغ والمعادلات، وبصورة مماثلة تُعَدّ المتغيرات أسماءً رمزيةً تمثِّل قيمةً من نوع بيانات معيّن، والفرق في لغة جو أنه عليك التأكد دائمًا من كتابة المتغير على الجانب الأيسر من المعادلة، ولنطبع الآن x كما يلي:

package main

import "fmt"

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

يكون الخرج كما يلي:

221

أعادت جو القيمة 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": "&"}

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

package main

import "fmt"

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

يكون الخرج كما يلي:

[one two three]

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

تأخذ المتغيرات مساحةً صغيرةً من ذاكرة الحاسوب لتسمح لك بوضع القيم في تلك المساحة.

التصريح عن المتغيرات

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

var i int

يمكن تهيئة المتغير من خلال استخدام المعامِل = كما يلي:

var i int = 1

يُطلق على كل من هذين النموذجين للتصريح بالتصريح الطويل للمتغيرات long variable declaration، في حين يمثِّل النوذج التالي الأسلوب القصير أو المختصر short variable declaration:

i := 1

في هذه الحالة نحن لانحدد نوع البيانات ولانستخدِم الكلمة المفتاحية var، حيث يستنتج جو الصنف تلقائيًّا.

تبنى مجتمع جو المصطلحات التالية من خلال الطرق الثلاث للتصريح عن المتغيرات:

  • استخدِم النموذج الطويل var i int عندما لا تُهيئ المتغير فقط.
  • استخدِم النموذج المختصر i := 1، عند التصريح والتهيئة.
  • إذا لم تكن ترغب في أن يستنتج جو نوع البيانات الخاصة بك، ولكنك لا تزال ترغب في استخدام تصريح قصير للمتغير، فيمكنك تمرير القيمة إلى باني نوع البيانات الذي تريده كما يلي:
i := int64(1)

في حين لا يُعَدّ استخدام النموذج الطويل التالي من المصطلحات الشائعة في جو:

var i int = 1

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

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

تأخذ أنواع البيانات المُعرّفة ضمن جو قيمًا صفرية على أساس قيمة أوليّة في حال لم تحدَّد لها قيمة أولية كما في المثال التالي:

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)
}

يكون الخرج كما يلي:

var a int =  0
var b string = ""
var c float64 = 0
var d bool = false

يُستخدم الرمز T% داخل الدالة fmt.Printf لطباعة نوع بيانات المتغير. لاحظ أن القيمة الصفرية المقابلة للسلاسل النصية تمثّلها السلسلة "" والقيمة الصفرية للبيانات المنطقية bool تمثّلها القيمة false. هذا مهم ففي بعض اللغات توضع قيم عشوائية للمتغيرات في حال لم تُهيئ بقيمة أولية، وبالتالي قد نرى أن متغيرًا منطقيًا يأخذ القيمة None أو شيء آخر، وهذا يُنتج عدم اتساقية للبيانات، فالصنف bool لا يمكن أن يكون إلا False أو True.

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

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

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

دعنا نلقي نظرةً على بعض الأمثلة باتباع القواعد المذكورة أعلاه:

صالح غير صالح التفسير
user-Name userName غير مسموح استخدام الواصلات Hyphens
4i i4 لا يمكن البدء برقم
user user$ لا يمكن استخدام أيّ رمز غير الشرطة السفلية
user Name userName لا ينبغي للمتغير أن يتكون من أكثر من كلمة واحدة

من الأمور التي يجب أخذها في الحسبان عند تسمية المتغيرات هو أنَّها حساسة لحالة المحارف، وهذا يعني أنَّ my_int و MY_INT و My_Int و mY_iNt كلها مختلفة، أي ينبغي أن تتجنب استخدام أسماء متغيرات متماثلة لضمان ألا يحدث خلط عندك أو عند المتعاونين معك حاليًا أو في المستقبل، كما أنّ حالة المحرف الأول من المتغير لها معنى خاص في جو، فإذا بدأ المتغير بمحرف كبير، فيمكن الوصول إلى هذا المتغير من خارج الحزمة التي أُعلن عنه فيها؛ أما إذا بدأ المتغير بمحرف صغير، فهو متاح فقط داخل الحزمة التي أُعلن عنه فيها كما في المثال التالي:

var Email string
var password string

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

من الشائع في جو استخدام أسماء متغيرات مختَصرة جدًا أو قصيرة، إذ يُفضّل كتابة user بدلًا من كتابة userName، كما يلعب النطاق Scope دورًا في اختصار اسم المتغير، فكلما كان نطاق المتغير أصغر، كلما كان اسم المتغير أصغر:

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

عند استخدام المتغيرات نفسها في أماكن مختلفة من البرنامج، يُفضّل إعطاءها أسماء واضحة لكي لا يربك ذلك الأشخاص الآخرين الذين يقرأون شيفرة البرنامج، لكن في المثال أعلاه استخدمنا المتغيرين i و n لأننا نستخدِمهما مرةً واحدةً فقط ومباشرةً، لذا لن يسبب ذلك أيّ مشكلة في قراءة أو فهم الشيفرة.

يُفضّل عند تسمية المتغيرات استخدام تنسيق سنام الجَمل camelCase أو سنام الجمل المرتفع CamelCase عند كتابة المتغيرات بدلًا من استعمال الشرطة السفلية multi_word أو العادية multi-word. الجدول التالي يتضمن بعض الملاحظات المفيدة:

شائع غير شائع التفسير
user_name userName الشرطات السفلية غير شائعة الاستخدام
index i لا يُفضّل استخدام الاسم الكامل وإنما الاسم المختَصر
serveHttp serveHTTP يجب كتابة الاختصارات بمحارف كبيرة

الخيار الأهم الذي عليك التمسك به هو الاتساق، فإذا بدأت العمل في مشروع يستخدِم تنسيق سنام الجَمل في تسمية المتغيرات، فمن الأفضل الاستمرار في استخدام ذلك التنسيق.

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

يمكن تغيير قيم المتغيرات في جو بسهولة كما تشير كلمة "متغير"، وهذا يعني أنه يمكنك إسناد قيمة مختلفة إلى متغير قد أُسنِدَت له قيمة مسبقًا بسهولة بالغة، إذ تُعَدّ القدرة على إعادة الإسناد مفيدةً للغاية، فقد تحتاج مثلًا خلال أطوار برنامجك إلى استلام قيم جديدة من المستخدِم وإسنادها إلى متغير قد حُدّدَت قيمته سابقًا أو قد تحتاج إلى تغيير قيمة متغير اعتمادًا على أحداث معينة، كما أنّ سهولة إعادة إسناد قيم المتغيرات مفيدة أيضًا في البرامج الكبيرة التي قد تحتاج خلالها إلى تغيير القيم باستمرار.

سنُسّند إلى المتغير i الذي نوعه int العدد 76 ثم نعيد إسناده بالقيمة 42:

package main

import "fmt"

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

    i = 42
    fmt.Println(i)
}

يكون الخرج كما يلي:

76
42

يوضِّح المثال أنه يمكننا تعريف متغير وإسناد قيمة له ثم تغيير قيمته بإعادة إسناد قيمة أخرى له ما بسهولة من خلال استخدام معامل الإسناد =.

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

بما أنّ لغة جو هي لغة تحتاج إلى تحديد النوع typed language، فعند إسناد قيمة جديدة إلى متغير يجب أن تكون هذه القيمة من نوع بيانات المتغير، فلا يمكن مثلًا إسناد قيمة تُمثّل عددًا صحيحًا إلى متغير من نوع سلسلة نصيّة أو العكس، فإذا حاولت فعل ذلك:

i := 72
i = "Sammy"

فستحصل على الخرج التالي الذي يُمثّل خطأً، والذي يخبرك فيه بأن مثل هذه العملية غير مسموحة كما أوضحنا:

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

لا يمكنك أيضًا تعريف أكثر من متغير واحد بالاسم نفسه:

var s string
var s string

ستحصل على خطأ كما يلي:

s redeclared in this block

لايمكنك استخدام المعامل =: بدلًا من = عند محاولة تعديل قيمة متغير كما أشرنا سابقًا كما يلي:

i := 5
i := 10

فالخرج التالي يُظهر خطًأ مفاده أنه لا يوجد متغير جديد على الطرف الأيسر، إذ تُفسِّر جو وجود المعامِل =: بأننا نريد التصريح عن متغير جديد أو تهيئته بقيمة أولية.

no new variables on left side of :=

ستحسِّن تسمية المتغيرات الخاصة بك بطريقة سليمة قابلية قراءة برنامجك سواءً لك أو للآخرين ولاسيما عند العودة إليه مستقبلًا.

الإسناد المتعدد

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

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

يكون الخرج كما يلي:

shark
2.05
15

هنا أُسند إلى المتغير j السلسلة "shark" والمتغير k القيمة 2.05 والمتغير l القيمة 15.

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

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

عند استخدام المتغيرات داخل البرنامج، من المهم أن تضع نطاق المتغير variable scope في حساباتك، إذ يشير نطاق المتغير إلى المواضع التي يمكن الوصول منها إلى المتغير داخل الشيفرة، حيث لا يمكن الوصول إلى جميع المتغيرات من جميع أجزاء البرنامج، فبعض المتغيرات عامة وبعضها محلي، إذ تُعرّف المتغيرات العامة خارج الدوال؛ أما المتغيرات المحلية، فتوجد داخل الدوال، ويعطي المثال التالي فكرةً عن المتغيرات العامة والمحلية:

package main
import "fmt"


var g = "global"

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

func main() {
    printLocal()
    fmt.Println(g)
}

يكون الخرج كما يلي:

local
global

استخدمنا var g = "global"‎ للتصريح عن متغير عام خارج الدالة ثم عرّفنا الدالة printLocal()‎ وبداخلها المتغير المحلي 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)
}

يكون الخرج كما يلي:

local
global
global

يختلف هذا المثال عن المثال السابق في أننا نحاول الوصول إلى المتغير العام g مباشرةً من داخل الدالة printLocal وهذا مايُفسّره خرج البرنامج، حيث تطبع الدالة printLocal قيمة كل من l و g هذه المرة وليس فقط l.

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

package main

import "fmt"

var g = "global"

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

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

يكون الخرج كما يلي:

undefined: l

أي أن المتغير غير مُعرّف في نطاق الدالة الرئيسية، إذ لا يمكنك الوصول إلى المتغير المحلي إلا ضمن النطاق الذي عُرّف ضمنه ونطاقه في هذا البرنامج هو الدالة printLocal، فلنلق الآن نظرةً على مثال آخر بحيث نستخدِم اسم المتغير نفسه لمتغير عام ومتغير محلي كما يلي:

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)
}

يكون الخرج كما يلي:

10
7
5

صرّحنا في البرنامج السابق عن المتغير num1 مرتين؛ مرة ضمن النطاق العام var num1 = 5 والأخرى num1 := 10 ضمن نطاق محلي داخل الدالة printNumbers.

عندما نطبع num1 من البرنامج الرئيسي -أي داخل الدالة main-، سنرى قيمة 5 مطبوعةً لأن main لا ترى سوى المتغير العام؛ أما عندما نطبع num1 من داخل الدالة printNumbers، فإنه سيرى المتغير المحلي وسوف يطبع القيمة 10، وعلى الرغم من أنّ printNumbers يُنشئ متغيرًا جديدًا num1 ويُسند له قيمة 10، إلا أنه لا يؤثر على المتغير العام وستبقى قيمته 5.

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

الثوابت

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

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

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

يكون الخرج كما يلي:

Sammy

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

cannot assign to shark

يمكن أن تكون الثوابت من دون نوع untyped، وهذا مفيد عند التعامل مع أعداد مثل بيانات الأعداد الصحيحة، فإذا كان الثابت من النوع untyped فيُحوّل صراحةً، حيث لا تُحوَّل الثوابت التي لديها نوع typed.

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)
}

يكون الخرج كما يلي:

8760
21900
21960

عرّفنا المتغير year على أنه ثابت من دون نوع untyped، وبالتالي يمكن استخدامه مع أيّ نوع بيانات آخر؛ أما لو حددنا له نوعًا وليكن int32 كما فعلنا مع الثابت leapYear، فلن نستطيع التعامل معه إلا مع بيانات من النوع نفسه لأنه typed constant.

عندما عرّفنا المتغير hours بدون تحدبد نوعه، استنتجت جو تلقائيًا أنه من النوع int لأننا أسندنا القيمة 24 له؛ أما عندما عرّفنا المتغير minutes، فقد حددنا له صراحةً نوع البيانات int32 من خلال التعليمة minutes := int32(60)‎.

دعنا الآن نتصفح كل عملية حسابية وسبب نجاحها:

hours * year

هنا المتغير hours من النوع الصحيح والثابت year من دون نوع، وبالتالي لإجراء العملية تحوِّل جو الثابت year إلى النوع المطلوب؛ أي تحوّله إلى النوع int.

minutes * year

هنا المتغير minutes من النوع int32 والثابت year من دون نوع، لذا تحوِّل جو الثابت year إلى النوع int32.

minutes * leapYear

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

إذا حاولت إجراء عملية حسابية مثل الضرب بين نوعين مختلفين أي typed، فلن ينجح الأمر:

fmt.Println(hours * leapYear)

يكون الخرج كما يلي:

invalid operation: hours * leapYear (mismatched types int and int32)

هنا hours هي من النوع int كما تحدثنا والثابت leapYear من النوع int32، بما أنّ جو لغة تعتمد على تحديد النوع typed language، فهذا يعني أنّ النوعين int و int32 غير متوافقين لإجراء العمليات الحسابية، ولحل المشكلة عليك تحويل أحدهما إلى نوع الآخر.

الخاتمة

لقد مررنا في هذا المقال على بعض حالات الاستخدام الشائعة للمتغيرات في جو، فالمتغيرات هي لبنة مهمة في البرمجة، إذ تُمثِّل حاضنةً لمختلف أنواع البيانات في جو ومن خلالها نستطيع إجراء العديد من العمليات وتخزين القيم التي نحتاجها.

ترجمة -وبتصرف- للمقال How To Use Variables and Constants in Go لصاحبه Gopher Guides.

اقرأ أيضًا


تفاعل الأعضاء

أفضل التعليقات

لا توجد أية تعليقات بعد



انضم إلى النقاش

يمكنك أن تنشر الآن وتسجل لاحقًا. إذا كان لديك حساب، فسجل الدخول الآن لتنشر باسم حسابك.

زائر
أضف تعليق

×   لقد أضفت محتوى بخط أو تنسيق مختلف.   Restore formatting

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   جرى استعادة المحتوى السابق..   امسح المحرر

×   You cannot paste images directly. Upload or insert images from URL.


×
×
  • أضف...