View on GitHub

dituoi_agp

Αρχές Γλωσσών Προγραμματισμού

9. Υποπρογράμματα

ΑΡΧΕΣ ΓΛΩΣΣΩΝ ΠΡΟΓΡΑΜΜΑΤΙΣΜΟΥ (R.W.Sebesta) - Κεφάλαιο 9

9.1 Εισαγωγή

Τα υποπρογράμματα αποτελούν αφαιρέσεις διεργασιών. Οι λεπτομέρειες μιας διεργασίας αντικαθίστανται με την κλήση ενός υποπρογράμματος. Έτσι διευκολύνεται η ανάγνωση του κώδικα, εξοικονομείται χώρος μνήμης και χρόνος κωδικοποίησης.

9.2 Βασικά στοιχεία υποπρογραμμάτων

9.2.1 Γενικά στοιχεία υποπρογραμμάτων

9.2.2 Βασικοί ορισμοί

Βασικά είδη υποπρογραμμάτων: διαδικασίες, συναρτήσεις

Κεφαλίδα υποπρογράμματος

Η κεφαλίδα ενός υποπρογράμματος καθορίζει το είδος του, το όνομά του (αν έχει) και τη λίστα παραμέτρων του.

Σώμα υποπρογράμματος

Το σώμα ενός υποπρογράμματος ορίζει τις ενέργειες που θα εκτελεί το υποπρόγραμμα.

Ιδιαίτερες περιπτώσεις

Στην Python οι ορισμοί των συναρτήσεων είναι εκτελέσιμοι. Οπότε μπορεί να γραφεί κώδικας της μορφής:

x = 1
if x == 1:
    def foo():
        print("A")
else:
    def foo():
        print("B")
foo()

Η εκτέλεση του παραπάνω κώδικα θα εμφανίσει A

Στην Ruby αν και συνήθως οι μέθοδοι ορίζονται σε κλάσεις, ωστόσο μπορούν να οριστούν και εκτός κλάσεων οπότε θεωρούνται μέθοδοι της κλάσης Object.

Στην Lua οι συναρτήσεις είναι ανώνυμες, αλλά μπορούν να οριστούν με τέτοιο τρόπο που να τους αποδίδεται όνομα

function cube(x) return x * x * x end

ή (με ανώνυμη συνάρτηση)

cube = function(x) return x * x * x end

Το πρωτόκολλο ενός υποπρογράμματος περιέχει τον αριθμό των παραμέτρων, τη σειρά με την οποία παρατίθενται, τον τύπο τους και εάν πρόκειται για συνάρτηση τον τύπο επιστροφής της.

Στη C και στη C++ χρησιμοποιούνται τα πρωτότυπα συναρτήσεων που αποτελούν δηλώσεις συναρτήσεων πριν τον ορισμό τους. Στις περισσότερες άλλες γλώσσες δεν χρησιμοποιούνται ξεχωριστές δηλώσεις των υποπρογραμμάτων.

Παράδειγμα με συνάρτηση χωρίς prototype στη C

Παράδειγμα με prototype στη C

Παράδειγμα με prototype σε header στη C

Παράδειγμα με prototype σε header στη C και ξεχωριστό αρχείο πηγαίου κώδικα για τη συνάρτηση που δηλώνεται στο header αρχείο

Η μεταγλώττιση και εκτέλεση σε αυτή την περίπτωση θα πρέπει να γίνει ως εξής:

$ gcc my_functions.c prototype3.c -o prototype3
$ ./prototype

9.2.3 Παράμετροι

Οι παράμετροι στην κεφαλίδα του υποπρογράμματος ονομάζονται τυπικές ή επίσημες (formal) παράμετροι ή ορίσματα, ενώ οι παράμετροι στην κλήση του υποπρογράμματος ονομάζονται πραγματικές (actual) παράμετροι ή απλά παράμετροι.

Παράμετροι θέσης και παράμετροι λέξεων κλειδιών

Παράδειγμα στην Python με παραμέτρους λέξεων κλειδιών (keywords)

