برنامج Golang التعليمي: تعلم لغة البرمجة Go للمبتدئين

ما هو جو؟

Go (المعروفة أيضًا باسم Golang) هي لغة برمجة مفتوحة المصدر طورتها شركة Google. وهي لغة مُجمَّعة ذات نوع ثابت. تدعم Go البرمجة المتزامنة، أي أنها تسمح بتشغيل عمليات متعددة في وقت واحد. ويتم تحقيق ذلك باستخدام القنوات والروتينات وما إلى ذلك. تحتوي لغة Go على جمع القمامة الذي يقوم بإدارة الذاكرة ويسمح بالتنفيذ المؤجل للوظائف.

سوف نتعلم جميع أساسيات Golang في البرنامج التعليمي Learn Go Language.

كيفية تنزيل وتثبيت GO

الخطوة 1) انتقل إلى البرنامج المساعد في التأليف https://golang.org/dl/. قم بتنزيل الملف الثنائي لنظام التشغيل الخاص بك.

الخطوة 2) Double انقر فوق المثبت وانقر فوق تشغيل.

الخطوة 3) انقر فوق التالي

الخطوة 4) حدد مجلد التثبيت وانقر فوق "التالي".

الخطوة 5) انقر فوق "إنهاء" بمجرد اكتمال التثبيت.

الخطوة 6) بمجرد اكتمال التثبيت، يمكنك التحقق منه عن طريق فتح الجهاز والكتابة

go version

سيعرض هذا إصدار go المثبت

برنامج Your First Go – Go Hello World!

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

go run <filename>

قم بإنشاء ملف يسمى first.go وأضف الكود أدناه إليه ثم احفظه

package main
import ("fmt")

func main() {
	fmt.Println("Hello World! This is my first Go program\n")
}

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

اذهب واركض أولاً

تستطيع أن ترى الطباعة الإخراج

Hello World! This is my first Go program

الآن دعونا نناقش البرنامج أعلاه.

الحزمة الرئيسية - يجب أن يبدأ كل برنامج Go Language باسم الحزمة. يسمح لنا Go باستخدام الحزم في برامج go أخرى، وبالتالي يدعم إمكانية إعادة استخدام التعليمات البرمجية. يبدأ تنفيذ برنامج Go بالكود الموجود داخل الحزمة المسماة main.

استيراد FMT - يستورد الحزمة FMT. تنفذ هذه الحزمة وظائف الإدخال/الإخراج.

func main() – هذه هي الوظيفة التي يبدأ منها تنفيذ البرنامج. يجب دائمًا وضع الوظيفة الرئيسية في الحزمة الرئيسية. ضمن main()، يمكنك كتابة الكود داخل { }.

fmt.Println – سيؤدي هذا إلى طباعة النص على الشاشة بواسطة وظيفة Println الخاصة بـ fmt.

ملاحظة: في الأقسام التالية من برنامج Go التعليمي هذا، عندما تذكر تنفيذ/تشغيل التعليمات البرمجية، فهذا يعني حفظ التعليمات البرمجية في ملف بامتداد .go وتشغيله باستخدام بناء الجملة

    go run <filename>

أنواع البيانات

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

هناك ثلاثة أنواع أساسية في لغة Go

أنواع رقمية - تمثل القيم الرقمية التي تتضمن قيمًا صحيحة وفاصلة عائمة وقيمًا مركبة. هناك أنواع رقمية مختلفة:

int8 - أعداد صحيحة موقعة 8 بت.

int16 - أعداد صحيحة موقعة 16 بت.

int32 - أعداد صحيحة موقعة 32 بت.

int64 - أعداد صحيحة موقعة 64 بت.

uint8 – 8 أعداد صحيحة غير موقعة.

uint16 – 16 أعداد صحيحة غير موقعة.

uint32 – 32 أعداد صحيحة غير موقعة.

uint64 – 64 أعداد صحيحة غير موقعة.

float32 – أرقام ذات فاصلة عائمة مكونة من 32 بت.

float64 – أرقام ذات فاصلة عائمة مكونة من 64 بت.

complex64 – يحتوي على أجزاء حقيقية وخيالية float32.

complex128 – يحتوي على أجزاء حقيقية وخيالية float32.

أنواع السلاسل - يمثل تسلسلًا من البايتات (الأحرف). يمكنك إجراء عمليات مختلفة على السلاسل مثل تجميع السلاسل واستخراج سلاسل فرعية وما إلى ذلك

أنواع منطقية - يمثل قيمتين، إما صحيح أو خطأ.

واجهة جولانج

واجهة جولانج عبارة عن مجموعة من توقيعات الطريقة التي يستخدمها النوع لتنفيذ سلوك الكائنات. الهدف الرئيسي لواجهة Golang هو توفير توقيعات الطريقة بالأسماء والوسائط وأنواع الإرجاع. الأمر متروك للنوع للإعلان عن الطريقة وتنفيذها. يمكن الإعلان عن واجهة في Golang باستخدام الكلمة الأساسية "interface".

المتغيرات

تشير المتغيرات إلى موقع الذاكرة الذي يخزن نوعًا ما من القيمة. تمثل معلمة النوع (في بناء الجملة أدناه) نوع القيمة التي يمكن تخزينها في موقع الذاكرة.

يمكن الإعلان عن المتغير باستخدام بناء الجملة

    var <variable_name> <type>

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

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

    var <variable_name> <type> = <value>

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

    var <variable_name> = <value>

يمكنك أيضًا الإعلان عن متغيرات متعددة باستخدام بناء الجملة

    var <variable_name1>, <variable_name2>  = <value1>, <value2>

يحتوي البرنامج أدناه في هذا البرنامج التعليمي Go على بعض أمثلة Golang للإعلانات المتغيرة

 
package main
import "fmt"

func main() {
    //declaring a integer variable x
    var x int
    x=3 //assigning x the value 3 
    fmt.Println("x:", x) //prints 3
    
    //declaring a integer variable y with value 20 in a single statement and prints it
    var y int=20
    fmt.Println("y:", y)
    
    //declaring a variable z with value 50 and prints it
    //Here type int is not explicitly mentioned 
    var z=50
    fmt.Println("z:", z)
    
    //Multiple variables are assigned in single line- i with an integer and j with a string
    var i, j = 100,"hello"
    fmt.Println("i and j:", i,j)
}

سيكون الإخراج

