Μια συνάρτηση σου επιτρέπει να ομαδοποιήσεις κώδικα σε μια επαναχρησιμοποιήσιμη μονάδα.
Αποτελείται από τη λέξη-κλειδί func, το όνομα της συνάρτησης και μια λίστα από μηδέν ή περισσότερες παραμέτρους και τύπους, χωρισμένες με κόμμα μέσα σε παρενθέσεις.
Όλες οι παράμετροι πρέπει να έχουν ρητά δηλωμένο τύπο. Δεν γίνεται εξαγωγή τύπου για τις παραμέτρους. Δεν υπάρχουν προεπιλεγμένες τιμές για τις παραμέτρους, οπότε όλες οι παράμετροι μιας συνάρτησης είναι υποχρεωτικές.
import "fmt"
// No parameters
func PrintHello() {
fmt.Println("Hello")
}
// Two parameters
func PrintGreetingName(greeting string, name string) {
fmt.Println(greeting + " " + name)
}
Παράμετροι του ίδιου τύπου μπορούν να δηλωθούν μαζί, ακολουθούμενες από μία μόνο δήλωση τύπου.
import "fmt"
func PrintGreetingName(greeting, name string) {
fmt.Println(greeting + " " + name)
}
Ας δούμε γρήγορα δύο όρους που συχνά συγχέονται μεταξύ τους: parameters και arguments.
Οι παράμετροι συνάρτησης είναι τα ονόματα που ορίζονται στην υπογραφή της συνάρτησης, όπως τα greeting και name στη συνάρτηση PrintGreetingName παραπάνω.
Τα ορίσματα συνάρτησης είναι οι συγκεκριμένες τιμές που περνάμε στις παραμέτρους της συνάρτησης όταν καλούμε τη συνάρτηση.
Για παράδειγμα, στο παρακάτω παράδειγμα, τα "Hello" και "Katrina" είναι τα ορίσματα που περνάμε στις παραμέτρους greeting και name:
PrintGreetingName("Hello", "Katrina")
Μετά τις παραμέτρους της συνάρτησης ακολουθούν μηδέν ή περισσότερες τιμές επιστροφής, οι οποίες πρέπει επίσης να έχουν ρητά δηλωμένο τύπο.
Οι μεμονωμένες τιμές επιστροφής γράφονται χωρίς παρενθέσεις, ενώ οι πολλαπλές τιμές επιστροφής περικλείονται σε παρενθέσεις.
Οι τιμές επιστρέφονται στον κώδικα που κάλεσε τη συνάρτηση με τη λέξη-κλειδί return.
Μπορούν να υπάρχουν πολλαπλές εντολές return μέσα σε μια συνάρτηση.
Η εκτέλεση της συνάρτησης τελειώνει μόλις συναντήσει μία από αυτές τις εντολές return.
Αν μια συνάρτηση πρέπει να επιστρέψει πολλαπλές τιμές, αυτές χωρίζονται με κόμμα.
func Hello(name string) string {
return "Hello " + name
}
func HelloAndGoodbye(name string) (string, string) {
return "Hello " + name, "Goodbye " + name
}
Η κλήση μιας συνάρτησης γίνεται γράφοντας το όνομα της συνάρτησης και περνώντας ορίσματα για κάθε παράμετρο της συνάρτησης μέσα σε παρενθέσεις.
import "fmt"
// No parameters, no return value
func PrintHello() {
fmt.Println("Hello")
}
// Called like this:
PrintHello()
// One parameter, one return value
func Hello(name string) string {
return "Hello " + name
}
// Called like this:
greeting := Hello("Dave")
// Multiple parameters, multiple return values
func SumAndMultiply(a, b int) (int, int) {
return a+b, a*b
}
// Called like this:
aplusb, atimesb := SumAndMultiply(a, b)
Όπως και οι παράμετροι, έτσι και οι τιμές επιστροφής μπορούν προαιρετικά να έχουν όνομα.
Αν χρησιμοποιήσεις ονομαστικές τιμές επιστροφής, μια εντολή return χωρίς ορίσματα θα επιστρέψει αυτές τις τιμές.
Αυτό είναι γνωστό ως "γυμνή" επιστροφή.
func SumAndMultiplyThenMinus(a, b, c int) (sum, mult int) {
sum, mult = a+b, a*b
sum -= c
mult -= c
return
}
Είναι επίσης σημαντικό να ξεκαθαρίσουμε την έννοια του περάσματος με τιμή και του περάσματος με αναφορά.
Πρώτα, ας ξεκαθαρίσουμε το πέρασμα με τιμή.
Στο παρακάτω παράδειγμα, όταν περνάμε τη μεταβλητή val στη συνάρτηση MultiplyByTwo, περνάμε ένα αντίγραφο του val.
Εξαιτίας αυτού, το newVal έχει την ενημερωμένη τιμή 4, αλλά η αρχική μεταβλητή val παραμένει 2.
Στο παρασκήνιο, η Go ουσιαστικά φτιάχνει ένα αντίγραφο της αρχικής τιμής, ώστε μόνο αυτό το αντίγραφο, δηλαδή το v, να τροποποιείται από τη συνάρτηση.
val := 2
func MultiplyByTwo(v int) int {
v = v * 2
return v
}
newVal := MultiplyByTwo(val)
// newval is 4, val is still 2 because only a copy of its value was passed into the function
Αυστηρά μιλώντας, στη Go όλα τα ορίσματα περνούν με τιμή, δηλαδή γίνεται ένα αντίγραφο της τιμής ή των δεδομένων που δίνονται στη συνάρτηση.
Αν όμως δεν θέλεις να φτιάχνεις αντίγραφο των δεδομένων που περνάς σε μια συνάρτηση και θέλεις να αλλάζεις τα δεδομένα μέσα στη συνάρτηση,
τότε θα πρέπει να χρησιμοποιήσεις pointers ως ορίσματα, δηλαδή πέρασμα με αναφορά.
Χρησιμοποιούμε έναν pointer για να πετύχουμε το πέρασμα με αναφορά.
Περνώντας pointer ορίσματα σε μια συνάρτηση,
μπορούμε να τροποποιήσουμε τα δεδομένα που περνιούνται στη συνάρτηση αντί να δουλεύουμε μόνο με ένα αντίγραφό τους.
Για την ώρα, αρκεί να ξέρεις ότι οι τύποι δεικτών αναγνωρίζονται από το * μπροστά από τον τύπο στην υπογραφή της συνάρτησης.
func HandlePointers(x, y *int) {
// Some logic to handle integer values referenced by pointers x and y
}
Αν η έννοια του pointer σε μπερδεύει, μην ανησυχείς.
Έχουμε ένα ξεχωριστό τμήμα πιο κάτω στο πρόγραμμα σπουδών που θα σε βοηθήσει να μάθεις καλά τους δείκτες.
Σημείωσε ότι τα slices και τα maps αποτελούν εξαιρέσεις στον παραπάνω κανόνα.
Όταν περνάμε ένα slice ή ένα map ως ορίσματα σε μια συνάρτηση, αντιμετωπίζονται ως τύποι δεικτών, ακόμα κι αν δεν υπάρχει ρητό * στον τύπο.
Αυτό σημαίνει ότι αν περάσουμε ένα slice ή ένα map σε μια συνάρτηση και τροποποιήσουμε τα δεδομένα του,
οι αλλαγές θα φανούν στο αρχικό slice ή map.
Σε αυτή την άσκηση θα γράψεις λίγο ακόμα κώδικα σχετικό με την προετοιμασία και το μαγείρεμα της καταπληκτικής λαζάνιας σου από το αγαπημένο σου βιβλίο συνταγών.
Έχεις τέσσερις εργασίες. Η πρώτη σχετίζεται με το μαγείρεμα αυτό καθαυτό, ενώ οι άλλες τρεις αφορούν την τέλεια προετοιμασία.
Για την επόμενη λαζάνια που θα ετοιμάσεις, θέλεις να βεβαιωθείς ότι έχεις κρατήσει αρκετό χρόνο ώστε να απολαύσεις το μαγείρεμα. Έχεις ήδη σχεδιάσει ποιες στρώσεις θα έχει η λαζάνια σου. Τώρα θέλεις να υπολογίσεις πόσο χρόνο θα πάρει η προετοιμασία με βάση αυτό.
Υλοποίησε μια συνάρτηση PreparationTime που δέχεται ένα slice από στρώσεις ως []string και τον μέσο χρόνο προετοιμασίας ανά στρώση σε λεπτά ως int.
Η συνάρτηση θα πρέπει να επιστρέφει την εκτίμηση για τον συνολικό χρόνο προετοιμασίας με βάση τον αριθμό των στρώσεων ως int.
Η Go δεν έχει προεπιλεγμένες τιμές για τις συναρτήσεις.
Αν ο μέσος χρόνος προετοιμασίας περαστεί ως 0 (η προεπιλεγμένη αρχική τιμή για ένα int), τότε θα πρέπει να χρησιμοποιηθεί η προεπιλεγμένη τιμή 2.
layers := []string{"sauce", "noodles", "sauce", "meat", "mozzarella", "noodles"}
PreparationTime(layers, 3)
// => 18
PreparationTime(layers, 0)
// => 12
Εκτός από το να κρατήσεις τον χρόνο, θέλεις επίσης να βεβαιωθείς ότι έχεις αρκετή σάλτσα και νουντλς για να μαγειρέψεις τη λαζάνια των ονείρων σου. Για κάθε στρώση νουντλς στη λαζάνια σου, θα χρειαστείς 50 γραμμάρια νουντλς. Για κάθε στρώση σάλτσας στη λαζάνια σου, θα χρειαστείς 0,2 λίτρα σάλτσας.
Όρισε τη συνάρτηση Quantities που παίρνει ως παράμετρο ένα slice από στρώσεις ως []string.
Η συνάρτηση θα προσδιορίσει τότε την ποσότητα νουντλς και σάλτσας που χρειάζεται για να φτιάξεις το γεύμα σου.
Το αποτέλεσμα θα πρέπει να επιστραφεί ως δύο τιμές: το noodles ως int και το sauce ως float64.
Quantities([]string{"sauce", "noodles", "sauce", "meat", "mozzarella", "noodles"})
// => 100, 0.4
Πριν λίγο καιρό επισκέφτηκες έναν φίλο και έφαγες λαζάνια εκεί. Ήταν καταπληκτική και είχε κάτι το ξεχωριστό. Ο φίλος σου έστειλε τη λίστα με τα συστατικά και σου είπε ότι το τελευταίο στοιχείο της λίστας είναι το "μυστικό συστατικό" που έκανε το γεύμα τόσο ξεχωριστό. Τώρα θέλεις να προσθέσεις κι εσύ αυτό το μυστικό συστατικό στη συνταγή σου.
Γράψε μια συνάρτηση AddSecretIngredient που δέχεται ως παραμέτρους δύο slices με συστατικά τύπου []string.
Η πρώτη παράμετρος είναι η λίστα που σου έστειλε ο φίλος σου, η δεύτερη είναι η λίστα συστατικών της δικής σου συνταγής.
Το τελευταίο στοιχείο στη λίστα συστατικών σου είναι πάντα "?".
Η συνάρτηση θα πρέπει να το αντικαταστήσει με το τελευταίο στοιχείο από τη λίστα του φίλου σου.
Σημείωση: Η AddSecretIngredient δεν επιστρέφει τίποτα, θα πρέπει να τροποποιήσεις απευθείας τη λίστα με τα συστατικά σου.
Η λίστα με τα συστατικά του φίλου σου δεν θα πρέπει να τροποποιηθεί.
Επίσης, καθώς το slice περνιέται σε μια συνάρτηση ως δείκτης, οι αλλαγές στα δύο ορίσματα []string που περνιούνται στην AddSecretIngredient τροποποιούνται άμεσα.
friendsList := []string{"noodles", "sauce", "mozzarella", "kampot pepper"}
myList := []string{"noodles", "meat", "sauce", "mozzarella","?"}
AddSecretIngredient(friendsList, myList)
// myList => []string{"noodles", "meat", "sauce", "mozzarella", "kampot pepper"}
Οι ποσότητες που αναγράφονται στο βιβλίο συνταγών σου αρκούν μόνο για δύο μερίδες λαζάνια. Επειδή θέλεις να μαγειρέψεις για περισσότερα άτομα την επόμενη φορά, θέλεις να υπολογίσεις τις ποσότητες για διαφορετικό αριθμό μερίδων.
Υλοποίησε μια συνάρτηση ScaleRecipe που παίρνει δύο παραμέτρους.
float64 που χρειάζονται για 2 μερίδες.Η συνάρτηση θα πρέπει να επιστρέφει ένα slice από float64 με τις ποσότητες που χρειάζονται για τον επιθυμητό αριθμό μερίδων.
Θέλεις όμως να κρατήσεις την αρχική συνταγή.
Αυτό σημαίνει ότι το όρισμα quantities δεν θα πρέπει να τροποποιηθεί σε αυτή τη συνάρτηση.
quantities := []float64{ 1.2, 3.6, 10.5 }
scaledQuantities := ScaleRecipe(quantities, 4)
// => []float64{ 2.4, 7.2, 21 }
Γράψου στο Exercism για να μάθεις και να κατακτήσεις Go με 34 έννοιες166 ασκήσεις και πραγματική καθοδήγηση από ανθρώπους, όλα δωρεάν.