def fun(x, y, z):
    print(
        f"Το πρώτο όρισμα είναι {x}, το δεύτερο όρισμα είναι {y}, το τρίτο όρισμα είναι {z}"
    )


fun(1, 2, 3) 
fun(x=1, y=2, z=3) # κλήση με keywords
fun(y=2, x=1, z=3) # κλήση με keywords
fun(1, z=3, y=2) # κλήση με απλά ορίσματα και keywords

θα εμφανίσει

Το πρώτο όρισμα είναι 1, το δεύτερο όρισμα είναι 2, το τρίτο όρισμα είναι 3
Το πρώτο όρισμα είναι 1, το δεύτερο όρισμα είναι 2, το τρίτο όρισμα είναι 3
Το πρώτο όρισμα είναι 1, το δεύτερο όρισμα είναι 2, το τρίτο όρισμα είναι 3
Το πρώτο όρισμα είναι 1, το δεύτερο όρισμα είναι 2, το τρίτο όρισμα είναι 3

Παράδειγμα στη C++ με “named arguments” που κάνει χρήση της δυνατότητας αρχικοποίησης δομών με χρήση ονομάτων πεδίων, π.χ. struct_initialization.cpp.

named_args.cpp

Προκαθορισμένες (default) τιμές

Σε γλώσσες όπως οι Python, Ruby, C++, PHP οι τυπικές παράμετροι μπορούν να έχουν προκαθορισμένες τιμές.

Παράδειγμα στην Python με προκαθορισμένες τιμές παραμέτρων

def fun(x=1, y=2):
    print(x, y)


fun() # κλήση της συνάρτησης fun χωρίς ορίσματα (το x λαμβάνει την τιμή 1 και το y λαμβάνει την τιμή 2)
fun(5) # το x λαμβάνει την τιμή 5 και το y λαμβάνει την προκαθορισμένη τιμή 2)
fun(x=5) # το x λαμβάνει την τιμή 5 και το y λαμβάνει την προκαθορισμένη τιμή 2)
fun(y=10) # το x λαμβάνει την προκαθορισμένη τιμή 1 και το y λαμβάνει την τιμή 10)
fun(5, 10) # το x λαμβάνει την τιμή 5 και το y λαμβάνει την τιμή 10)
fun(5, y=10) # το x λαμβάνει την τιμή 5 και το y λαμβάνει την τιμή 10)

θα εμφανίσει

1 2
5 2
5 2
1 10
5 10
5 10

Παράδειγμα στη C++ με προκαθορισμένες τιμές παραμέτρων (οι παράμετροι με προκαθορισμένες τιμές πρέπει να εμφανίζονται τελευταίοι στη λίστα παραμέτρων της συνάρτησης)

default_args.cpp

Μεταβλητός αριθμός παραμέτρων

Παράδειγμα στην Python

def fun(*args, **kwargs):
    print(args)
    print(kwargs)


fun(1, 2, 3, a="data1", b="data2")

θα εμφανίσει

(1, 2, 3)
{'a': 'data1', 'b': 'data2'}

Παράδειγμα χρήσης του *args έτσι ώστε να μπορούν να γίνουν αλλαγές χωρίς να επηρεάζεται παλιός κώδικας (backwards compatibility)

def fun(a,b):
    return a + b 

Έστω ότι υπάρχει ήδη ο παραπάνω κώδικας για τη συνάρτηση fun η οποία καλείται σε διάφορα σημεία τα οποία δεν θέλουμε να επηρεαστούν. Ωστόσο, επιθυμούμε να προσθέσουμε επιπλέον ορίσματα στη συνάρτηση fun.

def fun(a,b, *args):
    if args:
        return a + b + args[0]
    else:
        return a + b

print(fun(1,2)) # κλήση της συνάρτησης όπως παλιότερα
print(fun(1,2,3)) # κλήση της συνάρτησης με μια επιπλέον παράμετρο σε σχέση με πριν

Παράδειγμα στη C

H printf είναι ένα παράδειγμα variadic συνάρτησης. Η δήλωσή της είναι η ακόλουθη:

int printf(const char* format, ...);