x: 3
y: 20
z: 50
i and j: 100 hello

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

    <variable_name> := <value>

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

قم بإنشاء ملف يسمى assign.go بالكود التالي

package main
import ("fmt")

func main() {
	a := 20
	fmt.Println(a)

	//gives error since a is already declared
	a := 30
	fmt.Println(a)
}

نفّذ الأمر go، ثم قم بتشغيل sign.go لترى النتيجة كما يلي

./assign.go:7:4: no new variables on left side of :=		

المتغيرات المعلنة بدون قيمة أولية ستكون 0 للأنواع الرقمية، وخطأ للأنواع المنطقية، وسلسلة فارغة للسلاسل

ثابت

المتغيرات الثابتة هي تلك المتغيرات التي لا يمكن تغيير قيمتها بمجرد تعيينها. يتم الإعلان عن الثابت في لغة البرمجة Go باستخدام الكلمة الأساسية "const"

قم بإنشاء ملف يسمى constant.go وباستخدام الكود التالي

package main
import ("fmt")

func main() {
	const b =10
	fmt.Println(b)
	b = 30
	fmt.Println(b)
}

قم بتنفيذ go run Constant.go لترى النتيجة كما يلي

.constant.go:7:4: cannot assign to b

للحصول على أمثلة حلقة

تُستخدم الحلقات لتنفيذ مجموعة من العبارات بشكل متكرر بناءً على شرط ما. توفر معظم لغات البرمجة ثلاثة أنواع من الحلقات – for، while، do while. لكن لغة البرمجة Go تدعم الحلقة فقط.

بناء جملة حلقة Golang هو

for initialisation_expression; evaluation_expression; iteration_expression{
   // one or more statement
}

يتم تنفيذ التهيئة_التعبيرية أولاً (ومرة واحدة فقط) في حلقة Golang.

ثم يتم تقييم تعبير التقييم وإذا كان صحيحًا، فسيتم تنفيذ الكود الموجود داخل الكتلة.

يتم تنفيذ معرف iteration_expression، ويتم تقييم Evaluation_expression مرة أخرى. إذا كان هذا صحيحًا، فسيتم تنفيذ كتلة البيان مرة أخرى. سيستمر هذا حتى يصبح تعبير التقييم خطأ.

انسخ البرنامج أدناه إلى ملف وقم بتنفيذه لرؤية حلقة Golang for التي تطبع الأرقام من 1 إلى 5

package main
import "fmt"

func main() {  
var i int
for i = 1; i <= 5; i++ {
fmt.Println(i)
    }
}

الإخراج

1
2
3
4
5

إذا كان غير ذلك

إذا كان آخر هو بيان مشروط. النحو هو

if condition{
// statements_1
}else{
// statements_2
}

هنا يتم تقييم الشرط وإذا كان صحيحا سيتم تنفيذ العبارات_1 وإلا سيتم تنفيذ العبارات_2.

يمكنك استخدام عبارة if بدون else أيضًا. يمكنك أيضًا ربط عبارات if else . سوف تشرح البرامج أدناه المزيد حول ما إذا كان الأمر كذلك.

قم بتنفيذ البرنامج أدناه. يتحقق مما إذا كان الرقم x أقل من 10. إذا كان الأمر كذلك، فسيتم طباعة "x أقل من 10"

package main
import "fmt"

func main() {  
    var x = 50
    if x < 10 {
        //Executes if x < 10
        fmt.Println("x is less than 10")
    } 
}

نظرًا لأن قيمة x أكبر من 10، فلن يتم تنفيذ العبارة الموجودة في حالة حالة الكتلة.

الآن انظر البرنامج أدناه. في هذا البرنامج التعليمي للغة برمجة Go، لدينا كتلة else والتي سيتم تنفيذها عند فشل تقييم if.

package main
import "fmt"

func main() {  
    var x = 50
    if x < 10 {
        //Executes if x is less than 10
        fmt.Println("x is less than 10")
    } else {
        //Executes if x >= 10
        fmt.Println("x is greater than or equals 10")
    }
}

هذا البرنامج سوف يعطيك الإخراج

x is greater than or equals 10

الآن في هذا البرنامج التعليمي لـ Go، سنرى برنامجًا يحتوي على عدة كتل if else (متسلسلة إذا else). قم بتنفيذ مثال Go أدناه. يتحقق مما إذا كان الرقم أقل من 10 أو بين 10-90 أو أكبر من 90.

package main
import "fmt"

func main() {  
    var x = 100
    if x < 10 {
        //Executes if x is less than 10
        fmt.Println("x is less than 10")
    } else if x >= 10 && x <= 90 {
        //Executes if x >= 10 and x<=90
        fmt.Println("x is between 10 and 90")
    } else {
        //Executes if both above cases fail i.e x>90
        fmt.Println("x is greater than 90")
    }
}

هنا أولاً تتحقق حالة if مما إذا كانت x أقل من 10 أم لا. لذا فهو يتحقق من الشرط التالي (وإلا إذا) ما إذا كان يتراوح بين 10 و90 وهو خطأ أيضًا. لذلك يقوم بعد ذلك بتنفيذ الكتلة الموجودة أسفل القسم الآخر الذي يعطي الإخراج

x is greater than 90

مفاتيح

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

switch expression {
    case value_1:
        statements_1
    case value_2:
        statements_2
    case value_n:
        statements_n
    default:
        statements_default
    }

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

قم بتنفيذ البرنامج أدناه

package main
import "fmt"

func main() {  
    a,b := 2,1
    switch a+b {
    case 1:
        fmt.Println("Sum is 1")
    case 2:
        fmt.Println("Sum is 2")
    case 3:
        fmt.Println("Sum is 3")
    default:
        fmt.Println("Printing default")
    }
}

سوف تحصل على الإخراج كـ

Sum is 3		

قم بتغيير قيمة a وb إلى 3 وستكون النتيجة

Printing default

يمكنك أيضًا الحصول على قيم متعددة في الحالة عن طريق فصلها بفاصلة.

المصفوفات

يمثل المصفوفة حجمًا ثابتًا، وتسلسلًا محددًا للعناصر من نفس النوع. لا يمكن أن يكون لديك مصفوفة تحتوي على عدد صحيح وأحرف معًا. لا يمكنك تغيير حجم المصفوفة بمجرد تحديد الحجم.

صيغة التصريح عن المصفوفة هي

var arrayname [size] type

