HY100 : ΕΙΣΑΓΩΓΗ ΣΤΗΝ ΕΠΙΣΤΗΜΗ ΥΠΟΛΟΓΙΣΤΩΝ ΠΑΝΕΠΙΣΤΗΜΙΟ ΚΡΗΤΗΣ, ΣΧΟΛΗ ΘΕΤΙΚΩΝ ΕΠΙΣΤΗΜΩΝ, ΤΜΗΜΑ ΕΠΙΣΤΗΜΗΣ ΥΠΟΛΟΓΙΣΤΩΝ ΔΙΔΑΣΚΟΝΤΕΣ Αντώνιος Σαββίδης, Χρήστος Νικολάου
ΕΝΟΤΗΤΑ ΙI ΣΧΕΔΙΑΣΗ ΠΡΟΓΡΑΜΜΑΤΟΣ Διάλεξη 2η
Περιεχόμενα Δομημένος προγραμματισμός Εμβέλεια και χρήση μεταβλητών
Δομημένος προγραμματισμός (1/18) Η ανάλυση ενός σύνθετου προγραμματιστικού προβλήματος σε απλούστερα υποπροβλήματα, με τη σύνθεση στη συνέχεια των επιμέρους λύσεων –Προβλήματα = υποπρογράμματα –Σύνθεση = κλήση υποπρογραμμάτων Στην φάση της σχεδίασης του προγράμματος –προχωράμε από πάνω προς τα κάτω, με ιεραρχική ανάλυση όπως έχουμε ήδη παρουσιάσει top - down development Στην φάση υλοποίησης, δηλ. κώδικας –προχωράμε από κάτω προς τα πάνω, δηλ. από τα χαμηλότερα επίπεδα της ανάλυσης («φύλλα» στο «δεντράκι» σχεδίασης), τα οποία είναι ο πρώτα εμφανιζόμενος κώδικας bottom - up development
Δομημένος προγραμματισμός (2/18) Τα διάφορα υποπρογράμματα είναι μεταξύ τους ανεξάρτητα, γεγονός που σημαίνει ότι μπορούν να ελεγχθούν αυτόνομα για απουσία λαθών. –Έλεγχος σημαίνει ότι για κάθε ξεχωριστό υποπρόγραμμα μπορώ να φτιάξω ένα δοκιμαστικό πρόγραμμα «ελέγχου», με το οποίο να εντοπίζω τα πιθανά λάθη που έχω. –Μόνο όταν με διαδοχικούς κύκλους ελέγχου και επιδιόρθωσης με το δοκιμαστικό πρόγραμμα αποδειχθεί ότι το υποπρόγραμμα είναι σωστό το ενσωματώνω στο κυρίως πρόγραμμα Φτιάξε το υποπρόγραμμα πρόγραμμα ελέγχου Μεταγλώττιση λάθη? Διόρθωσε Εκτέλεση λάθη? Διόρθωσε Ενσωμάτωσε στο πρόγραμμα αρχή αρχή ΟΧΙ ΟΧΙ ΝΑΙ ΝΑΙ
Δομημένος προγραμματισμός (3/18) Ανεξάρτητη κατασκευή και έλεγχος υποπρογραμμάτων – παράδειγμα (1/2) FUNCTION(REAL) sum ARGUMENTS (ADDRESSEX(REAL) p, INTEGER n) BEGIN REAL s; FOR s = 0; n > 0; p = p+1, n = n-1 DO s = s + CONTENTOFEX(p); RESULT(s); END CONST_INTEGER N = 5; PROGRAM_BEGIN ARRAY(REAL, N) array; INPUT(array); OUTPUT(sum(array, N)); PROGRAM_END Κατασκευή υποπρογράμματος που δέχεται παράμετρο διεύθυνση REAL P και έναν ακέραιο Ν, ενώ επιστρέφει το άθροισμα όλων των REAL αριθμών από τη διεύθυνση P έως P+N-1 αυτό είναι το πρόγραμμα ελέγχου
Δομημένος προγραμματισμός (4/18) Ανεξάρτητη κατασκευή και έλεγχος υποπρογραμμάτων – παράδειγμα (2/2) PROCEDURE inputarray ARGUMENTS (ADDRESSEX(REAL) p, INTEGER n) BEGIN INTEGER i; FOR i = 0; i < n; i = i+1 DO BEGIN OUTPUTEX("Give p["); OUTPUTEX(i); OUTPUTEX("]:"); INPUTEX(p[i]); END CONST_INTEGER N = 5; PROGRAM_BEGIN ARRAY(REAL, N) array; inputarray(array, N); OUTPUT(array); PROGRAM_END αυτό είναι το πρόγραμμα ελέγχου Κατασκευή υποπρογράμματος που δέχεται παράμετρο διεύθυνση REAL P και έναν ακέραιο Ν, το οποίο διαβάζει από την είσοδο με τη σειρά τους REAL αριθμούς από τη διεύθυνση P έως P+N-1
Δομημένος προγραμματισμός (5/18) Είναι εφικτές αλλαγές στον κώδικα ενός υποπρογράμματος χωρίς να επηρεάζεται το υπόλοιπο πρόγραμμα από το οποίο χρησιμοποιείται το υποπρόγραμμα αυτό –Εφόσον δεν αλλάζουν οι παράμετροι και ο επιστρεφόμενος τύπος (αν είναι συνάρτηση) Δηλ. δεν αλλάζει η επικεφαλίδα του υποπρογράμματος –Εφόσον δεν αλλάζει η σημασιολογία του υποπρογράμματος Δηλ. «τι κάνει» το υποπρόγραμμα με τα ορίσματα, τι πλάγια αποτελέσματα μπορεί να έχει, καθώς και «τι επιστρέφει» (εάν είναι συνάρτηση) –Ωστόσο, εάν είναι αναγκαίες αλλαγές σε ένα υποπρόγραμμα πέραν των παραπάνω κανόνων πρέπει να εφαρμοστούν με προσοχή όλες οι απαραίτητες προσαρμογές στα υπόλοιπα τμήματα του προγράμματος που επηρεάζονται.
Δομημένος προγραμματισμός (6/18) Αλλαγές σε υποπρογράμματα – παραδείγματα (1/2) FUNCTION(REAL) sum ARGUMENTS (ADDRESSEX(REAL) p, INTEGER n) BEGIN REAL s = 0; INTEGER i = 0; WHILE i < n DO BEGIN s = s + p[i]; i = i + 1; END RESULT(s); END CONST_INTEGER N = 5; PROGRAM_BEGIN ARRAY(REAL, N) array; INPUT(array); OUTPUT(sum(array, N)); PROGRAM_END Αλλάξαμε τη sum ώστε να χρησιμοποιεί το συντακτικό χρήσης των διευθύνσεων ως πίνακες, δηλ. p[i], αλλάξαμε τη FOR σε WHILE, ενώ διατρέχουμε τα στοιχεία από p[0]…p[N-1] δεν αλλάζει καθόλου το πρόγραμμα ελέγχου …και βέβαια το βασικό πρόγραμμα τρέχει και πάλι σωστά
Δομημένος προγραμματισμός (7/18) Αλλαγές σε υποπρογράμματα – παραδείγματα (2/2) PROCEDURE sum ARGUMENTS (ADDRESSEX(REAL) p, INTEGER n, ADDRESSEX(REAL) result) BEGIN REAL s = 0; INTEGER i = 0; WHILE i < n DO BEGIN s = s + p[i]; i = i + 1; END CONTENTOFEX(result) = s; END CONST_INTEGER N = 5; PROGRAM_BEGIN ARRAY(REAL, N) array; REAL x; INPUT(array); sum(array, N, POSITIONOFEX(x)); OUTPUT(x); PROGRAM_END Αλλάξαμε τη sum ώστε να είναι PROCEDURE, με μία επιπλέον παράμετρο τη διεύθυνση REAL στην οποία θα βάλει το αποτέλεσμα. Αυτή η αλλαγή επηρεάζει την επικεφαλίδα αλλά και τον τρόπο χρήσης του sum. το πρόγραμμα ελέγχου έχει τις απαραίτητες αλλαγές …και μόνο με τις σωστές αλλαγές το βασικό πρόγραμμα τρέχει και πάλι σωστά
Δομημένος προγραμματισμός (8/18) Είναι εφικτές αλλαγές στον κώδικα του συνολικού προγράμματος χωρίς αυτές να επηρεάζουν τον κώδικα οποιουδήποτε υποπρογράμματος –Εφόσον το υποπρόγραμμα αυτό δεν χρησιμοποιεί καθολικές μεταβλητές στον κώδικα του Αυτό μπορείτε να το πετύχετε τις περισσότερες φορές –Εφόσον δεν αλλάζει κάποιος τύπος εγγραφής που χρησιμοποιείται μέσα στο υποπρόγραμμα αυτό Αυτό μπορεί να είναι αναπόφευκτο –Εφόσον δεν αλλάζει η επικεφαλίδα και η σημασιολογία κανενός από τα άλλα υποπρογράμματα που χρησιμοποιούνται από το υποπρόγραμμα αυτό Αυτό μπορεί να συμβεί λόγω αλλαγών σε άλλα υποπρογράμματα
Δομημένος προγραμματισμός (9/18) Αλλαγές στο πρόγραμμα και επήρεια στα υποπρογράμματα – παραδείγματα (1/3) RECORD Point RECBEGIN INTEGER x,y; RECEND; FUNCTION(REAL) distance ARGUMENTS (Point p1, Point p2) BEGIN RESULT(sqrt(sqr(p1.x-p2.x)+sqr(p1.y-p2.y))); END PROGRAM_BEGIN Point p1, p2; INPUT(p1.x); INPUT(p1.y); INPUT(p2.x); INPUT(p2.y); OUTPUT(distance(p1,p2)); PROGRAM_END RECORD Point2D RECBEGIN REAL X,Y; RECEND; Αλλάζοντας τον ορισμό του συγκεκριμένου RECORD πρέπει να αλλάξει και η distance. Εάν γίνουν οι αλλαγές είμαστε ok. πριν την αλλαγή μετά την αλλαγή
Δομημένος προγραμματισμός (10/18) Αλλαγές στο πρόγραμμα και επήρεια στα υποπρογράμματα – παραδείγματα (2/3) CONST_INTEGER N = 3; INTEGER i=0; ARRAY(STRING, N) names; PROCEDURE outputnames ARGUMENTS () BEGIN WHILE i<N DO BEGIN OUTPUT(names[i]); i = i+1; END PROGRAM_BEGIN INPUT(names); outputnames(); PROGRAM_END Το υποπρόγραμμα outputnames: (α) χρησιμοποιεί απευθείας τις καθολικές μεταβλητές i, names, και τη σταθερά N, (β) βασίζεται στην υπόθεση ότι το i τη στιγμή της κλήσης είναι πάντα 0. Εάν γίνει κάποια από τις παρακάτω αλλαγές θα πρέπει αναγκαστικά να αλλάζω κάθε φορά και την outputnames: –αλλαγή ονόματος της σταθεράς N –αλλαγή ονόματος οποιασδήποτε από τις μεταβλητές i ή names –μετακίνηση των δηλώσεων των μεταβλητών ή της σταθεράς μετά τον κώδικα της outputnames –αλλαγή της τιμής του i πριν τη κλήση σε τιμή διαφορετική του 0 από το βασικό πρόγραμμα –δύο διαδοχικές κλήσεις της outputnames (η δεύτερη δεν θα εκτυπώσει τίποτε)
Δομημένος προγραμματισμός (11/18) Αλλαγές στο πρόγραμμα και επήρεια στα υποπρογράμματα – παραδείγματα (3/3) CONST_INTEGER N = 3; ARRAY(STRING, N) names; PROCEDURE outputstringarray ARGUMENTS (ADDRESSEX(STRING) p, INTEGER n) BEGIN INTEGER i; FOR i = 0; i < n; i = i + 1 DO OUTPUT(p[i]); END PROGRAM_BEGIN INPUT(names); outputstringarray(names, N); PROGRAM_END Κάναμε το υποπρόγραμμα όσο το δυνατόν πιο ανεξάρτητο από οποιαδήποτε καθολική μεταβλητή, ή σταθερά, δηλ. βασισμένο μόνο στις παραμέτρους του και στις τοπικές του μεταβλητές, έχει γίνει δηλ. ένα black box
Δομημένος προγραμματισμός (12/18) Είναι πολύ συνηθισμένο σε ένα υποπρόγραμμα Α (κλητευτής, caller) να καλούμε ένα άλλο υποπρογράμματα Β (καλούμενος, called). Όταν συμβαίνει κάτι τέτοιο θεωρούμε ότι το Α είναι σε υψηλότερο επίπεδο ανάλυσης από το Β, κατά τη διαδικασία της ιεραρχικής σχεδίασης, ενώ ορίζουμε και την σχέση –Α Β, δηλ. Α καλεί το Β Π.χ., έστω το υποπρόγραμμα perimeter που δέχεται ως παράμετρο ένα οποιοδήποτε πολύπλευρο στο επίπεδο και επιστρέφει την περίμετρο του. –Αυτό το πρόγραμμα δεν θα πρέπει να χρησιμοποιεί τη συνάρτηση distance που κατασκευάσαμε προηγουμένως?
Δομημένος προγραμματισμός (13/18) Σχεδίαση της perimeter Άρα ξέρουμε ότι perimeter distance Επίσης ξέρουμε ότι program perimeter Πολλές φορές όταν σχεδιάζουμε το πρόγραμμα μας στο χαρτί αποφεύγουμε να γράφουμε με συντακτική ακρίβεια, αλλά επικεντρώνουμε το ενδιαφέρον μας στον ίδιο τον αλγόριθμο.
Δομημένος προγραμματισμός (14/18) Υλοποίηση της perimeter RECORD Point2D RECBEGIN REAL X,Y; RECEND; FUNCTION(REAL) distance ARGUMENTS (Point2D p1, Point2D p2); FUNCTION(REAL) perimeter ARGUMENTS (ADDRESSEX(Point2D) p, INTEGER n) BEGIN INTEGER i; REAL s = 0; FOR i=1; i < n; i = i+1 DO s = s + distance(p[i-1], p[i]); RESULT(s); END Πρόγραμμα FUNCTIONperimeter FUNCTIONdistance PROCEDUREinputpolygon PROCEDUREinputpoint Αυτή είναι η διαφαινόμενη ιεραρχική δομή του προγράμματος η οποία μας βοηθάει να προσδιορίσουμε τα υποπρογράμματα που πρέπει να υλοποιηθούν
Δομημένος προγραμματισμός (15/18) FUNCTION F1 FUNCTION F2 PROCEDURE P3 PROCEDURE P4 FUNCTION F5 FUNCTION F6 FUNCTION F7 Τι σημαίνει όταν αρχίσουμε να προσδιορίζουμε πως τα υποπρογράμματα καλούν άλλα υποπρογράμματα προκύπτει μία τέτοια δομή. Τι σημαίνει αυτό στον κώδικα ?
Δομημένος προγραμματισμός (16/18) FUNCTION(T1) F1 (...) BEGIN... F2();... P3();... P4();... END FUNCTION(T2) F2 (...) BEGIN... F5();... F6();... END PROCEDURE P3 (...) BEGIN... F7();... END PROCEDURE P4 (...) BEGIN... F7();... END Προφανώς έχουμε κάνει απλοποιήσεις στο συντακτικό για μεγαλύτερη ευκρίνεια
Δομημένος προγραμματισμός (17/18) Δηλ., ακολουθείται μία διαδικασία διαδοχικής εξειδίκευσης (stepwise refinement) του κώδικα του προγράμματος, –αρχίζοντας από το κεντρικό πρόγραμμα, ορίζοντας τα βασικά βήματα, τύπους και δεδομένα –και επαναπροσδιορίζοντας το κάθε βήμα με επιμέρους βήματα που συνήθως οδηγούν σε ανεξάρτητα υποπρογράμματα –τα οποία ορίζονται και πρόκειται να υλοποιηθούν αργότερα, ενώ θεωρούνται σαν κάποια black boxes με πολύ συγκεκριμένη συμπεριφορά Η τακτική του stepwise refinement είναι ένα βασικό στοιχείο του δομημένου προγραμματισμού (structured programming)
Δομημένος προγραμματισμός (18/18) Ευκολία ανάπτυξης –Διαίρει και βασίλευε Ταχύτητα ανάπτυξης –Απασχολούμαστε με μικρότερο πρόβλημα Ευελιξία ανάπτυξης –Μπορούμε να αλλάζουμε τα επιμέρους τμήματα εύκολα Οργάνωση ανάπτυξης –Κάθε τμήμα μπορεί να το αναλάβει και άλλος προγραμματιστής Ευκολία ελέγχου –Κάθε συνάρτηση μπορεί να ελεγχθεί απομονωμένα Ευκολία συντήρησης –Επεκτάσεις γίνονται σε συγκεκριμένες συναρτήσεις χωρίς να επηρεάζεται όλο το πρόγραμμα
Περιεχόμενα Δομημένος προγραμματισμός Εμβέλεια κα χρήση μεταβλητών
Εμβέλεια και χρήση μεταβλητών (1/5) Τοπική –Μέσα στο block του βασικού προγράμματος –Μέσα στο block υποπρογράμματος –Ως τυπικά ορίσματα υποπρογράμματος –Μέσα σε οποιαδήποτε εντολή block Καθολική –Εκτός υποπρογράμματος και εκτός του βασικού προγράμματος Οι μεταβλητές μπορούν να χρησιμοποιούνται στο πρόγραμμα –μόνο ακριβώς μετά το σημείο της δήλωσης τους, –στο «χώρο» που ισχύει η δήλωσή τους, ο οποίος λέγεται και εμβέλεια της μεταβλητής
Εμβέλεια και χρήση μεταβλητών (2/5) Εμβέλεια μεταβλητών Τοπικές μεταβλητές Στο block του προγράμματος Στις εντολές του προγράμματος Σε υποπρόγραμμα Στις εντολές του υποπρογράμματος Τυπικό όρισμα υποπρογράμματος Στις εντολές του υποπρογράμματος Σε εντολή block Στις εντολές του block Καθολικές μεταβλητές Πριν το πρόγραμμαΣτις εντολές των υποπρογραμμάτων που έπονται και του προγράμματος Μετά το πρόγραμμαΣτις εντολές των υποπρογραμμάτων που έπονται Που δηλώνονται Που χρησιμοποιούνται
Εμβέλεια και χρήση μεταβλητών (3/5) Η εμβέλεια μίας μεταβλητής μπορεί να εμπεριέχει την εμβέλεια άλλης μεταβλητής REAL x; PROCEDURE g AGRUMENTS(INTEGER x, INTEGER n) BEGIN INTEGER i; WHILE i < n+x DO BEGIN CHARACTER c;... END PROGRAM_BEGIN REAL y;... PROGRAM_END 1 2 1x 2x3 3n4 4i5 5c6 6y Για οποιεσδήποτε δύο μεταβλητές με το ίδιο όνομα Α, τότε: (α) εάν έχουν «ξένες» εμβέλειες, χωρίς η μία να είναι ευρύτερη από την άλλη, τότε μπορούν να χρησιμοποιούνται και οι δύο, η κάθε μία στο χώρο της. (β) αλλιώς, στη μικρότερη από τις δύο εμβέλειες, κάθε αναφορά στο Α σχετίζεται πάντα με τη μεταβλητή που δηλώνεται στην εμβέλεια αυτή.
Εμβέλεια και χρήση μεταβλητών (4/5) Επιπλέον κανόνες –Δεν επιτρέπεται στην ίδια εμβέλεια να δηλώνουμε μεταβλητές με το ίδιο όνομα –Δεν επιτρέπεται να υπάρχει κοινό όνομα για οποιοδήποτε μεταξύ των παρακάτω: καθολικές μεταβλητές συναρτήσεις ή διαδικασίες τύπους record
Εμβέλεια και χρήση μεταβλητών (5/5) Παραδείγματα RECORD Student RECBEGIN STRING name; RECEND; INTEGER Student; FUNCTION(REAL) max ARGUMENTS (REAL x, REAL y); REAL min, max; PROGRAM_BEGIN STRING min, max; REAL n; PROGRAM_END INTEGER n; PROCEDURE inputarray (ADDRESSEX(STRING) p, INTEGER n) BEGIN REAL p, n; END Σε ποιες περιπτώσεις παραβαίνουμε τους κανόνες?
Γενικές οδηγίες (1/6) Ονομασία μεταβλητών –Δηλώνετε τις βοηθητικές μεταβλητές ως τοπικές μεταβλητές πάντα –Δίνεται περιγραφικά ονόματα αντιπροσωπευτικά του ρόλου των μεταβλητών –Ακολουθήστε τους δικούς σας κανόνες για αναγνωριστικά ονόματα, οι οποίοι σας βοηθούν να διαβάζετε ευκολότερα το πρόγραμμά σας
Γενικές οδηγίες (2/6) INTEGER c = 0; INTEGER d = 0; CONST_INTEGER b = 10; FUNCTION(INTEGER) f ARGUMENTS (ARRAY(INTEGER,b) a) BEGIN FOR c = 0; c < b; c = c + 1 DO d = d + a[c]; RESULT(d / b); END FUNCTION(INTEGER) Average ARGUMENTS (ADDRESSEX(INTEGER) p, INTEGER n) BEGIN INTEGER sum = 0, i; FOR i = 0; i < n; i = i + 1 DO sum = sum + p[i]; RESULT(sum / n); END τι κάνει αυτή η συνάρτηση?
Γενικές οδηγίες (3/6) Σχόλια στο πρόγραμμα –Εξηγούν μόνο ότι δεν είναι προφανές –Εξηγούν μόνο ότι έχει σχέση με το πρόγραμμα –Τοποθετούνται πριν από τα τμήματα που σχολιάζουν –Δίνουν χρήσιμη πληροφορία –Είναι σύντομα και περιεκτικά –Γράφονται έτσι ώστε να μην διασπούν την δομή του κώδικα –Δεν γράφονται όσο ένα τμήμα είναι ευμετάβλητο – περιμένουμε να παγιωθεί η υλοποίησή του –Γράφουμε πάντα με λατινικούς χαρακτήρες
Γενικές οδηγίες (4/6) /* METATREPEI APO ENA STRING bits POU PERIEXEI 0 / 1 STON ANTISTOIXO ARITHMO STO SUSTHMA 10. EAN EXOUME PLEON TWN 32 XARAKTHRWN, OI EPIPLEON AGNOOUNTAI, ENW EAN UPARXEI XARAJXTHRAS EKTOW TWN 0 / 1 EPISTREFETAI 0. */ FUNCTION(INTEGER) BinaryStringToDecimal ARGUMENTS (STRING bits) BEGIN INTEGER i, number = 0; FORi = 0; i < string_length(bits) AND i < 32; i = i+1 DO BEGIN CHARACTER c = string_get(bits, i); number = number * 2; IF c EQUAL '1' THEN number = number + 1; ELSE IF c DIFFERENT '0' THEN BEGIN OUTPUT("Non binary digit found!"); RESULT(0); END RESULT(number); END
Γενικές οδηγίες (5/6) Στοίχιση του κώδικα –Χρησιμοποιούμε στοίχιση για να είναι εμφανέστερη η δομή αλλά και η λογική του κώδικα Η στοίχιση γίνεται κυρίως με τον χαρακτήρα tab του πληκτρολογίου αλλά και με κενά Βοηθά στον γρήγορο οπτικό έλεγχο και αναγνώριση του κώδικα –Κλασικές περιπτώσεις στις οποίες προσθέτουμε στοίχιση είναι: Οι εντολές ενός block Η μία εντολή ενός if, else, for, while Όταν οι παράμετροι κλήσης συνάρτησης είναι εκφράσεις που καταλαμβάνουν πολύ χώρο –Τοποθετούμε κενές γραμμές κώδικα μεταξύ διαφορετικών λογικών τμημάτων μέσα σε μία συνάρτηση για περισσότερη ευκρίνεια –Τοποθετούμε κενά μεταξύ των ορισμάτων των αριθμητικών εκφράσεων για ευκρίνεια
Γενικές οδηγίες (6/6) FUNCTION(INTEGER) f ARGUMENTS (INTEGER a, INTEGER b, INTEGER c) { BEGIN WHILE a > b + c DO b = b + 1; IF c < b THEN BEGIN c = b; b = a; END RESULT( g ( b * sqr(c) + a, c * sqr(a) + b, a * sqr(b) + c ) ); END z = sqr(a*c+sqrt(abs(x-y)*abs(y-w))); z = sqrt( a * c + sqrt( abs(x – y) * abs(y – w) ) );