Tutorial Golang: impara il linguaggio di programmazione Go per principianti
Cos'è Go?
Go (noto anche come Golang) è un linguaggio di programmazione open source sviluppato da Google. È un linguaggio compilato tipizzato staticamente. Go supporta la programmazione simultanea, ovvero consente di eseguire più processi contemporaneamente. Ciò si ottiene utilizzando canali, goroutine, ecc. Go Language dispone di garbage collection che a sua volta si occupa della gestione della memoria e consente l'esecuzione posticipata delle funzioni.
Impareremo tutte le basi di Golang in questo tutorial Learn Go Language.
Come scaricare e installare GO
Passo 1) Vai su https://golang.org/dl/. Scarica il binario per il tuo sistema operativo.
Passo 2) Double fare clic sul programma di installazione e fare clic su Esegui.
Passo 3) Fare clic su Avanti
Passo 4) Selezionare la cartella di installazione e fare clic su Avanti.
Passo 5) Fare clic su Fine al termine dell'installazione.
Passo 6) Una volta completata l'installazione puoi verificarla aprendo il terminale e digitando
go version
Verrà visualizzata la versione di go installata
Il tuo programma First Go – Go Hello World!
Crea una cartella chiamata studyGo. In questo tutorial sul linguaggio Go, creeremo i nostri programmi Go all'interno di questa cartella. I file Go vengono creati con l'estensione .andare. È possibile eseguire i programmi Go utilizzando la sintassi
go run <filename>
Crea un file chiamato first.go e aggiungi il codice seguente e salva
package main import ("fmt") func main() { fmt.Println("Hello World! This is my first Go program\n") }
Passare a questa cartella nel terminale. Esegui il programma utilizzando il comando
vai a correre prima. vai
È possibile vedere la stampa dell'output
Hello World! This is my first Go program
Ora parliamo del programma di cui sopra.
pacchetto principale: ogni programma Go Language dovrebbe iniziare con il nome di un pacchetto. Go ci consente di utilizzare i pacchetti in altri programmi Go e quindi supporta la riusabilità del codice. L'esecuzione di un programma Go inizia con il codice all'interno del pacchetto denominato main.
import fmt – importa il pacchetto fmt. Questo pacchetto implementa le funzioni di I/O.
func main() – Questa è la funzione da cui inizia l'esecuzione del programma. La funzione principale dovrebbe essere sempre inserita nel pacchetto principale. Sotto main(), puoi scrivere il codice all'interno di { }.
fmt.Println – Questo stamperà il testo sullo schermo tramite la funzione Println di fmt.
Nota: nelle sezioni seguenti di questo tutorial Go, quando menzioni esegui/esegui il codice, significa salvare il codice in un file con estensione .go ed eseguirlo utilizzando la sintassi
go run <filename>
Tipi di dati
I tipi (tipi di dati) rappresentano il tipo del valore memorizzato in una variabile, il tipo del valore restituito da una funzione, ecc.
Ci sono tre tipi base in Go Language
Tipi numerici – Rappresenta valori numerici che includono valori interi, a virgola mobile e complessi. I vari tipi numerici sono:
int8 – Interi con segno a 8 bit.
int16 – Interi con segno a 16 bit.
int32 – Interi con segno a 32 bit.
int64 – Interi con segno a 64 bit.
uint8 – Interi senza segno a 8 bit.
uint16 – Interi senza segno a 16 bit.
uint32 – Interi senza segno a 32 bit.
uint64 – Interi senza segno a 64 bit.
float32 – Numeri in virgola mobile a 32 bit.
float64 – Numeri in virgola mobile a 64 bit.
complex64 – ha float32 parti reali e immaginarie.
complex128 – ha float32 parti reali e immaginarie.
Tipi di stringa – Rappresenta una sequenza di byte (caratteri). Puoi eseguire varie operazioni sulle stringhe come concatenazione di stringhe, estrazione di sottostringhe, ecc
Tipi booleani – Rappresenta 2 valori, vero o falso.
Interfaccia Golang
Interfaccia Golang è una raccolta di firme di metodo utilizzate da un Type per implementare il comportamento degli oggetti. L'obiettivo principale dell'interfaccia Golang è fornire firme dei metodi con nomi, argomenti e tipi restituiti. Spetta a Type dichiarare e implementare il metodo. Un'interfaccia in Golang può essere dichiarata utilizzando la parola chiave "interfaccia".
Variabili
Le variabili puntano a una posizione di memoria che memorizza un qualche tipo di valore. Il parametro type (nella sintassi seguente) rappresenta il tipo di valore che può essere archiviato nella posizione di memoria.
La variabile può essere dichiarata utilizzando la sintassi
var <variable_name> <type>
Una volta dichiarata una variabile di un tipo, è possibile assegnare la variabile a qualsiasi valore di quel tipo.
Puoi anche dare un valore iniziale ad una variabile durante la dichiarazione stessa usando
var <variable_name> <type> = <value>
Se dichiari la variabile con un valore iniziale, vai a dedurre il tipo della variabile dal tipo di valore assegnato. Quindi puoi omettere il tipo durante la dichiarazione usando la sintassi
var <variable_name> = <value>
Inoltre, puoi dichiarare più variabili con la sintassi
var <variable_name1>, <variable_name2> = <value1>, <value2>
Il programma seguente in questo tutorial Go contiene alcuni esempi Golang di dichiarazioni di variabili
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) }
L'output sarà
x: 3 y: 20 z: 50 i and j: 100 hello
Go Language fornisce anche un modo semplice per dichiarare le variabili con valore omettendo la parola chiave var using
<variable_name> := <value>
Nota che hai usato := invece di =. Non è possibile utilizzare := solo per assegnare un valore a una variabile già dichiarata. := viene utilizzato per dichiarare e assegnare valore.
Crea un file chiamatoassign.go con il seguente codice
package main import ("fmt") func main() { a := 20 fmt.Println(a) //gives error since a is already declared a := 30 fmt.Println(a) }
Esegui go runassign.go per vedere il risultato come
./assign.go:7:4: no new variables on left side of :=
Le variabili dichiarate senza valore iniziale avranno 0 per i tipi numerici, false per i booleani e stringa vuota per le stringhe
Costante
Le variabili costanti sono quelle variabili il cui valore non può essere modificato una volta assegnato. Una costante nel linguaggio di programmazione Go viene dichiarata utilizzando la parola chiave "const"
Crea un file chiamato Constant.go e con il seguente codice
package main import ("fmt") func main() { const b =10 fmt.Println(b) b = 30 fmt.Println(b) }
Esegui go run Constant.go per vedere il risultato come
.constant.go:7:4: cannot assign to b
Per esempi di loop
I cicli vengono utilizzati per eseguire ripetutamente un blocco di istruzioni in base a una condizione. La maggior parte dei linguaggi di programmazione fornisce 3 tipi di cicli: for, while, do while. Ma il linguaggio di programmazione Go supporta solo il ciclo for.
La sintassi di un ciclo Golang for è
for initialisation_expression; evaluation_expression; iteration_expression{ // one or more statement }
L'inizializzazione_espressione viene eseguita per prima (e solo una volta) nel ciclo Golang for.
Quindi viene valutata la valutazione_espressione e se è vera viene eseguito il codice all'interno del blocco.
L'id iteration_expression viene eseguito e la valutazione_expression viene valutata nuovamente. Se è vero, il blocco di istruzioni viene eseguito nuovamente. Ciò continuerà finché la valutazione_espressione non diventa falsa.
Copia il programma seguente in un file ed eseguilo per vedere i numeri di stampa del ciclo Golang for da 1 a 5
package main import "fmt" func main() { var i int for i = 1; i <= 5; i++ { fmt.Println(i) } }
L'uscita è
1 2 3 4 5
Se altro
Se altrimenti è un'istruzione condizionale. La sinassi lo è
if condition{ // statements_1 }else{ // statements_2 }
Qui la condizione viene valutata e se è vera verranno eseguite le istruzioni_1, altrimenti verranno eseguite le istruzioni_2.
Puoi usare l'istruzione if anche senza else. Puoi anche aver concatenato le istruzioni if else. I programmi seguenti spiegheranno di più su se altro.
Eseguire il programma seguente. Controlla se un numero, x, è inferiore a 10. In tal caso, stamperà "x è inferiore a 10"
package main import "fmt" func main() { var x = 50 if x < 10 { //Executes if x < 10 fmt.Println("x is less than 10") } }
Qui poiché il valore di x è maggiore di 10, l'istruzione all'interno della condizione di blocco non verrà eseguita.
Ora guarda il programma qui sotto. In questo tutorial sul linguaggio di programmazione Go, abbiamo un blocco else che verrà eseguito in caso di fallimento della valutazione 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") } }
Questo programma ti darà un output
x is greater than or equals 10
Ora in questo tutorial Go, vedremo un programma con più blocchi if else (concatenati if else). Esegui l'esempio Go riportato di seguito. Controlla se un numero è inferiore a 10 o compreso tra 10 e 90 o superiore a 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") } }
Qui innanzitutto la condizione if controlla se x è inferiore a 10 e non lo è. Quindi controlla la condizione successiva (else if) se è compresa tra 10 e 90, anch'essa falsa. Quindi esegue il blocco nella sezione else che fornisce l'output
x is greater than 90
Interruttore
Switch è un'altra istruzione condizionale. Le istruzioni Switch valutano un'espressione e il risultato viene confrontato con un insieme di valori disponibili (casi). Una volta trovata una corrispondenza, vengono eseguite le istruzioni associate a quella corrispondenza (caso). Se non viene trovata alcuna corrispondenza, non verrà eseguito nulla. Puoi anche aggiungere un caso predefinito allo switch che verrà eseguito se non vengono trovate altre corrispondenze. La sintassi dell'interruttore è
switch expression { case value_1: statements_1 case value_2: statements_2 case value_n: statements_n default: statements_default }
Qui il valore dell'espressione viene confrontato con i valori in ciascun caso. Una volta trovata una corrispondenza, vengono eseguite le istruzioni associate a quel caso. Se non viene trovata alcuna corrispondenza, vengono eseguite le istruzioni nella sezione predefinita.
Eseguire il programma seguente
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") } }
Otterrai l'output come
Sum is 3
Cambia il valore di aeb in 3 e il risultato sarà
Printing default
Puoi anche avere più valori in un caso separandoli con una virgola.
Array
L'array rappresenta una dimensione fissa, denominata sequenza di elementi dello stesso tipo. Non è possibile avere un array che contenga sia numeri interi che caratteri. Non è possibile modificare la dimensione di un array una volta definita la dimensione.
La sintassi per dichiarare un array è
var arrayname [size] type
A ogni elemento dell'array può essere assegnato un valore utilizzando la sintassi
arrayname [index] = value
L'indice dell'array inizia da 0 alla taglia 1.
È possibile assegnare valori agli elementi dell'array durante la dichiarazione utilizzando la sintassi
arrayname := [size] type {value_0,value_1,…,value_size-1}
Puoi anche ignorare il parametro size mentre dichiari l'array con valori sostituendo size con ... e il compilatore troverà la lunghezza dal numero di valori. La sintassi è
arrayname := […] type {value_0,value_1,…,value_size-1}
È possibile trovare la lunghezza dell'array utilizzando la sintassi
len(arrayname)
Esegui l'esempio Go riportato di seguito per comprendere l'array
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]) }
Uscita
Two 3 [One Two Three] [1 2 3 4 5] 5
Funzione Golang Slice e Aggiungi
Una porzione è una porzione o un segmento di un array. Oppure è una vista parziale o parziale di un array sottostante a cui punta. Puoi accedere agli elementi di una sezione utilizzando il nome della sezione e il numero di indice proprio come fai in un array. Non è possibile modificare la lunghezza di un array, ma è possibile modificare la dimensione di una sezione.
I contenuti di una sezione sono in realtà i puntatori agli elementi di un array. Significa se modifichi qualsiasi elemento in una sezione, anche il contenuto dell'array sottostante verrà influenzato.
La sintassi per creare una sezione è
var slice_name [] type = array_name[start:end]
Ciò creerà una sezione denominata nome_fetta da un array denominato nome_array con gli elementi nell'indice dall'inizio alla fine-1.
Ora in questo tutorial di Golang, eseguiremo il programma seguente. Il programma creerà una sezione dall'array e la stamperà. Inoltre, puoi vedere che la modifica del contenuto nella sezione modificherà l'array effettivo.
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) }
Questo stamperà il risultato come
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]
Ci sono alcune funzioni come Golang len, Golang append che puoi applicare alle fette
len(nome_fetta) – restituisce la lunghezza della fetta
aggiungi(nome_fetta, valore_1, valore_2) – L'aggiunta Golang viene utilizzata per aggiungere valore_1 e valore_2 a una sezione esistente.
append(slice_nale1,slice_name2…) – aggiunge slice_name2 a slice_name1
Eseguire il seguente programma.
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) }
L'output sarà
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]
Il programma crea prima 2 fette e ne stampa la lunghezza. Quindi ha aggiunto una fetta all'altra e quindi ha aggiunto una stringa alla fetta risultante.
funzioni
Una funzione rappresenta un blocco di istruzioni che esegue un compito specifico. Una dichiarazione di funzione ci dice il nome della funzione, il tipo restituito e i parametri di input. La definizione di funzione rappresenta il codice contenuto nella funzione. La sintassi per dichiarare la funzione è
func function_name(parameter_1 type, parameter_n type) return_type { //statements }
I parametri e i tipi restituiti sono facoltativi. Inoltre, puoi restituire più valori da una funzione.
Ora in questo tutorial su Golang, eseguiamo il seguente esempio di Golang. Qui la funzione denominata calc accetterà 2 numeri ed eseguirà l'addizione e la sottrazione e restituirà entrambi i 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) }
L'output sarà
Sum 25 Diff 5
Pack
I pacchetti vengono utilizzati per organizzare il codice. In un grande progetto non è possibile scrivere il codice in un unico file. Il linguaggio di programmazione Go ci consente di organizzare il codice in diversi pacchetti. Ciò aumenta la leggibilità e la riusabilità del codice. Un programma Go eseguibile dovrebbe contenere un pacchetto denominato main e l'esecuzione del programma inizia dalla funzione denominata main. Puoi importare altri pacchetti nel nostro programma usando la sintassi
import package_name
Vedremo e discuteremo in questo tutorial di Golang, come creare e utilizzare i pacchetti nel seguente esempio di Golang.
Passo 1) Crea un file chiamato package_example.go e aggiungi il codice seguente
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) }
Nel programma sopra fmt è un pacchetto che il linguaggio di programmazione Go ci fornisce principalmente per scopi di I/O. Inoltre, puoi vedere un pacchetto denominato calcolo. All'interno di main() puoi vedere una somma dei passi :=calcolo.Do_add(x,y). Significa che stai invocando la funzione Do_add dal calcolo del pacchetto.
Passo 2) Innanzitutto, dovresti creare il calcolo del pacchetto all'interno di una cartella con lo stesso nome nella cartella src di go. Il percorso installato di go può essere trovato dalla variabile PATH.
Per Mac, trova il percorso eseguendo echo $PATH
Quindi il percorso è /usr/local/go
Per Windows, trova il percorso eseguendo echo %GOROOT%
Qui il percorso è C:\Go\
Passo 3) Passare alla cartella src (/usr/local/go/src per Mac e C:\Go\src per Windows). Ora dal codice, il nome del pacchetto è calcolo. Go richiede che il pacchetto venga inserito in una directory con lo stesso nome nella directory src. Crea una directory denominata calcolo nella cartella src.
Passo 4) Crea un file chiamato calc.go (puoi dare qualsiasi nome, ma il nome del pacchetto nel codice è importante. Qui dovrebbe essere calcolo) all'interno della directory di calcolo e aggiungi il codice seguente
package calculation func Do_add(num1 int, num2 int)(int) { sum := num1 + num2 return sum }
Passo 5) Esegui il comando go install dalla directory di calcolo che compilerà calc.go.
Passo 6) Ora torna a package_example.go ed esegui go run package_example.go. L'output sarà Somma 25.
Tieni presente che il nome della funzione Do_add inizia con una lettera maiuscola. Questo perché in Go se il nome della funzione inizia con una lettera maiuscola significa che altri programmi possono vederlo (accedere) altrimenti altri programmi non possono accedervi. Se il nome della funzione fosse do_add , avresti ricevuto l'errore
non può fare riferimento al nome non esportato CALCULATION.CALC..
Differire e impilare i differiti
Le istruzioni di rinvio vengono utilizzate per rinviare l'esecuzione di una chiamata di funzione finché la funzione che contiene l'istruzione di rinvio non completa l'esecuzione.
Impariamo questo con un esempio:
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()") }
L'output sarà
Inside the main() Inside the sample()
Qui l'esecuzione di sample() viene posticipata fino al completamento dell'esecuzione della funzione di inclusione (main()).
Il differimento in pila utilizza più istruzioni di differimento. Supponiamo di avere più istruzioni di differimento all'interno di una funzione. Go inserisce tutte le chiamate di funzione differite in uno stack e, una volta restituita la funzione di inclusione, le funzioni in stack vengono eseguite nel Ordine Last In First Out (LIFO). Puoi vederlo nell'esempio seguente.
Esegui il codice seguente
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) }
L'output sarà
4 3 2 1
Qui il codice all'interno di main() viene eseguito per primo, quindi le chiamate di funzione differite vengono eseguite nell'ordine inverso, ovvero 4, 3,2,1.
Puntatori
Prima di spiegare i puntatori, discuteremo innanzitutto dell'operatore "&". L'operatore '&' viene utilizzato per ottenere l'indirizzo di una variabile. Significa che '&a' stamperà l'indirizzo di memoria della variabile a.
In questo tutorial di Golang, eseguiremo il programma seguente per visualizzare il valore di una variabile e l'indirizzo di quella variabile
package main import "fmt" func main() { a := 20 fmt.Println("Address:",&a) fmt.Println("Value:",a) }
Il risultato sarà
Address: 0xc000078008 Value: 20
Una variabile puntatore memorizza l'indirizzo di memoria di un'altra variabile. È possibile definire un puntatore utilizzando la sintassi
var variable_name *type
L'asterisco (*) rappresenta che la variabile è un puntatore. Capirai di più eseguendo il programma seguente
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)}
L'output sarà
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
Strutture
Una struttura è un tipo di dati definito dall'utente che a sua volta contiene un ulteriore elemento dello stesso tipo o di un tipo diverso.
L'utilizzo di una struttura è un processo in 2 fasi.
Innanzitutto, crea (dichiara) un tipo di struttura
In secondo luogo, crea variabili di quel tipo per memorizzare valori.
Le strutture vengono utilizzate principalmente quando si desidera archiviare insieme dati correlati.
Considera un'informazione sui dipendenti che contiene nome, età e indirizzo. Puoi gestirlo in 2 modi
Crea 3 array: un array memorizza i nomi dei dipendenti, uno memorizza l'età e il terzo memorizza l'età.
Dichiara un tipo di struttura con 3 campi: nome, indirizzo ed età. Crea un array di quel tipo di struttura in cui ogni elemento è un oggetto struttura con nome, indirizzo ed età.
Il primo approccio non è efficiente. In questi tipi di scenari, le strutture sono più convenienti.
La sintassi per dichiarare una struttura è
type structname struct { variable_1 variable_1_type variable_2 variable_2_type variable_n variable_n_type }
Un esempio di dichiarazione di struttura è
type emp struct { name string address string age int }
Qui viene creato un nuovo tipo definito dall'utente denominato emp. Ora puoi creare variabili del tipo emp usando la sintassi
var variable_name struct_name
Un esempio è
var empdata1 emp
È possibile impostare valori per empdata1 come
empdata1.name = "John" empdata1.address = "Street-1, Bangalore" empdata1.age = 30
Puoi anche creare una variabile di struttura e assegnare valori in base a
empdata2 := emp{"Raj", "Building-1, Delhi", 25}
Qui è necessario mantenere l'ordine degli elementi. Raj verrà mappato con il nome, l'elemento successivo con l'indirizzo e l'ultimo con l'età.
Esegui il codice seguente
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) }
Uscita
John Raj
Metodi (non funzioni)
Un metodo è una funzione con un argomento ricevente. Archidal punto di vista tecnico, è tra la parola chiave func e il nome del metodo. La sintassi di un metodo è
func (variable variabletype) methodName(parameter1 paramether1type) { }
Convertiamo il programma di esempio precedente per utilizzare metodi anziché funzioni.
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 non è un linguaggio orientato agli oggetti e non ha il concetto di classe. I metodi danno un'idea di ciò che fai nei programmi orientati agli oggetti in cui le funzioni di una classe vengono invocate utilizzando la sintassi nomeoggetto.nomefunzione()
Concorrenza
Go supporta l'esecuzione simultanea di attività. Significa che Go può eseguire più attività contemporaneamente. È diverso dal concetto di parallelismo. Nel parallelismo, un'attività è suddivisa in piccole sottoattività e viene eseguita in parallelo. Ma in simultaneità, più attività vengono eseguite contemporaneamente. La concorrenza viene raggiunta in Go utilizzando Goroutine e canali.
Goroutine
Una goroutine è una funzione che può essere eseguita contemporaneamente ad altre funzioni. Di solito quando viene invocata una funzione il controllo viene trasferito nella funzione chiamata e, una volta completata l'esecuzione, il controllo ritorna alla funzione chiamante. La funzione chiamante continua quindi la sua esecuzione. La funzione chiamante attende che la funzione invocata completi l'esecuzione prima di procedere con il resto delle istruzioni.
Ma nel caso della goroutine, la funzione chiamante non attenderà il completamento dell'esecuzione della funzione invocata. L'esecuzione continuerà con le istruzioni successive. È possibile avere più goroutine in un programma.
Inoltre, il programma principale uscirà una volta completata l'esecuzione delle sue istruzioni e non attenderà il completamento delle goroutine invocate.
Goroutine viene invocata utilizzando la parola chiave go seguita da una chiamata di funzione.
Esempio
go add(x,y)
Comprenderai le goroutine con gli esempi Golang riportati di seguito. Eseguire il programma seguente
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") } }
L'output sarà
In main In main In main In main In main
Qui il programma principale ha completato l'esecuzione ancor prima dell'avvio della goroutine. Il display() è una goroutine che viene invocata utilizzando la sintassi
go function_name(parameter list)
Nel codice precedente, main() non attende il completamento di display() e main() ha completato la sua esecuzione prima che display() eseguisse il suo codice. Quindi l'istruzione print all'interno di display() non è stata stampata.
Ora modifichiamo il programma per stampare anche le istruzioni da display(). Aggiungiamo un ritardo di 2 secondi nel ciclo for di main() e un ritardo di 1 secondo nel ciclo for di 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") } }
L'output sarà in qualche modo simile a
In display In main In display In display In main In display In display In main In main In main
Qui puoi vedere che entrambi i cicli vengono eseguiti in modo sovrapposto a causa dell'esecuzione simultanea.
Canali
I canali sono un modo in cui le funzioni comunicano tra loro. Può essere pensato come un mezzo in cui una routine inserisce i dati e vi accede un'altra routine nel server Golang.
Un canale può essere dichiarato con la sintassi
channel_variable := make(chan datatype)
Esempio:
ch := make(chan int)
È possibile inviare dati a un canale utilizzando la sintassi
channel_variable <- variable_name
Esempio
ch <- x
È possibile ricevere dati da un canale utilizzando la sintassi
variable_name := <- channel_variable
Esempio
y := <- ch
Negli esempi di goroutine in linguaggio Go sopra riportati, hai visto che il programma principale non aspetta la goroutine. Ma non è così quando sono coinvolti i canali. Supponiamo che se una goroutine invia i dati al canale, main() attenderà l'istruzione che riceve i dati del canale finché non riceverà i dati.
Lo vedrai negli esempi di lingua Go riportati di seguito. Per prima cosa, scrivi una goroutine normale e osserva il comportamento. Quindi modificare il programma per utilizzare i canali e vedere il comportamento.
Eseguire il programma seguente
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()") }
L'output sarà
Inside main()
Il main() ha terminato l'esecuzione ed è uscito prima dell'esecuzione della goroutine. Quindi la stampa all'interno del display() non è stata eseguita.
Ora modifica il programma sopra per utilizzare i canali e vedere il comportamento.
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) }
L'output sarà
Inside display() Inside main() Printing x in main() after taking from channel: 1234
Qui ciò che accade è che main() al raggiungimento di x := <-ch attenderà i dati sul canale ch. Il display() ha un'attesa di 5 secondi e quindi invia i dati al canale ch. Il main() alla ricezione dei dati dal canale viene sbloccato e continua la sua esecuzione.
Il mittente che invia i dati al canale può informare i ricevitori che non verranno aggiunti altri dati al canale chiudendo il canale. Viene utilizzato principalmente quando si utilizza un loop per inviare dati a un canale. Un canale può essere chiuso utilizzando
close(channel_name)
E dal lato del ricevitore, è possibile verificare se il canale è chiuso utilizzando una variabile aggiuntiva durante il recupero dei dati dal canale utilizzando
variable_name, status := <- channel_variable
Se lo stato è Vero significa che hai ricevuto dati dal canale. Se falso significa che stai tentando di leggere da un canale chiuso
Puoi anche utilizzare i canali per la comunicazione tra goroutine. È necessario utilizzare 2 goroutine: una invia i dati al canale e l'altra riceve i dati dal canale. Vedi il programma qui sotto
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()") }
Qui ci sono 2 subroutine, una spinge i dati sul canale e l'altra stampa i dati sul canale. La funzione add_to_channel aggiunge i numeri da 0 a 9 e chiude il canale. Contemporaneamente attende la funzione fetch_from_channel
x, flag := <- ch e una volta che i dati diventano disponibili, stampa i dati. Esce quando il flag è falso, il che significa che il canale è chiuso.
L'attesa in main() viene data per impedire l'uscita di main() finché le goroutine non terminano l'esecuzione.
Esegui il codice e visualizza l'output come
Read data Send data 0 1 2 3 4 5 6 7 8 9 Empty channel Inside main()
Seleziona
Select può essere visto come un'istruzione switch che funziona sui canali. Qui le dichiarazioni del caso saranno un'operazione di canale. Di solito, ogni istruzione del caso verrà letta dal canale. Quando uno qualsiasi dei casi è pronto (il canale viene letto), viene eseguita l'istruzione associata a quel caso. Se sono pronti più casi, ne sceglierà uno casuale. È possibile avere un caso predefinito che viene eseguito se nessuno dei casi è pronto.
Vediamo il codice seguente
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) } }
L'esecuzione del programma sopra riportato darà l'output:
from data2()
Qui l'istruzione select attende che i dati siano disponibili in uno qualsiasi dei canali. Il data2() aggiunge dati al canale dopo una sospensione di 2 secondi che causerà l'esecuzione del secondo caso.
Aggiungi un caso predefinito alla selezione nello stesso programma e guarda l'output. Qui, una volta raggiunto il blocco selezionato, se nessun caso ha dati pronti sul canale, eseguirà il blocco predefinito senza attendere che i dati siano disponibili su qualsiasi canale.
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") } }
Questo programma darà l'output:
Default case executed
Questo perché quando è stato raggiunto il blocco selezionato, nessun canale aveva dati da leggere. Quindi, viene eseguito il caso predefinito.
mutex
Mutex è la forma abbreviata di mutua esclusione. Mutex viene utilizzato quando non si desidera consentire l'accesso a una risorsa da parte di più subroutine contemporaneamente. Mutex ha 2 metodi: Blocca e Sblocca. Mutex è contenuto nel pacchetto di sincronizzazione. Quindi, devi importare il pacchetto di sincronizzazione. Le istruzioni che devono essere eseguite in modo mutuamente esclusivo possono essere inserite all'interno di mutex.Lock() e mutex.Unlock().
Impariamo il mutex con un esempio che conta il numero di volte in cui viene eseguito un ciclo. In questo programma ci aspettiamo che la routine venga eseguita in loop 10 volte e il conteggio venga memorizzato nella somma. Chiami questa routine 3 volte quindi il conteggio totale dovrebbe essere 30. Il conteggio è memorizzato in una variabile globale count.
Innanzitutto, esegui il programma senza 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) }
Guarda il risultato
Count after i=1 Count: 11 Count after i=3 Count: 12 Count after i=2 Count: 13 Final Count: 13
Il risultato potrebbe essere diverso quando lo esegui, ma il risultato finale non sarà 30.
Qui ciò che accade è che 3 goroutine stanno tentando di aumentare il conteggio dei loop memorizzato nella variabile count. Supponiamo che in un momento il conteggio sia 5 e goroutine1 incrementerà il conteggio a 6. I passaggi principali includono
Conteggio copie su temp
Incremento temp
Conservare la temperatura per contare
Supponiamo subito dopo aver eseguito il passaggio 3 con goroutine1; un'altra goroutine potrebbe avere un vecchio valore, ad esempio 3 esegue i passaggi precedenti e memorizza 4, il che è sbagliato. Ciò può essere prevenuto utilizzando mutex che fa sì che altre routine attendano quando una routine sta già utilizzando la variabile.
Ora eseguirai il programma con mutex. Qui i 3 passaggi sopra menzionati vengono eseguiti in 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) }
Ora l'output sarà
Count after i=3 Count: 21 Count after i=2 Count: 28 Count after i=1 Count: 30 Final Count: 30
Qui otteniamo il risultato atteso come output finale. Poiché le istruzioni di lettura, incremento e riscrittura del conteggio vengono eseguite in un mutex.
Gestione degli errori
Gli errori sono condizioni anomale come la chiusura di un file che non è aperto, l'apertura di un file che non esiste, ecc. Le funzioni solitamente restituiscono errori come ultimo valore restituito.
L'esempio seguente fornisce ulteriori informazioni sull'errore.
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") }
L'output sarà:
open /invalid.txt: no such file or directory
Qui abbiamo provato ad aprire un file inesistente e ha restituito l'errore alla variabile er. Se il file è valido, l'errore sarà nullo
Errori personalizzati
Utilizzando questa funzione, puoi creare errori personalizzati. Questo viene fatto utilizzando New() del pacchetto error. Riscriveremo il programma precedente per utilizzare errori personalizzati.
Esegui il programma seguente
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) } }
L'output sarà:
Custom error message:File name is wrong
Qui area() restituisce l'area di un quadrato. Se l'input è inferiore a 1, area() restituisce un messaggio di errore.
Lettura di file
I file vengono utilizzati per archiviare dati. Go ci consente di leggere i dati dai file
Per prima cosa crea un file, data.txt, nella tua directory attuale con il contenuto seguente.
Line one Line two Line three
Ora esegui il programma seguente per vedere che stampa il contenuto dell'intero file come output
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)) }
Qui i dati, err := ioutil.ReadFile(“data.txt”) legge i dati e restituisce una sequenza di byte. Durante la stampa viene convertito in formato stringa.
Scrittura di file
Lo vedrai con un programma
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 } }
Qui viene creato un file, test.txt. Se il file esiste già, il contenuto del file verrà troncato. Writeline() viene utilizzato per scrivere il contenuto nel file. Successivamente, hai chiuso il file utilizzando Close().
Cheat Sheet
In questo tutorial di Go abbiamo trattato:
Argomento | Descrizione | Sintassi |
---|---|---|
Tipi di base | Numerico, stringa, bool | |
Variabili | Dichiarare e assegnare valori alle variabili | var nome_variabile tipo var nome_variabile tipo = valore var nome_variabile1, nome_variabile2 = valore1, valore2 nome_variabile := valore |
Costante | Variabili il cui valore non può essere modificato una volta assegnato | const variabile = valore |
Per Loop | Esegue istruzioni in un ciclo. | per inizializzazione_espressione; espressione_valutazione; espressione_iterazione{ // una o più istruzioni } |
Se altro | È una dichiarazione condizionale | se condizione{ // istruzioni_1 Else {} // istruzioni_2 } |
interruttore | Dichiarazione condizionale con più casi | cambia espressione { valore caso_1: dichiarazioni_1 valore caso_2: dichiarazioni_2 caso valore_n: dichiarazioni_n di default: istruzioni_default } |
Italia | Sequenza denominata dimensione fissa di elementi dello stesso tipo | nomearray := [dimensione] tipo {valore_0,valore_1,…,valore_dimensione-1} |
Taglia | Porzione o segmento di un array | var nome_fetta [] tipo = nome_array[inizio:fine] |
funzioni | Blocco di istruzioni che esegue un compito specifico | func nome_funzione(tipo parametro_1, tipo parametro_n) tipo_ritorno { //dichiarazioni } |
Pack | Vengono utilizzati per organizzare il codice. Aumenta la leggibilità e la riusabilità del codice | importa nome_pacchetto |
Differire | Rimanda l'esecuzione di una funzione finché la funzione che la contiene non termina l'esecuzione | rinviare nome_funzione(elenco_parametri) |
Puntatori | Memorizza l'indirizzo di memoria di un'altra variabile. | var nome_variabile *tipo |
Structure | Tipo di dati definito dall'utente che a sua volta contiene un ulteriore elemento dello stesso tipo o di un tipo diverso | tipo nomestruttura struttura { variabile_1 variabile_1_tipo variabile_2 variabile_2_tipo variabile_n variabile_n_tipo } |
Metodi | Un metodo è una funzione con un argomento ricevente | funz (variabile tipo variabile) nomemetodo(elenco_parametri) { } |
Goroutine | Una funzione che può essere eseguita contemporaneamente ad altre funzioni. | vai nome_funzione(elenco_parametri) |
canale | Modo in cui le funzioni comunicano tra loro. Un mezzo su cui una routine inserisce i dati e a cui accede un'altra routine. | Dichiarare: ch := make(chan int) Invia dati al canale: variabile_canale <- nome_variabile Ricevi dal canale: nome_variabile := <- variabile_canale |
Seleziona | Dichiarazione Switch che funziona sui canali. Le dichiarazioni del caso costituiranno un'operazione di canale. Quando uno qualsiasi dei canali è pronto con i dati, viene eseguita l'istruzione associata a quel caso | Selezionare { caso x := <-chan1: fmt.Println(x) caso y := <-chan2: fmt.Println(y) } |
mutex | Mutex viene utilizzato quando non si desidera consentire l'accesso a una risorsa da parte di più subroutine contemporaneamente. Mutex ha 2 metodi: Blocca e Sblocca | mutex.Lock() //dichiarazioni mutex.Unlock(). |
Leggi i file | Legge i dati e restituisce una sequenza di byte. | Dati, err := ioutil.ReadFile(nome file) |
Scrivi file | Scrive i dati in un file | l, err := f.WriteString(text_to_write) |