يمكن تعيين قيمة لكل عنصر من عناصر المصفوفة باستخدام بناء الجملة

arrayname [index] = value

يبدأ فهرس المصفوفة من 0 إلى الحجم-1.

يمكنك تعيين قيم لعناصر المصفوفة أثناء الإعلان باستخدام بناء الجملة

arrayname := [size] type {value_0,value_1,…,value_size-1} 

يمكنك أيضًا تجاهل معلمة الحجم أثناء الإعلان عن المصفوفة بالقيم عن طريق استبدال الحجم بـ ... وسيجد المترجم الطول من عدد القيم. بناء الجملة هو

arrayname :=  […] type {value_0,value_1,…,value_size-1}

يمكنك العثور على طول المصفوفة باستخدام بناء الجملة

len(arrayname)

قم بتنفيذ مثال Go أدناه لفهم المصفوفة

package main
import "fmt"

func main() {  
    var numbers [3] string //Declaring a string array of size 3 and adding elements 
    numbers[0] = "One"
    numbers[1] = "Two"
    numbers[2] = "Three"
    fmt.Println(numbers[1]) //prints Two
    fmt.Println(len(numbers)) //prints 3
    fmt.Println(numbers) // prints [One Two Three]

    directions := [...] int {1,2,3,4,5} // creating an integer array and the size of the array is defined by the number of elements 
    fmt.Println(directions) //prints [1 2 3 4 5]
    fmt.Println(len(directions)) //prints 5

    //Executing the below commented statement prints invalid array index 5 (out of bounds for 5-element array)
    //fmt.Println(directions[5]) 
}

الناتج

Two
3
[One Two Three]
[1 2 3 4 5]
5

شريحة Golang ووظيفة الإلحاق

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

محتويات الشريحة هي في الواقع مؤشرات لعناصر المصفوفة. هذا يعني إذا قمت بتغيير أي عنصر في شريحة ما، فسوف تتأثر محتويات المصفوفة الأساسية أيضًا.

بناء الجملة لإنشاء شريحة هو

var slice_name [] type = array_name[start:end]

سيؤدي هذا إلى إنشاء شريحة تسمى Slice_name من مصفوفة تسمى array_name مع العناصر الموجودة في الفهرس من البداية إلى النهاية 1.

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

package main
import "fmt"

func main() {  
    // declaring array
    a := [5] string {"one", "two", "three", "four", "five"}
    fmt.Println("Array after creation:",a)

    var b [] string = a[1:4] //created a slice named b
    fmt.Println("Slice after creation:",b)

    b[0]="changed" // changed the slice data
    fmt.Println("Slice after modifying:",b)
    fmt.Println("Array after slice modification:",a)
}

سيؤدي هذا إلى طباعة النتيجة كـ

Array after creation: [one two three four five]
Slice after creation: [two three four]
Slice after modifying: [changed three four]
Array after slice modification: [one changed three four five]

هناك وظائف معينة مثل Golang len وGolang append والتي يمكنك تطبيقها على الشرائح

لين (اسم_الشريحة) - إرجاع طول الشريحة

إلحاق (اسم_الشريحة، القيمة_1، القيمة_2) - يتم استخدام إلحاق Golang لإلحاق value_1 وvalue_2 بالشريحة الموجودة.

إلحاق (slice_nale1،slice_name2...) - يُلحق Slice_name2 إلى Slice_name1

قم بتنفيذ البرنامج التالي .

package main
import "fmt"

func main() {  
	a := [5] string {"1","2","3","4","5"}
	slice_a := a[1:3]
	b := [5] string {"one","two","three","four","five"}
	slice_b := b[1:3]

    fmt.Println("Slice_a:", slice_a)
    fmt.Println("Slice_b:", slice_b)
    fmt.Println("Length of slice_a:", len(slice_a))
    fmt.Println("Length of slice_b:", len(slice_b))

    slice_a = append(slice_a,slice_b...) // appending slice
    fmt.Println("New Slice_a after appending slice_b :", slice_a)
    
    slice_a = append(slice_a,"text1") // appending value
    fmt.Println("New Slice_a after appending text1 :", slice_a)
}

سيكون الإخراج

Slice_a: [2 3]
Slice_b: [two three]
Length of slice_a: 2
Length of slice_b: 2
New Slice_a after appending slice_b : [2 3 two three]
New Slice_a after appending text1 : [2 3 two three text1]

يقوم البرنامج أولاً بإنشاء شريحتين وطباعة طولهما. ثم قام بإلحاق شريحة واحدة بالأخرى ثم قام بإلحاق سلسلة بالشريحة الناتجة.

وظائف

تمثل الوظيفة كتلة من البيانات التي تؤدي مهمة محددة. يخبرنا إعلان الوظيفة باسم الوظيفة ونوع الإرجاع ومعلمات الإدخال. يمثل تعريف الوظيفة الكود الموجود في الوظيفة. بناء الجملة للإعلان عن الوظيفة هو

func function_name(parameter_1 type, parameter_n type) return_type {
//statements
}

المعلمات وأنواع الإرجاع اختيارية. يمكنك أيضًا إرجاع قيم متعددة من دالة.

الآن في هذا البرنامج التعليمي للغة Golang، دعنا ننفذ مثال Golang التالي. هنا ستقبل الدالة المسماة calc رقمين وتقوم بالجمع والطرح وتعيد القيمتين.

package main
import "fmt"

//calc is the function name which accepts two integers num1 and num2
//(int, int) says that the function returns two values, both of integer type.
func calc(num1 int, num2 int)(int, int) {  
    sum := num1 + num2
    diff := num1 - num2
    return sum, diff
}

func main() {  
    x,y := 15,10

    //calls the function calc with x and y an d gets sum, diff as output
    sum, diff := calc(x,y) 
    fmt.Println("Sum",sum)
    fmt.Println("Diff",diff) 
}

سيكون الإخراج

Sum 25
Diff 5

الباقات

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

import package_name

سنرى ونناقش في هذا البرنامج التعليمي Golang، كيفية إنشاء الحزم واستخدامها في مثال Golang التالي.

الخطوة 1) قم بإنشاء ملف يسمى package_example.go وأضف الكود أدناه

package main
import "fmt"
//the package to be created
import "calculation"

func main() {  
	x,y := 15,10
	//the package will have function Do_add()
sum := calculation.Do_add(x,y)
fmt.Println("Sum",sum) 
}

