جشنواره ۵۰٪ تخفیف فرانش به مدت محدود

جشنواره ۵۰٪ تخفیف فرانش محدود

0 روز 0 ساعت 0 دقیقه 0 ثانیه

شروع یادگیری

زبان برنامه نویسی GO چیست؟

آموزش زبان برنامه نویسی Go + دانلود فیلم رایگان گو

در این مقاله آموزش زبان برنامه نویسی گو Go را از پایه برای شما شرح می دهیم. ابتدا یک تعریف مختصر از این نوع زبان برنامه نویسی متن باز داریم سپس شیوه‌ی دانلود و نصب آن را برای شما بیان می کنیم و در انتها به شرح این نوع زبان برنامه نویسی می پردازیم.

آیا تمایل دارید که اولین برنامه ی Go را ایجاد کنید؟ پس تا انتهای این آموزش با ما همراه باشید.

زبان برنامه نویسی Go گو چیست؟

Go (که با نام Golang نیز شناخته می‌شود)، یک زبان برنامه‌نویسی متن باز است. گوگل سازنده‌ی اصلی این زبان است. این زبان از نوع کامپایل استاتیک است. Go از برنامه‌نویسی همروند پشتیبانی می‌کند. به عبارت دیگر می‌توان فرایند‌های متعددی را به صورت همزمان اجرا کرد. برنامه‌نویسی همروند را می‌توان با استفاده Goroutineها و Channelها عملی کرد. Go عملکرد جمع‌آوری زباله (Garbage Collection) نیز دارد که خود حافظه را مدیریت می‌کند و اجرای Defer توابع را امکان‌پذیر می‌سازد.

 

دانلود زبان برنامه نویسی Go

گام 1: برای دانلود زبان برنامه نویسی Go به آدرس https://golang.org/dl بروید و فایل باینری مربوط به سیستم‌عامل‌تان را دانلود کنید.

انتخاب سیستم عامل
انتخاب سیستم عامل

گام 2: روی فایل Installer، دابل‌کلیک کرده و Run را بزنید.

Run this file
Run this file

گام 3: Next را بزنید.

click next
click next

گام 4: پوشه‌ی نصب را انتخاب کنید و Next را بزنید.

select & next
select & next

گام 5: پس از پایان نصب، Finish را بزنید.

finish
finish

گام 6: پس از پایان نصب، می‌توانید با باز کردن ترمینال و تایپ دستور زیر، آن را تأیید کنید.

go version

این دستور نسخه‌ی Go نصب‌شده را نشان می‌دهد.

دستور جهت نمایش نسخه‌ی Go نصب‌شده
دستور جهت نمایش نسخه‌ی Go نصب‌شده

دوره آموزش گرس هاپر

آموزش پروژه محور زبان برنامه نویسی Go

در این بخش از آموزش گام به گام زبان برنامه نویسی Go پوشه‌ای با نام 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")
}
ایجاد فایل first.go
ایجاد فایل first.go

در ترمینال به این پوشه بروید. با استفاده از دستور زیر برنامه را اجرا کنید.

go run first.go

می‌بینید که خروجی زیر چاپ می‌شود.

Hello World! This is my first Go program

بیایید کمی درباره‌ی کد بالا حرف بزنیم.

package main: هر برنامه‌ی Go باید با یک نام پکیج شروع شود. Go به ما این امکان را می‌دهد که از پکیج‌ها در سایر برنامه‌های Go استفاده کنیم و اینطور از قابلیت استفاده‌ی مجدد از کد پشتیبانی می‌کند. اجرای برنامه از قطعه کدی داخل پکیج main آغاز می‌شود.

import fmt: پکیج fmt را ایمپورت می‌کند. این پکیج توابع I/O را پیاده‌سازی می‌کند.

func main(): این همان تابعی است که اجرای برنامه از آن آغاز می‌شود. تایع main همیشه باید در پکیج main قرار گیرد. زیر main()، می‌توانید کدتان را در یک {} بنویسید.

fmt.PrintLn: این کد از تابع PrintLn موجود در پکیج fmt استفاده می‌کند و متن خروجی را چاپ می‌کند.

نکته: در ادامه‌ی این آموزش Go، وقتی می‌گوییم کد را اجرا کنید، منظورمان این است که کد موجود در فایل Go را ذخیره و سپس آن را با استفاده از دستور زیر اجرا کنید:

go run <filename>

 

 

آموزش گام به گام برنامه نویسی شی گرا (OOP) را به شما پیشنهاد می کنیم:

یادگیری برنامه نویسی شی گرا از صفر تا صد

انواع داده در برنامه نویسی Go