Δείτε τη σελίδα στο cppreference για τις variadic functions στη C: https://en.cppreference.com/w/c/variadic

variadic_function.c

ισοδύναμος κώδικας σε Python

def sum_all(*args):
    return sum(args)

print(sum_all(10,20))
print(sum_all(10,20,30))
print(sum_all(10,20,30,40))

θα εμφανίσει

30
60
100

9.2.4 Διαδικασίες και συναρτήσεις

Μόνο κάποιες παλιότερες γλώσσες όπως η Ada, Pascal και Fortran υποστηρίζουν τις διαδικασίες. Οι διαδικασίες είναι συλλογές εντολών που ορίζουν υπολογισμούς που καθορίζονται με βάση τις παραμέτρους. Οι συναρτήσεις είναι μια αφαίρεση των μαθηματικών συναρτήσεων. Ωστόσο οι συναρτήσεις μπορούν να προκαλούν side-effects, δηλαδή να αλλάζουν τιμές παραμέτρων ή τιμές άλλων μεταβλητών που ορίζονται εκτός των συναρτήσεων.

9.3 Ζητήματα σχεδιασμού για υποπρογράμματα

Υπερφορτωμένο είναι ένα υποπρόγραμμα που έχει το ίδιο όνομα με ένα άλλο υποπρόγραμμα στο ίδιο περιβάλλον αναφοράς

Γενικό είναι ένα υποπρόγραμμα το οποίο μπορεί να κληθεί με παραμέτρους διαφορετικών τύπων από κλήση σε κλήση.

Κλειστότητα είναι ένα ένθετο υποπρόγραμμα και το περιβάλλον αναφοράς του.

9.4 Τοπικά περιβάλλοντα αναφοράς

9.4.1 Τοπικές μεταβλητές

Τα υποπρογράμματα μπορούν να ορίζουν τις δικές τους μεταβλητές, ορίζοντας με αυτό τον τρόπο τοπικά περιβάλλοντα αναφοράς.

Παράδειγμα με τοπικές μεταβλητές (στατικές και δυναμικής δέσμευσης μνήμης) στη C++

static2.cpp

9.4.2 Ένθετα υποπρογράμματα

Η πρώτη γλώσσα που υποστήριξε την ένθεση υποπρογραμμάτων, δηλαδή τον ορισμό ενός υποπρογράμματος μέσα σε ένα άλλο υποπρόγραμμα ήταν η Algol 60. Άλλες γλώσσες προγραμματισμού που επίσης υποστηρίζουν την ένθεση υποπρογραμμάτων είναι οι Ada, Pascal, Python, JavaScript, Ruby και Lua.

Παράδειγμα ένθετης συνάρτησης στη python

def outer_fun():
    a = 1
    print(f"αρχή συνάρτησης outer_fun, a={a}")
    def inner_fun(x):
        print(f"κλήση ένθετης συνάρτησης inner_fun, x={x}, a={a}")

    inner_fun(2)
    print(f"τέλος συνάρτησης outer_fun, a={a}")

outer_fun()

θα εμφανίσει

αρχή συνάρτησης outer_fun, a=1
κλήση ένθετης συνάρτησης inner_fun, x=2, a=1
τέλος συνάρτησης outer_fun, a=1

9.5 Μέθοδοι μεταβίβασης παραμέτρων

Μέθοδος μεταβίβασης παραμέτρων είναι ο τρόπος με τον οποίο οι παράμετροι μεταδίδονται προς και από τα καλούμενα υποπρογράμματα.

9.5.1 Μοντέλα σημασιολογίας μεταβίβασης παραμέτρων

Υπάρχουν δύο εννοιολογικά μοντέλα για τον τρόπο μεταφοράς δεδομένων στις παραμέτρους

9.5.2 Μοντέλα υλοποίησης μεταβίβασης παραμέτρων

9.5.2.1 Μεταβίβαση κατά τιμή ή κατ’ αξία (pass by value)

Η τιμή της πραγματικής μεταβλητής χρησιμοποιείται για την αρχικοποίηση της αντίστοιχης τυπικής παραμέτρου (λειτουργία εισόδου).

