Εκμάθηση Golang
Τι είναι το Go;
Go (γνωστό και ως Golang) είναι μια γλώσσα προγραμματισμού ανοιχτού κώδικα που αναπτύχθηκε από την Google. Είναι μια στατικά τυποποιημένη μεταγλωττισμένη γλώσσα. Το Go υποστηρίζει ταυτόχρονο προγραμματισμό, δηλαδή επιτρέπει την ταυτόχρονη εκτέλεση πολλών διεργασιών. Αυτό επιτυγχάνεται με τη χρήση καναλιών, γορουτίνων κ.λπ. Η γλώσσα Go έχει συλλογή σκουπιδιών η οποία κάνει η ίδια τη διαχείριση της μνήμης και επιτρέπει την αναβαλλόμενη εκτέλεση των λειτουργιών.
Θα μάθουμε όλα τα βασικά του Golang σε αυτό το σεμινάριο Learn Go Language.
Πώς να κατεβάσετε και να εγκαταστήσετε το GO
Βήμα 1) Πηγαίνετε στο https://golang.org/dl/. Κατεβάστε το δυαδικό αρχείο για το λειτουργικό σας σύστημα.
Βήμα 2) Double κάντε κλικ στο πρόγραμμα εγκατάστασης και κάντε κλικ στην επιλογή Εκτέλεση.
Βήμα 3) Κάντε κλικ στο κουμπί Επόμενο

Βήμα 4) Επιλέξτε το φάκελο εγκατάστασης και κάντε κλικ στο Επόμενο.

Βήμα 5) Κάντε κλικ στο Finish μόλις ολοκληρωθεί η εγκατάσταση.

Βήμα 6) Μόλις ολοκληρωθεί η εγκατάσταση, μπορείτε να το επαληθεύσετε ανοίγοντας το τερματικό και πληκτρολογώντας
go version
Αυτό θα εμφανίσει την εγκατεστημένη έκδοση του go

Το πρώτο σας πρόγραμμα Go – Go Hello World!
Δημιουργήστε έναν φάκελο που ονομάζεται studyGo. Σε αυτό το σεμινάριο γλώσσας Go, θα δημιουργήσουμε τα προγράμματα go μέσα σε αυτόν τον φάκελο. Τα αρχεία Go δημιουργούνται με την επέκταση .πηγαίνω. Μπορείτε να εκτελέσετε προγράμματα Go χρησιμοποιώντας τη σύνταξη
go run <filename>
Δημιουργήστε ένα αρχείο με το όνομα first.go και προσθέστε τον παρακάτω κώδικα σε αυτό και αποθηκεύστε
package main
import ("fmt")
func main() {
fmt.Println("Hello World! This is my first Go program\n")
}

Πλοηγηθείτε σε αυτόν τον φάκελο στο τερματικό σας. Εκτελέστε το πρόγραμμα χρησιμοποιώντας την εντολή
πήγαινε τρέξε πρώτα.πήγαινε
Μπορείτε να δείτε την εκτύπωση εξόδου
Hello World! This is my first Go program