انواع داده در Go، نشان‌دهنده‌ی نوع مقداری است که در یک متغیر ذخیره می‌شود یا نوع مقداری که یک تابع برمی‌گرداند و … .

در Go سه نوع تایپ اصلی داریم.

انواع عددی (Numeric)

این انواع نشان‌دهنده‌ی اعداد صحیح (Integer)، ممیز شناور (Floating Point) و مقادیر مختلط (Complex) هستند.

در زیر انواع مختلف نوع عددی را می‌بینید:

  • int8: اعداد صحیح 8 بیتی علامت‌دار
  • int16: اعداد صحیح 16 بیتی علامت‌دار
  • int32: اعداد صحیح 32 بیتی علامت‌دار
  • int64: اعداد صحیح 64 بیتی علامت‌دار
  • uint8: اعداد صحیح 8 بیتی بدون علامت‌
  • uint16: اعداد صحیح 16 بیتی بدون علامت‌
  • uint32: اعداد صحیح 32 بیتی بدون علامت‌
  • uint64: اعداد صحیح 64 بیتی بدون علامت‌
  • float32: اعداد ممیز شناور 32 بیتی
  • float64: اعداد ممیز شناور 64 بیتی
  • complex64: دارای بخش‌های حقیقی و موهومی نوع float32
  • complex128: دارای بخش‌های حقیقی و موهومی نوع float32

انواع رشته‌ (String)

این انواع نشان‌دهنده‌ی مجموعه‌ای از بایت‌ها (کاراکترها) هستند. می‌توانید عملیات‌های مختلفی را روی رشته‌ها اجرا کنید: مثل الحاق (Concatenation)، Extract، Substring و … .

مقادیر بولین (Boolean)

حاوی دو مقدار True یا False است.

 

دوره‌های مرتبط در فرانش

تعریف متغیرها در زبان برنامه نویسی گو (Variable)

متغیرها به مکانی از حافظ اشاره می‌کنند که نوعی مقدار در آن ذخیره شده است. پارامتر type (در دستور زیر) نوع داده‌ای را نشان می‌دهد که می‌توانیم در آن مکان حافظه ذخیره کنیم. برای چگونگی تعریف متغیر در زبان برنامه نویسی Go با ادامه مقاله همراه باشید.

با استفاده از دستور زیر می‌توانیم متغیر تعریف کنیم:

var <variable_name> <type>

وقتی متغیری از یک نوع خاص تعریف کردید، می‌توانید هر مقداری از آن نوع را به متغیر بدهید.

با استفاده از دستور زیر می‌توانید هنگام تعریف متغیر به آن یک مقدار اولیه نیز بدهید:

var <variable_name> <type> = <value>

اگر متغیری را با نوع اولیه تعریف کنید، Go نوع متغیر را از روی نوع مقدار اولیه تعیین می‌کند. پس می‌توانید با استفاده از سینتکس زیر، type را از آن حذف کنید:

var <variable_name> = <value>

با دستور زیر می‌توانید چند متغیر تعریف کنید:

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

در برنامه‌ی زیر مثال‌هایی از تعریف متغیر را مشاهده می‌کنید:

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 برای تعریف ساده‌تر متغیرها دستور زیر را دارد که 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)
}

assign.go را اجرا کنید تا نتایج زیر را ببینید:

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

متغیرهایی که با مقداردهی اولیه تعریف می‌شوند، دارای مقدار اولیه هستند. این مقدار برای انواع عددی، 0، برای انواع بولین False و برای رشته‌ها یک رشته‌ی خالی است.

 

ثابت های زبان گو ( Constants in Golang)

متغیرهای ثابت در زبان برنامه نویسی Go آن دسته از متغیرهایی هستند که نمی‌توانیم مقدار آنها را پس از تعیین عوض کنیم. با استفاده از واژه‌ی کلیدی ‘const’ می‌توانیم یک ثابت در go تعریف کنیم.

فایلی با نام constant.go بسازید و کد زیر را به آن اضافه کنید:

package main
import ("fmt")

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

constant.go را اجرا کنید تا نتایج زیر را ببینید:

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

حلقه ها (Loops) در برنامه نویسی Go

از حلقه‌ها برای اجرای یک بلوک از کد طبق شرطی خاص استفاده می‌کنیم. بیشتر زبان‌های برنامه‌نویسی دارای سه نوع حلقه هستند: for، while و do while. اما Go تنها از حلقه‌ی for پشتیبانی می‌کند.

دستور این حلقه به شکل زیر است:

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

اول عبارت initialisation_expression اجرا می‌شود (و تنها یکبار).

سپس evaluation_expression بررسی می‌شود و در صورت برقرار بودن شرط، کد درون بلوک اجرا می‌شود.

