Η βασική λειτουργία σε όλες αυτές τις συναρτήσεις είναι η εκτύπωση γραµµών.

Σχετικά έγγραφα
Επανάληψη. Εντολές while, for, do-while

Συναρτήσεις. Κατασκευαστικά Τεµάχια για τη ηµιουργία Αρθρωτών Προγραµµάτων

Η τιµή εξόδου κάποιας συνάρτησης µπορεί να είναι δείκτης, π.χ.

ΣΥΝΑΡΤΗΣΕΙΣ. Διαφάνειες από τους Robert Sedgewick και Kevin Wayne

Να εκτυπωθούν οι πρώτες Ν σειρές του τριγώνου, χρησιµοποιώντας ένα πίνακα µεγέθους Ν στοιχείων (η Ν-οστή σειρά περιέχει Ν στοιχεία).

Εισαγωγή στον Προγραμματισμό

Δείκτες (Pointers) Ένας δείκτης είναι μια μεταβλητή με τιμή μια διεύθυνση μνήμης. 9.8

Διάλεξη 3η: Τύποι Μεταβλητών, Τελεστές, Είσοδος/Έξοδος

Δοκιμή και Αποσφαλμάτωση Testing and Debugging

Κεφάλαιο 8.7. Πίνακες & Συναρτήσεις ( ιάλεξη 17) ιδάσκων: ηµήτρης Ζεϊναλιπούρ

Προγραμματισμός Η/Υ (ΤΛ2007 )

Α. unsigned int Β. double. Γ. int. unsigned char x = 1; x = x + x ; x = x * x ; x = x ^ x ; printf("%u\n", x); Β. unsigned char

Η βασική συνάρτηση προγράμματος main()

Πίνακες. Οι πίνακες αποτελούν ένα σηµαντικό δοµηµένο τύπο δεδοµένων (structured data type) ή πιο απλά µία δοµή δεδοµένων (data structure).

Ηβασικήσυνάρτηση προγράμματος main()

Κεφάλαιο , 3.2: Συναρτήσεις II. ( ιάλεξη 12) ιδάσκων: ηµήτρης Ζεϊναλιπούρ

Κεφάλαιο , 3.2: Συναρτήσεις II. (Διάλεξη 12)

Προγραμματισμός Ι (ΗΥ120)

ΤΕΜ-101 Εισαγωγή στους Η/Υ Εξεταστική Ιανουαρίου 2011 Θέματα Β

Μεθόδων Επίλυσης Προβλημάτων

Προγραµµατισµός. Αναδροµή (1/2)

ΣΥΝΑΡΤΗΣΕΙΣ (Functions)

Μεθόδων Επίλυσης Προβλημάτων

Δομημένος Προγραμματισμός (ΤΛ1006)

Περαιτέρω για Συναρτήσεις

Κεφάλαιο 3.1, : Συναρτήσεις I. (Διάλεξη 11)

Κεφάλαιο 3.1, : Συναρτήσεις I. ( ιάλεξη 11) ιδάσκων: ηµήτρης Ζεϊναλιπούρ

Συναρτήσεις. Εισαγωγή

Κεφάλαιο : Επαναλήψεις (for, do-while)

Μεθόδων Επίλυσης Προβλημάτων

ΣΧΕΔΙΑΣΗ ΚΑΙ ΥΛΟΠΟΙΗΣΗ ΛΟΓΙΣΜΙΚΟΥ

Η Γλώσσα C Μία Σφαιρική Ανασκόπηση

Προγραμματισμός Η/Υ. Ενότητα 3: Top Down Σχεδιασμός

Διάλεξη 15η: Αναδρομή, μέρος 1ο

scanf() scanf() stdin scanf() printf() int float double %lf float

Η γλώσσα προγραμματισμού C

Σε γενικές γραμμές, είναι καλή πρακτική να γράϕουμε προγράμματα C που αποτελούνται από πολλές και μικρές συναρτήσεις, παρά από λίγες και μεγάλες.

Αναδροµή (Recursion) ύο παρεξηγήσεις. Σκέψου Αναδροµικά. Τρίγωνο Sierpinski Μη αναδροµικός ορισµός;

Εισαγωγή στον Προγραµµατισµό. Διάλεξη 3 η : Επίλυση Προβληµάτων Χειµερινό Εξάµηνο 2011

Α Β Γ static; printf("%c\n", putchar( A +1)+2); B DB BD. int i = 0; while (++i); printf("*");

Προγραμματισμός Η/Υ. Ενότητα 7: Συναρτήσεις

ΕΡΓΑΣΤΗΡΙΟ 6: Συναρτήσεις και Αναδρομή