Τώρα ας συζητήσουμε το παραπάνω πρόγραμμα.
πακέτο main – Κάθε πρόγραμμα Go Language πρέπει να ξεκινά με ένα όνομα πακέτου. Το Go μας επιτρέπει να χρησιμοποιούμε πακέτα σε άλλα προγράμματα go και επομένως υποστηρίζει την επαναχρησιμοποίηση κώδικα. Η εκτέλεση ενός προγράμματος Go ξεκινά με τον κώδικα μέσα στο πακέτο που ονομάζεται main.
εισαγωγή fmt – εισάγει το πακέτο fmt. Αυτό το πακέτο υλοποιεί τις λειτουργίες I/O.
func main() – Αυτή είναι η συνάρτηση από την οποία ξεκινά η εκτέλεση του προγράμματος. Η κύρια λειτουργία πρέπει πάντα να τοποθετείται στην κύρια συσκευασία. Κάτω από το main(), μπορείτε να γράψετε τον κώδικα μέσα στο { }.
fmt.Println – Αυτό θα εκτυπώσει το κείμενο στην οθόνη από τη συνάρτηση Println του fmt.
Σημείωση: Στις παρακάτω ενότητες αυτού του οδηγού Go, όταν αναφέρετε την εκτέλεση/εκτέλεση του κώδικα, σημαίνει να αποθηκεύσετε τον κώδικα σε ένα αρχείο με επέκταση .go και να τον εκτελέσετε χρησιμοποιώντας τη σύνταξη
go run <filename>
Τύποι δεδομένων
Οι τύποι (τύποι δεδομένων) αντιπροσωπεύουν τον τύπο της τιμής που είναι αποθηκευμένη σε μια μεταβλητή, τον τύπο της τιμής που επιστρέφει μια συνάρτηση κ.λπ.
Υπάρχουν τρεις βασικοί τύποι στη γλώσσα Go
Αριθμητικοί τύποι – Αντιπροσωπεύουν αριθμητικές τιμές που περιλαμβάνουν ακέραιους, κινητής υποδιαστολής και μιγαδικές τιμές. Διάφοροι αριθμητικοί τύποι είναι:
int8 – ακέραιοι αριθμοί με υπογραφή 8 bit.
int16 – ακέραιοι αριθμοί με υπογραφή 16 bit.
int32 – ακέραιοι αριθμοί με υπογραφή 32 bit.
int64 – ακέραιοι αριθμοί με υπογραφή 64 bit.
uint8 – ακέραιοι ανυπόγραφοι 8 bit.
uint16 – ακέραιοι ανυπόγραφοι 16 bit.
uint32 – ακέραιοι ανυπόγραφοι 32 bit.
uint64 – ακέραιοι ανυπόγραφοι 64 bit.
float32 – αριθμοί κινητής υποδιαστολής 32 bit.
float64 – αριθμοί κινητής υποδιαστολής 64 bit.
complex64 – έχει float32 πραγματικά και φανταστικά μέρη.
complex128 – έχει float32 πραγματικά και φανταστικά μέρη.
Τύποι χορδών – Αντιπροσωπεύει μια ακολουθία byte (χαρακτήρες). Μπορείτε να κάνετε διάφορες λειτουργίες σε συμβολοσειρές όπως συνένωση συμβολοσειρών, εξαγωγή υποσυμβολοσειράς κ.λπ
Boolean τύποι – Αντιπροσωπεύει 2 τιμές, είτε true είτε false.
Διεπαφή Golang
Διεπαφή Golang είναι μια συλλογή υπογραφών μεθόδων που χρησιμοποιούνται από έναν Τύπο για την υλοποίηση της συμπεριφοράς των αντικειμένων. Ο κύριος στόχος της διεπαφής Golang είναι να παρέχει υπογραφές μεθόδων με ονόματα, ορίσματα και τύπους επιστροφής. Εναπόκειται σε έναν Τύπο να δηλώσει και να εφαρμόσει τη μέθοδο. Μια διεπαφή στο Golang μπορεί να δηλωθεί χρησιμοποιώντας τη λέξη-κλειδί "διεπαφή".
Μεταβλητές
Οι μεταβλητές δείχνουν σε μια θέση μνήμης που αποθηκεύει κάποιο είδος τιμής. Η παράμετρος τύπου (στην παρακάτω σύνταξη) αντιπροσωπεύει τον τύπο της τιμής που μπορεί να αποθηκευτεί στη θέση μνήμης.
Η μεταβλητή μπορεί να δηλωθεί χρησιμοποιώντας τη σύνταξη
var <variable_name> <type>
Μόλις δηλώσετε μια μεταβλητή ενός τύπου, μπορείτε να αντιστοιχίσετε τη μεταβλητή σε οποιαδήποτε τιμή αυτού του τύπου.
Μπορείτε επίσης να δώσετε μια αρχική τιμή σε μια μεταβλητή κατά τη διάρκεια της ίδιας της δήλωσης χρησιμοποιώντας
var <variable_name> <type> = <value>
Εάν δηλώσετε τη μεταβλητή με μια αρχική τιμή, κάντε συμπέρασμα για τον τύπο της μεταβλητής από τον τύπο της τιμής που έχει εκχωρηθεί. Έτσι, μπορείτε να παραλείψετε τον τύπο κατά τη δήλωση χρησιμοποιώντας τη σύνταξη
var <variable_name> = <value>
Επίσης, μπορείτε να δηλώσετε πολλές μεταβλητές με τη σύνταξη
var <variable_name1>, <variable_name2> = <value1>, <value2>
Το παρακάτω πρόγραμμα σε αυτό το σεμινάριο Go έχει μερικά παραδείγματα δηλώσεων μεταβλητών Golang
package main
import "fmt"
func main() {
//declaring a integer variable x
var x int
x=3 //assigning x the value 3
fmt.Println("x:", x) //prints 3
//declaring a integer variable y with value 20 in a single statement and prints it
var y int=20
fmt.Println("y:", y)
//declaring a variable z with value 50 and prints it
//Here type int is not explicitly mentioned
var z=50
fmt.Println("z:", z)
//Multiple variables are assigned in single line- i with an integer and j with a string
var i, j = 100,"hello"
fmt.Println("i and j:", i,j)
}
Η έξοδος θα είναι
x: 3 y: 20 z: 50 i and j: 100 hello
Το Go Language παρέχει επίσης έναν εύκολο τρόπο δήλωσης των μεταβλητών με τιμή, παραλείποντας τη λέξη-κλειδί var χρησιμοποιώντας
<variable_name> := <value>
Σημειώστε ότι χρησιμοποιήσατε := αντί του =. Δεν μπορείτε να χρησιμοποιήσετε το := απλώς για να εκχωρήσετε μια τιμή σε μια μεταβλητή που έχει ήδη δηλωθεί. := χρησιμοποιείται για να δηλώσει και να εκχωρήσει τιμή.
Δημιουργήστε ένα αρχείο που ονομάζεται assign.go με τον ακόλουθο κώδικα
package main
import ("fmt")
func main() {
a := 20
fmt.Println(a)
//gives error since a is already declared
a := 30
fmt.Println(a)
}
Εκτελέστε το go run assign.go για να δείτε το αποτέλεσμα ως
./assign.go:7:4: no new variables on left side of :=
Οι μεταβλητές που δηλώνονται χωρίς αρχική τιμή θα έχουν 0 για αριθμητικούς τύπους, false για Boolean και κενή συμβολοσειρά για συμβολοσειρές
Σταθερά
Σταθερές μεταβλητές είναι εκείνες οι μεταβλητές των οποίων η τιμή δεν μπορεί να αλλάξει μετά την εκχώρηση. Μια σταθερά στη γλώσσα προγραμματισμού Go δηλώνεται χρησιμοποιώντας τη λέξη-κλειδί "const"
Δημιουργήστε ένα αρχείο με την ονομασία konstant.go και με τον παρακάτω κώδικα
package main
import ("fmt")
func main() {
const b =10
fmt.Println(b)
b = 30
fmt.Println(b)
}
Εκτελέστε το go run συνεχές.go για να δείτε το αποτέλεσμα ως
.constant.go:7:4: cannot assign to b
Για Παραδείγματα βρόχου
Οι βρόχοι χρησιμοποιούνται για την εκτέλεση ενός μπλοκ εντολών επανειλημμένα με βάση μια συνθήκη. Οι περισσότερες από τις γλώσσες προγραμματισμού παρέχουν 3 τύπους βρόχων – for, while, do while. Αλλά η γλώσσα προγραμματισμού Go υποστηρίζει μόνο βρόχο.
Η σύνταξη ενός βρόχου Golang for είναι
for initialisation_expression; evaluation_expression; iteration_expression{
// one or more statement
}
Η αρχική_έκφραση εκτελείται πρώτη (και μόνο μία φορά) στον βρόχο για Golang.
Έπειτα αξιολογείται η value_expression και αν είναι αληθές εκτελείται ο κώδικας μέσα στο μπλοκ.
Το αναγνωριστικό iteration_expression εκτελείται και η value_expression αξιολογείται ξανά. Εάν είναι αλήθεια, το μπλοκ δηλώσεων εκτελείται ξανά. Αυτό θα συνεχιστεί έως ότου η αξιολόγηση_έκφραση γίνει ψευδής.
Αντιγράψτε το παρακάτω πρόγραμμα σε ένα αρχείο και εκτελέστε το για να δείτε τους αριθμούς εκτύπωσης βρόχου Golang από το 1 έως το 5
package main
import "fmt"
func main() {
var i int
for i = 1; i <= 5; i++ {
fmt.Println(i)
}
}
Η έξοδος είναι
1 2 3 4 5
Αν αλλιώς
Αν αλλιώς είναι δήλωση υπό όρους. Το σύναξ είναι
if condition{
// statements_1
}else{
// statements_2
}
Εδώ η συνθήκη αξιολογείται και αν είναι αληθής, θα εκτελεστούν οι δηλώσεις_1 αλλιώς θα εκτελεστούν οι δηλώσεις_2.
Μπορείτε επίσης να χρησιμοποιήσετε τη δήλωση if χωρίς άλλη. Μπορείτε επίσης να έχετε αλυσιδωτές δηλώσεις if else. Τα παρακάτω προγράμματα θα εξηγήσουν περισσότερα για το αν αλλιώς.
Εκτελέστε το παρακάτω πρόγραμμα. Ελέγχει εάν ένας αριθμός, x, είναι μικρότερος από 10. Εάν ναι, θα εκτυπώσει "x is λιγότερο από 10"
package main
import "fmt"
func main() {
var x = 50
if x < 10 {
//Executes if x < 10
fmt.Println("x is less than 10")
}
}
Εδώ, δεδομένου ότι η τιμή του x είναι μεγαλύτερη από 10, η δήλωση στο εσωτερικό του μπλοκ if δεν θα εκτελεστεί.
Δείτε τώρα το παρακάτω πρόγραμμα. Σε αυτό το σεμινάριο γλώσσας προγραμματισμού Go, έχουμε ένα μπλοκ else που θα εκτελεστεί σε περίπτωση αποτυχίας της αξιολόγησης if.
package main
import "fmt"
func main() {
var x = 50
if x < 10 {
//Executes if x is less than 10
fmt.Println("x is less than 10")
} else {
//Executes if x >= 10
fmt.Println("x is greater than or equals 10")
}
}
Αυτό το πρόγραμμα θα σας δώσει έξοδο
x is greater than or equals 10
Τώρα σε αυτό το σεμινάριο Go, θα δούμε ένα πρόγραμμα με πολλαπλά μπλοκ if else (αλυσιδωμένα αν αλλιώς). Εκτελέστε το παρακάτω παράδειγμα Go. Ελέγχει εάν ένας αριθμός είναι μικρότερος από 10 ή είναι μεταξύ 10-90 ή μεγαλύτερος από 90.
package main
import "fmt"
func main() {
var x = 100
if x < 10 {
//Executes if x is less than 10
fmt.Println("x is less than 10")
} else if x >= 10 && x <= 90 {
//Executes if x >= 10 and x<=90
fmt.Println("x is between 10 and 90")
} else {
//Executes if both above cases fail i.e x>90
fmt.Println("x is greater than 90")
}
}
Εδώ πρώτα η συνθήκη if ελέγχει αν το x είναι μικρότερο από 10 και δεν είναι. Έτσι ελέγχει την επόμενη συνθήκη (αλλιώς αν) αν είναι μεταξύ 10 και 90 που είναι επίσης ψευδής. Στη συνέχεια, εκτελεί το μπλοκ κάτω από την ενότητα else που δίνει την έξοδο
x is greater than 90
διακόπτης
Το Switch είναι μια άλλη υπό όρους δήλωση. Οι δηλώσεις διακόπτη αξιολογούν μια έκφραση και το αποτέλεσμα συγκρίνεται με ένα σύνολο διαθέσιμων τιμών (περιπτώσεων). Μόλις βρεθεί μια αντιστοίχιση, οι δηλώσεις που σχετίζονται με αυτήν την αντιστοίχιση (περίπτωση) εκτελούνται. Εάν δεν βρεθεί αντιστοιχία, δεν θα εκτελεστεί τίποτα. Μπορείτε επίσης να προσθέσετε μια προεπιλεγμένη περίπτωση στον διακόπτη, η οποία θα εκτελεστεί εάν δεν βρεθούν άλλες αντιστοιχίσεις. Η σύνταξη του διακόπτη είναι
switch expression {
case value_1:
statements_1
case value_2:
statements_2
case value_n:
statements_n
default:
statements_default
}
Εδώ η τιμή της έκφρασης συγκρίνεται με τις τιμές σε κάθε περίπτωση. Μόλις βρεθεί μια αντιστοίχιση, οι δηλώσεις που σχετίζονται με αυτήν την υπόθεση εκτελούνται. Εάν δεν βρεθεί αντιστοίχιση, εκτελούνται οι δηλώσεις κάτω από την προεπιλεγμένη ενότητα.
Εκτελέστε το παρακάτω πρόγραμμα
package main
import "fmt"
func main() {
a,b := 2,1
switch a+b {
case 1:
fmt.Println("Sum is 1")
case 2:
fmt.Println("Sum is 2")
case 3:
fmt.Println("Sum is 3")
default:
fmt.Println("Printing default")
}
}
Θα λάβετε την έξοδο ως
Sum is 3
Αλλάξτε την τιμή των a και b σε 3 και το αποτέλεσμα θα είναι
Printing default
Μπορείτε επίσης να έχετε πολλαπλές τιμές σε μια υπόθεση, χωρίζοντάς τις με κόμμα.
Array
Ο πίνακας αντιπροσωπεύει ένα σταθερό μέγεθος, με όνομα ακολουθία στοιχείων του ίδιου τύπου. Δεν μπορείτε να έχετε έναν πίνακα που περιέχει και ακέραιους και χαρακτήρες σε αυτόν. Δεν μπορείτε να αλλάξετε το μέγεθος ενός πίνακα αφού ορίσετε το μέγεθος.
Η σύνταξη για τη δήλωση ενός πίνακα είναι
var arrayname [size] type
Σε κάθε στοιχείο πίνακα μπορεί να εκχωρηθεί τιμή χρησιμοποιώντας τη σύνταξη
arrayname [index] = value
Ο δείκτης πίνακα ξεκινά από 0 έως μέγεθος-1.
Μπορείτε να εκχωρήσετε τιμές σε στοιχεία πίνακα κατά τη δήλωση χρησιμοποιώντας τη σύνταξη
arrayname := [size] type {value_0,value_1,…,value_size-1}
Μπορείτε επίσης να αγνοήσετε την παράμετρο μεγέθους ενώ δηλώνετε τον πίνακα με τιμές αντικαθιστώντας το μέγεθος με ... και ο μεταγλωττιστής θα βρει το μήκος από τον αριθμό των τιμών. Σύνταξη είναι
arrayname := […] type {value_0,value_1,…,value_size-1}
Μπορείτε να βρείτε το μήκος του πίνακα χρησιμοποιώντας τη σύνταξη
len(arrayname)
Εκτελέστε το παρακάτω παράδειγμα Go για να κατανοήσετε τον πίνακα
package main
import "fmt"
func main() {
var numbers [3] string //Declaring a string array of size 3 and adding elements
numbers[0] = "One"
numbers[1] = "Two"
numbers[2] = "Three"
fmt.Println(numbers[1]) //prints Two
fmt.Println(len(numbers)) //prints 3
fmt.Println(numbers) // prints [One Two Three]
directions := [...] int {1,2,3,4,5} // creating an integer array and the size of the array is defined by the number of elements
fmt.Println(directions) //prints [1 2 3 4 5]
fmt.Println(len(directions)) //prints 5
//Executing the below commented statement prints invalid array index 5 (out of bounds for 5-element array)
//fmt.Println(directions[5])
}
Παραγωγή
Two 3 [One Two Three] [1 2 3 4 5] 5
Λειτουργία φέτας και προσάρτησης Golang
Ένα slice είναι ένα τμήμα ή τμήμα ενός πίνακα. Ή είναι μια όψη ή μερική όψη ενός υποκείμενου πίνακα στον οποίο δείχνει. Μπορείτε να αποκτήσετε πρόσβαση στα στοιχεία ενός slice χρησιμοποιώντας το όνομα του τμήματος και τον αριθμό ευρετηρίου, όπως ακριβώς κάνετε σε έναν πίνακα. Δεν μπορείτε να αλλάξετε το μήκος ενός πίνακα, αλλά μπορείτε να αλλάξετε το μέγεθος μιας φέτας.
Τα περιεχόμενα ενός slice είναι στην πραγματικότητα οι δείκτες προς τα στοιχεία ενός πίνακα. Σημαίνει Εάν αλλάξετε οποιοδήποτε στοιχείο σε ένα slice, θα επηρεαστούν επίσης τα περιεχόμενα του υποκείμενου πίνακα.
Η σύνταξη για τη δημιουργία ενός slice είναι
var slice_name [] type = array_name[start:end]
Αυτό θα δημιουργήσει ένα slice με το όνομα slice_name από έναν πίνακα με το όνομα array_name με τα στοιχεία στο ευρετήριο από την αρχή έως το τέλος-1.
Τώρα σε αυτό το σεμινάριο Golang, θα εκτελέσουμε το παρακάτω πρόγραμμα. Το πρόγραμμα θα δημιουργήσει ένα κομμάτι από τον πίνακα και θα το εκτυπώσει. Επίσης, μπορείτε να δείτε ότι η τροποποίηση των περιεχομένων στο slice θα τροποποιήσει τον πραγματικό πίνακα.
package main
import "fmt"
func main() {
// declaring array
a := [5] string {"one", "two", "three", "four", "five"}
fmt.Println("Array after creation:",a)
var b [] string = a[1:4] //created a slice named b
fmt.Println("Slice after creation:",b)
b[0]="changed" // changed the slice data
fmt.Println("Slice after modifying:",b)
fmt.Println("Array after slice modification:",a)
}
Αυτό θα εκτυπώσει το αποτέλεσμα ως
Array after creation: [one two three four five] Slice after creation: [two three four] Slice after modifying: [changed three four] Array after slice modification: [one changed three four five]
Υπάρχουν ορισμένες λειτουργίες όπως Golang len, Golang append τις οποίες μπορείτε να εφαρμόσετε σε φέτες
φακός (όνομα_φέτας) – επιστρέφει το μήκος της φέτας
προσάρτημα(όνομα_τμήματος, τιμή_1, τιμή_2) – Η προσθήκη Golang χρησιμοποιείται για την προσθήκη των τιμών_1 και τιμής_2 σε ένα υπάρχον τμήμα.
προσάρτημα(slice_nale1,slice_name2…) – προσθέτει το slice_name2 στο slice_name1
Εκτελέστε το παρακάτω πρόγραμμα.
package main
import "fmt"
func main() {
a := [5] string {"1","2","3","4","5"}
slice_a := a[1:3]
b := [5] string {"one","two","three","four","five"}
slice_b := b[1:3]
fmt.Println("Slice_a:", slice_a)
fmt.Println("Slice_b:", slice_b)
fmt.Println("Length of slice_a:", len(slice_a))
fmt.Println("Length of slice_b:", len(slice_b))
slice_a = append(slice_a,slice_b...) // appending slice
fmt.Println("New Slice_a after appending slice_b :", slice_a)
slice_a = append(slice_a,"text1") // appending value
fmt.Println("New Slice_a after appending text1 :", slice_a)
}
Η έξοδος θα είναι
Slice_a: [2 3] Slice_b: [two three] Length of slice_a: 2 Length of slice_b: 2 New Slice_a after appending slice_b : [2 3 two three] New Slice_a after appending text1 : [2 3 two three text1]
Το πρόγραμμα δημιουργεί πρώτα 2 φέτες και εκτυπώνει το μήκος του. Στη συνέχεια προσάρτησε τη μία φέτα στην άλλη και στη συνέχεια προσάρτησε ένα κορδόνι στην προκύπτουσα φέτα.
Συναρτήσεις
Μια συνάρτηση αντιπροσωπεύει ένα μπλοκ εντολών που εκτελεί μια συγκεκριμένη εργασία. Μια δήλωση συνάρτησης μας λέει το όνομα της συνάρτησης, τον τύπο επιστροφής και τις παραμέτρους εισαγωγής. Ο ορισμός συνάρτησης αντιπροσωπεύει τον κώδικα που περιέχεται στη συνάρτηση. Η σύνταξη για τη δήλωση της συνάρτησης είναι
func function_name(parameter_1 type, parameter_n type) return_type {
//statements
}
Οι παράμετροι και οι τύποι επιστροφής είναι προαιρετικοί. Επίσης, μπορείτε να επιστρέψετε πολλές τιμές από μια συνάρτηση.
Τώρα σε αυτό το σεμινάριο Golang, ας εκτελέσουμε το ακόλουθο παράδειγμα Golang. Εδώ η συνάρτηση με το όνομα calc θα δέχεται 2 αριθμούς και θα εκτελεί την πρόσθεση και την αφαίρεση και επιστρέφει και τις δύο τιμές.
package main
import "fmt"
//calc is the function name which accepts two integers num1 and num2
//(int, int) says that the function returns two values, both of integer type.
func calc(num1 int, num2 int)(int, int) {
sum := num1 + num2
diff := num1 - num2
return sum, diff
}
func main() {
x,y := 15,10
//calls the function calc with x and y an d gets sum, diff as output
sum, diff := calc(x,y)
fmt.Println("Sum",sum)
fmt.Println("Diff",diff)
}
Η έξοδος θα είναι
Sum 25 Diff 5
Πακέτα
Τα πακέτα χρησιμοποιούνται για την οργάνωση του κώδικα. Σε ένα μεγάλο έργο, δεν είναι εφικτό να γράψετε κώδικα σε ένα μόνο αρχείο. Η γλώσσα προγραμματισμού Go μας επιτρέπει να οργανώσουμε τον κώδικα σε διαφορετικά πακέτα. Αυτό αυξάνει την αναγνωσιμότητα και την επαναχρησιμοποίηση κώδικα. Ένα εκτελέσιμο πρόγραμμα Go θα πρέπει να περιέχει ένα πακέτο με το όνομα main και η εκτέλεση του προγράμματος ξεκινά από τη συνάρτηση με το όνομα main. Μπορείτε να εισάγετε άλλα πακέτα στο πρόγραμμά μας χρησιμοποιώντας τη σύνταξη
import package_name
Θα δούμε και θα συζητήσουμε σε αυτό το σεμινάριο Golang, πώς να δημιουργήσετε και να χρησιμοποιήσετε πακέτα στο ακόλουθο παράδειγμα Golang.
Βήμα 1) Δημιουργήστε ένα αρχείο με το όνομα package_example.go και προσθέστε τον παρακάτω κώδικα
package main
import "fmt"
//the package to be created
import "calculation"
func main() {
x,y := 15,10
//the package will have function Do_add()
sum := calculation.Do_add(x,y)
fmt.Println("Sum",sum)
}
Στο παραπάνω πρόγραμμα το fmt είναι ένα πακέτο που μας παρέχει η γλώσσα προγραμματισμού Go κυρίως για σκοπούς εισόδου/εξόδου. Επίσης, μπορείτε να δείτε ένα πακέτο με το όνομα υπολογισμός. Μέσα στο main() μπορείτε να δείτε ένα άθροισμα βημάτων := υπολογισμός.Do_add(x,y). Σημαίνει ότι καλείτε τη συνάρτηση Do_add από τον υπολογισμό του πακέτου.
Βήμα 2) Αρχικά, θα πρέπει να δημιουργήσετε τον υπολογισμό του πακέτου μέσα σε έναν φάκελο με το ίδιο όνομα στον φάκελο src of the go. Η εγκατεστημένη διαδρομή go μπορεί να βρεθεί από τη μεταβλητή PATH.
Για Mac, βρείτε τη διαδρομή εκτελώντας το echo $PATH