iteration_expression اجرا و evaluation_expression دوباره بررسی می‌شود. اگر برقرار باشد، بلوک دستورات مجدداً اجرا می‌شود. این اجرا تا زمان برقرار بودن شرط ادامه می‌یابد.

دستورات زیر را در یک فایل Go کپی و آن را اجرا کنید. می‌بینید که اعداد 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 , else در زبان برنامه نویسی گو

if else در زبان GO یک دستور شرطی است. سینتکس آن به صورت زیر است:

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

در اینجا condition بررسی می‌شود و اگر true باشد، statements_1 اجرا می‌شود. در صورت false بودن، statements_2 اجرا می‌شود.

می‌توانید دستور if را بدون else نیز اجرا کنید. هم چنین می‌توانید چند دستور if else را به هم وصل کنید. در برنامه‌ی زیر بیشتر با دستور if else آشنا می‌شوید.

برنامه‌ی زیر را اجرا کنید. این برنامه بررسی می‌کند که یک عدد، x، کمتر از 10 است یا نه. اگر کمتر باشد، پیغام ” x is less than 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 است، عبارت درون بلوک شرط if اجرا نمی‌شود.

حالا به برنامه‌ی زیر دقت کنید. یک بلوک else داریم که اگر شرط درست نباشد، اجرا می‌شود.

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

حالا بیایید یک برنامه دارای چندین دستور if else را ببینیم (chained if-else). مثال زیر را اجرا کنید. این برنامه بررسی می‌کند که یک عدد کمتر از 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 (if condition) بررسی می‌کند که آیا x کمتر از 10 است یا نه. سپس شرط بعدی را بررسی می‌کند (else of) که می‌گوید آیا x بین 10 و 90 است یا خیر. که این شرط نیز نادرست است. سپس بلوک زیر else را اجرا می‌کند که خروجی زیر را به ما می‌دهد:

x is greater than 90

دستور Switch در Golang

Switch یکی دیگر از دستورات شرطی زبان برنامه نویسی گو است. دستورات Switch یک Statement را بررسی می‌کنند و نتیجه‌ی آن با مجموعه‌ای از مقادیر موجود (Switch Case) مقایسه می‌شود. وقتی نمونه‌ای مشابه پیدا شد، دستور آن Case اجرا می‌شود. اگر نمونه‌ی مشابه پیدا نشد، چیزی اجرا نمی‌شود. با اضافه کردن یک بلوک default به Switch، در صورت پیدا نشدن نمونه‌ی مشابه، این بلوک اجرا می‌شود. دستور Switch در golang به شکل زیر است:

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

در اینجا مقدار expression با مقادیر هر case مقایسه می‌شود. وقتی نمونه‌ای مشابه پیدا شد، دستور آن Case اجرا می‌شود. اگر نمونه‌ی مشابه پیدا نشد، دستور بلوک 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

می‌توانید مقادیر مختلفی در case داشته باشید. باید آنها را با ، از هم جدا کنید.

آرایه ها (Array) در زبان برنامه نویسی گو

آرایه ها (Arrays) در GO نشانگر مجموعه‌ای دارای نام با طول ثابت از المنت‌هاست که نوع یکسان دارند. نمی‌توانید یک آرایه با دو نوع صحیح و رشته داشته باشید. پس از تعریف آرایه نمی‌توانید اندازه‌ی آن را تغییر دهید.

دستور تعریف آرایه به صورت زیر است:

var arrayname [size] type

می‌توانید با دستور زیر به هر المنت آرایه مقدار بدهید:

arrayname [index] = value

ایندکس آرایه از 0 تا size-1 است.

می‌توانید در زمان تعریف با دستور زیر به المنت‌های آرایه مقدار بدهید:

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

وقتی از دستور بالا استفاده می‌کنید، می‌توانید پارامتر size را حذف کنید و … بگذارید. کامپایلر از تعداد valueها سایز را می‌فهمد. دستور زیر را ببینید:

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

با استفاده از دستور زیر می‌توانید اندازه‌ی یک آرایه را بفهمید:

len(arrayname)

کد زیر را برای فهمیدن بهتر آرایه‌ها اجرا کنید:

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

slice در زبان Golang

یک Slice در زبان golang بخش یا قسمتی از یک آرایه است. یک Slice می‌تواند یک ویو یا بخشی از ویوِ یک آرایه باشد که به آن اشاره می‌کند. می‌توانید مثل یک آرایه، با استفاده از نام و ایندکس به المنت‌های یک Slice دسترسی داشته باشید. نمی‌توانید اندازه‌ی آرایه را تغییر دهید، اما اندازه‌ی Slice قابل تغییر است.