9.5.2.2 Μεταβίβαση κατ’ αποτέλεσμα (pass by result)

Δεν μεταβιβάζεται καμία τιμή στο υποπρόγραμμα κατά την κλήση. Η τελική τιμή της τυπικής παραμέτρου μεταδίδεται στην αντίστοιχη τοπική μεταβλητή.

9.5.2.3 Μεταβίβαση κατά τιμή και αποτέλεσμα (pass by value-result) ή με αντιγραφή

Συνδυασμός της μεταβίβασης κατά τιμή και της μεταβίβασης κατά αποτέλεσμα. Η πραγματική παράμετρος αντιγράφεται στην τυπική όταν ξεκινά το υποπρόγραμμα και μετά αντιγράφεται ξανά πίσω όταν τερματίζεται το υποπρόγραμμα.

9.5.2.4 Μεταβίβαση κατά αναφορά (pass by reference)

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

9.5.2.5 Μεταβίβαση κατά όνομα (pass by name)

Είναι μέθοδος μεταβίβασης διπλής λειτουργίας. Λειτουργεί με αντικατάσταση στο κείμενο του υποπρογράμματος του ονόματος του ορίσματος (παρόμοια με τις μακροεντολές της C). Δεν χρησιμοποιείται πλέον σε κάποια διαδεδομένη γλώσσα.

Δείτε σχετικά στο https://stackoverflow.com/questions/838079/what-is-pass-by-name-and-how-does-it-work-exactly

9.5.3 Υλοποίηση μεθόδων μεταβίβασης παραμέτρων

Στις περισσότερες σύγχρονες γλώσσες, η επικοινωνία μεταξύ παραμέτρων γίνεται μέσω της στοίβας χρόνου εκτέλεσης.

9.5.4 Μέθοδοι μεταβίβασης παραμέτρων σε γνωστές γλώσσες

C

Στη C όλες οι παράμετροι μεταβιβάζονται κατά τιμή. Με τη χρήση δεικτών επιτυγχάνεται η μεταβίβαση κατά αναφορά, και επιπλέον με τη χρήση του const στις τυπικές παραμέτρους η μονόδρομη μεταβίβαση κατά αναφορά.

pass_by_value.c

C++

Στη C++ όλες οι παράμετροι μεταβιβάζονται κατά τιμή. H C++ περιέχει ένα ειδικό δείκτη, την αναφορά που χρησιμοποιείται στις τυπικές παραμέτρους έτσι ώστε να υποδηλώσει μεταβίβαση με αναφορά.

pass_by_value.cpp

Java

Στη Java όλες οι παράμετροι μεταβιβάζονται κατά τιμή. Ωστόσο, επιτυγχάνεται προσομοίωση μεταβίβασης με αναφορά με χρήση αντικειμένων.

Example1.java

Python

Στην Python όλες οι παράμετροι μεταβιβάζονται κατά εκχώρηση. Ωστόσο, επιτυγχάνεται προσομοίωση μεταβίβασης με αναφορά με χρήση αντικειμένων.

pass_by_assignment.py

9.5.5 Έλεγχος τύπων παραμέτρων

Στην C (από την έκδοση C99 και μετά), C++, Java και C# γίνεται έλεγχος τύπων παραμέτρων. Στην Python και στην Ruby δεν γίνεται έλεγχος τύπων παραμέτρων.

9.5.6 Διατάξεις πολλαπλών διαστάσεων ως παράμετροι

Οι διατάξεις δύο διαστάσεων στη C είναι διατάξεις διατάξεων και αποθηκεύονται κατά γραμμές στη μνήμη.

Αντιστοίχιση δισδιάστατου πίνακα M x N (M γραμμές και N στήλες) σε μονοδιάστατο πίνακα στη C.

διεύθυνση(mat[i][j]) -> διεύθυνση(mat[0][0])  +  (i * N + j) * μέγεθος_τύπου_δεδομένων_περιεχομένων_του_mat)