Άρα η διαδρομή είναι /usr/local/go
Για τα Windows, βρείτε τη διαδρομή εκτελώντας echo %GOROOT%

Εδώ η διαδρομή είναι C:\Go\
Βήμα 3) Μεταβείτε στο φάκελο src (/usr/local/go/src για Mac και C:\Go\src για Windows). Τώρα από τον κωδικό, το όνομα του πακέτου είναι υπολογισμός. Το Go απαιτεί το πακέτο να τοποθετηθεί σε έναν κατάλογο με το ίδιο όνομα στον κατάλογο src. Δημιουργήστε έναν κατάλογο με το όνομα υπολογισμός στο φάκελο src.
Βήμα 4) Δημιουργήστε ένα αρχείο με το όνομα calc.go (Μπορείτε να δώσετε οποιοδήποτε όνομα, αλλά το όνομα του πακέτου στον κώδικα έχει σημασία. Εδώ θα πρέπει να είναι υπολογισμός) μέσα στον κατάλογο υπολογισμών και προσθέστε τον παρακάτω κώδικα
package calculation
func Do_add(num1 int, num2 int)(int) {
sum := num1 + num2
return sum
}
Βήμα 5) Εκτελέστε την εντολή go install από τον κατάλογο υπολογισμών που θα μεταγλωττίσει το calc.go.