محتوای یک Slice درواقع اشاره‌گرهایی به المنت‌های یک آرایه‌اند. به عبارت دیگر،  با تغییر المنت‌های یک Slice، محتوای آرایه‌ی زیرین نیز تغییر می‌کند.

با دستور زیر می‌توانید یک Slice بسازید:

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

این دستور یک Slice با نام slice_name از آرایه‌ی array_name می‌سازد که از ایندکس start شروع و به ایندکس end-1 ختم می‌شود.

برنامه‌ی زیر را اجرا کنید. این برنامه یک Slice از یک آرایه می‌سازد و آن را نمایش می‌دهد. هم‌چنین می‌بینید که تغییر محتوای Slice، آرایه‌ی اصلی را نیز تغییر می‌دهد.

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]

توابع مشخصی داریم که می‌توانیم از آنها برای یک Slice استفاده کنیم.

(len(slice_name: طول Slice را برمی‌گرداند.

(append(slice_name, value_1, value_2: از این تابع برای اضافه کردن value_1 و value_2 به یک Slice موجود استفاده می‌شود.

برنامه‌ی زیر را اجرا کنید.

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]

این برنامه اول دو Slice می‌سازد و طول‌شان را چاپ می‌کند. سپس یک Slice را به دیگری اضافه می‌کند. بعد از آن یک رشته را به Slice نهایی اضافه می‌کند.

 

توابع در برنامه نویسی GO یا (Function)

یک تابع در GO، بلوکی از دستورات است که کاری انجام می‌دهد. در تعریف یک تابع، نام تابع، نوع مقدار خروجی و پارامترهای ورودی را می‌بینیم. تعریف تابع (Function) حاوی کد تابع است. دستور تعریف یک تابع به شکل زیر است:

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

نوشتن پارامترهای ورودی و نوع داده‌ی خروجی اختیاری است. هم‌چنین یک تابع می‌تواند چند مقدار را برگرداند.

بیایید مثال زیر را اجرا کنیم. تابع 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 

از package ها در زبان برنامه نویسی Go برای سازمان‌دهی کدها استفاده می‌شود. در پروژه‌های بزرگ، نمی‌توان تمام کد را در یک فایل نوشت. Go این امکان را برایمان فراهم می‌کند که با استفاده از پکیج‌های مختلف کد را مرتب کنیم. با اینکار خوانایی کد و قابلیت استفاده‌ی مجدد از آن افزایش می‌یابد. یک برنامه‌ی قابل اجرای Go دارای یک پکیج به نام main است و برنامه با اجرای تابع main آغاز می‌شود. با دستور زیر می‌توانید پکیج‌های دیگر را به برنامه ایمپورت کنید:

import package_name

در مثال زیر با هم نحوه‌ی ساخت و استفاده از پکیج‌ها را می‌بینیم.

گام 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 برای انجام امور I/O به ما می‌دهد. یک پکیج هم به نام calculation می‌بینید. در تابع main()، کد sum := calculation.Do_add(x,y) را می‌بینید. با این کد تابع Do-add را از پکیج calculation فراخوانی می‌کنید.

گام 2: اول، باید پکیج calculation را در پوشه‌ای با همان نام در پوشه‌ی src بسازید. می‌توانید مسیر نصب Go را در متغیر PATH ببینید.

در سیستم عامل مک، دستور echo $PATH را اجرا کنید:

مسیر نصب در مک /usr/local/go است.

در ویندوز، مسیر نصب را با اجرای دستور echo %GOROOT% پیدا کنید:

مسیر نصب در ویندوز C:\Go\ است.

گام 3: به پوشه‌ی src (برای مک /usr/local/go/src و برای ویندوز C:\Go\src) بروید. نام پکیج calculation است. در Go لازم است پکیج را در پوشه‌ای همنام در پوشه‌ی src قرار دهید. در پوشه‌ی src یک پوشه با نام calculation بسازید.

گام 4: یک فایل به نام calc.go در پوشه‌ی calculation بسازید (می‌توانید هر نامی برایش انتخاب کنید، اما نام پکیج در برنامه مهم است. در اینجا باید calculation باشد) و کد زیر را به آن اضافه کنید:

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

گام 5: فرمان go install را در پوشه‌ی calculation اجرا کنید. این فرمان calc.go را کامپایل می‌کند.

گام 6: حالا به فایل package_example.go برگردید و package_example.go را اجرا کنید. خروجی Sum 25 خواهد بود.

دقت کنید که نام تابع Do_add با حرف بزرگ شروع می‌شود. در Go، اگر نام تابع با حرف بزرگ شروع شود، از سایر برنامه‌ها قابل دسترسی است و در غیر این صورت قابل دسترسی نخواهد بود. اگر نام تابع do_add بود شما پیغام خطای ‘cannot refer to unexported name calculation.calc.’ را دریافت می‌کردید.

Defer و Defer های استک شده در زبان برنامه نویسی Go

از دستور Defer در زبان GO برای عقب انداختن اجرای یک تابع استفاده می‌شود تا زمانی که تابع حاوی دستور Defer اجرای خود را به پایان برساند.

بیایید با یک مثال آن را یاد بگیریم:

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

در مثال بالا، اجرای تابع sample() تا اتمام اجرای تابع main() به تعویق می‌افتد.

Deferهای استک شده درواقع استفاده از چند دستور Defer است. چند دستور Defer را در یک تابع درنظر بگیرید. 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() اجرا می‌شوند و سپس فراخوانی‌های توابع Defer به صورت معکوس اجرا می‌شوند، به عبارت دیگر به صورت 4، 3، 2، 1.

اشاره گر ها یا Pointer در برنامه نویسی GO

بیایید قبل از توضیح اشاره گر ها (Pointers) در Go درباره‌ی عملگر & حرف بزنیم. از عملگر & برای گرفتن آدرس متغیر استفاده می‌شود. مثلاً دستور &a آدرس حافظه‌ی متغیر a را برمی‌گرداند.

دستور زیر را اجرا کنید تا مقدار و آدرس یک متغیر را ببینید:

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

ساختار یا Structure در Golang

Structure نوع داده‌ای است که کاربر تعریف می‌کند و حاوی یک یا چند المنت‌ با نوع یکسان یا متفاوت است.

برای استفاده از یک Structure باید دو مرحله را طی کنید.

اول، یک نوع Structure بسازید.

دوم، برای ذخیره‌ی مقادیر، متغیرهایی از آن نوع بسازید.

معمولاً زمانی که می‌خواهیم داده‌های مرتبط به هم را با هم ذخیره کنیم، از Structure استفاده می‌کنیم.

اطلاعات یک کارمند را درنظر بگیرید که شامل نام، سن و آدرس است. می‌توانید به دو صورت این اطلاعات را ذخیره کنید.

1) سه آرایه بسازید. در آرایه‌ی اول نام کارمندان، در آرایه‌ی دوم سن و در آرایه‌ی سوم آدرس آنها را ذخیره کنید.

2) یک نوع ساختار با سه فیلدِ نام، سن و آدرس بسازید. حالا آرایه‌ای از این ساختار بسازید که هر المنت آرایه دارای نام، آدرس و سن است.

راهکار اول مناسب نیست. در سناریوهای این‌ چنینی، ساختارها مناسب‌ترند.

با دستور زیر می‌توانید یک ساختار تعریف کنید:

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 ساخته می‌شود. حالا می‌توانید با استفاده از دستور زیر متغیرهای این نوع را تعریف کنید:

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}