في البرنامج أعلاه fmt عبارة عن حزمة توفرها لنا لغة برمجة Go بشكل أساسي لأغراض الإدخال/الإخراج. يمكنك أيضًا رؤية حساب الحزمة المسمى. داخل main() يمكنك رؤية مجموع الخطوات := account.Do_add(x,y). هذا يعني أنك تستدعي الدالة Do_add من حساب الحزمة.

الخطوة 2) أولاً، يجب عليك إنشاء حساب الحزمة داخل مجلد يحمل نفس الاسم ضمن مجلد src الخاص بـ go. يمكن العثور على المسار المثبت لـ go من متغير PATH.

بالنسبة لنظام التشغيل Mac، ابحث عن المسار عن طريق تنفيذ echo $PATH

إذن المسار هو /usr/local/go

بالنسبة لنظام Windows، ابحث عن المسار عن طريق تنفيذ echo %GOROOT%

هنا المسار هو C:\Go\

الخطوة 3) انتقل إلى مجلد src (/usr/local/go/src لنظام التشغيل Mac وC:\Go\src لنظام التشغيل Windows). الآن من الكود، اسم الحزمة هو calculate. يتطلب Go وضع الحزمة في دليل بنفس الاسم ضمن دليل src. قم بإنشاء دليل باسم calculate في مجلد src.

الخطوة 4) قم بإنشاء ملف يسمى calc.go (يمكنك إعطاء أي اسم، ولكن اسم الحزمة في الكود مهم. هنا يجب أن يكون الحساب) داخل دليل الحساب وأضف الكود أدناه

package calculation
  
func Do_add(num1 int, num2 int)(int) {
    sum := num1 + num2
    return sum
}

الخطوة 5) قم بتشغيل الأمر go install من دليل الحساب الذي سيقوم بتجميع calc.go.

الخطوة 6) ارجع الآن إلى package_example.go وقم بتشغيل اذهب إلى تشغيل package_example.go. سيكون الناتج مجموع 25.

لاحظ أن اسم الدالة Do_add يبدأ بحرف كبير. وذلك لأنه في Go إذا كان اسم الوظيفة يبدأ بحرف كبير فهذا يعني أن البرامج الأخرى يمكنها رؤيتها (الوصول إليها) وإلا فلن تتمكن البرامج الأخرى من الوصول إليها. إذا كان اسم الوظيفة do_add، فستحصل على الخطأ

لا يمكن الإشارة إلى حساب الاسم غير المُصدَّر.calc..

تأجيل وتأجيل التراص

تُستخدم عبارات التأجيل لتأجيل تنفيذ استدعاء دالة حتى تكتمل الوظيفة التي تحتوي على عبارة التأجيل التنفيذ.

لنتعلم ذلك بمثال:

package main
import "fmt"

func sample() {  
    fmt.Println("Inside the sample()")
}
func main() {  
    //sample() will be invoked only after executing the statements of main()
    defer sample()
    fmt.Println("Inside the main()")
}

سيكون الإخراج

Inside the main()
Inside the sample()

هنا يتم تأجيل تنفيذ العينة () حتى يكتمل تنفيذ الوظيفة المتضمنة (الرئيسية ()).

يستخدم تكديس التأجيل عبارات تأجيل متعددة. لنفترض أن لديك عبارات تأجيل متعددة داخل إحدى الوظائف. يضع Go جميع استدعاءات الوظائف المؤجلة في مكدس، وبمجرد عودة الوظيفة المتضمنة، يتم تنفيذ الوظائف المكدسة في ترتيب ما يدخل أولاً يخرج أولاً (LIFO). يمكنك أن ترى هذا في المثال أدناه.

قم بتنفيذ الكود أدناه

package main
import "fmt"

func display(a int) {  
    fmt.Println(a)
}
func main() {  
    defer display(1)
    defer display(2)
    defer display(3)
    fmt.Println(4)
}

سيكون الإخراج

4
3
2
1			

هنا يتم تنفيذ التعليمات البرمجية الموجودة داخل main() أولاً، ثم يتم تنفيذ استدعاءات الوظائف المؤجلة بترتيب عكسي، أي 4، 3,2,1،XNUMX،XNUMX.

Pointers

قبل شرح المؤشرات، دعونا أولاً نناقش عامل "&". يُستخدم عامل "&" للحصول على عنوان متغير. وهذا يعني أن "&a" سيطبع عنوان الذاكرة للمتغير a.

في هذا البرنامج التعليمي لـ Golang، سنقوم بتنفيذ البرنامج أدناه لعرض قيمة المتغير وعنوان ذلك المتغير

package main
import "fmt"

func main() {
	a := 20
	fmt.Println("Address:",&a)
	fmt.Println("Value:",a)
}

ستكون النتيجة

Address: 0xc000078008
Value: 20

يقوم متغير المؤشر بتخزين عنوان الذاكرة لمتغير آخر. يمكنك تحديد مؤشر باستخدام بناء الجملة

	var variable_name *type

تمثل العلامة النجمية (*) المتغير كمؤشر. سوف تفهم المزيد عن طريق تنفيذ البرنامج أدناه

package main
import "fmt"

func main() {
	//Create an integer variable a with value 20
	a := 20
	
	//Create a pointer variable b and assigned the address of a
	var b *int = &a

	//print address of a(&a) and value of a  
	fmt.Println("Address of a:",&a)
	fmt.Println("Value of a:",a)

	//print b which contains the memory address of a i.e. &a
	fmt.Println("Address of pointer b:",b)

	//*b prints the value in memory address which b contains i.e. the value of a
	fmt.Println("Value of pointer b",*b)

	//increment the value of variable a using the variable b
	*b = *b+1

	//prints the new value using a and *b
	fmt.Println("Value of pointer b",*b)
	fmt.Println("Value of a:",a)}

سيكون الإخراج

Address of a: 0x416020
Value of a: 20
Address of pointer b: 0x416020
Value of pointer b 20
Value of pointer b 21
Value of a: 21

هياكل

البنية هي نوع بيانات محدد من قبل المستخدم والذي يحتوي في حد ذاته على عنصر آخر من نفس النوع أو نوع مختلف.

استخدام الهيكل هو عملية من خطوتين.

أولاً، قم بإنشاء (إعلان) نوع البنية

ثانيًا، قم بإنشاء متغيرات من هذا النوع لتخزين القيم.