Βήμα 6) Τώρα επιστρέψτε στο package_example.go και εκτελέστε το go run package_example.go. Η έξοδος θα είναι άθροισμα 25.
Σημειώστε ότι το όνομα της συνάρτησης Do_add ξεκινά με κεφαλαίο γράμμα. Αυτό συμβαίνει επειδή στο Go αν το όνομα της συνάρτησης ξεκινά με κεφαλαίο γράμμα σημαίνει ότι άλλα προγράμματα μπορούν να το δουν (πρόσβαση) διαφορετικά άλλα προγράμματα δεν μπορούν να έχουν πρόσβαση σε αυτό. Εάν το όνομα της συνάρτησης ήταν do_add, τότε θα λάβατε το σφάλμα
δεν μπορεί να αναφέρεται σε μη εξαγόμενο όνομα υπολογισμού.calc..
Defer and stacking defers
Οι δηλώσεις Defer χρησιμοποιούνται για την αναβολή της εκτέλεσης μιας κλήσης συνάρτησης έως ότου η συνάρτηση που περιέχει την εντολή defer ολοκληρώσει την εκτέλεση.
Ας το μάθουμε αυτό με ένα παράδειγμα:
package main
import "fmt"
func sample() {
fmt.Println("Inside the sample()")
}
func main() {
//sample() will be invoked only after executing the statements of main()
defer sample()
fmt.Println("Inside the main()")
}
Η έξοδος θα είναι
Inside the main() Inside the sample()
Εδώ η εκτέλεση του sample() αναβάλλεται μέχρι να ολοκληρωθεί η εκτέλεση της συνάρτησης εγκλεισμού (main()).
Η στοίβαξη αναβολής χρησιμοποιεί πολλαπλές δηλώσεις αναβολής. Ας υποθέσουμε ότι έχετε πολλαπλές δηλώσεις αναβολής μέσα σε μια συνάρτηση. Το Go τοποθετεί όλες τις αναβαλλόμενες κλήσεις συναρτήσεων σε μια στοίβα και μόλις επιστρέψει η συνάρτηση εγκλεισμού, οι στοιβαγμένες συναρτήσεις εκτελούνται στο Παραγγελία Last In First Out (LIFO). Μπορείτε να το δείτε στο παρακάτω παράδειγμα.
Εκτελέστε τον παρακάτω κώδικα
package main
import "fmt"
func display(a int) {
fmt.Println(a)
}
func main() {
defer display(1)
defer display(2)
defer display(3)
fmt.Println(4)
}
Η έξοδος θα είναι
4 3 2 1
Εδώ εκτελείται πρώτα ο κώδικας μέσα στο main() και μετά οι αναβαλλόμενες κλήσεις συναρτήσεων εκτελούνται με την αντίστροφη σειρά, δηλαδή 4, 3,2,1.
δείκτες
Πριν εξηγήσουμε τους δείκτες, ας συζητήσουμε πρώτα τον τελεστή '&'. Ο τελεστής '&' χρησιμοποιείται για τη λήψη της διεύθυνσης μιας μεταβλητής. Σημαίνει ότι το '&a' θα εκτυπώσει τη διεύθυνση μνήμης της μεταβλητής a.
Σε αυτό το σεμινάριο Golang, θα εκτελέσουμε το παρακάτω πρόγραμμα για να εμφανίσουμε την τιμή μιας μεταβλητής και τη διεύθυνση αυτής της μεταβλητής
package main
import "fmt"
func main() {
a := 20
fmt.Println("Address:",&a)
fmt.Println("Value:",a)
}
Το αποτέλεσμα θα είναι
Address: 0xc000078008 Value: 20
Μια μεταβλητή δείκτη αποθηκεύει τη διεύθυνση μνήμης μιας άλλης μεταβλητής. Μπορείτε να ορίσετε έναν δείκτη χρησιμοποιώντας τη σύνταξη
var variable_name *type
Ο αστερίσκος(*) αντιπροσωπεύει τη μεταβλητή είναι δείκτης. Θα καταλάβετε περισσότερα εκτελώντας το παρακάτω πρόγραμμα
package main
import "fmt"
func main() {
//Create an integer variable a with value 20
a := 20
//Create a pointer variable b and assigned the address of a
var b *int = &a
//print address of a(&a) and value of a
fmt.Println("Address of a:",&a)
fmt.Println("Value of a:",a)
//print b which contains the memory address of a i.e. &a
fmt.Println("Address of pointer b:",b)
//*b prints the value in memory address which b contains i.e. the value of a
fmt.Println("Value of pointer b",*b)
//increment the value of variable a using the variable b
*b = *b+1
//prints the new value using a and *b
fmt.Println("Value of pointer b",*b)
fmt.Println("Value of a:",a)}
Η έξοδος θα είναι
Address of a: 0x416020 Value of a: 20 Address of pointer b: 0x416020 Value of pointer b 20 Value of pointer b 21 Value of a: 21
Δομές
Μια Δομή είναι ένας τύπος δεδομένων που ορίζεται από το χρήστη, ο οποίος περιέχει ένα ακόμη στοιχείο του ίδιου ή διαφορετικού τύπου.
Η χρήση μιας δομής είναι μια διαδικασία 2 βημάτων.
Πρώτα, δημιουργήστε(δηλώστε) έναν τύπο δομής
Δεύτερον, δημιουργήστε μεταβλητές αυτού του τύπου για αποθήκευση τιμών.
Οι δομές χρησιμοποιούνται κυρίως όταν θέλετε να αποθηκεύσετε σχετικά δεδομένα μαζί.
Σκεφτείτε μια πληροφορία υπαλλήλου που περιλαμβάνει όνομα, ηλικία και διεύθυνση. Μπορείτε να το χειριστείτε αυτό με 2 τρόπους
Δημιουργήστε 3 συστοιχίες - η μία συστοιχία αποθηκεύει τα ονόματα των υπαλλήλων, η μία αποθηκεύει την ηλικία και η τρίτη αποθηκεύει την ηλικία.
Δηλώστε έναν τύπο δομής με 3 πεδία - όνομα, διεύθυνση και ηλικία. Δημιουργήστε έναν πίνακα αυτού του τύπου δομής όπου κάθε στοιχείο είναι ένα αντικείμενο δομής με όνομα, διεύθυνση και ηλικία.
Η πρώτη προσέγγιση δεν είναι αποτελεσματική. Σε αυτού του είδους τα σενάρια, οι δομές είναι πιο βολικές.
Η σύνταξη για τη δήλωση μιας δομής είναι
type structname struct {
variable_1 variable_1_type
variable_2 variable_2_type
variable_n variable_n_type
}
Ένα παράδειγμα δήλωσης δομής είναι
type emp struct {
name string
address string
age int
}
Εδώ δημιουργείται ένας νέος τύπος που ορίζεται από το χρήστη με το όνομα emp. Τώρα, μπορείτε να δημιουργήσετε μεταβλητές του τύπου emp χρησιμοποιώντας τη σύνταξη
var variable_name struct_name
Ένα παράδειγμα είναι
var empdata1 emp
Μπορείτε να ορίσετε τιμές για το empdata1 ως
empdata1.name = "John" empdata1.address = "Street-1, Bangalore" empdata1.age = 30
Μπορείτε επίσης να δημιουργήσετε μια μεταβλητή δομής και να εκχωρήσετε τιμές κατά
empdata2 := emp{"Raj", "Building-1, Delhi", 25}
Εδώ, πρέπει να διατηρήσετε τη σειρά των στοιχείων. Το Raj θα αντιστοιχιστεί με το όνομα, το επόμενο στοιχείο προς διεύθυνση και το τελευταίο με την ηλικία.
Εκτελέστε τον παρακάτω κώδικα
package main
import "fmt"
//declared the structure named emp
type emp struct {
name string
address string
age int
}
//function which accepts variable of emp type and prints name property
func display(e emp) {
fmt.Println(e.name)
}
func main() {
// declares a variable, empdata1, of the type emp
var empdata1 emp
//assign values to members of empdata1
empdata1.name = "John"
empdata1.address = "Street-1, London"
empdata1.age = 30
//declares and assign values to variable empdata2 of type emp
empdata2 := emp{"Raj", "Building-1, Paris", 25}
//prints the member name of empdata1 and empdata2 using display function
display(empdata1)
display(empdata2)
}
Παραγωγή
John Raj
Μέθοδοι (όχι συναρτήσεις)
Μια μέθοδος είναι μια συνάρτηση με όρισμα δέκτη. Archiτεχνικά, είναι μεταξύ της λέξης-κλειδιού func και του ονόματος της μεθόδου. Η σύνταξη μιας μεθόδου είναι
func (variable variabletype) methodName(parameter1 paramether1type) {
}
Ας μετατρέψουμε το παραπάνω παράδειγμα προγράμματος ώστε να χρησιμοποιεί μεθόδους αντί για συνάρτηση.
package main
import "fmt"
//declared the structure named emp
type emp struct {
name string
address string
age int
}
//Declaring a function with receiver of the type emp
func(e emp) display() {
fmt.Println(e.name)
}
func main() {
//declaring a variable of type emp
var empdata1 emp
//Assign values to members
empdata1.name = "John"
empdata1.address = "Street-1, Lodon"
empdata1.age = 30
//declaring a variable of type emp and assign values to members
empdata2 := emp {
"Raj", "Building-1, Paris", 25}
//Invoking the method using the receiver of the type emp
// syntax is variable.methodname()
empdata1.display()
empdata2.display()
}
Το Go δεν είναι μια αντικειμενοστραφή γλώσσα και δεν έχει την έννοια της κλάσης. Οι μέθοδοι δίνουν μια αίσθηση του τι κάνετε σε αντικειμενοστραφή προγράμματα όπου οι συναρτήσεις μιας κλάσης καλούνται χρησιμοποιώντας τη σύνταξη objectname.functionname()
Συγχρονισμός
Το Go υποστηρίζει την ταυτόχρονη εκτέλεση εργασιών. Σημαίνει ότι το Go μπορεί να εκτελέσει πολλές εργασίες ταυτόχρονα. Διαφέρει από την έννοια του παραλληλισμού. Στον παραλληλισμό, μια εργασία χωρίζεται σε μικρές δευτερεύουσες εργασίες και εκτελούνται παράλληλα. Αλλά ταυτόχρονα, πολλές εργασίες εκτελούνται ταυτόχρονα. Η ταυτόχρονη λειτουργία επιτυγχάνεται στο Go χρησιμοποιώντας Goroutines και Channels.
Γορουτίνες
Το goroutine είναι μια συνάρτηση που μπορεί να εκτελεστεί ταυτόχρονα με άλλες λειτουργίες. Συνήθως όταν καλείται μια συνάρτηση, το στοιχείο ελέγχου μεταφέρεται στην καλούμενη συνάρτηση και μόλις ολοκληρωθεί ο έλεγχος εκτέλεσής του επιστρέφει στη συνάρτηση κλήσης. Στη συνέχεια, η συνάρτηση κλήσης συνεχίζει την εκτέλεσή της. Η συνάρτηση κλήσης περιμένει να ολοκληρώσει την εκτέλεση η συνάρτηση που καλείται πριν προχωρήσει με τις υπόλοιπες δηλώσεις.
Αλλά στην περίπτωση του goroutine, η συνάρτηση κλήσης δεν θα περιμένει να ολοκληρωθεί η εκτέλεση της συνάρτησης που επικαλείται. Θα συνεχίσει να εκτελείται με τις επόμενες δηλώσεις. Μπορείτε να έχετε πολλές γορουτίνες σε ένα πρόγραμμα.
Επίσης, το κύριο πρόγραμμα θα βγει όταν ολοκληρώσει την εκτέλεση των δηλώσεών του και δεν θα περιμένει την ολοκλήρωση των γορουτινών που επικαλείται.
Το Goroutine καλείται χρησιμοποιώντας λέξη-κλειδί μετάβαση ακολουθούμενη από κλήση συνάρτησης.
Παράδειγμα
go add(x,y)
Θα καταλάβετε τις γορουτίνες με τα παρακάτω παραδείγματα Golang. Εκτελέστε το παρακάτω πρόγραμμα
package main
import "fmt"
func display() {
for i:=0; i<5; i++ {
fmt.Println("In display")
}
}
func main() {
//invoking the goroutine display()
go display()
//The main() continues without waiting for display()
for i:=0; i<5; i++ {
fmt.Println("In main")
}
}
Η έξοδος θα είναι
In main In main In main In main In main
Εδώ το κύριο πρόγραμμα ολοκλήρωσε την εκτέλεση ακόμη και πριν ξεκινήσει η γορουτίνα. Το display() είναι ένα goroutine που καλείται χρησιμοποιώντας τη σύνταξη
go function_name(parameter list)
Στον παραπάνω κώδικα, η main() δεν περιμένει να ολοκληρωθεί η display() και η main() ολοκλήρωσε την εκτέλεσή της πριν η display() εκτελέσει τον κώδικά της. Έτσι, η δήλωση print inside display() δεν εκτυπώθηκε.
Τώρα τροποποιούμε το πρόγραμμα για να εκτυπωθούν και οι δηλώσεις από το display(). Προσθέτουμε μια χρονική καθυστέρηση 2 δευτερολέπτων στον βρόχο for της main() και μια καθυστέρηση 1 δευτερολέπτου στον βρόχο for της οθόνης().
package main
import "fmt"
import "time"
func display() {
for i:=0; i<5; i++ {
time.Sleep(1 * time.Second)
fmt.Println("In display")
}
}
func main() {
//invoking the goroutine display()
go display()
for i:=0; i<5; i++ {
time.Sleep(2 * time.Second)
fmt.Println("In main")
}
}
Η έξοδος θα είναι κάπως παρόμοια με
In display In main In display In display In main In display In display In main In main In main
Εδώ μπορείτε να δείτε ότι και οι δύο βρόχοι εκτελούνται με αλληλεπικαλυπτόμενο τρόπο λόγω της ταυτόχρονης εκτέλεσης.
Κανάλια
Τα κανάλια είναι ένας τρόπος για τις λειτουργίες να επικοινωνούν μεταξύ τους. Μπορεί να θεωρηθεί ως ένα μέσο όπου μια ρουτίνα τοποθετεί δεδομένα και είναι προσβάσιμη από μια άλλη ρουτίνα στον διακομιστή Golang.
Ένα κανάλι μπορεί να δηλωθεί με τη σύνταξη
channel_variable := make(chan datatype)
Παράδειγμα:
ch := make(chan int)
Μπορείτε να στείλετε δεδομένα σε ένα κανάλι χρησιμοποιώντας τη σύνταξη
channel_variable <- variable_name
Παράδειγμα
ch <- x
Μπορείτε να λάβετε δεδομένα από ένα κανάλι χρησιμοποιώντας τη σύνταξη
variable_name := <- channel_variable
Παράδειγμα
y := <- ch
Στα παραπάνω παραδείγματα γλώσσας Go του goroutine, έχετε δει ότι το κύριο πρόγραμμα δεν περιμένει για το gorutine. Αλλά αυτό δεν συμβαίνει όταν εμπλέκονται κανάλια. Ας υποθέσουμε ότι εάν μια goroutine ωθεί δεδομένα στο κανάλι, η main() θα περιμένει στη δήλωση λήψης δεδομένων καναλιού μέχρι να λάβει τα δεδομένα.
Θα το δείτε στα παρακάτω παραδείγματα γλώσσας Go. Πρώτα, γράψτε μια κανονική γορουτίνα και δείτε τη συμπεριφορά. Στη συνέχεια, τροποποιήστε το πρόγραμμα για να χρησιμοποιήσετε κανάλια και δείτε τη συμπεριφορά.
Εκτελέστε το παρακάτω πρόγραμμα
package main
import "fmt"
import "time"
func display() {
time.Sleep(5 * time.Second)
fmt.Println("Inside display()")
}
func main() {
go display()
fmt.Println("Inside main()")
}
Η έξοδος θα είναι
Inside main()
Το main() ολοκλήρωσε την εκτέλεση και έφυγε πριν από την εκτέλεση του goroutine. Έτσι η εκτύπωση μέσα στην οθόνη() δεν εκτελέστηκε.
Τώρα τροποποιήστε το παραπάνω πρόγραμμα για να χρησιμοποιήσετε κανάλια και δείτε τη συμπεριφορά.
package main
import "fmt"
import "time"
func display(ch chan int) {
time.Sleep(5 * time.Second)
fmt.Println("Inside display()")
ch <- 1234
}
func main() {
ch := make(chan int)
go display(ch)
x := <-ch
fmt.Println("Inside main()")
fmt.Println("Printing x in main() after taking from channel:",x)
}
Η έξοδος θα είναι
Inside display() Inside main() Printing x in main() after taking from channel: 1234
Εδώ αυτό που συμβαίνει είναι η main() όταν φτάσει στο x := <-ch θα περιμένει για δεδομένα στο κανάλι ch. Η οθόνη () έχει αναμονή 5 δευτερολέπτων και στη συνέχεια ωθήστε τα δεδομένα στο κανάλι ch. Το main() κατά τη λήψη των δεδομένων από το κανάλι ξεμπλοκάρεται και συνεχίζει την εκτέλεσή του.
Ο αποστολέας που ωθεί δεδομένα στο κανάλι μπορεί να ενημερώσει τους δέκτες ότι δεν θα προστεθούν άλλα δεδομένα στο κανάλι κλείνοντας το κανάλι. Αυτό χρησιμοποιείται κυρίως όταν χρησιμοποιείτε βρόχο για να προωθήσετε δεδομένα σε ένα κανάλι. Ένα κανάλι μπορεί να κλείσει χρησιμοποιώντας
close(channel_name)
Και στο τέλος του δέκτη, μπορείτε να ελέγξετε εάν το κανάλι είναι κλειστό χρησιμοποιώντας μια πρόσθετη μεταβλητή κατά την ανάκτηση δεδομένων από το κανάλι χρησιμοποιώντας
variable_name, status := <- channel_variable
Εάν η κατάσταση είναι True σημαίνει ότι λάβατε δεδομένα από το κανάλι. Αν είναι false, σημαίνει ότι προσπαθείτε να διαβάσετε από ένα κλειστό κανάλι
Μπορείτε επίσης να χρησιμοποιήσετε κανάλια για επικοινωνία μεταξύ γορουτίνες. Χρειάζεται να χρησιμοποιήσετε 2 γορουτίνες - η μία ωθεί δεδομένα στο κανάλι και η άλλη λαμβάνει τα δεδομένα από το κανάλι. Δείτε το παρακάτω πρόγραμμα
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()")
}
Εδώ υπάρχουν 2 υπορουτίνες, η μία σπρώχνει δεδομένα στο κανάλι και άλλες εκτυπώνει δεδομένα στο κανάλι. Η συνάρτηση add_to_channel προσθέτει τους αριθμούς από το 0 έως το 9 και κλείνει το κανάλι. Ταυτόχρονα η συνάρτηση fetch_from_channel αναμένει στο
x, flag := <- ch και μόλις τα δεδομένα γίνουν διαθέσιμα, εκτυπώνει τα δεδομένα. Φεύγει όταν η σημαία είναι ψευδής που σημαίνει ότι το κανάλι είναι κλειστό.
Η αναμονή στο main() δίνεται για να αποτραπεί η έξοδος του main() έως ότου οι goroutine ολοκληρώσουν την εκτέλεση.
Εκτελέστε τον κώδικα και δείτε την έξοδο ως
Read data Send data 0 1 2 3 4 5 6 7 8 9 Empty channel Inside main()
Αγορά
Το Select μπορεί να προβληθεί ως δήλωση διακόπτη που λειτουργεί σε κανάλια. Εδώ οι δηλώσεις περίπτωσης θα είναι μια λειτουργία καναλιού. Συνήθως, κάθε δήλωση περίπτωσης θα διαβάζεται από το κανάλι. Όταν οποιαδήποτε από τις περιπτώσεις είναι έτοιμη (το κανάλι διαβάζεται), τότε εκτελείται η εντολή που σχετίζεται με αυτήν την υπόθεση. Εάν είναι έτοιμες πολλές θήκες, θα επιλέξει μια τυχαία. Μπορείτε να έχετε μια προεπιλεγμένη περίπτωση που εκτελείται εάν καμία από τις περιπτώσεις δεν είναι έτοιμη.
Ας δούμε τον παρακάτω κώδικα
package main
import "fmt"
import "time"
//push data to channel with a 4 second delay
func data1(ch chan string) {
time.Sleep(4 * time.Second)
ch <- "from data1()"
}
//push data to channel with a 2 second delay
func data2(ch chan string) {
time.Sleep(2 * time.Second)
ch <- "from data2()"
}
func main() {
//creating channel variables for transporting string values
chan1 := make(chan string)
chan2 := make(chan string)
//invoking the subroutines with channel variables
go data1(chan1)
go data2(chan2)
//Both case statements wait for data in the chan1 or chan2.
//chan2 gets data first since the delay is only 2 sec in data2().
//So the second case will execute and exits the select block
select {
case x := <-chan1:
fmt.Println(x)
case y := <-chan2:
fmt.Println(y)
}
}
Η εκτέλεση του παραπάνω προγράμματος θα δώσει την έξοδο:
from data2()
Εδώ η δήλωση επιλογής περιμένει να είναι διαθέσιμα δεδομένα σε οποιοδήποτε από τα κανάλια. Το data2() προσθέτει δεδομένα στο κανάλι μετά από αναστολή λειτουργίας 2 δευτερολέπτων που θα προκαλέσει την εκτέλεση της δεύτερης υπόθεσης.
Προσθέστε μια προεπιλεγμένη περίπτωση στην επιλογή στο ίδιο πρόγραμμα και δείτε την έξοδο. Εδώ, όταν φτάσετε στο επιλεγμένο μπλοκ, εάν καμία περίπτωση δεν έχει έτοιμα δεδομένα στο κανάλι, θα εκτελέσει το προεπιλεγμένο μπλοκ χωρίς να περιμένει να είναι διαθέσιμα δεδομένα σε οποιοδήποτε κανάλι.
package main
import "fmt"
import "time"
//push data to channel with a 4 second delay
func data1(ch chan string) {
time.Sleep(4 * time.Second)
ch <- "from data1()"
}
//push data to channel with a 2 second delay
func data2(ch chan string) {
time.Sleep(2 * time.Second)
ch <- "from data2()"
}
func main() {
//creating channel variables for transporting string values
chan1 := make(chan string)
chan2 := make(chan string)
//invoking the subroutines with channel variables
go data1(chan1)
go data2(chan2)
//Both case statements check for data in chan1 or chan2.
//But data is not available (both routines have a delay of 2 and 4 sec)
//So the default block will be executed without waiting for data in channels.
select {
case x := <-chan1:
fmt.Println(x)
case y := <-chan2:
fmt.Println(y)
default:
fmt.Println("Default case executed")
}
}
Αυτό το πρόγραμμα θα δώσει την έξοδο:
Default case executed
Αυτό οφείλεται στο γεγονός ότι όταν έφτασε το μπλοκ επιλογής, κανένα κανάλι δεν είχε δεδομένα για ανάγνωση. Έτσι, η προεπιλεγμένη περίπτωση εκτελείται.
Mutex
Το Mutex είναι η σύντομη μορφή για τον αμοιβαίο αποκλεισμό. Το Mutex χρησιμοποιείται όταν δεν θέλετε να επιτρέψετε την πρόσβαση σε έναν πόρο από πολλές υπορουτίνες ταυτόχρονα. Το Mutex έχει 2 μεθόδους – Κλείδωμα και Ξεκλείδωμα. Το Mutex περιέχεται στο πακέτο συγχρονισμού. Επομένως, πρέπει να εισαγάγετε το πακέτο συγχρονισμού. Οι εντολές που πρέπει να εκτελούνται αμοιβαία αποκλειστικά μπορούν να τοποθετηθούν στο mutex.Lock() και στο mutex.Unlock().
Ας μάθουμε το mutex με ένα παράδειγμα που μετράει πόσες φορές εκτελείται ένας βρόχος. Σε αυτό το πρόγραμμα περιμένουμε ότι η ρουτίνα θα τρέξει βρόχο 10 φορές και η καταμέτρηση αποθηκεύεται σε άθροισμα. Καλείτε αυτήν τη ρουτίνα 3 φορές, οπότε το συνολικό πλήθος πρέπει να είναι 30. Το πλήθος αποθηκεύεται σε μια καθολική καταμέτρηση μεταβλητών.
Πρώτα, εκτελείτε το πρόγραμμα χωρίς mutex
package main
import "fmt"
import "time"
import "strconv"
import "math/rand"
//declare count variable, which is accessed by all the routine instances
var count = 0
//copies count to temp, do some processing(increment) and store back to count
//random delay is added between reading and writing of count variable
func process(n int) {
//loop incrementing the count by 10
for i := 0; i < 10; i++ {
time.Sleep(time.Duration(rand.Int31n(2)) * time.Second)
temp := count
temp++
time.Sleep(time.Duration(rand.Int31n(2)) * time.Second)
count = temp
}
fmt.Println("Count after i="+strconv.Itoa(n)+" Count:", strconv.Itoa(count))
}
func main() {
//loop calling the process() 3 times
for i := 1; i < 4; i++ {
go process(i)
}
//delay to wait for the routines to complete
time.Sleep(25 * time.Second)
fmt.Println("Final Count:", count)
}
Δείτε το αποτέλεσμα
Count after i=1 Count: 11 Count after i=3 Count: 12 Count after i=2 Count: 13 Final Count: 13
Το αποτέλεσμα μπορεί να είναι διαφορετικό όταν το εκτελείτε, αλλά το τελικό αποτέλεσμα δεν θα είναι 30.
Εδώ αυτό που συμβαίνει είναι ότι 3 γορουτίνες προσπαθούν να αυξήσουν τον αριθμό βρόχων που είναι αποθηκευμένος στη μεταβλητή μέτρηση. Ας υποθέσουμε ότι η καταμέτρηση μιας στιγμής είναι 5 και το goroutine1 πρόκειται να αυξήσει το πλήθος στο 6. Τα κύρια βήματα περιλαμβάνουν
Αριθμός αντιγράφων σε θερμοκρασία
Αύξηση θερμοκρασίας
Αποθηκεύστε τη θερμοκρασία πίσω για μέτρηση
Ας υποθέσουμε ότι αμέσως μετά την εκτέλεση του βήματος 3 από το goroutine1. μια άλλη goroutine μπορεί να έχει μια παλιά τιμή, ας πούμε ότι το 3 κάνει τα παραπάνω βήματα και αποθηκεύει το 4 πίσω, κάτι που είναι λάθος. Αυτό μπορεί να αποφευχθεί με τη χρήση mutex που αναγκάζει άλλες ρουτίνες να περιμένουν όταν μια ρουτίνα χρησιμοποιεί ήδη τη μεταβλητή.
Τώρα θα εκτελέσετε το πρόγραμμα με mutex. Εδώ τα παραπάνω 3 βήματα εκτελούνται σε mutex.
package main
import "fmt"
import "time"
import "sync"
import "strconv"
import "math/rand"
//declare a mutex instance
var mu sync.Mutex
//declare count variable, which is accessed by all the routine instances
var count = 0
//copies count to temp, do some processing(increment) and store back to count
//random delay is added between reading and writing of count variable
func process(n int) {
//loop incrementing the count by 10
for i := 0; i < 10; i++ {
time.Sleep(time.Duration(rand.Int31n(2)) * time.Second)
//lock starts here
mu.Lock()
temp := count
temp++
time.Sleep(time.Duration(rand.Int31n(2)) * time.Second)
count = temp
//lock ends here
mu.Unlock()
}
fmt.Println("Count after i="+strconv.Itoa(n)+" Count:", strconv.Itoa(count))
}
func main() {
//loop calling the process() 3 times
for i := 1; i < 4; i++ {
go process(i)
}
//delay to wait for the routines to complete
time.Sleep(25 * time.Second)
fmt.Println("Final Count:", count)
}
Τώρα η έξοδος θα είναι
Count after i=3 Count: 21 Count after i=2 Count: 28 Count after i=1 Count: 30 Final Count: 30
Εδώ παίρνουμε το αναμενόμενο αποτέλεσμα ως τελικό αποτέλεσμα. Επειδή οι δηλώσεις ανάγνωσης, αύξησης και επανεγγραφής της μέτρησης εκτελούνται σε mutex.
Σφάλμα χειρισμού
Τα σφάλματα είναι μη φυσιολογικές συνθήκες όπως το κλείσιμο ενός αρχείου που δεν είναι ανοιχτό, το άνοιγμα ενός αρχείου που δεν υπάρχει κ.λπ. Οι συναρτήσεις συνήθως επιστρέφουν σφάλματα ως την τελευταία τιμή επιστροφής.
Το παρακάτω παράδειγμα εξηγεί περισσότερα σχετικά με το σφάλμα.
package main
import "fmt"
import "os"
//function accepts a filename and tries to open it.
func fileopen(name string) {
f, er := os.Open(name)
//er will be nil if the file exists else it returns an error object
if er != nil {
fmt.Println(er)
return
}else{
fmt.Println("file opened", f.Name())
}
}
func main() {
fileopen("invalid.txt")
}
Η έξοδος θα είναι:
open /invalid.txt: no such file or directory
Εδώ προσπαθήσαμε να ανοίξουμε ένα αρχείο που δεν υπήρχε και επέστρεψε το σφάλμα στη μεταβλητή er. Εάν το αρχείο είναι έγκυρο, τότε το σφάλμα θα είναι μηδενικό
Προσαρμοσμένα σφάλματα
Χρησιμοποιώντας αυτήν τη δυνατότητα, μπορείτε να δημιουργήσετε προσαρμοσμένα σφάλματα. Αυτό γίνεται χρησιμοποιώντας τη New() του πακέτου σφαλμάτων. Θα ξαναγράψουμε το παραπάνω πρόγραμμα για να χρησιμοποιήσουμε προσαρμοσμένα σφάλματα.
Εκτελέστε το παρακάτω πρόγραμμα
package main
import "fmt"
import "os"
import "errors"
//function accepts a filename and tries to open it.
func fileopen(name string) (string, error) {
f, er := os.Open(name)
//er will be nil if the file exists else it returns an error object
if er != nil {
//created a new error object and returns it
return "", errors.New("Custom error message: File name is wrong")
}else{
return f.Name(),nil
}
}
func main() {
//receives custom error or nil after trying to open the file
filename, error := fileopen("invalid.txt")
if error != nil {
fmt.Println(error)
}else{
fmt.Println("file opened", filename)
}
}
Η έξοδος θα είναι:
Custom error message:File name is wrong
Εδώ το area() επιστρέφει το εμβαδόν ενός τετραγώνου. Εάν η είσοδος είναι μικρότερη από 1, τότε η area() επιστρέφει ένα μήνυμα σφάλματος.
Ανάγνωση αρχείων
Τα αρχεία χρησιμοποιούνται για την αποθήκευση δεδομένων. Το Go μας επιτρέπει να διαβάζουμε δεδομένα από τα αρχεία
Πρώτα δημιουργήστε ένα αρχείο, data.txt, στον παρόντα κατάλογο με το παρακάτω περιεχόμενο.
Line one Line two Line three
Τώρα εκτελέστε το παρακάτω πρόγραμμα για να δείτε ότι εκτυπώνει τα περιεχόμενα ολόκληρου του αρχείου ως έξοδο
package main
import "fmt"
import "io/ioutil"
func main() {
data, err := ioutil.ReadFile("data.txt")
if err != nil {
fmt.Println("File reading error", err)
return
}
fmt.Println("Contents of file:", string(data))
}
Εδώ τα δεδομένα, err := ioutil.ReadFile(“data.txt”) διαβάζει τα δεδομένα και επιστρέφει μια ακολουθία byte. Κατά την εκτύπωση μετατρέπεται σε μορφή συμβολοσειράς.
Σύνταξη αρχείων
Αυτό θα το δείτε με ένα πρόγραμμα
package main
import "fmt"
import "os"
func main() {
f, err := os.Create("file1.txt")
if err != nil {
fmt.Println(err)
return
}
l, err := f.WriteString("Write Line one")
if err != nil {
fmt.Println(err)
f.Close()
return
}
fmt.Println(l, "bytes written")
err = f.Close()
if err != nil {
fmt.Println(err)
return
}
}
Εδώ δημιουργείται ένα αρχείο, test.txt. Εάν το αρχείο υπάρχει ήδη, τότε τα περιεχόμενα του αρχείου περικόπτονται. Η Writeline() χρησιμοποιείται για την εγγραφή των περιεχομένων στο αρχείο. Μετά από αυτό, κλείσατε το αρχείο χρησιμοποιώντας το Close().
Cheat Sheet
Σε αυτό το σεμινάριο Go, καλύψαμε,
| Θέμα | Τεχνικές Περιγραφές | Σύνταξη |
|---|---|---|
| Βασικοί τύποι | Αριθμητικό, συμβολοσειρά, bool | |
| Μεταβλητές | Δηλώστε και εκχωρήστε τιμές σε μεταβλητές | var variable_name var variable_name type = τιμή var variable_name1, variable_name2 = value1, value2 variable_name := τιμή |
| Σταθερά | Μεταβλητές των οποίων η τιμή δεν μπορεί να αλλάξει μετά την εκχώρηση | μεταβλητή const = τιμή |
| Για βρόχο | Εκτελέστε εντολές σε βρόχο. | για αρχικοποίηση_έκφραση? αξιολόγηση_έκφραση; iteration_expression{ // μία ή περισσότερες δηλώσεις } |
| Αν αλλιώς | Είναι δήλωση υπό όρους | αν συνθήκη{ // δηλώσεις_1 Else {} // δηλώσεις_2 } |
| αλλαγή | Δήλωση υπό όρους με πολλαπλές περιπτώσεις | αλλαγή έκφρασης { τιμή περίπτωσης_1: δηλώσεις_1 τιμή περίπτωσης_2: δηλώσεις_2 τιμή περίπτωσης_n: δηλώσεις_ν προεπιλογή: statements_default } |
| Παράταξη | Σταθερό μέγεθος με όνομα ακολουθία στοιχείων του ίδιου τύπου | όνομα πίνακα := [μέγεθος] τύπος {value_0,value_1,…,value_size-1} |
| Φέτα | Τμήμα ή τμήμα ενός πίνακα | var slice_name [] type = array_name[start:end] |
| Συναρτήσεις | Μπλοκ εντολών που εκτελεί μια συγκεκριμένη εργασία | func function_name (parameter_1 type, parameter_n type) return_type { //δηλώσεις } |
| Πακέτα | Χρησιμοποιούνται για την οργάνωση του κώδικα. Αυξάνει την αναγνωσιμότητα και την επαναχρησιμοποίηση κώδικα | εισαγωγή πακέτου_name |
| Αναβάλλω | Αναβάλλει την εκτέλεση μιας συνάρτησης έως ότου η συνάρτηση που περιέχει ολοκληρώσει την εκτέλεση | αναβολή ονόματος_συνάρτησης(παράμετρος_λίστα) |
| δείκτες | Αποθηκεύει τη διεύθυνση μνήμης μιας άλλης μεταβλητής. | var variable_name *type |
| Structure | Τύπος δεδομένων που ορίζεται από τον χρήστη, ο οποίος περιέχει ένα ακόμη στοιχείο του ίδιου ή διαφορετικού τύπου | πληκτρολογήστε structname struct { variable_1 variable_1_type variable_2 variable_2_type variable_n variable_n_type } |
| Μέθοδοι | Μια μέθοδος είναι μια συνάρτηση με όρισμα δέκτη | func (variable variabletype) methodName(parameter_list) { } |
| Γορουτίνα | Μια συνάρτηση που μπορεί να εκτελεστεί ταυτόχρονα με άλλες λειτουργίες. | go function_name(parameter_list) |
| Κανάλι | Τρόπος για τις λειτουργίες να επικοινωνούν μεταξύ τους. Ένα μέσο στο οποίο μια ρουτίνα τοποθετεί δεδομένα και είναι προσβάσιμη από μια άλλη ρουτίνα. | Δηλώνω: ch := make(chan int) Αποστολή δεδομένων στο κανάλι: channel_variable <- variable_name Λήψη από το κανάλι: variable_name := <- channel_variable |
| Αγορά | Δήλωση αλλαγής που λειτουργεί σε κανάλια. Οι δηλώσεις υπόθεσης θα είναι λειτουργία καναλιού. Όταν οποιοδήποτε κανάλι είναι έτοιμο με δεδομένα, τότε εκτελείται η δήλωση που σχετίζεται με αυτήν την περίπτωση | επιλέξτε { περίπτωση x := <-chan1: fmt.Println(x) περίπτωση y := <-chan2: fmt.Println(y) } |
| Mutex | Το Mutex χρησιμοποιείται όταν δεν θέλετε να επιτρέψετε την πρόσβαση σε έναν πόρο από πολλές υπορουτίνες ταυτόχρονα. Το Mutex έχει 2 μεθόδους – Κλείδωμα και Ξεκλείδωμα | mutex.Lock() //δηλώσεις mutex.Unlock(). |
| Διαβάστε αρχεία | Διαβάζει τα δεδομένα και επιστρέφει μια ακολουθία byte. | Δεδομένα, err := ioutil.ReadFile(όνομα αρχείου) |
| Γράψτε το αρχείο | Γράφει δεδομένα σε ένα αρχείο | l, err := f.WriteString(text_to_write) |