در اینجا باید ترتیب المنت‌ها را حفظ کنید. Raj مقدار متغیر name، مقدار بعدی برای متغیر address و مقدار آخر age است.

کد زیر را اجرا کنید:

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

متدها (Methods)

متد تابعی با یک آرگومان ورودی است. این مقدار بین کلمه‌ی کلیدی 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() در زبان‌های برنامه‌نویسی شئ‌گرا هستند.

 

 

در این آموزش توضیح می دهیم که چگونه اولین برنامه خود به زبان Ruby را به آسانی ایجاد نمایید.

آموزش Ruby به زبان فارسی

 

همروندی در Go یا Concurrency

Go از اجرای همروند وظایف پشتیبانی می‌کند. به عبارت ساده‌تر Go می‌تواند وظایف متعدد را به صورت همزمان اجرا کند. همروندی در GO با اجرای موازی فرق دارد. در اجرای موازی، یک وظیفه به وظایف کوچک‌تر تقسیم می‌شود و این وظایف به صورت موازی اجرا می‌شوند. اما در همروندی، چند وظیفه به صورت همزمان اجرا می‌شوند. در Go با استفاده از Goroutineها وChannelها می‌توانیم همروندی داشته باشیم.

مدیریت گروه های Goroutine در زبان Go

Goroutine در زبان GO تابعی است که می‌تواند به صورت همروند با سایر توابع اجرا شود. معمولاً وقتی تابعی فراخوانی می‌شود، کنترل اجرا به آن داده می‌شود تا اجرای آن پایان یابد. پس از پایان اجرا، کنترل به تابعی که فراخوانی را انجام داده، برمی‌گردد. بعد از آن این تابع اجرای خود را ادامه می‌دهد. این تابع منتظر پایان اجرای تابعی که فراخوانی کرده می‌ماند و پس از پایان اجرا، اجرای سایر دستورات را از سر می‌گیرد.

