Tutorial Golang: Învață limbajul de programare Go pentru începători
Ce este Go?
Go (cunoscut și sub numele de Golang) este un limbaj de programare open source dezvoltat de Google. Este un limbaj compilat tipizat static. Go acceptă programarea concomitentă, adică permite rularea mai multor procese simultan. Acest lucru se realizează folosind canale, goroutine, etc. Go Language are garbage collection care face ea însăși gestionarea memoriei și permite execuția amânată a funcțiilor.
Vom învăța toate elementele de bază ale Golang în acest tutorial Learn Go Language.
Cum să descărcați și să instalați GO
Pas 1) Mergi la pagina https://golang.org/dl/. Descărcați binarul pentru sistemul dvs. de operare.
Pas 2) Double faceți clic pe programul de instalare și faceți clic pe Run.
Pas 3) Faceți clic pe Următorul
Pas 4) Selectați folderul de instalare și faceți clic pe Următorul.
Pas 5) Faceți clic pe Terminare odată ce instalarea este finalizată.
Pas 6) Odată ce instalarea este finalizată, o puteți verifica deschizând terminalul și tastând
go version
Aceasta va afișa versiunea go instalată
Programul First Go – Go Hello World!
Creați un folder numit studyGo. În acest tutorial în limba Go, vom crea programele noastre go în interiorul acestui folder. Fișierele Go sunt create cu extensia .merge. Puteți rula programe Go folosind sintaxa
go run <filename>
Creați un fișier numit first.go și adăugați codul de mai jos în el și salvați
package main import ("fmt") func main() { fmt.Println("Hello World! This is my first Go program\n") }
Navigați la acest folder din terminalul dvs. Rulați programul folosind comanda
du-te fugi mai întâi.du-te
Puteți vedea imprimarea de ieșire
Hello World! This is my first Go program
Acum să discutăm despre programul de mai sus.
pachet principal – Fiecare program Go Language ar trebui să înceapă cu un nume de pachet. Go ne permite să folosim pachete în alte programe go și, prin urmare, acceptă reutilizarea codului. Execuția unui program Go începe cu codul din interiorul pachetului numit main.
import fmt – importă pachetul fmt. Acest pachet implementează funcțiile I/O.
func main() – Aceasta este funcția de la care începe execuția programului. Funcția principală ar trebui să fie întotdeauna plasată în pachetul principal. Sub main(), puteți scrie codul în { }.
fmt.Println – Aceasta va imprima textul pe ecran prin funcția Println a fmt.
Notă: În secțiunile de mai jos ale acestui tutorial Go, când menționați executarea/executarea codului, înseamnă să salvați codul într-un fișier cu extensia .go și să îl rulați folosind sintaxa
go run <filename>
Tipuri de date
Tipurile (tipurile de date) reprezintă tipul valorii stocate într-o variabilă, tipul valorii returnate de o funcție etc.
Există trei tipuri de bază în Go Language
Tipuri numerice – Reprezintă valori numerice care includ valori întregi, virgulă mobilă și complexe. Diferite tipuri numerice sunt:
int8 – numere întregi cu semn pe 8 biți.
int16 – numere întregi cu semn pe 16 biți.
int32 – numere întregi cu semn pe 32 biți.
int64 – numere întregi cu semn pe 64 biți.
uint8 – numere întregi fără semn pe 8 biți.
uint16 – numere întregi fără semn pe 16 biți.
uint32 – numere întregi fără semn pe 32 biți.
uint64 – numere întregi fără semn pe 64 biți.
float32 – numere în virgulă mobilă pe 32 de biți.
float64 – numere în virgulă mobilă pe 64 de biți.
complex64 – are float32 părți reale și imaginare.
complex128 – are float32 părți reale și imaginare.
Tipuri de șiruri – Reprezintă o secvență de octeți (caractere). Puteți face diverse operații pe șiruri, cum ar fi concatenarea șirurilor, extragerea subșirurilor etc
Tipuri booleene – Reprezintă 2 valori, fie adevărate fie false.
Interfața Golang
Interfața Golang este o colecție de semnături de metodă utilizată de un tip pentru a implementa comportamentul obiectelor. Scopul principal al interfeței Golang este de a oferi semnături de metodă cu nume, argumente și tipuri de returnare. Este la latitudinea unui Type să declare și să implementeze metoda. O interfață în Golang poate fi declarată folosind cuvântul cheie „interfață”.
Variabile
Variabilele indică o locație de memorie care stochează un fel de valoare. Parametrul tip (în sintaxa de mai jos) reprezintă tipul de valoare care poate fi stocată în locația de memorie.
Variabila poate fi declarată folosind sintaxa
var <variable_name> <type>
Odată ce declarați o variabilă de tip, puteți atribui variabila oricărei valori de tipul respectiv.
De asemenea, puteți da o valoare inițială unei variabile în timpul declarației în sine folosind
var <variable_name> <type> = <value>
Dacă declarați variabila cu o valoare inițială, mergeți și deduceți tipul variabilei din tipul valorii atribuite. Deci, puteți omite tipul în timpul declarației folosind sintaxa
var <variable_name> = <value>
De asemenea, puteți declara mai multe variabile cu sintaxa
var <variable_name1>, <variable_name2> = <value1>, <value2>
Programul de mai jos din acest tutorial Go are câteva exemple Golang de declarații de variabile
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) }
Ieșirea va fi
x: 3 y: 20 z: 50 i and j: 100 hello
Go Language oferă, de asemenea, o modalitate ușoară de a declara variabilele cu valoare prin omiterea cuvântului cheie var folosind
<variable_name> := <value>
Rețineți că ați folosit := în loc de =. Nu puteți folosi := doar pentru a atribui o valoare unei variabile care este deja declarată. := este folosit pentru a declara și a atribui valoare.
Creați un fișier numit assign.go cu următorul cod
package main import ("fmt") func main() { a := 20 fmt.Println(a) //gives error since a is already declared a := 30 fmt.Println(a) }
Executați go run assign.go pentru a vedea rezultatul ca
./assign.go:7:4: no new variables on left side of :=
Variabilele declarate fără valoare inițială vor avea 0 pentru tipurile numerice, false pentru boolean și șir gol pentru șiruri
constante
Variabilele constante sunt acele variabile a căror valoare nu poate fi modificată odată ce au fost atribuite. O constantă în limbajul de programare Go este declarată prin utilizarea cuvântului cheie „const”
Creați un fișier numit constant.go și cu următorul cod
package main import ("fmt") func main() { const b =10 fmt.Println(b) b = 30 fmt.Println(b) }
Executați go run constant.go pentru a vedea rezultatul ca
.constant.go:7:4: cannot assign to b
Pentru exemple de buclă
Buclele sunt folosite pentru a executa un bloc de instrucțiuni în mod repetat pe baza unei condiții. Majoritatea limbajelor de programare oferă 3 tipuri de bucle - for, while, do while. Dar limbajul de programare Go acceptă doar bucla for.
Sintaxa unei bucle pentru Golang este
for initialisation_expression; evaluation_expression; iteration_expression{ // one or more statement }
Expresia_inițializare este executată mai întâi (și doar o dată) în Golang pentru bucla.
Apoi evaluation_expression este evaluată și dacă este adevărat, codul din interiorul blocului este executat.
Id-ul expresiei_iterație este executat, iar expresia_evaluării este evaluat din nou. Dacă este adevărat, blocul de instrucțiuni este executat din nou. Aceasta va continua până când expresia_evaluare devine falsă.
Copiați programul de mai jos într-un fișier și executați-l pentru a vedea Golang pentru numere de tipărire în buclă de la 1 la 5
package main import "fmt" func main() { var i int for i = 1; i <= 5; i++ { fmt.Println(i) } }
Ieșirea este
1 2 3 4 5
Dacă altceva
Dacă else este o declarație condiționată. Sinaxa este
if condition{ // statements_1 }else{ // statements_2 }
Aici condiția este evaluată și dacă este adevărată instrucțiunile_1 vor fi executate, altfel vor fi executate instrucțiunile_2.
Puteți folosi declarația if și fără else. De asemenea, puteți avea declarații if else înlănțuite. Programele de mai jos vor explica mai multe despre dacă altfel.
Executați programul de mai jos. Verifică dacă un număr, x, este mai mic de 10. Dacă da, va afișa „x este mai mic de 10”
package main import "fmt" func main() { var x = 50 if x < 10 { //Executes if x < 10 fmt.Println("x is less than 10") } }
Aici, deoarece valoarea lui x este mai mare decât 10, instrucțiunea din interiorul dacă condiția blocului nu va fi executată.
Acum vedeți programul de mai jos. În acest tutorial despre limbajul de programare Go, avem un bloc else care va fi executat la eșecul evaluării 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") } }
Acest program vă va oferi rezultate
x is greater than or equals 10
Acum, în acest tutorial Go, vom vedea un program cu mai multe blocuri if else (înlănțuite dacă else). Executați exemplul Go de mai jos. Verifică dacă un număr este mai mic de 10 sau este între 10-90 sau mai mare de 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") } }
Aici, mai întâi, condiția if verifică dacă x este mai mic de 10 și nu este. Deci verifică următoarea condiție (altfel dacă) dacă este între 10 și 90, ceea ce este, de asemenea, fals. Deci apoi execută blocul din secțiunea else care dă rezultatul
x is greater than 90
Intrerupator
Switch este o altă declarație condiționată. Instrucțiunile Switch evaluează o expresie și rezultatul este comparat cu un set de valori disponibile (cazuri). Odată ce o potrivire este găsită, instrucțiunile asociate cu acea potrivire (caz) sunt executate. Dacă nu se găsește nicio potrivire, nimic nu va fi executat. De asemenea, puteți adăuga un caz implicit pentru comutare, care va fi executat dacă nu sunt găsite alte potriviri. Sintaxa comutatorului este
switch expression { case value_1: statements_1 case value_2: statements_2 case value_n: statements_n default: statements_default }
Aici valoarea expresiei este comparată cu valorile din fiecare caz. Odată ce este găsită o potrivire, instrucțiunile asociate cu acel caz sunt executate. Dacă nu se găsește nicio potrivire, instrucțiunile din secțiunea implicită sunt executate.
Executați programul de mai jos
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") } }
Veți obține rezultatul ca
Sum is 3
Schimbați valoarea lui a și b la 3 și rezultatul va fi
Printing default
De asemenea, puteți avea mai multe valori într-un caz, separându-le cu virgulă.
Arrays
Array reprezintă o dimensiune fixă, numită secvență de elemente de același tip. Nu puteți avea o matrice care conține atât numere întregi, cât și caractere. Nu puteți modifica dimensiunea unei matrice odată ce ați definit dimensiunea.
Sintaxa pentru declararea unui tablou este
var arrayname [size] type
Fiecărui element de matrice i se poate atribui o valoare folosind sintaxa
arrayname [index] = value
Indexul matricei începe de la 0 la mărimea-1.
Puteți atribui valori elementelor de matrice în timpul declarației folosind sintaxa
arrayname := [size] type {value_0,value_1,…,value_size-1}
De asemenea, puteți ignora parametrul dimensiune în timp ce declarați matricea cu valori, înlocuind dimensiunea cu ... iar compilatorul va găsi lungimea din numărul de valori. Sintaxa este
arrayname := […] type {value_0,value_1,…,value_size-1}
Puteți găsi lungimea matricei folosind sintaxa
len(arrayname)
Executați exemplul Go de mai jos pentru a înțelege matricea
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]) }
producție
Two 3 [One Two Three] [1 2 3 4 5] 5
Golang Slice și funcție de adăugare
O felie este o porțiune sau un segment dintr-o matrice. Sau este o vedere sau o vedere parțială a unui tablou subiacent către care indică. Puteți accesa elementele unei secțiuni folosind numele și numărul de index la fel ca într-o matrice. Nu puteți modifica lungimea unei matrice, dar puteți modifica dimensiunea unei felii.
Conținutul unei felii sunt de fapt pointerii către elementele unui tablou. Inseamna dacă modificați orice element dintr-o felie, conținutul matricei de bază va fi de asemenea afectat.
Sintaxa pentru crearea unei felii este
var slice_name [] type = array_name[start:end]
Aceasta va crea o porțiune numită slice_name dintr-o matrice numită array_name cu elementele de la index de la început la sfârșit-1.
Acum, în acest tutorial Golang, vom executa programul de mai jos. Programul va crea o porțiune din matrice și o va imprima. De asemenea, puteți vedea că modificarea conținutului din felie va modifica matricea reală.
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) }
Acesta va imprima rezultatul ca
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]
Există anumite funcții precum Golang len, Golang append pe care le puteți aplica pe felii
len(nume_slice) – returnează lungimea feliei
append(nume_slice, valoare_1, valoare_2) – Golang append este folosit pentru a adăuga value_1 și value_2 la o porțiune existentă.
append(slice_nale1,slice_name2...) – adaugă slice_name2 la slice_name1
Executați următorul program.
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) }
Ieșirea va fi
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]
Programul creează mai întâi 2 felii și i-a imprimat lungimea. Apoi a atașat o felie la alta și apoi a atașat un șir la felia rezultată.
funcţii
O funcție reprezintă un bloc de instrucțiuni care îndeplinește o anumită sarcină. O declarație de funcție ne spune numele funcției, tipul de returnare și parametrii de intrare. Definiția funcției reprezintă codul conținut în funcție. Sintaxa pentru declararea funcției este
func function_name(parameter_1 type, parameter_n type) return_type { //statements }
Parametrii și tipurile de returnare sunt opționale. De asemenea, puteți returna mai multe valori dintr-o funcție.
Acum, în acest tutorial Golang, să rulăm următorul exemplu Golang. Aici funcția numită calc va accepta 2 numere și efectuează adunarea și scăderea și returnează ambele valori.
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) }
Ieșirea va fi
Sum 25 Diff 5
Pachete
Pachetele sunt folosite pentru a organiza codul. Într-un proiect mare, nu este fezabil să scrieți cod într-un singur fișier. Limbajul de programare Go ne permite să organizăm codul în diferite pachete. Acest lucru crește lizibilitatea și reutilizarea codului. Un program Go executabil ar trebui să conțină un pachet numit main și execuția programului începe de la funcția numită main. Puteți importa alte pachete în programul nostru folosind sintaxa
import package_name
Vom vedea și discuta în acest tutorial Golang, cum să creați și să utilizați pachete în următorul exemplu Golang.
Pas 1) Creați un fișier numit package_example.go și adăugați codul de mai jos
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) }
În programul de mai sus, fmt este un pachet pe care limbajul de programare Go ni-l oferă în principal pentru scopuri I/O. De asemenea, puteți vedea un pachet numit calcul. În interiorul main() puteți vedea o sumă pasă := calcul.Do_add(x,y). Înseamnă că invocați funcția Do_add din calculul pachetului.
Pas 2) În primul rând, ar trebui să creați calculul pachetului într-un folder cu același nume sub folderul src din go. Calea instalată a go poate fi găsită din variabila PATH.
Pentru Mac, găsiți calea executând echo $PATH
Deci calea este /usr/local/go
Pentru Windows, găsiți calea executând echo %GOROOT%
Aici calea este C:\Go\
Pas 3) Navigați la folderul src (/usr/local/go/src pentru Mac și C:\Go\src pentru Windows). Acum, din cod, numele pachetului este de calcul. Go necesită ca pachetul să fie plasat într-un director cu același nume sub directorul src. Creați un director numit calcul în folderul src.
Pas 4) Creați un fișier numit calc.go (puteți da orice nume, dar numele pachetului din cod contează. Aici ar trebui să fie calcul) în directorul de calcul și adăugați codul de mai jos
package calculation func Do_add(num1 int, num2 int)(int) { sum := num1 + num2 return sum }
Pas 5) Rulați comanda go install din directorul de calcul care va compila calc.go.
Pas 6) Acum reveniți la package_example.go și rulați go run package_example.go. Ieșirea va fi Suma 25.
Rețineți că numele funcției Do_add începe cu o literă majusculă. Acest lucru se datorează faptului că în Go, dacă numele funcției începe cu o literă majusculă, înseamnă că alte programe îl pot vedea (accesa), altfel alte programe nu îl pot accesa. Dacă numele funcției a fost do_add , atunci ați fi primit eroarea
nu se poate face referire la calculul numelui neexportat.calc..
Amânare și stivuire amână
Instrucțiunile Defer sunt folosite pentru a amâna execuția unui apel de funcție până când funcția care conține instrucțiunea defer finalizează execuția.
Să învățăm asta cu un exemplu:
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()") }
Ieșirea va fi
Inside the main() Inside the sample()
Aici execuția sample() este amânată până când se finalizează execuția funcției incluse(main()).
Stacking defer folosește mai multe instrucțiuni defer. Să presupunem că aveți mai multe instrucțiuni defer în interiorul unei funcții. Go plasează toate apelurile de funcții amânate într-o stivă și, odată ce funcția de încadrare revine, funcțiile stivuite sunt executate în Comanda Last In First Out (LIFO). Puteți vedea acest lucru în exemplul de mai jos.
Executați codul de mai jos
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) }
Ieșirea va fi
4 3 2 1
Aici codul din main() se execută mai întâi, iar apoi apelurile de funcții amânate sunt executate în ordine inversă, adică 4, 3,2,1.
Pointeri
Înainte de a explica indicatorii, să discutăm mai întâi despre operatorul „&”. Operatorul „&” este folosit pentru a obține adresa unei variabile. Înseamnă că „&a” va tipări adresa de memorie a variabilei a.
În acest tutorial Golang, vom executa programul de mai jos pentru a afișa valoarea unei variabile și adresa acelei variabile
package main import "fmt" func main() { a := 20 fmt.Println("Address:",&a) fmt.Println("Value:",a) }
Rezultatul va fi
Address: 0xc000078008 Value: 20
O variabilă pointer stochează adresa de memorie a altei variabile. Puteți defini un pointer folosind sintaxa
var variable_name *type
Asteriscul(*) reprezintă variabila este un pointer. Veți înțelege mai multe executând programul de mai jos
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)}
Ieșirea va fi
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
Structuri
O structură este un tip de date definit de utilizator, care conține încă un element de același tip sau diferit.
Utilizarea unei structuri este un proces în 2 etape.
Mai întâi, creați (declarați) un tip de structură
În al doilea rând, creați variabile de acest tip pentru a stoca valori.
Structurile sunt utilizate în principal atunci când doriți să stocați împreună date asociate.
Luați în considerare o informație despre angajați care are numele, vârsta și adresa. Puteți gestiona asta în 2 moduri
Creați 3 matrice - o matrice stochează numele angajaților, una stochează vârsta și al treilea stochează vârsta.
Declarați un tip de structură cu 3 câmpuri - nume, adresă și vârstă. Creați o matrice de acel tip de structură în care fiecare element este un obiect de structură având nume, adresă și vârstă.
Prima abordare nu este eficientă. În astfel de scenarii, structurile sunt mai convenabile.
Sintaxa pentru declararea unei structuri este
type structname struct { variable_1 variable_1_type variable_2 variable_2_type variable_n variable_n_type }
Un exemplu de declarație de structură este
type emp struct { name string address string age int }
Aici este creat un nou tip definit de utilizator numit emp. Acum, puteți crea variabile de tip emp folosind sintaxa
var variable_name struct_name
Un exemplu este
var empdata1 emp
Puteți seta valori pentru empdata1 ca
empdata1.name = "John" empdata1.address = "Street-1, Bangalore" empdata1.age = 30
De asemenea, puteți crea o variabilă de structură și aloca valori prin
empdata2 := emp{"Raj", "Building-1, Delhi", 25}
Aici, trebuie să mențineți ordinea elementelor. Raj va fi mapat după nume, următorul element de adresat și ultimul pentru vârsta.
Executați codul de mai jos
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) }
producție
John Raj
Metode (nu funcții)
O metodă este o funcție cu un argument receptor. Archidin punct de vedere tehnic, este între cuvântul cheie func și numele metodei. Sintaxa unei metode este
func (variable variabletype) methodName(parameter1 paramether1type) { }
Să convertim exemplul de program de mai sus pentru a folosi metode în loc de funcție.
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 nu este un limbaj orientat pe obiecte și nu are conceptul de clasă. Metodele oferă o imagine a ceea ce faci în programele orientate pe obiecte în care funcțiile unei clase sunt invocate folosind sintaxa nume obiect.numefuncție()
Concurenta
Go acceptă executarea concomitentă a sarcinilor. Înseamnă că Go poate executa mai multe sarcini simultan. Este diferit de conceptul de paralelism. În paralelism, o sarcină este împărțită în subsarcini mici și sunt executate în paralel. Dar în concurență, mai multe sarcini sunt executate simultan. Concurența se realizează în Go folosind Goroutines și Canale.
Goroutine
O goroutine este o funcție care poate rula concomitent cu alte funcții. De obicei, atunci când o funcție este invocată, controlul este transferat în funcția apelată și, odată finalizată, controlul de execuție revine la funcția de apelare. Funcția de apelare își continuă apoi execuția. Funcția apelantă așteaptă ca funcția invocată să finalizeze execuția înainte de a continua cu restul instrucțiunilor.
Dar în cazul goroutine, funcția de apelare nu va aștepta finalizarea execuției funcției invocate. Va continua să se execute cu următoarele instrucțiuni. Puteți avea mai multe goroutine într-un program.
De asemenea, programul principal se va închide odată ce își încheie executarea instrucțiunilor și nu va aștepta finalizarea goroutinelor invocate.
Goroutine este invocată folosind cuvântul cheie go urmat de un apel de funcție.
Exemplu
go add(x,y)
Veți înțelege goroutine cu exemplele Golang de mai jos. Executați programul de mai jos
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") } }
Ieșirea va fi
In main In main In main In main In main
Aici programul principal și-a încheiat execuția chiar înainte de a începe goroutine. Display() este o rutină care este invocată folosind sintaxa
go function_name(parameter list)
În codul de mai sus, main() nu așteaptă ca display() să se finalizeze, iar main() și-a încheiat execuția înainte ca display() să-și execute codul. Deci declarația print din display() nu a fost tipărită.
Acum modificăm programul pentru a imprima instrucțiunile și din display(). Adăugăm o întârziere de 2 secunde în bucla for a main() și o întârziere de 1 secundă în bucla for a afișajului().
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") } }
Ieșirea va fi oarecum similară cu
In display In main In display In display In main In display In display In main In main In main
Aici puteți vedea că ambele bucle sunt executate într-un mod suprapus din cauza execuției concurente.
Canale
Canalele sunt o modalitate prin care funcțiile pot comunica între ele. Poate fi considerat ca un mediu în care o rutină plasează date și este accesată de o altă rutină pe serverul Golang.
Un canal poate fi declarat cu sintaxa
channel_variable := make(chan datatype)
Exemplu:
ch := make(chan int)
Puteți trimite date către un canal folosind sintaxa
channel_variable <- variable_name
Exemplu
ch <- x
Puteți primi date de la un canal folosind sintaxa
variable_name := <- channel_variable
Exemplu
y := <- ch
În exemplele de limbă Go de mai sus de goroutine, ați văzut că programul principal nu așteaptă goroutine. Dar nu este cazul când sunt implicate canale. Să presupunem că dacă o rutină împinge date către canal, main() va aștepta instrucțiunea care primește datele canalului până când primește datele.
Veți vedea acest lucru în exemplele de limbă Go de mai jos. Mai întâi, scrieți o rutină normală și vedeți comportamentul. Apoi modificați programul pentru a utiliza canale și a vedea comportamentul.
Executați programul de mai jos
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()") }
Ieșirea va fi
Inside main()
Main() a terminat execuția și a ieșit înainte ca goroutine să se execute. Deci tipărirea din interiorul display() nu a fost executată.
Acum modificați programul de mai sus pentru a utiliza canalele și a vedea comportamentul.
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) }
Ieșirea va fi
Inside display() Inside main() Printing x in main() after taking from channel: 1234
Aici ceea ce se întâmplă este main() la atingerea x := <-ch va aștepta date pe canalul ch. Display-ul () are o așteptare de 5 secunde și apoi împinge datele către canalul de canal. Main() la primirea datelor de la canal este deblocat și își continuă execuția.
Expeditorul care transmite date pe canal poate informa receptorii că nu vor mai fi adăugate date la canal prin închiderea canalului. Acesta este folosit în principal atunci când utilizați o buclă pentru a împinge date către un canal. Un canal poate fi închis folosind
close(channel_name)
Și la capătul receptorului, este posibil să verificați dacă canalul este închis folosind o variabilă suplimentară în timp ce preluați date de pe canal folosind
variable_name, status := <- channel_variable
Dacă starea este True înseamnă că ați primit date de la canal. Dacă este fals, înseamnă că încercați să citiți de pe un canal închis
De asemenea, puteți utiliza canale pentru comunicarea între goroutine. Trebuie să utilizați 2 goroutine - unul împinge datele către canal, iar celălalt primește datele de la canal. Vezi programul de mai jos
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()") }
Aici există 2 subrutine, una împinge datele către canal și cealaltă imprimă datele către canal. Funcția add_to_channel adaugă numerele de la 0 la 9 și închide canalul. Simultan, funcția fetch_from_channel așteaptă la
x, flag := <- ch și odată ce datele devin disponibile, se tipărește datele. Iese odată ce steagul este fals, ceea ce înseamnă că canalul este închis.
Așteptarea în main() este dată pentru a preveni ieșirea din main() până când goroutines termină execuția.
Executați codul și vedeți rezultatul ca
Read data Send data 0 1 2 3 4 5 6 7 8 9 Empty channel Inside main()
Selectați
Select poate fi văzut ca o declarație de comutare care funcționează pe canale. Aici declarațiile de caz vor fi o operațiune de canal. De obicei, fiecare declarație de caz va fi citită încercare de pe canal. Când oricare dintre cazuri este gata (canalul este citit), atunci instrucțiunea asociată cu acel caz este executată. Dacă sunt pregătite mai multe cazuri, va alege unul aleatoriu. Puteți avea un caz implicit care este executat dacă niciunul dintre cazuri nu este pregătit.
Să vedem codul de mai jos
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) } }
Executarea programului de mai sus va da rezultatul:
from data2()
Aici declarația select așteaptă ca datele să fie disponibile pe oricare dintre canale. Data2() adaugă date la canal după un somn de 2 secunde, ceea ce va determina executarea celui de-al doilea caz.
Adăugați un caz implicit la selectare în același program și vedeți rezultatul. Aici, la atingerea blocului select, dacă niciun caz nu are date pregătite pe canal, acesta va executa blocul implicit fără a aștepta ca datele să fie disponibile pe orice canal.
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") } }
Acest program va da rezultatul:
Default case executed
Acest lucru se datorează faptului că, atunci când a ajuns blocul de selectare, niciun canal nu avea date de citit. Deci, cazul implicit este executat.
mutex
Mutex este forma scurtă pentru excluderea reciprocă. Mutex este folosit atunci când nu doriți să permiteți accesarea unei resurse de mai multe subrutine în același timp. Mutex are 2 metode – Blocare și deblocare. Mutex este conținut în pachetul de sincronizare. Deci, trebuie să importați pachetul de sincronizare. Instrucțiunile care trebuie să fie executate reciproc exclusiv pot fi plasate în interiorul mutex.Lock() și mutex.Unlock().
Să învățăm mutex cu un exemplu care numără de câte ori este executată o buclă. În acest program ne așteptăm ca rutina să ruleze bucla de 10 ori și numărul este stocat în sumă. Apelați această rutină de 3 ori, astfel încât numărul total ar trebui să fie 30. Numărul este stocat într-un număr global de variabile.
În primul rând, rulați programul fără 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) }
Vezi rezultatul
Count after i=1 Count: 11 Count after i=3 Count: 12 Count after i=2 Count: 13 Final Count: 13
Rezultatul poate fi diferit atunci când îl executați, dar rezultatul final nu va fi 30.
Aici ceea ce se întâmplă este că 3 goroutine încearcă să mărească numărul de bucle stocat în numărul de variabile. Să presupunem că la un moment dat numărul este 5 și goroutine1 va crește numărul la 6. Pașii principali includ
Număr de copii la temp
Creșteți temperatura
Stocați temperatura înapoi pentru a număra
Să presupunem că la scurt timp după efectuarea pasului 3 de către goroutine1; o altă goroutină ar putea avea o valoare veche, să spunem că 3 face pașii de mai sus și stochează 4 înapoi, ceea ce este greșit. Acest lucru poate fi prevenit prin utilizarea mutex care face ca alte rutine să aștepte atunci când o rutină folosește deja variabila.
Acum veți rula programul cu mutex. Aici cei 3 pași menționați mai sus sunt executați într-un 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) }
Acum ieșirea va fi
Count after i=3 Count: 21 Count after i=2 Count: 28 Count after i=1 Count: 30 Final Count: 30
Aici obținem rezultatul așteptat ca rezultat final. Deoarece instrucțiunile de citire, creștere și rescriere a numărului sunt executate într-un mutex.
Eroare de manipulare
Erorile sunt condiții anormale, cum ar fi închiderea unui fișier care nu este deschis, deschiderea unui fișier care nu există, etc. Funcțiile returnează de obicei erori ca ultima valoare returnată.
Exemplul de mai jos explică mai multe despre eroare.
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") }
Ieșirea va fi:
open /invalid.txt: no such file or directory
Aici am încercat să deschidem un fișier inexistent și a returnat eroarea variabilei. Dacă fișierul este valid, atunci eroarea va fi nulă
Erori personalizate
Folosind această funcție, puteți crea erori personalizate. Acest lucru se face prin utilizarea New() a pachetului de erori. Vom rescrie programul de mai sus pentru a folosi erorile personalizate.
Rulați programul de mai jos
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) } }
Ieșirea va fi:
Custom error message:File name is wrong
Aici area() returnează aria unui pătrat. Dacă intrarea este mai mică de 1, atunci area() returnează un mesaj de eroare.
Citirea fișierelor
Fișierele sunt folosite pentru stocarea datelor. Go ne permite să citim date din fișiere
Mai întâi creați un fișier, data.txt, în directorul dvs. actual cu conținutul de mai jos.
Line one Line two Line three
Acum rulați programul de mai jos pentru a vedea că imprimă conținutul întregului fișier ca rezultat
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)) }
Aici datele, err := ioutil.ReadFile(“data.txt”) citește datele și returnează o secvență de octeți. În timpul imprimării, acesta este convertit în format șir.
Scrierea fișierelor
Veți vedea asta cu un program
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 } }
Aici este creat un fișier, test.txt. Dacă fișierul există deja, atunci conținutul fișierului este trunchiat. Writeline() este folosit pentru a scrie conținutul fișierului. După aceea, ați închis fișierul folosind Close().
Cheat Sheet
În acest tutorial Go, am acoperit,
Subiect | Descriere | Sintaxă |
---|---|---|
Tipuri de bază | Numeric, șir, bool | |
Variabile | Declarați și atribuiți valori variabilelor | var tipul nume_variabilă var nume_variabilă tip = valoare var nume_variabilă1, nume_variabilă2 = valoare1, valoare2 nume_variabilă := valoare |
constante | Variabile a căror valoare nu poate fi modificată odată ce au fost atribuite | const variabilă = valoare |
Pentru Loop | Executați instrucțiuni într-o buclă. | pentru initialisation_expression; expresie_evaluare; expresie_iterație{ // una sau mai multe declarații } |
Dacă altceva | Este o declarație condiționată | dacă condiția{ // declarații_1 } else { // declarații_2 } |
comuta | Declarație condiționată cu mai multe cazuri | schimba expresia { valoarea cazului_1: declarații_1 valoarea cazului_2: declarații_2 case value_n: declarații_n implicit: statements_default } |
Mulțime | Secvență de nume de dimensiune fixă a elementelor de același tip | arrayname := [dimensiune] tip {value_0,value_1,…,value_size-1} |
Felie | Porțiune sau segment dintr-o matrice | var slice_name [] type = array_name[start:end] |
funcţii | Bloc de declarații care îndeplinește o anumită sarcină | func function_name(parameter_1 type, parameter_n type) return_type { //instrucțiuni } |
Pachete | Sunt folosite pentru a organiza codul. Mărește lizibilitatea și reutilizarea codului | import package_nam |
Amâna | Amână execuția unei funcții până când funcția care o conține termină execuția | defer function_name(lista_parametri) |
Pointeri | Stochează adresa de memorie a unei alte variabile. | var nume_variabilă *tip |
Structure | Tip de date definit de utilizator, care conține încă un element de același tip sau diferit | tip structname struct { variabilă_1 variabilă_1_tip variabilă_2 variabilă_2_tip variabilă_n variabilă_n_tip } |
Aplicate | O metodă este o funcție cu un argument receptor | func (variabilă tip variabilă) methodName(lista_parametri) { } |
Goroutine | O funcție care poate rula concomitent cu alte funcții. | mergeți numele_funcției(lista_parametrilor) |
Canal | Modul prin care funcțiile pot comunica între ele. Un mediu pe care o rutină plasează date și este accesată de o altă rutină. | Declara: ch := make(chan int) Trimiteți date către canal: variabilă_canal <- nume_variabilă Primiți de pe canal: nume_variabilă := <- variabilă_canal |
Selectați | Declarație Switch care funcționează pe canale. Declarațiile de caz vor fi o operațiune de canal. Când oricare dintre canale este gata cu date, atunci instrucțiunea asociată cu acel caz este executată | Selectați { caz x := <-chan1: fmt.Println(x) cazul y := <-chan2: fmt.Println(y) } |
mutex | Mutex este folosit atunci când nu doriți să permiteți accesarea unei resurse de mai multe subrutine în același timp. Mutex are 2 metode – Blocare și deblocare | mutex.Lock() //instrucțiuni mutex.Unlock(). |
Citiți fișiere | Citește datele și returnează o secvență de octeți. | Date, err := ioutil.ReadFile(nume fișier) |
Scrieți fișierul | Scrie date într-un fișier | l, err := f.WriteString(text_to_write) |