Όταν ένας πίνακας μεταβιβάζεται ως παράμετρος, η τυπική παράμετρος θα πρέπει να περιλαμβάνει το πλήθος των στηλών στο δεύτερο ζεύγος αγκυλών (λόγω της δυνατότητας ξεχωριστής μεταγλώττισης των υποπρογραμμάτων). Συνεπώς, δεν μπορεί να χρησιμοποιηθεί η ίδια συνάρτηση για δισδιάστατο πίνακα με διαφορετικό αριθμό στηλών.

matrix1.c

Μια λύση στο παραπάνω πρόβλημα είναι η μεταβίβαση του πίνακα ως δείκτη καθώς και των διαστάσεών του.

matrix2.c

ή με χρήση μακροεντολής για “καθαρότερο” κώδικα.

matrix2b.c

Java

Στη Java οι διατάξεις είναι αντικείμενα. Κάθε διάταξη διαθέτει μια σταθερά length που είναι ίση με το μήκος της διάταξης και η οποία αρχικοποιείται όταν δημιουργείται η διάταξη. Κάθε στοιχείο μιας διάταξης μπορεί να είναι μια άλλη διάταξη.

Example1.java

9.5.7 Θέματα σχεδιασμού

Στη C++ (αλλά και σε άλλες γλώσσες προγραμματισμού) οι διατάξεις μεταβιβάζονται στα υποπρογράμματα με αναφορά προκειμένου να αποφευχθεί η επιβάρυνση που θα προκαλούσε η μεταβίβαση κατά τιμή καθώς σε αυτή την περίπτωση θα χρειαζόταν να αντιγραφούν όλα τα δεδομένα της διάταξης, κάτι που θα απαιτούσε επιπλέον χρόνο αλλά και χώρο.

9.5.8 Παραδείγματα μεταβίβασης παραμέτρων

Η ακόλουθη συνάρτηση δεν πραγματοποιεί σωστή αντιμετάθεση των παραμέτρων που δέχεται. Αν και οι μεταβλητές a και b ανταλλάσσουν τιμές, οι τιμές των c και d δεν αλλάζουν διότι δεν επιστρέφεται κάτι από τη συνάρτηση.

void swap1(int a, int b) {
    int temp = a;
    a = b;
    b = temp;
}
...
swap1(c,d);

Σωστή συνάρτηση αντιμετάθεση (με χρήση δεικτών).

void swap2(int *a, int *b) {
    int temp = *a;
    *a = *b;
    *b = temp;
}
...
swap1(&c,&d);

Συνάρτηση αντιμετάθεσης στη C++ (με χρήση αναφορών)

void swap3(int& a, int& b) {
    int temp = a;
    a = b;
    b = temp;
}
...
swap1(c,d);

9.6 Παράμετροι που είναι υποπρογράμματα

Στη C και στη C++ οι συναρτήσεις δεν μπορούν να μεταβιβαστούν ως παράμετροι, μπορούν όμως οι δείκτες σε συναρτήσεις.

9.7 Έμμεση κλήση υποπρογραμμάτων

Υπάρχουν περιπτώσεις που τα υποπρογράμματα πρέπει να καλούνται έμμεσα και συνήθως αυτό συμβαίνει όταν το υποπρόγραμμα που πρέπει να κληθεί δεν είναι γνωστό παρά μόνο στην εκτέλεση.

9.8 Ζητήματα σχεδιασμού για συναρτήσεις

9.8.1 Συναρτησιακές παράπλευρες συνέπειες

Οι περισσότερες προστακτικές γλώσσες μπορούν να εφαρμόζουν μεταβίβαση κατά τιμή ή μεταβίβαση κατά αναφορά. Η μεταβίβαση κατά αναφορά μπορεί να προκαλεί παράπλευρες συνέπειες. Από την άλλη μεριά οι αμιγείς συναρτησιακές γλώσσες δεν παρουσιάζουν παράπλευρες συνέπειες.

9.8.2 Τύποι επιστρεφόμενων τιμών

Οι συναρτήσεις στη C μπορούν να επιστρέφουν τιμές οποιουδήποτε τύπου εκτός από διατάξεις και συναρτήσεις. Ωστόσο, τόσο οι διατάξεις αλλά και οι συναρτήσεις επιστρέφονται μέσω δεικτών.