اما Goroutineها منتظر پایان اجرای تابعی که فراخوانی کرده‌اند، نمی‌مانند. آنها به اجرای دستورات خود ادامه می‌دهند. می‌توانید در یک برنامه چند Goroutine داشته باشید.

علاوه بر این، برنامه نیز پس از اجرای کامل دستوراتش پایان می‌پذیرد و منتظر اتمام اجرای Goroutineهای فراخوانی شده نمی‌ماند.

می توانید یک Goroutine را با دستور زیر فراخوانی کنید.

مثال:

go add(x,y)

با مثال زیر بهتر متوجه Goroutineها می‌شوید. برنامه‌ی زیر را اجرا کنید:

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 به پایان می‌رسد. display() یک Goroutine است که با دستور زیر فراخوانی می‌شود:

go function_name(parameter list)

در کد بالا، main() منتظر پایان اجرای display() نمی‌ماند و اجرایش را پیش از اجرای display() به پایان می‌رساند. به همین دلیل دستور print درون display() نمایش داده نشد.

حالا برنامه را به گونه‌ای تغییر می‌دهیم که دستور درون display() را نیز نشان دهد. یک تأخیر زمانی 2 ثانیه‌ای را به حلقه‌ی main() و یک تأخیر زمانی 1 ثانیه‌ای را به حلقه‌ی display() اضافه می‌کنیم.

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

در اینجا می‌بینید که به دلیل اجرای همروند، هر دو حلقه با هم اجرا شده‌اند.

Channel یا کانال ها در زبان Go

کانال روشی است تا توابع بتوانند با استفاده از آن با هم ارتباط داشته باشند. می‌توانیم آنها را واسطی در نظر بگیریم که یک رویه در آن داده قرار می‌دهد و رویه‌ای دیگر آن داده را از آنجا برمی‌دارد.

با استفاده از دستور زیر می‌توانید یک کانال تعریف کنید:

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() روی دستور دریافت داده منتظر می‌ماند تا داده‌ها را دریافت کند.

این موضوع را در مثال پایین می‌بینید. اول یک Goroutine ساده بنویسید و رفتار آن را بررسی کنید. سپس به جای Goroutine از کانال استفاده کنید و رفتار آنها را نیز بررسی کنید.

برنامه‌ی زیر را اجرا کنید:

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 نماند. پس دستور print درون display() اجرا نشد.

حالا در برنامه‌ی بالا از دستور کانال استفاده و رفتارش را بررسی کنید.

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() بعد از رسیدن به دستور :=<-ch منتظر داده‌های کانال ch می‌ماند. display() یک انتظار 5 ثانیه‌ای دارد و سپس داده‌ها را به کانال ch می‌فرستد. main()  که روی دستور دریافت منتظر است، از حالت قفل خارج می‌شود و به اجرای دستوراتش ادامه می‌دهد.

فرستنده‌ی داده به کانال می‌تواند با بستن کانال به گیرنده بگوید که دیگر داده‌ای به کانال فرستاده نمی‌شود. این رویکرد زمانی کاربرد دارد که از حلقه‌ها برای فرستادن داده به یک کانال استفاده می‌کنیم. می‌توانیم یک کانال را با دستور زیر ببندیم:

close(channel_name)

در سمت گیرنده نیز می‌توانیم با استفاد از یک متغیر اضافه ببینیم که آیا کانال بسته است یا خیر:

variable_name, status := <- channel_variable

اگر status، True باشد، یعنی داده‌ها را از کانال دریافت کرده‌اید. اگر False باشد یعنی می‌خواهید از یک کانال بسته داده بخوانید.

می‌توانید از کانال‌ها برای ارتباط بین Goroutineها نیز استفاده کنیم. به دو Goroutine نیاز داریم: یکی برای فرستادن داده به کانال و یکی برای خواندن داده از آن. برنامه‌ی زیر را ببینید:

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

در برنامه‌ی بالا دو subroutine داریم: یکی داده‌ها را به کانال می‌فرستد و دیگری داده‌های کانال را نمایش می‌دهد. تابع add_to_channel اعداد 0 تا 9 را به کانال اضافه کرده و آن را می‌بندد. به صورت همزمان، تابع fetch_from_channel در دستور x, flag := <- ch منتظر رسیدن داده‌ها می‌ماند و با رسیدنشان آنها را نمایش می‌دهد. پس از False شدن فلگ اجرایش به پایان می‌رسد چون کانال بسته شده است.

در main() از تأخیر استفاده کرده‌ایم تا پیش از اتمام اجرای Goroutine بسته نشود.

خروجی به شکل زیر است:

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

دستور Select در زبان Go