Κεφάλαιο : Επαναλήψεις (oι βρόγχοιfor, do-while) (Διάλεξη 10) Εντολές Επανάληψης που θα καλυφθούν σήμερα

ΕΝΤΟΛΕΣ ΕΠΑΝΑΛΗΨΗΣ. for (παράσταση_1; παράσταση_2; παράσταση_3) εντολή επόμενη εντολή

Επανάληψη για τις Τελικές εξετάσεις. (Διάλεξη 24) ΕΠΛ 032: ΠΡΟΓΡΑΜΜΑΤΙΣΜΟΣ ΜΕΘΟΔΩΝ ΕΠΙΛΥΣΗΣ ΠΡΟΒΛΗΜΑΤΩΝ

Κεφάλαιο Πίνακες Ι. ( ιάλεξη 15) ιδάσκων: ηµήτρης Ζεϊναλιπούρ

Βαθμός Σχόλια. lab5 PASS PASS PASS PASS PASS. Οριακά PASS - Καλή δουλειά

Προγραμματισμός Η/Υ (ΤΛ2007 )

Αναδρομή (Recursion) Πώς να λύσουμε ένα πρόβλημα κάνοντας λίγη δουλειά και ανάγοντας το υπόλοιπο να λυθεί με τον ίδιο τρόπο.

Αναδρομή Ανάλυση Αλγορίθμων

ΕΠΛ 131 ΑΡΧΕΣ ΠΡΟΓΡΑΜΜΑΤΙΣΜΟΥ Ι

Η γλώσσα προγραμματισμού C

Ανάπτυξη Μεγάλων Εφαρµογών στη Γλώσσα C (Programming in the large)

Ανάπτυξη και Σχεδίαση Λογισμικού

ΕΡΓΑΣΤΗΡΙΑΚΕΣ ΑΣΚΗΣΕΙΣ C ΣΕΙΡΑ 2 η

Βαθμός Σχόλια. lab PASS 1194 PASS 1238 PASS 1239 PASS

Ανάπτυξη και Σχεδίαση Λογισμικού

ΔΟΜΗΜΕΝΟΣ ΠΡΟΓΡΑΜΜΑΤΙΣΜΟΣ

ΠΡΟΓΡΑΜΜΑΤΙΣΜΟΣ Η/Υ Ακαδημαϊκό έτος ΤΕΤΡΑΔΙΟ ΕΡΓΑΣΤΗΡΙΟΥ #4

Στόχοι και αντικείμενο ενότητας. Πέρασμα Πίνακα σε Συνάρτηση (συν.) Πέρασμα Πίνακα σε Συνάρτηση. #8.. Ειδικά Θέματα Αλγορίθμων

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

Δομημένος Προγραμματισμός. Τμήμα Επιχειρηματικού Σχεδιασμού και Πληροφοριακών Συστημάτων

Επανάληψη για τις Τελικές εξετάσεις

Κεφάλαιο 2.6: Είσοδος / Έξοδος εδοµένων, Μορφοποίηση εδοµένων Εξόδου. ( ιάλεξη 7) ιδάσκων: ηµήτρης Ζεϊναλιπούρ

ΠΛΗΡΟΦΟΡΙΚΗ Ι JAVA Τμήμα θεωρίας με Α.Μ. σε 3, 7, 8 & 9 6/12/07

Διαδικασιακός Προγραμματισμός

Κεφάλαιο 6: Συναρτήσεις IΙΙ Αρθρωτός Προγραμματισμός. Δείκτες (Διάλεξη 14)

Εισαγωγή στον Προγραµµατισµό. Διάλεξη 8 η : Συναρτήσεις Χειµερινό Εξάµηνο 2011

12. Συναρτήσεις (Μέρος ΙI)

Εισαγωγή στον Προγραμματισμό

Εισαγωγή στον Προγραμματισμό

Κεφάλαιο Πίνακες Ι. (Διάλεξη 16)

Εισαγωγή στην C. Μορφή Προγράµµατος σε γλώσσα C

Εισαγωγή στον Προγραµµατισµό. Διάλεξη 2 η : Βασικές Έννοιες της γλώσσας προγραµµατισµού C Χειµερινό Εξάµηνο 2011

Προγραμματισμό για ΗΜΥ

Προγραμματισμός Ι. Προχωρημένα Θέματα. Δημήτρης Μιχαήλ. Τμήμα Πληροφορικής και Τηλεματικής Χαροκόπειο Πανεπιστήμιο

Sheet2 Α.Μ FAIL. οριακό PASS. - Έπρεπε να χρησιµοποιήσετε συναρτήσεις από τη string.h

Εισαγωγή στον δομημένο προγραμματισμό

ΕΡΓΑΣΤΗΡΙΟ 1 - ΣΗΜΕΙΩΣΕΙΣ