9.8.3 Πλήθος επιστρεφόμενων τιμών

Στις περισσότερες γλώσσες προγραμματισμού οι συναρτήσεις μπορούν να επιστρέφουν μόνο μια τιμή. Ωστόσο, γλώσσες όπως η Ruby και η Python επιτρέπουν την επιστροφή πολλών τιμών.

def fun(x):
    return x, 2*x, 3*x

a,b,c = fun(10) # οι τιμές που λαμβάνουν οι μεταβλητές a, b, c είναι 10, 20 και 30 αντίστοιχα.

9.9. Υπερφορτωμένα υποπρογράμματα

Ένα υποπρόγραμμα είναι υπερφορτωμένο αν έχει το ίδιο όνομα με άλλα υποπρογράμματα στο ίδιο περιβάλλον αναφοράς.

overload_example.cpp

Στις C++, Java, C# ο τύπος επιστροφής των συναρτήσεων δεν μπορεί να χρησιμοποιηθεί για τη διαφοροποίηση μεταξύ υπερφορτωμένων συναρτήσεων.

Για παράδειγμα οι ακόλουθοι ορισμοί συναρτήσεων στη C++ θα οδηγήσουν σε αδυναμία μεταγλώττισης.

int fun(int x) {
  return 0;
}

float fun(int x) {
  return 0.0;
}

overload_error.cpp

Επιπλέον, υπερφορτωμένα υποπρογράμματα με προεπιλεγμένες παραμέτρους μπορεί να οδηγήσουν σε ασάφεια για το ποιο υποπρόγραμμα καλείται. Δείτε για παράδειγμα τον ακόλουθο κώδικα σε C++.

void fun(float b = 0.0)
{
    cout << "Calling function fun(float)" << endl;
}

void fun()
{
    cout << "Calling function fun()" << endl;
}

overload_problem.cpp

Η κλήση fun(); είναι ασαφής (ambiguous) και προκαλεί σφάλμα μεταγλώττισης.

Η C δεν υποστηρίζει υπερφόρτωση συναρτήσεων. Δείτε για παράδειγμα:

no_overload_support.c

9.10 Γενικά υποπρογράμματα

Ένα πολυμορφικό υποπρόγραμμα δέχεται παραμέτρους διαφορετικών τύπων σε διαφορετικές ενεργοποιήσεις. Ο παραμετρικός πολυμορφισμός αφορά υποπρογράμματα που δέχονται γενικές παραμέτρους και τα υποπρογράμματα αυτά συχνά αποκαλούνται γενικά ή γενερικά.

9.10.1 Γενικές συναρτήσεις στη C++

Οι γενικές συναρτήσεις στη C++ ονομάζονται πρότυπες (templates) συναρτήσεις.

Παράδειγμα σύγκρισης γενικής συνάρτησης με μακροεντολή.

template1.cpp

Μια γενική συνάρτηση ταξινόμησης.

generic_sort.cpp

9.10.2 Γενικές μέθοδοι στη Java 5.0

H Java ξεκίνησε να υποστηρίζει γενικούς τύπους και μεθόδους στην έκδοση 5.0. Η δε υλοποίηση στη Java διαφέρει από την υλοποίηση της C++. Σημαντικές διαφορές είναι οι ακόλουθες:

  1. Στη Java οι γενικές παράμετροι πρέπει να είναι κλάσεις και όχι βασικοί τύποι.
  2. Στη Java κατασκευάζεται ένα μόνο αντίγραφο του κώδικα (raw κώδικας) ακόμα και αν η γενική μέθοδος καλείται με διαφορετικούς τύπους δεδομένων (στη C++ δημιουργείται, κατά τη μεταγλώττιση, ξεχωριστό αντίγραφο του κώδικα για κάθε κλήση του υποπρογράμματος που χρησιμοποιεί διαφορετικούς τύπους).
  3. Στη Java μπορούν να οριστούν περιορισμοί (bounds) για το εύρος των κλάσεων που μπορούν να μεταβιβαστούν στη γενική μέθοδο ως γενικές παράμετροι.
  4. H Java υποστηρίζει τύπους μπαλαντέρ, π.χ. Collection<?> σημαίνει οποιοσδήποτε τύπος συλλογής με περιεχόμενα αντικείμενα οποιασδήποτε κλάσης (collection of unknown).