دستور Select در زبان Go را می‌توانیم یک دستور Switch در نظر بگیریم که روی کانال‌ها اجرا می‌شود. در اینجا دستورات Case عملکردهای کانال هستند. معمولاً، هر دستور Case یک تلاش برای خواندن کانال است. اگر هریک از Caseها آماده باشد (کانال خوانده شده)، آن‌وقت دستور مرتبط به Case اجرا می‌‌شود. اگر چند دستور Case آماده باشند، یک دستور به صورت تصادفی انتخاب می‌شود. یک Case هم به صورت default داریم که در صورت آماده نبودن هیچ‌یک از Caseها اجرا می‌شود.

بیایید کد زیر را بررسی کنیم:

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

در کد بالا دستور Select منتظر آماده شدن داده در کانال‌ها می‌شود. data2() پس از تأخیر دو ثانیه‌ای به کانال داده اضافه می‌کند که موجب اجرا شدن Case دوم می‌شود.

یک default case به همان برنامه اضافه کرده و خروجی را ببینید. در کد زیر، با رسیدن به Select، اگر هیچ کانالی داده‌ی آماده نداشت، بلوک default بدون انتظار برای رسیدن داده اجرا می‌شود.

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

این خروجی برنامه است. چون هیچ کانالی داده نداشت، default case اجرا شده است.

mutex golang

Mutex مخفف عبارت Mutual Exclusion است. زمانی از Mutex استفاده می‌کنیم که نمی‌خواهیم چند Subroutine همزمان به یک منبع دسترسی داشته باشند. Mutex دو متد دارد: Lock و Unlock. Mutex در پکیج sync قرار دارد. پس باید پکیج sync را به درون برنامه ایمپورت کنید. دستوراتی که می‌خواهیم به صورت انحصار متقابل اجرا شوند را در mutex.Lock() و mutex.Unlock() قرار می‌دهیم.

بیایید با یک مثال Mutex را یاد بگیریم. این برنامه تعداد اجرای یک حلقه را می‌شمارد. انتظار داریم این روتین 10 مرتبه اجرا و مجموع تکرار‌ها ذخیره ‌شود. این روتین را 3 بار اجرا می‌کنیم که مجموع کل 30 می‌شود. این مجموع در یک متغیر سراسری count ذخیره می‌شود.

اول، برنامه را بدون 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 Goroutine می‌خواهند مقدار متغیر count را افزایش دهند. فرض کنید که count مقدارش 5 باشد و goroutine1 بخواهد آن را 6 کند. گام‌های اصلی به شکل زیرند:

  1. کپی کردن count در temp
  2. افزایش temp
  3. ذخیره‌ی temp در count

فرض کنید که پس از پایان اجرای گام 3 توسط goroutine1، یک Goroutine دیگر که دارای مقدار قبلی 3 است، مراحل بالا را تکرار و مقدار 4 را ذخیره می‌کند، که غلط است. در این شرایط می‌توانیم از Mutex استفاده کنیم. اگر یکی از روتین‌ها در حال استفاده از متغیر باشد، 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

در کد بالا خروجی موردنظر را به‌دست می‌آوریم. چون دستورات خواندن، افزایش و نوشتن مقدار در count در یک Mutex اجرا می‌شود.

مدیریت خطا در GO

خطاها در GO شرایطی غیرعادی هستند: مثلاً بستن فایلی که باز نیست و باز کردن فایلی که وجود ندارد و … . توابع معمولاً خطا را به عنوان مقدار بازگشت تابع برمی‌گردانند.

مثال زیر بیشتر درباره‌ی خطاها توضیح می‌دهد:

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 شد. اگر فایل موجود باشد، خطا null می‌شود.

خطاهای شخصی

با استفاده از این قابلیت می‌توانید خطاهای شخصی بسازید. می‌توانید با استفاده از new() در پکیج error از این قابلیت استفاده کنید. برنامه‌ی بالا را از نو می‌نویسیم تا از خطاهای شخصی استفاده کنیم.

برنامه‌ی زیر را اجرا کنید:

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

در اینجا area() محیط یک مربع است. اگر ورودی کمتر از 1 باشد، area() پیغام خطا برمی‌گرداند.

خواندن فایل در زبان Go

از فایل‌ها برای ذخیره‌ی داده استفاده می‌شود. با 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”) فایل را می‌خواند و دنباله‌ای از بایت‌ها را برمی‌گرداند. Go در زمان نمایش آن را به فرمت رشته درمی‌آورد.

نوشتن فایل‌ها

بیایید نوشتن را با یک برنامه بررسی کنیم:

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 ساخته می‌شود. اگر فایل از قبل موجود باشد، محتوای فایل Truncate می‌شود. از دستور WtiteLine() برای نوشتن در فایل استفاده می‌شود. بعد از آن با استفاده از دستور Close() می‌توانید فایل را ببندید.