Βαθμός Σχόλια. lab6 PASS PASS. - Πολύ καλή δουλειά, αλλά προσπάθησε να κάνεις την άσκηση χρησιµοποιώντας συναρτήσεις από το string.

Γλώσσα Προγραμματισμού C. Προγραμματισμός HY: Γλώσσα Προγραμματισμού C. Γρήγορος Πίνακας Αναφοράς Σύνταξης. Εισήγηση #4. Επαναληπτικές δομές:

Προγραμματισμό για ΗΜΥ

Δομημένος Προγραμματισμός. Τμήμα Επιχειρηματικού Σχεδιασμού και Πληροφοριακών Συστημάτων

Προγραμματισμός Η/Υ (ΤΛ2007 )

ΔΟΜΗΜΕΝΟΣ ΠΡΟΓΡΑΜΜΑΤΙΣΜΟΣ

Δομημένος Προγραμματισμός

Προγραμματισμός Ι (ΗΥ120)

Δομημένος Προγραμματισμός (ΤΛ1006)

Μεθόδων Επίλυσης Προβλημάτων

Προγραμματισμός Ι. Εγγραφές. Δημήτρης Μιχαήλ. Τμήμα Πληροφορικής και Τηλεματικής Χαροκόπειο Πανεπιστήμιο

Εισαγωγή στον Προγραµµατισµό. Πανεπιστήµιο Θεσσαλίας Τµήµα Ηλεκτρολόγων Μηχανικών και Μηχανικών Η/Υ

Κεφάλαιο 6: Συναρτήσεις IΙΙ Αρθρωτός Προγραμματισμός. (Διάλεξη 14) Παράδειγμα: Αλλαγή τιμής μεταβλητής μόνο τοπικά

Στην ενότητα αυτή θα µελετηθούν τα εξής επιµέρους θέµατα: ΕΠΛ 131 Αρχές Προγραµµατισµού I 4-2

Προγραμματισμός Ι. Είσοδος/Έξοδος. Δημήτρης Μιχαήλ. Ακ. Έτος Τμήμα Πληροφορικής και Τηλεματικής Χαροκόπειο Πανεπιστήμιο

ΥΠΟΛΟΓΙΣΤΕΣ ΙΙ. Τι είναι ; Συναρτήσεις. Παράδειγμα #1. double convert ( double cm ) { double inch;

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

lab13grades Άσκηση 2 -Σωστά απελευθερώνετε ολόκληρη τη λίστα και την κεφαλή

Δείκτες (Pointers) Ένας δείκτης είναι μια μεταβλητή με τιμή μια διεύθυνση μνήμης. 9.8

Προγραμματισμός Η/Υ (ΤΛ2007 )

Εισαγωγή στον Προγραμματισμό

Transcript:

draw_tric(4, $ ); $$$$ $$$ $$ $ draw_trid(5, a ); aaaaa aaaa aaa aa a Άρθρωση Η βασική λειτουργία σε όλες αυτές τις συναρτήσεις είναι η εκτύπωση γραµµών. /* Συνάρτηση για την εκτύπωση γραµµής δεδοµένου µήκους, και αποτελούµενης από δεδοµένο χαρακτήρα. */ void draw_line (int len, char fill) { int col = len; while (col > 0) {putchar(fill); col = col 1; draw_line(10, % ); %%%%%%%%%% draw_line(15, k ); kkkkkkkkkkkkkkk 35

/* Επαναδιατύπωση των συναρτήσεων draw_tria, draw_trib, draw_tric, και draw_trid. */ void draw_tria (int size, char fill) { int row = 1; while (row <= size) { putchar( \n ); draw_line(row, fill); draw_line(size row, ); row = row + 1; Οι εσωτερικές επαναλήψεις έχουν αντικατασταθεί από τις σχετικές κλήσεις της βασικής συνάρτησης draw_line. Έτσι δεν εµφανίζεται ρητά η διπλή επανάληψη. void draw_triβ (int size, char fill) { int row = 1; while (row <= size) { putchar( \n ); draw_line(size row, ); draw_line(row, fill); row = row + 1; void draw_tric (int size, char fill) { int row = size; while (row > 0) { putchar( \n ); draw_line(row, fill); draw_line(size row, ); row = row 1; 36

void draw_trid (int size, char fill) { int row = size; while (row > 0) { putchar( \n ); draw_line(size row, ); draw_line(row, fill); row = row 1; Τώρα µπορούµε να σχεδιάσουµε τετράγωνα και τρίγωνα διαφόρων µεγεθών και γεµισµάτων. Επίσης µπορούµε να συνθέσουµε περίπλοκα σχήµατα βάσει κάθετων τοποθετήσεων απλούστερων σχηµάτων. Όµως δεν µπορούµε να συνθέσουµε σχήµατα βάσει οριζόντιων τοποθετήσεων απλούστερων σχηµάτων, π.χ. rombus, kite, butterfly, ή windmill. Για οριζόντιες τοποθετήσεις, δεν πρέπει τα σχήµατα να δηµιουργούνται εξ ολοκλήρου, αλλά κάθε σειρά τους να µπορεί να δηµιουργείται ξεχωριστά. ηλαδή χρειάζεται περαιτέρω διάσπαση του προβλήµατος και διαχωρισµός του κεντρικού υποπροβλήµατος που είναι η δηµιουργία δεδοµένης σειράς του σχήµατος. /* Η συνάρτηση draw_line_tria εκτυπώνει δεδοµένη σειρά δεδοµένου τριγώνου τύπου Α. Η παράµετρος size δίνει το µέγεθος του τριγώνου, η παράµετρος row τον αριθµό της σειράς (οι σειρές απαριθµούνται από το 1 µέχρι το size), και η παράµετρος fill δίνει το χαρακτήρα που απαρτίζει το σχήµα */ draw_line_tria (int size, int row, char fill) { draw_line(row, fill); draw_line(size row, ); 37

Προσοχή: Ο ειδικός χαρακτήρας για καινούρια γραµµή (\n) δεν αποτελεί µέρος της σειράς. void draw_tria (int size, char fill) { int row = 1; while (row <= size) {putchar( \n ); draw_line_tria(size, row, fill); row = row + 1; Βασικές συναρτήσεις για υπόλοιπους τύπους τριγώνων. Για τρίγωνα τύπου Β void draw_line_trib (int size, int row, char fill) {draw_line(size row, ); draw_line(row, fill); Για τρίγωνα τύπου C void draw_line_tric (int size, int row, char fill) {draw_line(size row + 1, fill); draw_line(row 1, ); Για τρίγωνα τύπου D void draw_line_trid (int size, int row, char fill) {draw_line(row 1, ); draw_line(size row + 1, fill); 38

Σύνθεση Ανεµόµυλων Σειρά 1 Α Σειρά 1 C + +++++ ++ ++++ +++ +++ ++++ ++ Σειρά 5 Α Σειρά 5 C ++++++ Σειρά 1 B Σειρά 1 D ++++++ ++ ++++ +++ +++ ++++ ++ +++++ + Σειρά 5 Β Σειρά 5 D ανεµόµυλος µεγέθους 5 αποτελούµενος από το χαρακτήρα +. void draw_windmill (int size, char fill) { int row; row = 1; while (row <= size) { putchar( \n ); draw_line_tria(size, row, fill); draw_line_tric(size, row, fill); row = row + 1; row = 1; while (row <= size) { putchar( \n ); draw_line_trib(size, row, fill); draw_line_trid(size, row, fill); row = row + 1; 39

Περαιτέρω Άρθρωση /* Συνάρτηση που δηµιουργεί το πάνω µισό του ανεµόµυλου */ void draw_tophalf_wm (int size, char fill) {int row = 1; while (row <= size) { putchar( \n ); draw_line_tria(size, row, fill); draw_line_tric(size, row, fill); row = row + 1; /* Συνάρτηση που δηµιουργεί το κάτω µισό του ανεµόµυλου */ void draw_bottomhalf_wm (int size, char fill) {int row = 1; while (row <= size) { putchar( \n ); draw_line_trib(size, row, fill); draw_line_trid(size, row, fill); row = row + 1; /* Συνάρτηση για ολόκληρο τον ανεµόµυλο. */ void draw_windmill (int size, char fill) { draw_tophalf_wm(size,fill); draw_bottomhalf_wm(size, fill); 40

ιάσπαση Προβλήµατος draw_windmill draw_tophalf_wm draw_bottomhalf_wm draw_line_tria draw_line_tric draw_line_trib draw_line_trid draw_line 41

Πλευρικά Φαινόµενα ή Παρενέργειες (Side-effects) Οποιαδήποτε ενέργεια εκ µέρους µίας συνάρτησης η οποία δεν συµβάλλει στη δηµιουργία των αποτελεσµάτων που η συνάρτηση άµεσα (διαµέσου της εντολής return) ή έµµεσα (διαµέσου παραµέτρων-διευθύνσεων) επιστρέφει, νοουµένου ότι οι συνέπειες αυτής της ενέργειας παραµένουν και µετά την ολοκλήρωση της κλήσης της συνάρτησης. Κεντρικό παράδειγµα πλευρικού φαινοµένου είναι η ανάθεση τιµών σε καθολικές µεταβλητές. Καθολικές µεταβλητές αποτελούν εξωγενείς παράγοντες ως προς τις συναρτήσεις, και η χρήση τους, γενικά και όχι µόνο στο πλαίσιο πλευρικών φαινοµένων, πρέπει να είναι περιορισµένη και άκρως δικαιολογηµένη. Ενέργειες που δεν συµβάλλουν στη δηµιουργία του αποτελέσµατος, αλλά οι συνέπειές τους δεν παραµένουν µετά την ολοκλήρωση της συνάρτησης, δεν αποτελούν πλευρικά φαινόµενα. Απλά αποτελούν αχρείαστη διεργασία. Ένας τέτοιος πλεονασµός, µπορεί µεν να µην οδηγεί σε λάθος, αλλά οπωσδήποτε µειώνει την ποιότητα του κώδικα. 42

Ολόκληρη η διεργασία µίας συνάρτησης, η οποία δεν επιστρέφει άµεσα ή έµµεσα αποτελέσµατα, αποτελεί πλευρικό φαινόµενο. Σε συναρτήσεις των οποίων η λειτουργία είναι στην ουσία τα πλευρικά φαινόµενα που εκδηλώνουν, π.χ. scanf, printf, κτλ, τυχόν τιµή εξόδου, συνήθως υποδηλώνει επιτυχία ή αποτυχία στην εκτέλεση της λειτουργίας. Όπου πλευρικά φαινόµενα δεν αποτελούν την ουσία της λειτουργίας της συνάρτησης, η ύπαρξή τους πρέπει να είναι άκρως δικαιολογηµένη, διότι τέτοια φαινόµενα µειώνουν την αυτοδυναµία της συνάρτησης. Γενικά η χρήση καθολικών µεταβλητών µειώνει την αυτοδυναµία της συνάρτησης. Παραδείγµατα χρήσιµων πλευρικών φαινοµένων: - Επισήµανση επιτυχίας ή αποτυχίας. - Ενηµέρωση κάποιας κατάστασης η οποία συµβάλλει στον υπολογισµό του αποτελέσµατος για την επόµενη κλήση της συνάρτησης. Εποµένως πλευρικά φαινόµενα αποτελούν το έµµεσο ίχνος της συνάρτησης. Το άµεσο ίχνος είναι το αποτέλεσµα που επιστρέφει. 43

Παράδειγµα: Υπολογισµός δεδοµένου πλήθους όρων της ακολουθίας των πρώτων αριθµών. 2, 3, 5, 7, 11, 17, 19,..... Βασικό υποπρόβληµα: Είναι δεδοµένος φυσικός αριθµός, µεγαλύτερος του 2, πρώτος αριθµός; Αλγόριθµος Είσοδος: ο φυσικός αριθµός n Έξοδος: ναι ή όχι Εάν ο n δεν διαιρείται ακριβώς από κανένα από τους φυσικούς αριθµούς, από το 2 µέχρι το µεγαλύτερο αριθµό που δεν υπερβαίνει την τετραγωνική ρίζα του n, τότε ο n είναι πρώτος αριθµός. ιαφορετικά δεν είναι. Εκλέπτυνση του αλγόριθµου prime το υπόλοιπο της διαίρεσης του n µε το 2 δεν είναι 0 x 3 Ενόσω (prime και το x δεν υπερβαίνει την τετραγωνική ρίζα του n) prime το υπόλοιπο της διαίρεσης του n µε το x δεν είναι 0 x x + 1 Επέστρεψε prime 44

Περαιτέρω εκλέπτυνση prime (n % 2) 0 x 3 Ενόσω (prime και (x * x) <= n) prime (n % x) 0 x x + 1 Επέστρεψε prime Κώδικας: ορισµός συνάρτησης is_prime int is_prime (int n) { int prime = (n % 2)!= 0, x = 3; while (prime && (x * x) <= n) { prime = (n % x)!= 0; x = x + 1; return prime; Κυρίως υποπρόβληµα: Υπολογισµός του πρώτου αριθµού που ακολουθεί τον πρώτο αριθµό, ο οποίος αποτελεί τον τρέχοντα όρο της ακολουθίας. Ο νέος όρος γίνεται ο καινούριος τρέχοντας όρος. Π.χ. εάν ο τρέχοντας όρος είναι το 5, ο όρος που τον ακολουθεί είναι το 7, και αυτός αντικαθιστά το 5 ως ο νέος τρέχοντας όρος. 45

Άλγόριθµος Έστω current_prime ο τρέχοντας όρος της ακολουθίας. Ο νέος τρέχοντας όρος είναι ο µικρότερος φυσικός αριθµός, ο οποίος είναι µεγαλύτερος από τον current_prime και είναι πρώτος αριθµός. Εκλέπτυνση του αλγόριθµου current_prime current_prime + 1 Ενόσω (current_prime δεν είναι πρώτος αριθµός) current_prime current_prime + 1 Περαιτέρω εκλέπτυνση current_prime current_prime + 1 /* καθολική µεταβλητή */ Ενόσω (!is_prime(current_prime)) current_prime current_prime + 1 46

Κώδικας: συνάρτηση get_prime η οποία επιστρέφει τον τρέχοντα όρο και παράλληλα υπολογίζει τον επόµενο όρο (ως πλευρικό φαινόµενο). int get_prime ( ) { int res = current_prime; current_prime = current_prime + 1; while (!is_prime(current_prime)) current_prime = current_prime + 1; return res; Ολόκληρο το Πρόγραµµα #include <stdio.h> int current_prime = 2; /* ο πρώτος, πρώτος αριθµός, είναι το 2 */ int get_prime ( ); void main ( ) { int count = 1, total; printf( Ποιό είναι το πλήθος των αριθµών; ); scanf( %d, &total); while (count <= total) {printf( \n%d, get_prime( )); count = count + 1; int is_prime (int n) {.... int get_prime ( ) {.... 47

Βελτίωση: Η µεταβλητή current_prime δεν χρειάζεται να είναι καθολική, αλλά τοπική της get_prime. int get_prime ( ) {static int current_prime = 2; int res = current_prime; current_prime = current_prime + 1; while (!is_prime(current_prime)) current_prime = current_prime + 1; return res; Η ερµηνεία του προσδιοριστή static: Η µεταβλητή current_prime θα δηµιουργηθεί µία φορά, όταν η συνάρτηση get_prime κληθεί για πρώτη φορά. Η αρχικοποίησή της στη τιµή 2 επίσης θα γίνει µόνο µία φορά, µε την δηµιουργία της. Αυτή η ύπαρξη της current_prime θα διαρκέσει µέχρι τον τερµατισµό του προγράµµατος (δηλαδή µέχρι την ολοκλήρωση της συνάρτησης main) και εποµένως κάθε επόµενη κλήση της συνάρτησης get_prime αναφέρεται στην ίδια µεταβλητή current_prime. 48

Εάν δεν υπήρχε ο προσδιορισµός static στη δήλωση της current_prime, κάθε κλήση της συνάρτησης get_value θα είχε ως αποτέλεσµα την εκ νέου δηµιουργία και αρχικοποίηση (στη τιµή 2) της µεταβλητής current_prime, η διάρκεια ύπαρξης της οποίας θα ήταν µόνο η διάρκεια εκτέλεσης της εν λόγω κλήσης της συνάρτησης get_prime. #include <stdio.h> int get_prime ( ); Ολόκληρο το Πρόγραµµα void main ( ) { int count = 1, total; printf( Ποιό είναι το πλήθος των αριθµών; ); scanf( %d, &total); while (count <= total) {printf( \n%d, get_prime( )); count = count + 1; int is_prime (int n) { int prime = (n % 2)!= 0, x = 3; while (prime && (x * x) <= n) { prime = (n % x)!= 0; x = x + 1; return prime; int get_prime ( ) {static int current_prime = 2; int res = current_prime; current_prime = current_prime + 1; while (!is_prime(current_prime)) current_prime = current_prime + 1; return res; primes.c 49

$ cc o primes primes.c $ primes Ποιό είναι το πλήθος των αριθµών; 5 2 3 5 7 11 Αναδροµικότητα Η συνάρτηση ορίζεται διαµέσου του εαυτού της, µε άλλα λόγια καλεί τον εαυτό της. Παράδειγµα: Συνάρτηση για τον υπολογισµό του παραγοντικού (factorial) ενός φυσικού αριθµού. 0! = 1 1! = 1 2! = 1 2 3! = 1 2 3 = 2! 3....... n! = 1 2.... (n 1) n = (n 1)! n Ορισµός Βάσει Επανάληψης int factorial (int x) /* υποθέτει ότι x 0 */ {int res = 1; while (x > 1) {res = res * x; x = x 1; return res; 50

Αναδροµικός Ορισµός int factorial (int x) {if (x == 0 x == 1) return 1; else return x * factorial(x 1); factorial(4) 4 * factorial(3) 4 * 3 * factorial(2) 4 * 3 * 2 * factorial(1) 4 * 3 * 2 * 1 24 ιαχωρισµός ανάµεσα σε τερµατικές (βασικές) και αναδροµικές (γενικές) περιπτώσεις, µε χρήση εντολής επιλογής. Η αναδροµική κλήση πρέπει να είναι ένα βήµα πιο κοντά σε τερµατική περίπτωση, από την κλήση που οδήγησε σε αυτό. Ισοδυναµία µε επανάληψη. Παράδειγµα: Υπολογισµός όρων της ακολουθίας Fibonacci. 0, 1, 1, 2, 3, 5, 8, 13,..... Η απαρίθµηση των όρων αρχίζει από το 0: Fib(0) = 0 Fib(1) = 1 Fib(2) = 0 + 1 = 1 Fib(3) = 1 + 1 = 2 Fib(4) = 1 + 2 = 3........ Fib(n) = Fib(n 2) + Fib(n 1) 51

Αναδροµικός Ορισµός int Fib (int x) { if (x == 0) return 0; else if (x == 1) return 1; else return Fib(x 2) + Fib(x 1); ιπλή αναδροµικότητα δύο αναδροµικές κλήσεις. Fib(5) 5 Fib(3) + Fib(4) Fib(1) + Fib(2) Fib(2) + Fib(3) 1 Fib(0) + Fib(1) Fib(0) + Fib(1) Fib(1) + Fib(2) 0 1 0 1 1 Fib(0) + Fib(1) Ο ορισµός δεν είναι αποδοτικός 0 1 52

Αποδοτικός Ορισµός µε Χρήση Επανάληψης int Fib (int x) { int a = 0, b = 1, temp; while (x!= 0) { temp = a; a = b; b = b + temp; x = x 1; return a; Fib(5) 5 x a b 5 0 1 4 1 1 3 1 2 2 2 3 1 3 5 0 5 8 επιστροφή της τιµής της a Οι µεταβλητές a και b αποτελούν ένα µετακινούµενο παραθυράκι στην ακολουθία Fibonacci. Αρχικοποιούνται στους πρώτους δύο όρους της ακολουθίας, και στη συνέχεια το περιεχόµενο της κάθε µίας διαδοχικά αντικαθιστάται από τον επόµενό του όρο. 53

Παράδειγµα: Μέγιστος κοινός διαιρέτης δύο ακεραίων. int gcd (int n, int m) { int r = m % n; if (r == 0) return n; else return gcd(n, r); Παράδειγµα: Συνάρτηση sqrt για τον υπολογισµό τετραγωνικών ριζών. Οι σηµαντικές µεταβλητές είναι: Ο αριθµός x του οποίου χρειάζεται να υπολογιστεί η τετραγωνική ρίζα, και ο οποίος αποτελεί την παράµετρο της sqrt. Η µεταβλητή, έστω y, που δίνει την τρέχουσα προσέγγιση στην τετραγωνική ρίζα. Αυτές οι δύο τιµές αποτελούν τις παραµέτρους µίας βοηθητικής συνάρτησης, έστω της συνάρτησης sqrt_aux, η οποία στην ουσία θα κάνει τον υπολογισµό. double sqrt_aux (double x, double y) { if (good_enough(x,y)) return y; else return sqrt_aux(x, next_approx(x,y)); Η sqrt_aux καλείται από την sqrt, και µε αυτό τον τρόπο αρχικοποιείται η παράµετρος y αρχική προσέγγιση: double sqrt (double x) {if (x >= 0) return sqrt_aux(x,x); else {printf( \nerror ); return 1; 54

Αναδροµικότητα σε Σχέση µε τη Συνάρτηση main Παράδειγµα: Επανδιατύπωση του προγράµµατος για τον υπολογισµό του µέσου όρου µίας απροσδιορίστου πλήθους σειράς θετικών πραγµατικών αριθµών. #include <stdio.h> void main (void) {static double sum = 0.0, value; static int numbers = 0; printf( \nenter number: ); scanf( %lf, &value); if (value > 0.0) {sum = sum + value; numbers = numbers + 1; main(); /* πήγαινε να διαβάσεις τον επόµενο αριθµό */ else if (numbers > 0) printf( \naverage is %f, sum/numbers); else printf( \nerror No data ); rec_aver.c Οι µεταβλητές sum, value, και numbers δηµιουργούνται µόνο µία φορά, κατά την αρχική, αυτόµατη, κλήση της συνάρτησης main. Όσον αφορά τις µεταβλητές sum και numbers, αυτό είναι αναγκαίο, διαφορετικά οι εν λόγω πληροφορίες θα χάνονται ανάµεσα στις αναδροµικές κλήσεις της main. Κάθε κλήση απλά ενηµερώνει αυτές τις πληροφορίες. Εποµένως οι µεταβλητές sum και numbers αποτελούν καθολική µνήµη για όλες τις κλήσεις της main. 55

Σε αντίθεση, ο προσδιορισµός της µεταβλητής value ως static δεν είναι αναγκαίος, αλλά γίνεται για λόγους αποδοτικότητας. Με αυτό τον τρόπο, η µεταβλητή δεν χρειάζεται να δηµιουργείται εκ νέου για κάθε νέο αριθµό (δηλαδή για τη κάθε νέα κλήση της main). Βάσει του προσδιορισµού static, η διάρκεια ζωής των µεταβλητών sum, numbers, και value, είναι η διάρκεια ζωής του προγράµµατος. $ cc o aver rec_aver.c $ aver Enter number: 45 Enter number: 56 Enter number: 13 Enter number: 0 Average is 38.000000 Παράδειγµα: Πρόγραµµα το οποίο διαβάζει µίαν απροσδιορίστου πλήθους σειρά ακεραίων αριθµών (το τέλος της σειράς υποδηλώνεται από το 0) και εκτυπώνει αυτούς τους αριθµούς σε αντίστροφη σειρά. #include <stdio.h> void main (void) {int value printf( \nenter number: ); scanf( %d, &value); if (value == 0); /* µην κάνεις τίποτα */ else {main(); /* πήγαινε να διαβάσεις τον επόµενο αριθµό */ printf( \n%d, value) reverse.c 56

Γιατί η µεταβλητή value δεν πρέπει να είναι static Σε αντίθεση µε το προηγούµενο πρόγραµµα, στο πρόγραµµα reverse.c, η µεταβλητή value δεν πρέπει να είναι static. Αυτό διότι είναι αναγκαίο η µεταβλητή να δηµιουργείται εκ νέου, σε κάθε κλήση της συνάρτησης main, προς αποθήκευση του νέου αριθµού. Εποµένως η σειρά των αριθµών αποθηκεύεται (ή καλύτερα στοιβάζεται) στην ακολουθία των διαφορετικών υπάρξεων (περιπτώσεων) της µεταβλητής value. Λογική κάθε κλήσης της main ιαβάζω τον (πρώτο ή επόµενο) αριθµό. Εάν ο αριθµός είναι το 0, δηλαδή το τέλος της ακολουθίας δεν χρειάζεται να κάνω τίποτα. ιαφορετικά ο αριθµός ανήκει στην σειρά, αλλά µπορεί να ακολουθείται από άλλους αριθµούς. Έτσι πρώτα χρειάζεται να διαβαστούν τυχόν υπόλοιποι αριθµοί, να αντιστραφούν, και στη συνέχεια να εκτυπωθεί αυτός ο αριθµός. Θα καλέσω τον εαυτό µου για να επεξεργαστεί τυχόν υπόλοιπους αριθµούς, και στη συνέχεια απλά θα εκτυπώσω αυτό τον αριθµό. 57

$ cc o rev reverse.c $ rev Enter number: 23 Enter number: 45 Enter number: 89 Enter number: 10 Enter number: 0 10 89 45 23 $ main(5) value 0 main(4) value 10 main(3) value 89 main(2) value 45 main(1) value 23 58

$ cc rev $ rev Enter number: 0 $ main(1) value 0 Σηµαντικά Πλεονεκτήµατα που Απορρέουν από τη Χρήση Συναρτήσεων Αφαιρετικότητα σε διαδικασίες (procedural abstraction) Παράλληλη ανάπτυξη Επαναχρησιµοποίηση Οι συναρτήσεις πρέπει να επιδεικνύουν όσο το δυνατό υψηλότερη αυτοδυναµία. 59

Αυτοδύναµες Συναρτήσεις Τύνουν να έχουν παραµέτρους. εν εξαρτούνται από εξωγενείς παράγοντες. Κάνουν δικαιολογηµένη χρήση πλευρικών φαινοµένων. υστυχώς η C δεν επιτρέπει τον ορισµό τοπικών συναρτήσεων (local functions), δηλαδή συναρτήσεων που ανήκουν αποκλειστικά σε άλλες συναρτήσεις (π.χ. οι συναρτήσεις draw_tophalf_wm και draw_bottomhalf_wm κανονικά θα έπρεπε να είναι τοπικές της draw_windmill, και η συνάρτηση sqrt_aux θα έπρεπε να ήταν τοπική της sqrt, διότι αυτές οι συναρτήσεις παρέχουν λειτουργικότητα, αποκλειστικά για τις ανάγκες των συναρτήσεων draw_windmill και sqrt αντιστοίχως). Όλες οι συναρτήσεις σε ένα C πρόγραµµα, στην ουσία είναι σε καθολικό επίπεδο. Πριν κληθεί κάποια συνάρτηση χρειάζεται να δηλωθεί η διεπαφή της (διαµέσου του πρωτότυπου της) ή να ορισθεί απευθείας. 60

61