FREE

ΕΝΟΤΗΤΑ 13 GO - Functions (Part 3)

Στο σημερινό δωρεάν μάθημα GO θα δούμε μιλήσουμε λίγο περισσότερο για το πως χρησιμοποιούμε τα functions σαν data types ή types γενικότερα, πως λειτουργεί ένα defer statement και τέλος πως ορίζονται τα recursive functions.

Όπως ήδη γνωρίζουμε από την μικρή μας περιήγηση μέχρι τώρα στο documentation της GO, υπάρχουν πολλά functions που μας προσφέρονται έτοιμα από την ίδια την γλώσσα για να μας διευκολύνουν στην ανάπτυξη του κώδικα μας. Γενικότερα στην GO, τα functions θεωρούνται και χρησιμοποιούνται σαν types ακριβώς με τον ίδιο τρόπο όπως χρησιμοποιείτε και το int, string και bool types. Αυτή η ιδιότητα των functions μας δίνει την ικανότητα να περάσουμε functions σαν παραμέτρους σε άλλα functions, να επιστρέφουμε function σαν αποτέλεσμα καλώντας ένα άλλο function, και να αναθέτουμε functions σε μεταβλητές. Μπορούμε επίσης και να δημιουργήσουμε τα δικά μας function types αν το επιθυμούμε.

Όλα αυτά ορίζονται από το signature του function. Εκεί καθορίζουμε το function type που δέχεται μια function όπως το είδος των δεδομένων που θα επιστραφούν. Ας δούμε ένα απλό παράδειγμα για να κατανοήσουμε λίγο καλύτερα την έννοια του type.

main.go


package main

import "fmt"

type calc func(int, int)string

func main() {
    calculator(add, 5, 6)
}

func add(i, j int)string{
    result:=i+j
    return fmt.Sprintf("Added %d + %d = %d", i, j, result)
}

func calculator(f calc, i, j int){
    fmt.Println(f(i,j))
}

Output


Added 5 + 6 = 11

Ο κώδικας μας ξεκινάει με το να ορίζει μια μεταβλητή calc να είναι type func το οποίο δέχεται δύο int τιμές και επιστρέφει ένα string.

Μετά ορίζουμε μια function με το όνομα add η οποία έχει ακριβώς το ίδιο signature όπως και το type calc. Δηλαδή δέχεται δύο int τιμές και επιστέφει ένα string. Αυτό είναι απαραίτητη προϋπόθεση όταν ορίζουμε type μεταβλητές κάποιου function. Πρέπει το signature της function που θα ορίσουμε να είναι ίδιο με εκείνο της type μεταβλητής.

Μετά ορίζουμε το function calculator όπου περνάμε το calc σαν input παράμετρο μαζί με τις int παραμέτρους i και j. Αυτή είναι μια ακόμα ιδιότητα των functions – μπορούμε να περάσουμε μια function σαν παράμετρο σε μια άλλη function. Οποιαδήποτε function ταιριάζει στο signature με την calc μπορούμε να την περάσουμε στην calculator.

Μέσα στην main function καλούμε την calculator με input τιμές add, 5, και 6. Στην ουσία περνάμε την add function της οποία το signature συμφωνεί με εκείνο της calc. Η add function θα προσθέσει τις τιμές από τα δύο int και θα επιστρέψει ένα Sprintf αποτέλεσμα. Επειδή η Sprintf δεν τυπώνει άμεσα στο terminal, χρησιμοποιούμε την Println για αυτό το σκοπό.

Θα μπορούσαμε όμως αντί να χρησιμοποιήσουμε το type, να περνούσαμε το signature της function σαν input παράμετρο σε μια άλλη function όπως δείχνει και το παρακάτω παράδειγμα.

main.go


package main

import "fmt"

func main() {
    calculator(add, 5, 6)
    calculator(subtract, 10,5)
}

func add(i, j int)int{
    return i+j
}

func subtract(i, j int)int{
    return i-j
}

Output


11
5

Μια ακόμη ιδιότητα που μπορούμε να προσθέσουμε στις functions είναι εκείνη της defer. Επειδή είμαι σίγουρος ότι δεν θα καταλάβετε τον ορισμό από μόνο του, καλύτερα είναι πρώτα να δούμε ένα απλό παράδειγμα και μετά να αναλύσουμε την συμπεριφορά του προγράμματος μας.

main.go


package main

import "fmt"

func main() {
    defer done()
    fmt.Println("Main: Start")
    fmt.Println("Main: End")
}
func done(){
    fmt.Println("Now Iam done")
}

Output


Main: Start
Main: End
Now Iam done

Τι ιδιότητα δίνει η defer στην function done? Αν και την έχουμε καλέσει πρώτη στον κώδικα της main εκείνη θα εκτελεστεί τελευταία, λίγο πριν ολοκληρωθεί η main. Και αυτή ακριβώς είναι η ιδιότητα που αποκτάει η function done. Εκτελείται τελευταία λίγο πριν ολοκληρωθεί ο κώδικας της εξωτερικής function στην οποία ανήκει που στην δική μας περίπτωση είναι η main. Όπως καταλαβαίνετε οι defer functions είναι ιδανικές για να καθαρίσουμε βάσεις, αρχεία κτλ μετά το πέρας εκτέλεσης κάποιας εργασίας.

Πριν κλείσουμε και αυτή την ενότητα που ολοκληρώνει την αναφορά μας στα functions, καλό θα ήταν να μιλήσουμε και για τα recursive functions. Ένα recursive function είναι ικανό να καλέσει τον εαυτό του άπειρες φορές ή μέχρι κάποια συνθήκη που έχουμε ορίσει να γίνει true. Ο τρόπος που ορίζουμε recursive functions είναι να καλέσει μια function τον εαυτό της καλώντας το όνομα της στο return statement.

main.go

    
package main

import "fmt"

func feedMe(portion int, eaten int)int{
    eaten = portion + eaten
    if eaten >=5{
        fmt.Printf("I am full! I've eaten %d\n", eaten)
        return eaten
    }
    fmt.Printf("I'm still hungry! I've eaten %d\n", eaten)
    return feedMe(portion, eaten)
}

func main() {
    fmt.Println(feedMe(1,0))
}
    
    

Output

    
I'm still hungry! I've eaten 1
I'm still hungry! I've eaten 2
I'm still hungry! I've eaten 3
I'm still hungry! I've eaten 4
I am full! I've eaten 5
5
        
    
    

Σε αυτό το παράδειγμα, το πιο σημαντικό σημείο είναι η τελευταία γραμμή κώδικα μέσα στην function feedMe. Αντί να επιστρέψει μια τιμή, καλεί τον εαυτό της για να εκτελεστεί πάλι. Αυτό θα συνεχιστεί για πάντα εκτός και αν ορίσουμε κάποια συνθήκη που θα σταματήσει αυτή την συνεχόμενη ενέργεια. Εδώ η συνθήκη μας είναι όταν η μεταβλητή eaten φτάσει την τιμή 5 ο κώδικας μας να μας επιστρέψει την τιμή και το πρόγραμμα να σταματήσει την εκτέλεση του.