تُستخدم الهياكل بشكل أساسي عندما تريد تخزين البيانات ذات الصلة معًا.

فكر في جزء من معلومات الموظف التي تحتوي على الاسم والعمر والعنوان. يمكنك التعامل مع هذا بطريقتين

قم بإنشاء 3 مصفوفات - مصفوفة واحدة تخزن أسماء الموظفين، ومصفوفة واحدة تخزن العمر، ومصفوفة ثالثة تخزن العمر.

قم بتعريف نوع البنية باستخدام 3 حقول- الاسم والعنوان والعمر. قم بإنشاء مصفوفة من نوع البنية هذا حيث يكون كل عنصر عبارة عن كائن هيكلي له اسم وعنوان وعمر.

النهج الأول ليس فعالا. في مثل هذه السيناريوهات، تكون الهياكل أكثر ملاءمة.

بناء الجملة للإعلان عن الهيكل هو

type structname struct {
   variable_1 variable_1_type
   variable_2 variable_2_type
   variable_n variable_n_type
}

مثال على إعلان الهيكل هو

type emp struct {
    name string
    address string
    age int
}

هنا يتم إنشاء نوع جديد محدد من قبل المستخدم يسمى emp. الآن، يمكنك إنشاء متغيرات من النوع emp باستخدام بناء الجملة

	var variable_name struct_name

ومن الأمثلة على ذلك

var empdata1 emp 

يمكنك تعيين قيم empdata1 كـ

empdata1.name = "John"
	empdata1.address = "Street-1, Bangalore"
	empdata1.age = 30

يمكنك أيضًا إنشاء متغير هيكل وتعيين القيم من خلاله

empdata2 := emp{"Raj", "Building-1, Delhi", 25}

هنا، تحتاج إلى الحفاظ على ترتيب العناصر. سيتم تعيين راج على الاسم والعنصر التالي للعنوان والأخير للعمر.

قم بتنفيذ الكود أدناه

package main
import "fmt"

//declared the structure named emp
type emp struct {
        name string
        address string
        age int
}       

//function which accepts variable of emp type and prints name property
func display(e emp) {
          fmt.Println(e.name)
}

func main() {
// declares a variable, empdata1, of the type emp
var empdata1 emp
//assign values to members of empdata1
empdata1.name = "John"
empdata1.address = "Street-1, London"
empdata1.age = 30

//declares and assign values to variable empdata2 of type emp
empdata2 := emp{"Raj", "Building-1, Paris", 25}

//prints the member name of empdata1 and empdata2 using display function
display(empdata1)
display(empdata2)
}

الناتج

John
Raj

الأساليب (وليس الوظائف)

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

func (variable variabletype) methodName(parameter1 paramether1type) {  
}

لنقم بتحويل البرنامج المثالى أعلاه لاستخدام الأساليب بدلاً من الوظيفة.

package main
import "fmt"

//declared the structure named emp
type emp struct {
    name string
    address string
    age int
}

//Declaring a function with receiver of the type emp
func(e emp) display() {
    fmt.Println(e.name)
}

func main() {
    //declaring a variable of type emp
    var empdata1 emp
    
    //Assign values to members
    empdata1.name = "John"
    empdata1.address = "Street-1, Lodon"
    empdata1.age = 30

    //declaring a variable of type emp and assign values to members
    empdata2 := emp {
        "Raj", "Building-1, Paris", 25}

    //Invoking the method using the receiver of the type emp
   // syntax is variable.methodname()
    empdata1.display()
    empdata2.display()
}

Go ليست لغة موجهة للكائنات ولا تحتوي على مفهوم الفصل. تعطي الطرق فكرة عما تفعله في البرامج الموجهة للكائنات حيث يتم استدعاء وظائف الفئة باستخدام بناء الجملة objectname.functionname()

التوافق

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

جوروتين

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

ولكن في حالة goroutine، لن تنتظر وظيفة الاستدعاء حتى يكتمل تنفيذ الوظيفة التي تم استدعاؤها. وسوف يستمر في التنفيذ مع العبارات التالية. يمكن أن يكون لديك goroutines متعددة في البرنامج.

كما أن البرنامج الرئيسي سيخرج بمجرد انتهاءه من تنفيذ بياناته ولن ينتظر إكمال goroutines التي تم استدعاؤها.

يتم استدعاء Goroutine باستخدام الكلمة الأساسية go متبوعة باستدعاء دالة.

مثال

go add(x,y)

سوف تفهم goroutines مع أمثلة Golang أدناه. قم بتنفيذ البرنامج أدناه

package main
import "fmt"
    
func display() {
	for i:=0; i<5; i++ {
		fmt.Println("In display")
	}
}

func main() {
	//invoking the goroutine display()
	go display()
	//The main() continues without waiting for display()
	for i:=0; i<5; i++ {
		fmt.Println("In main")
	}
}

سيكون الإخراج

In main
In main
In main
In main
In main

هنا أكمل البرنامج الرئيسي التنفيذ حتى قبل بدء goroutine. إن العرض () عبارة عن روتين يتم استدعاؤه باستخدام بناء الجملة

go function_name(parameter list)

في الكود أعلاه، لا ينتظر الدالة main() حتى يكتمل العرض()، ويكمل الدالة main() تنفيذه قبل أن ينفذ العرض() الكود الخاص به. لذلك لم تتم طباعة عبارة الطباعة داخل العرض ().

الآن نقوم بتعديل البرنامج لطباعة البيانات من العرض () أيضًا. نضيف تأخيرًا زمنيًا قدره ثانيتان في حلقة for للتابع main() وتأخيرًا لمدة ثانية واحدة في حلقة for للعرض().

package main
import "fmt"
import "time"
    
func display() {
	for i:=0; i<5; i++ {
		time.Sleep(1 * time.Second)
		fmt.Println("In display")
	}
}

func main() {
	//invoking the goroutine display()
	go display()
	for i:=0; i<5; i++ {
		time.Sleep(2 * time.Second)
		fmt.Println("In main")
	}
}

سيكون الإخراج مشابهًا إلى حد ما لـ

In display
In main
In display
In display
In main
In display
In display
In main
In main
In main

هنا يمكنك رؤية تنفيذ كلتا الحلقتين بطريقة متداخلة بسبب التنفيذ المتزامن.

القنوات

القنوات هي وسيلة للوظائف للتواصل مع بعضها البعض. يمكن اعتباره وسيلة حيث يقوم روتين واحد بوضع البيانات ويمكن الوصول إليها بواسطة روتين آخر في خادم Golang.