تقلب نامه

در این آموزش زبان‌ برنامه ‌نویسی Go موضوعات زیر را بررسی کردیم:

موضوع توضیحات سینتکس
انواع پایه Numerc, String, bool
متغیرها ساخت متغیرها و مقداردهی به آنها var variable_name type var variable_name type = value var variable_name1, variable_name2 = value1, value2 variable_name := value
ثابت‌ها متغیرهایی که مقادیرشان پس از مقداردهی قابل تغییر نیستند. const variable = value
حلقه‌ی For دستورات را در یک حلقه اجرا می‌کند. for initialisation_expression; evaluation_expression; iteration_expression{ // one or more statement }
If else یک دستور شرطی. if condition{ // statements_1 }else{ // statements_2 }
switch دستورات شرطیِ دارای چند case. switch expression { case value_1: statements_1 case value_2: statements_2 case value_n: statements_n default: statements_default }
آرایه دنباله‌ای ثابت و دارای نام از المنت‌هایی هم نوع. arrayname := [size] type {value_0,value_1,…,value_size-1}
Slice بخش یا قسمتی از آرایه. var slice_name [] type = array_name[start:end]
توابع بلوکی از دستورات که کاری انجام می‌دهند. func function_name(parameter_1 type, parameter_n type) return_type { //statements }
پکیج‌ برای سازمان‌دهی کدها از پکیج استفاده می‌شود. پکیج‌ها قابلیت استفاده‌ی مجدد و خوانایی کد را افزایش می‌دهند. import package_nam
Defer دستور یک تابع را تا پایان اجرای تابع حاوی این دستور به تعویق می‌اندازد. defer function_name(parameter_list)
اشاره‌گر مکان حافظه‌ی متغیری دیگر را ذخیره می‌کند. var variable_name *type
Structure نوع داده‌ای کاربر که دارای یک یا چند المنت با نوع یکسان یا متفاوت است. type structname struct { variable_1 variable_1_type variable_2 variable_2_type variable_n variable_n_type }
متدها متد، تابعی است که یک آرگومان ورودی دارد. func (variable variabletype) methodName(parameter_list) { }
Goroutine تابعی که می‌تواند به صورت همروند با توابع دیگر اجرا شود. go function_name(parameter_list)
کانال روشی است که کانال‌ها با استفاده از آن می‌توانند با هم ارتباط داشته باشند. واسطی است که روتینی در آن داده می‌گذارد و روتین دیگر آن داده را برمی‌دارد. Declare: ch := make(chan int) Send data to channel: channel_variable <- variable_name Receive from channel: variable_name := <- channel_variable
Select Select را می‌توانیم یک دستور Switch درنظر بگیریم که روی کانال‌ها اجرا می‌شود. در اینجا دستورات Case عملکردهای کانال هستند. معمولاً، هر دستور Case یک تلاش برای خواندن کانال است. select { case x := <-chan1: fmt.Println(x) case y := <-chan2: fmt.Println(y) }
Mutex زمانی از Mutex استفاده می‌کنیم که نمی‌خواهیم چند Subroutine همزمان به یک منبع دسترسی داشته باشند. Mutex دو متد دارد: Lock و Unlock. mutex.Lock() //statements mutex.Unlock().
خواندن فایل‌ها خواندن داده‌ها و برگرداندن دنباله‌ای از بایت‌ها. Data, err := ioutil.ReadFile(filename)
نوشتن فایل‌ها نوشتن داده در فایل. l, err := f.WriteString(text_to_write)

دوره‌های مرتبط در فرانش

 

همانطور که مشاهده کردید در این بخش از سایت فرانش، دانلود و نصب کامپایلر، ساختار زبان برنامه نویسی Go، چگونگی ایجاد آن، انواع داده و بررسی متغیرها را آموزش دادیم. هر گونه نظر، پیشنهاد و یا انتقادی را در بخش دیدگاه انتهای مقاله با ما در میان بگذارید.

مرتضی شایق

مرتضی شایق

مدیر دیجیتال مارکتینگ

فعال در دیجیتال مارکتینگ در تخصص‌های: SEO (بهینه‌سازی سایت برای موتورهای جستجوگر)، SEM (بازاریابی در موتورهای جستجوگر)، UX (تجربه کاربری)

3 دیدگاه

  • خیلی کامل و خوب بود برای شروع و در کل عالی بود

    پاسخ

    • خواهش میکنم امیرجان

  • اگه دوست داری تصویرت نمایش داده بشه کلیک کن

    پاسخ

دیدگاهتان را بنویسید