9.10.3 Γενικές μέθοδοι στη C# 2005

Οι γενικές μέθοδοι στη C# είναι παρόμοιες με τις γενικές μεθόδους στη Java, αλλά δεν υποστηρίζονται τύποι μπαλαντέρ.

9.10.4 Γενικές συναρτήσεις στη F#

H συναρτησιακή γλώσσα F# υποστηρίζει γενικές συναρτήσεις, αλλά είναι λιγότερο χρήσιμες από τις γενικές συναρτήσεις των C++, Java και C#.

9.11 Υπερφορτωμένοι τελεστές που ορίζει ο χρήστης

Υπερφόρτωση τελεστών υποστηρίζουν οι γλώσσες: Ada, C++, Python, Ruby.

Παράδειγμα υπερφόρτωσης τελεστών στην Python. Υλοποίηση πράξεων +, -, * για intervals. Δείτε την περιγραφή των πράξεων με διαστήματα στο https://en.wikipedia.org/wiki/Interval_arithmetic.

class Interval:
    def __init__(self, start, end):
        self.start = start
        self.end = end
    
    def __str__(self):
        return f'({self.start},{self.end})'

    def __add__(self, other):
        return Interval(self.start + other.start, self.end + other.end)

    def __sub__(self, other):
        return Interval(self.start - other.end, self.end - other.start)

    def __mul__(self, other):
        minimum = min([self.start * other.start, self.start * other.end, self.end * other.start, self.end*other.end])
        maximum = max([self.start * other.start, self.start * other.end, self.end * other.start, self.end*other.end])
        return Interval(minimum, maximum)

i1 = Interval(1,5)
i2 = Interval(4,6)
print(i1+i2)
print(i1-i2)
print(i2-i1)
print(i1*i2)

θα εμφανίσει

(5,11)
(-5,1)
(-1,5)
(4,30)

9.12 Κλειστότητες

Κλειστότητα (closure) είναι ένα υποπρόγραμμα και το περιβάλλον αναφοράς στο οποίο ορίζεται. Οι κλειστότητες είναι χρήσιμες σε γλώσσες που επιτρέπουν ένθετα υποπρογράμματα, έχουν στατική εμβέλεια και επιτρέπουν την επιστροφή υποπρογραμμάτων από συναρτήσεις και την εκχώρησή τους σε μεταβλητές.

Οι κλειστότητες υποστηρίζονται από τις γλώσσες συναρτησιακού προγραμματισμού, από τις περισσότερες γλώσσες σεναρίων και από την C# (προστακτική γλώσσα).

Παράδειγμα κλειστότητας σε JavaScript

function makeAdder(x) {
    return function(y) {return x + y;}
}

var add10 = makeAdder(10);
var add5 = makeAdder(5);

console.log("Add 10 to 20: " + add10(20));
console.log("Add 5 to 20: " + add5(20));

sebesta_9_12.js

Σε αυτό το παράδειγμα η κλειστότητα είναι η ανώνυμη συνάρτηση που ορίζεται μέσα στη συνάρτηση makeAdder. Η μεταβλητή x που αναφέρεται μέσα στη συνάρτηση κλειστότητας προσδένεται με την παράμετρο που στέλνεται κάθε φορά στη makeAdder. Στο παράδειγμα, η συνάρτηση makeAdder καλείται δύο φορές, μια με παράμετρο 10 και μια με παράμετρο 5. Κάθε κλήση επιστρέφει διαφορετική εκδοχή της κλειστότητας.

θα εμφανίσει

Add 10 to 20: 30
Add 5 to 20: 25

Παραδείγματα κλειστοτήτων με την Python

Μια συρρουτίνα (coroutine) είναι ένα ειδικό υποπρόγραμμα που έχει πολλαπλές εισόδους.

Δείτε και το Coroutines As Threads