يمكن الإعلان عن القناة باستخدام بناء الجملة

channel_variable := make(chan datatype)

على سبيل المثال:

	ch := make(chan int)

يمكنك إرسال البيانات إلى قناة باستخدام بناء الجملة

channel_variable <- variable_name

مثال

    ch <- x

يمكنك تلقي البيانات من قناة باستخدام بناء الجملة

    variable_name := <- channel_variable

مثال

   y := <- ch

في أمثلة لغة goroutine المذكورة أعلاه، رأيت أن البرنامج الرئيسي لا ينتظر goroutine. ولكن هذا ليس هو الحال عندما يتعلق الأمر بالقنوات. لنفترض أنه إذا قام goroutine بدفع البيانات إلى القناة، فإن الدالة main() ستنتظر العبارة التي تتلقى بيانات القناة حتى تحصل على البيانات.

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

قم بتنفيذ البرنامج أدناه

package main
import "fmt"
import "time"
    
func display() {
	time.Sleep(5 * time.Second)
	fmt.Println("Inside display()")
}

func main() {
	go display()
	fmt.Println("Inside main()")
}

سيكون الإخراج

Inside main()

أنهى main() التنفيذ وخرج قبل تنفيذ goroutine. لذلك لم يتم تنفيذ الطباعة داخل الشاشة ().

الآن قم بتعديل البرنامج أعلاه لاستخدام القنوات ورؤية السلوك.

package main
import "fmt"
import "time"
    
func display(ch chan int) {
	time.Sleep(5 * time.Second)
	fmt.Println("Inside display()")
	ch <- 1234
}

func main() {
	ch := make(chan int) 
	go display(ch)
	x := <-ch
	fmt.Println("Inside main()")
	fmt.Println("Printing x in main() after taking from channel:",x)
}

سيكون الإخراج

Inside display()
Inside main()
Printing x in main() after taking from channel: 1234

ما يحدث هنا هو main() عند الوصول إلى x := <-ch سينتظر البيانات الموجودة على القناة ch. ينتظر العرض () لمدة 5 ثوانٍ ثم يدفع البيانات إلى القناة ch. يتم إلغاء حظر main() عند تلقي البيانات من القناة ويستمر تنفيذها.

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

close(channel_name)

ومن جهة جهاز الاستقبال يمكن التحقق مما إذا كانت القناة مغلقة باستخدام متغير إضافي أثناء جلب البيانات من القناة باستخدام

variable_name, status := <- channel_variable

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

يمكنك أيضًا استخدام القنوات للتواصل بين goroutines. تحتاج إلى استخدام 2 goroutines - أحدهما يدفع البيانات إلى القناة والآخر يتلقى البيانات من القناة. انظر البرنامج أدناه

package main
import "fmt"
import "time"

//This subroutine pushes numbers 0 to 9 to the channel and closes the channel
func add_to_channel(ch chan int) {	
	fmt.Println("Send data")
	for i:=0; i<10; i++ {
		ch <- i //pushing data to channel
	}
	close(ch) //closing the channel

}

//This subroutine fetches data from the channel and prints it.
func fetch_from_channel(ch chan int) {
	fmt.Println("Read data")
	for {
		//fetch data from channel
x, flag := <- ch

		//flag is true if data is received from the channel
//flag is false when the channel is closed
if flag == true {
			fmt.Println(x)
		}else{
			fmt.Println("Empty channel")
			break	
		}	
	}
}

func main() {
	//creating a channel variable to transport integer values
	ch := make(chan int)

	//invoking the subroutines to add and fetch from the channel
	//These routines execute simultaneously
	go add_to_channel(ch)
	go fetch_from_channel(ch)

	//delay is to prevent the exiting of main() before goroutines finish
	time.Sleep(5 * time.Second)
	fmt.Println("Inside main()")
}

يوجد هنا روتينان فرعيان، أحدهما يدفع البيانات إلى القناة والآخر يطبع البيانات إلى القناة. تضيف الدالة add_to_channel الأرقام من 2 إلى 0 وتغلق القناة. في نفس الوقت تنتظر الدالة fetch_from_channel

x, flag := <- ch وبمجرد توفر البيانات، يقوم بطباعة البيانات. يتم الخروج بمجرد أن تكون العلامة خاطئة مما يعني أن القناة مغلقة.

يتم الانتظار في main() لمنع الخروج من main() حتى تنتهي goroutines من التنفيذ.

قم بتنفيذ الكود وشاهد الإخراج كـ

Read data
Send data
0
1
2
3
4
5
6
7
8
9
Empty channel
Inside main()

أختار

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

دعونا نرى الكود أدناه

package main
import "fmt"
import "time"

//push data to channel with a 4 second delay
func data1(ch chan string) {  
    time.Sleep(4 * time.Second)
    ch <- "from data1()"
}

//push data to channel with a 2 second delay
func data2(ch chan string) {  
    time.Sleep(2 * time.Second)
    ch <- "from data2()"
}

func main() {
    //creating channel variables for transporting string values
    chan1 := make(chan string)
    chan2 := make(chan string)
    
    //invoking the subroutines with channel variables
    go data1(chan1)
    go data2(chan2)
    
    //Both case statements wait for data in the chan1 or chan2.
    //chan2 gets data first since the delay is only 2 sec in data2().
    //So the second case will execute and exits the select block
    select {
    case x := <-chan1:
        fmt.Println(x)
    case y := <-chan2:
        fmt.Println(y)
    }
}

تنفيذ البرنامج أعلاه سوف يعطي الإخراج:

from data2()

هنا تنتظر عبارة التحديد إتاحة البيانات في أي من القنوات. تضيف الدالة data2() البيانات إلى القناة بعد سكون لمدة ثانيتين مما يؤدي إلى تنفيذ الحالة الثانية.

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

package main
import "fmt"
import "time"

//push data to channel with a 4 second delay
func data1(ch chan string) {  
    time.Sleep(4 * time.Second)
    ch <- "from data1()"
}

//push data to channel with a 2 second delay
func data2(ch chan string) {  
    time.Sleep(2 * time.Second)
    ch <- "from data2()"
}

func main() {
    //creating channel variables for transporting string values  
    chan1 := make(chan string)
    chan2 := make(chan string)
    
    //invoking the subroutines with channel variables
    go data1(chan1)
    go data2(chan2)

    //Both case statements check for data in chan1 or chan2.
    //But data is not available (both routines have a delay of 2 and 4 sec)
    //So the default block will be executed without waiting for data in channels.
    select {
    case x := <-chan1:
        fmt.Println(x)
    case y := <-chan2:
        fmt.Println(y)
    default:
    	fmt.Println("Default case executed")
    }
}

سيعطي هذا البرنامج الإخراج:

Default case executed			

وذلك لأنه عند الوصول إلى كتلة التحديد، لم يكن لدى أي قناة بيانات للقراءة. لذلك، يتم تنفيذ الحالة الافتراضية.

مزامنة

Mutex هو الشكل المختصر للاستبعاد المتبادل. يتم استخدام Mutex عندما لا تريد السماح لمورد بالوصول إليه بواسطة برامج فرعية متعددة في نفس الوقت. يحتوي Mutex على طريقتين - Lock و Unlock. يتم تضمين Mutex في حزمة sync. لذا، عليك استيراد حزمة sync. يمكن وضع العبارات التي يجب تنفيذها بشكل متبادل حصريًا داخل mutex.Lock() و mutex.Unlock().

دعونا نتعلم كائن المزامنة باستخدام مثال وهو حساب عدد مرات تنفيذ الحلقة. في هذا البرنامج نتوقع أن يتم تشغيل الروتين 10 مرات ويتم تخزين العدد بشكل إجمالي. يمكنك استدعاء هذا الروتين 3 مرات لذا يجب أن يكون العدد الإجمالي 30. يتم تخزين العدد في عدد متغير عام.

أولاً، تقوم بتشغيل البرنامج بدون كائن المزامنة (mutex).

package main
import "fmt"
import "time"
import "strconv"
import "math/rand"
//declare count variable, which is accessed by all the routine instances
var count = 0

//copies count to temp, do some processing(increment) and store back to count
//random delay is added between reading and writing of count variable
func process(n int) {
	//loop incrementing the count by 10
	for i := 0; i < 10; i++ {
		time.Sleep(time.Duration(rand.Int31n(2)) * time.Second)
		temp := count
		temp++
		time.Sleep(time.Duration(rand.Int31n(2)) * time.Second)
		count = temp
	}
	fmt.Println("Count after i="+strconv.Itoa(n)+" Count:", strconv.Itoa(count))
}

func main() {
	//loop calling the process() 3 times
	for i := 1; i < 4; i++ {
		go process(i)
	}

	//delay to wait for the routines to complete
	time.Sleep(25 * time.Second)
	fmt.Println("Final Count:", count)
}

انظر النتيجة

 Count after i=1 Count: 11
Count after i=3 Count: 12
Count after i=2 Count: 13
Final Count: 13

يمكن أن تكون النتيجة مختلفة عند تنفيذها ولكن النتيجة النهائية لن تكون 30.

ما يحدث هنا هو أن 3 goroutines تحاول زيادة عدد الحلقات المخزنة في عدد المتغيرات. لنفترض في لحظة أن العدد هو 5 وأن goroutine1 سيزيد العدد إلى 6. تتضمن الخطوات الرئيسية

نسخ العد إلى درجة الحرارة

زيادة درجة الحرارة

قم بتخزين درجة الحرارة مرة أخرى للعد

لنفترض أنه بعد وقت قصير من تنفيذ الخطوة 3 بواسطة goroutine1؛ قد يكون لـ goroutine آخر قيمة قديمة، مثلاً 3 يقوم بالخطوات المذكورة أعلاه ويخزن 4 مرة أخرى، وهذا خطأ. يمكن منع ذلك باستخدام كائن المزامنة (mutex) الذي يتسبب في انتظار إجراءات أخرى عندما يستخدم روتين واحد المتغير بالفعل.

الآن ستقوم بتشغيل البرنامج باستخدام كائن المزامنة (mutex). هنا يتم تنفيذ الخطوات الثلاث المذكورة أعلاه في كائن المزامنة (mutex).

package main
import "fmt"
import "time"
import "sync"
import "strconv"
import "math/rand"

//declare a mutex instance
var mu sync.Mutex

//declare count variable, which is accessed by all the routine instances
var count = 0

//copies count to temp, do some processing(increment) and store back to count
//random delay is added between reading and writing of count variable
func process(n int) {
	//loop incrementing the count by 10
	for i := 0; i < 10; i++ {
		time.Sleep(time.Duration(rand.Int31n(2)) * time.Second)
		//lock starts here
		mu.Lock()
		temp := count
		temp++
		time.Sleep(time.Duration(rand.Int31n(2)) * time.Second)
		count = temp
		//lock ends here
		mu.Unlock()
	}
	fmt.Println("Count after i="+strconv.Itoa(n)+" Count:", strconv.Itoa(count))
}

func main() {
	//loop calling the process() 3 times
	for i := 1; i < 4; i++ {
		go process(i)
	}

	//delay to wait for the routines to complete
	time.Sleep(25 * time.Second)
	fmt.Println("Final Count:", count)
}

الآن سيكون الإخراج

 Count after i=3 Count: 21
Count after i=2 Count: 28
Count after i=1 Count: 30
Final Count: 30

هنا نحصل على النتيجة المتوقعة كمخرج نهائي. لأن عبارات القراءة والزيادة والكتابة مرة أخرى يتم تنفيذها في كائن المزامنة.

معالجة الخطأ

الأخطاء هي حالات غير طبيعية مثل إغلاق ملف غير مفتوح، أو فتح ملف غير موجود، وما إلى ذلك. عادةً ما تُرجع الوظائف الأخطاء كقيمة الإرجاع الأخيرة.

يوضح المثال أدناه المزيد عن الخطأ.

package main
import "fmt"
import "os"

//function accepts a filename and tries to open it.
func fileopen(name string) {
    f, er := os.Open(name)

    //er will be nil if the file exists else it returns an error object  
    if er != nil {
        fmt.Println(er)
        return
    }else{
    	fmt.Println("file opened", f.Name())
    }
}

func main() {  
    fileopen("invalid.txt")
}

سوف يكون الإخراج:

open /invalid.txt: no such file or directory

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

الأخطاء المخصصة

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

قم بتشغيل البرنامج أدناه

package main
import "fmt"
import "os"
import "errors"

//function accepts a filename and tries to open it.
func fileopen(name string) (string, error) {
    f, er := os.Open(name)

    //er will be nil if the file exists else it returns an error object  
    if er != nil {
        //created a new error object and returns it  
        return "", errors.New("Custom error message: File name is wrong")
    }else{
    	return f.Name(),nil
    }
}

func main() {  
    //receives custom error or nil after trying to open the file
    filename, error := fileopen("invalid.txt")
    if error != nil {
        fmt.Println(error)
    }else{
    	fmt.Println("file opened", filename)
    }  
}

سوف يكون الإخراج:

Custom error message:File name is wrong

هنا تُرجع المنطقة () مساحة المربع. إذا كان الإدخال أقل من 1، فستُرجع الدالة Area()‎ رسالة خطأ.

قراءة الملفات

يتم استخدام الملفات لتخزين البيانات. Go يسمح لنا بقراءة البيانات من الملفات

قم أولاً بإنشاء ملف، data.txt، في دليلك الحالي بالمحتوى أدناه.

Line one
Line two
Line three

الآن قم بتشغيل البرنامج أدناه لرؤيته يطبع محتويات الملف بأكمله كإخراج

package main
import "fmt"
import "io/ioutil"

func main() {  
    data, err := ioutil.ReadFile("data.txt")
    if err != nil {
        fmt.Println("File reading error", err)
        return
    }
    fmt.Println("Contents of file:", string(data))
}

هنا البيانات، err := ioutil.ReadFile(“data.txt”) يقرأ البيانات ويعيد تسلسل البايت. أثناء الطباعة يتم تحويله إلى تنسيق السلسلة.

كتابة الملفات

سوف ترى هذا مع البرنامج

package main
import "fmt"
import "os"

func main() {  
    f, err := os.Create("file1.txt")
    if err != nil {
        fmt.Println(err)
        return
    }
    l, err := f.WriteString("Write Line one")
    if err != nil {
        fmt.Println(err)
        f.Close()
        return
    }
    fmt.Println(l, "bytes written")
    err = f.Close()
    if err != nil {
        fmt.Println(err)
        return
    }
}

هنا يتم إنشاء ملف test.txt. إذا كان الملف موجودًا بالفعل، فسيتم اقتطاع محتويات الملف. يُستخدم Writeline() لكتابة محتويات الملف. بعد ذلك قمت بإغلاق الملف باستخدام Close().

ورقة الغش

في هذا البرنامج التعليمي Go، تناولنا،

موضوع الوصف بناء الجملة
أنواع أساسية رقمية، سلسلة، منطقية
المتغيرات إعلان وتعيين القيم للمتغيرات نوع فار متغير_اسم
فار متغير_اسم النوع = القيمة
فار متغير_اسم1، متغير_اسم2 = القيمة1، القيمة2
اسم_المتغير := القيمة
ثابت المتغيرات التي لا يمكن تغيير قيمتها بمجرد تعيينها متغير ثابت = القيمة
لحلقة تنفيذ البيانات في حلقة. من أجل التهيئة_التعبير؛ Evaluation_expression; تكرار_التعبير{
// عبارة واحدة أو أكثر
}
إذا كان غير ذلك وهو عبارة مشروطة إذا شرط {
// البيانات_1
آخر {}
// البيانات_2
}
تحول بيان شرطي مع حالات متعددة تبديل التعبير {
قيمة الحالة_1:
البيانات_1
قيمة الحالة_2:
البيانات_2
قيمة الحالة_ن:
البيانات_ن
الافتراضي:
statements_default
}
مجموعة حجم ثابت يسمى تسلسل العناصر من نفس النوع اسم المصفوفة:= [الحجم] اكتب {value_0,value_1,...,value_size-1}
شريحة جزء أو قطعة من المصفوفة فار شريحة_اسم [] النوع = اسم المصفوفة[البداية:النهاية]
وظائف كتلة من البيانات التي تؤدي مهمة محددة func function_name (نوع المعلمة_1، نوع المعلمة_n) return_type {
//صياغات
}
الباقات تستخدم لتنظيم الكود. يزيد من إمكانية قراءة التعليمات البرمجية وإمكانية إعادة استخدامها import package_nam
تأجيل يؤجل تنفيذ دالة حتى تنتهي الدالة المحتوية من التنفيذ تأجيل اسم_الوظيفة (قائمة_المعلمات)
Pointers يخزن عنوان الذاكرة لمتغير آخر. فار متغير_اسم *نوع
الهيكلية نوع البيانات المحدد من قبل المستخدم والذي يحتوي في حد ذاته على عنصر آخر من نفس النوع أو نوع مختلف اكتب بنية اسم البنية {
متغير_1 متغير_1_نوع
متغير_2 متغير_2_نوع
متغير_ن متغير_ن_نوع
}
طرق الطريقة هي دالة ذات وسيطة المتلقي func (نوع متغير متغير) اسم الطريقة (parameter_list) {
}
جوروتين وظيفة يمكن تشغيلها بالتزامن مع وظائف أخرى. انتقل إلى اسم الوظيفة (قائمة_المعلمات)
قناة طريقة للوظائف للتواصل مع بعضها البعض. وسيلة يقوم أحد الروتينات بوضع البيانات عليها ويتم الوصول إليها بواسطة روتين آخر. نعلن ما يلي:
الفصل := جعل (تشان كثافة العمليات)
إرسال البيانات إلى القناة:
قناة_متغير <- اسم متغير
الاستلام من القناة:
اسم المتغير := <-channel_variable
أختار عبارة التبديل التي تعمل على القنوات. ستكون عبارات الحالة عبارة عن عملية قناة. عندما تكون أي قناة جاهزة بالبيانات، يتم تنفيذ العبارة المرتبطة بهذه الحالة يختار {
الحالة س := <-chan1:
FMT.Println(x)
الحالة ذ := <-chan2:
FMT.Println(ذ)
}
مزامنة يتم استخدام Mutex عندما لا تريد السماح بالوصول إلى المورد من خلال إجراءات فرعية متعددة في نفس الوقت. لدى Mutex طريقتان – القفل وفتح القفل كائن المزامنة (mutex.Lock())
//صياغات
Mutex.Unlock().
قراءة الملفات يقرأ البيانات ويعيد تسلسل البايت. البيانات، خطأ: = ioutil.ReadFile(اسم الملف)
كتابة الملف يكتب البيانات إلى ملف ل، يخطئ:= f.WriteString(text_to_write)