οµές Επιλογής Εντολές if και switch οµή Ελέγχου Control Structure Ένας συνδυασµός ατοµικών εντολών σε µία λογική µονάδα, όπου υπάρχει µόνο ένα σηµείο εισόδου και ένα σηµείο εξόδου. οµή Ελέγχου για Επιλογή Selection Control Structure Μία δοµή ελέγχου η οποία επιλέγει ανάµεσα σε εναλλακτικές εντολές, απλές ή σύνθετες. Υπενθύµιση: Μία σύνθετη εντολή εί µία ακολουθία εντολών, η οποία εκφράζεται ως { εντολή 1 ; εντολή 2 ;.... εντολή n ; } Συνθήκη Condition Μία έκφραση η οποία εί είτε αληθής (true) είτε ψευδής (false). Εί µία λογική έκφραση (βλέπετε παρακάτω). Το ψεύδος αντιπροσωπεύεται από το 0 και η αλήθεια από οποιαδήποτε άλλη ακέραια τιµή, συνήθως το 1.
Η C δεν έχει ξεχωριστό τύπο για λογικές τιµές (true και false) όπως συµβαίνει µε πολλές άλλες γλώσσες, π.χ. η Modula-2 έχει τον τύπο BOOLEAN, µε τιµές TRUE και FALSE. H προσοµοίωση των λογικών τιµών µε ακεραίους παρέχει στη C µεγαλύτερη ευελιξία. Π.χ. ο ακέραιος αριθµός που αντιπροσωπεύει true σε κάποια συµφραζόµενα, µπορεί κάλλιστα να έχει χρησιµότητα πέραν την επισήµανσης της κατάστασης της αλήθειας. Σχεσιακοί Τελεστές Relational Operators < µικρότερο από > µεγαλύτερο από <= µικρότερο ή ίσο µε >= µεγαλύτερο ή ίσο µε == ίσο µε!= διάφορο του Σηµείωση: οκιµή για ισότητα ανάµεσα σε πραγµατικούς αριθµούς. 1.29999 == 1.2999? false fabs(1.29999 1.2999) < 0.0001 true 2
Σχεσιακή Έκφραση Relational Expression <έκφραση> <σχεσιακός τελεστής> <έκφραση> Αποτιµείται σε λογική τιµή. Λογικοί Τελεστές Logical Operators && σύζευξη (δυαδικός τελεστής) διάζευξη (δυαδικός τελεστής)! άρνηση (µοναδιαίος τελεστής) Πίνακες Αλήθειας για Λογικούς Τελεστές τελεστέος 1 τελεστέος 2 && true true true true false false false true false false false false τελεστέος 1 τελεστέος 2 true true true true false true false true true false false false 3
τελεστέος! true false false true Λογικές Εκφράσεις Logical Expressions Εκφράσεις αποτελούµενες από λογικές τιµές και λογικούς τελεστές. Αποτιµούνται σε λογικές τιµές. <λογική_τιµή> ::= true false <λογική_έκφραση> ::= <λογική_τιµή> <σχεσιακή_έκφραση> (<λογική_έκφραση>)!<λογική_έκφραση> <λογική_έκφραση> && <λογική_έκφραση> <λογική_έκφραση> <λογική_έκφραση> Σηµείωση: Οποιαδήποτε έκφραση η οποία αποτιµείται σε ακέραιο αριθµό µπορεί να ερµηνευθεί ως λογική τιµή, π.χ. κλήσεις συναρτήσεων που επιστρέφουν ακεραίους. Αποτίµηση Λογικών Εκφράσεων Η αποτίµηση αρχίζει από τα αριστερά και προχωρεί µέχρι το σηµείο που χρειάζεται να προχωρήσει (short-circuit evaluation), π.χ. 0 && Ε 0 η αποτίµηση της έκφρασης Ε δεν χρειάζεται 4
1 Ε 1 η αποτίµηση της έκφρασης Ε δεν χρειάζεται Σειρά Συνθηκών σε µία Λογική Έκφραση Ο κανόνας αποτίµησης εί για λόγους αποδοτικότητας, αλλά επίσης έχει µεγάλη χρησιµότητα αναφορικά µε τη σειρά των συνθηκών σε µία λογική έκφραση, π.χ. (y % x) > 2 && x!= 0 Εάν το x ισούται µε 0, η αποτίµηση αυτής της έκφρασης θα οδηγήσει σε λάθος. Όµως η ακόλουθη διατύπωση x!= 0 && (y % x) > 2 δεν έχει πρόβληµα λόγω του κανόνα αποτίµησης. Εάν το x ισούται µε 0 η συνθήκη (y % x) > 2 δεν θα αποτιµηθεί. Εάν το x δεν ισούται µε 0, η αποτίµηση της έκφρασης (y % x) > 2 δεν θα εγείρει πρόβληµα. Γενικά σε µία σύζευξη, Ε 1 && Ε 2, η συνθήκη Ε 1 πρέπει να αποτελεί τον αριστερό τελεστέο, εάν στην περίπτωση που αποτιµείται σε 0, τυχόν αποτίµηση της Ε 2 θα οδηγήσει σε πρόβληµα. Με αυτή την σειρά αποτρέπεται η αποτίµηση της Ε 2. 5
Παροµοίως σε µία διάζευξη, Ε 1 Ε 2, η συνθήκη Ε 1 πρέπει να αποτελεί τον αριστερό τελεστέο, εάν στην περίπτωση που αποτιµείται σε 1, τυχόν αποτίµηση της Ε 2 θα οδηγήσει σε πρόβληµα. Με αυτή τη σειρά αποτρέπεται η αποτίµηση της Ε 2. Παράδειγµα: Εί δεδοµένος φυσικός αριθµός palindrome; int palindrome (int N) {int left = Length(N), right = 1; while (left >= right && NthDigit(N,left) == NthDigit(N,right)) {left = left 1; right = right + 1;} return left < right; } Εάν οι δύο συνθήκες στη πιο πάνω σύζευξη αντιστρέφονταν, τι θα συνέβαινε στη περίπτωση µονοψήφιων αριθµών; Μετά την πρώτη επαλήθευση, οι µεταβλητές left και right θα έχουν τιµές 0 και 2 αντιστοίχως, και εποµένως θα κληθεί η NthDigit µε λανθασµένα ορίσµατα NthDigit(N,0) == NthDigit(N,2) 6
Παράδειγµα: Εί τα ψηφία ενός φυσικού αριθµού σε φθίνουσα σειρά από τα αριστερά προς τα δεξιά; int sorted (int N) {int i = Length(N); while (i > 1 && NthDigit(N,i) >= NthDigit(N, i 1)) i = i 1; return i == 1; } Τι θα γινόταν εάν αντιστρέφονταν οι συνθήκες της συζευξης; Ακόµη και σε περιπτώσεις που η σειρά των συνθηκών σε µία σύζευξη/διάζευξη µπορεί να εί τυχαία, µπορεί να γίνει εκµετάλευση του κανόνα αποτίµησης προς µείωση του υπολογιστικού κόστους. Π.χ. έστω η σύζευξη Ε 1 && Ε 2 όπου το κόστος υπολογισµού (αποτίµησης) της Ε 1 εί πολύ µεγάλο, ενώ της Ε 2 εί σχετικά πολύ µικρό. Έστω ότι η Ε 1 αποτιµείται σε 1 και η Ε 2 σε 0. Αυτή η σειρά των συνθηκών δεν αποφεύγει το µεγάλο κόστος αποτίµησης της Ε 1. Η σειρά Ε 2 && Ε 1, όµως το αποφεύγει. 7
Εποµένως η ορθή πρακτική εί να µπαίνει στα αριστερά η συνθήκη, της οποίας η αποτίµηση συνεπάγεται το λιγότερο κόστος. Προτεραιότητες κλήσεις συναρτήσεων υψηλότερη προτεραιότητα! + & (µοναδιαίοι τελεστές) * / % + < <= >= > ==!= && = χαµηλότερη προτεραιότητα Σύγκριση Χαρακτήρων Οι σχεσιακοί τελεστές µπορούν να εφαρµοστούν σε χαρακτήρες. έκφραση τιµή 9 >= 0 true a < e true B <= A false Z == z false a <= A false 8
Ανάθεση Λογικών Τιµών int even, odd; even = (n % 2 == 0); ή even = n % 2 == 0; odd ==!even; int is_lower, is_upper, is_letter, white_space; char ch; scanf( %c, &ch); is_lower = a <= ch && ch <= z ; is_upper = A <= ch && ch <= Z ; is_letter = is_lower is_upper; white_space = ch == \n ch = ; Συµπλήρωµα Συνθηκών Complementing Conditions Το συµπλήρωµα µίας συνθήκης εί η άρνησή της, π.χ. Συνθήκη Συµπλήρωµα 0 1 1 0 item == SENT!(item == SENT) ή item!= SENT item > SENT item <= SENT (n % 2) == 0 (n % 2)!= 0 9
Κανόνες DeMorgan!(E 1 && E 2 )!E 1!E 2!(E 1 E 2 )!E 1 &&!E 2 Απόδειξη Ε 1 Ε 2 &&!!Ε 1!Ε 2 1 1 1 0 0 0 0 1 0 0 1 0 1 1 0 1 0 1 1 0 1 0 0 0 1 1 1 1 Ε 1 Ε 2!!Ε 1!Ε 2 && 1 1 1 0 0 0 0 1 0 1 0 0 1 0 0 1 1 0 1 0 0 0 0 0 1 1 1 1 10
Παραδείγµατα!(left >= right && NthDigit(N, left) == NthDigit(N, right))!(left >= right)!(nthdigit(n, left) == NthDigit(N, right)) left < right NthDigit(N, left)!= NthDigit(N, right)!(i > 1 && NthDigit(N,i) >= NthDigit(N, i 1))!(i > 1)!(NthDigit(N, i) >= NthDigit(N, i 1)) i <= 1 NthDigit(N,i) < NthDigit(N, i 1)!(age > 25 && (status == S status == D ))!(age > 25)!(status == S status == D ) age <= 25 (status!= S && status!= D ) Παράδειγµα: Ένας χρόνος εί δίσεχτος εάν διαιρείται ακριβώς µε το 4, εκτός και εάν διαιρείται ακριβώς µε το 100, στην οποία περίπτωση θα πρέπει να διαιρείται ακριβώς και µε το 400. int is_leap (int year) {return year % 4 == 0 && (year % 100!= 0 year % 400 == 0); } 11
is_leap(1996) 1996 % 4 == 0 && (1996 % 100!= 0 1996 % 400 == 0) 1 && (1 0) 1 && 1 1 is_leap(2000) 2000 % 4 == 0 && (2000 % 100!= 0 2000 % 400 == 0) 1 && (0 1) 1 && 1 1 is_leap (1900) 1900 % 4 == 0 && (1900 % 100!= 0 1900 % 400 == 0) 1 && (0 0) 1 && 0 0 12
Εντολή if if (συνθήκη) εντολή if (C) S C? S if ( i == 1) N = N/10; if ( i == 1) { D = N % 10; N = N / 10; } if (συνθήκη) εντολή 1 else εντολή 2 if (C) S 1 else S 2 C? S 2 S 1 13
if (x == 0) D = 0; else D = D / x; if (x > 0.0) { y = sqrt(x); z = sqrt(y); } else { y = 0.0; z = 0.0; } if (x == 0); /* do nothing */ else y = 1/x; if (x!= 0) y = 1/x; if (x > 5) y = x * 2; else ; /* do nothing */ if (x > 5) y = x * 2; Γενικά if (C); /* do nothing */ else S if (!C) S Χρησιµοποιείται εάν εί πιο φυσικό να εκφραστεί η συνθήκη C παρά η άρνησή της. if (C) S else; /* do nothing */ if (C) S εν έχει νόηµα να υπάρχει κενό else 14
Φώλιασµα Επιλογών if (x > y) if (x > z) res = x; else res = z; else if (y > z) res = y; else res = z; Τι υπολογίζει αυτό το φώλιασµα επιλογών; x > y y > z x > z res = z res = y res = z res = x 15
Γενικά C 4 S 1 C 2 S 2 S 3 C 5 S 4 C 1 S 5 C 6 C 3 S 6 S 7 C 7 S 8 Περίπλοκη λογική που κάνει τον κώδικα δυσανάγνωστο. 16
if (C 1 ) if (C 2 ) if (C 4 ) S 1 else S 2 else if (C 5 ) S 3 else S 4 else if (C 3 ) if (C 6 ) S 5 else S 6 else if (C 7 ) S 7 else S 8 Παράδειγµα: Εάν κάποιος χρόνος εί δίσεχτος. if (year % 4 == 0) if (year % 100 == 0) if (year % 400 == 0) leap = 1; else leap = 0; else leap = 1; else leap = 0; 17
year % 4 == 0 leap = 0 year % 100 == 0 leap = 1 year % 400 == 0 leap = 0 leap = 1 ιακλάδωση µόνο στη κατεύθυνση του Ο πιο συνηθισµένος τρόπος φωλιάσµατος επιλογών, απλά επειδή αποτελεί τον πιο φυσικό τρόπο έκφρασης, εί όταν η διακλάδωση γίνεται µόνο στην κατεύθυνση του. Έτσι υπάρχει µία αλυσίδα επιλογών. 18
C 1 S 1 C 2 S 2 C n-1 S n-1 S n if (C 1 ) S 1 else if (C 2 ) S 2 else if (C 3 ) S 3...... else if (C n-1 ) S n-1 else S n 19
Παραδείγµατα if (x > z && x > y) res = x; else if (y > x && y > z) res = y; else res = z; if (year % 4 == 0 && year % 100!= 0) leap = 1; else if (year % 4 == 0 && year % 100 == 0 && year % 400 == 0) leap = 1; else leap = 0; if (weighted_aver >= 8.5) printf( \nάριστα ); else if (weighted_aver < 8.5 && weighted_aver >= 6.5) printf( \nλίαν Καλώς ); else if (weighted_aver < 6.5 && weighted_aver > 5.0) printf( \nκαλώς ); else printf( \naποτυχία ); 20
Εντολή switch switch (έκφραση_ελέγχου) { σύνολο_ετικεττών_1 εντολές_1 break; σύνολο_ετικεττών_2 εντολές_2 break;.. σύνολο_ετικεττών_n εντολές_n break; default: εντολές_d } Η έκφραση_ελέγχου πρέπει να εί βαθµωτού τύπου, π.χ. char ή int, αλλά double. 21
Παραδείγµατα char choice; scanf( %c, &choice); switch(choice) { case B : case b : draw_butterfly(size, fill); break; case W : case w : draw_windmill(size, fill); break; case K : case k : draw_kite(size, fill); break; case R : case r : draw_rombus(size, fill); break; default: } printf( \nwrong choice ); 22
switch (year % 4 == 0) { case 0: leap = 0; break; case 1: switch (year % 100 == 0) { case 0: leap = 1; break; case 1: switch (year % 400 == 0) { case 0: leap = 0; break; case 1: leap = 1; } } } Κοινά Λάθη if (0 <= x <= 4) printf( \nh συνθήκη ευσταθεί ); Έστω ότι x έχει την τιµή 5. ((0 <= x) <= 4) ((0 <= 5) <= 4) 1 <= 4 1 23
Ορθός τρόπος διατύπωσης: if (0 <= x && x <= 4) printf( \nh συνθήκη ευσταθεί ); (0 <= 5 && 5 <= 4) (1 && 0) 0 Χρήση ανάθεσης (=) αντί για ισότητα (==) if (x = 10) printf( \nτο x εί 10 ); Η καταχώριση της τιµής στη µεταβλητή εί το πλευρικό φαινόµενο του τελεστή =. Επίσης ο τελεστής επιστρέφει την τιµή που έχει καταχωρήσει. Εποµένως η συνθήκη (x = 10) πάντοτε θα αποτιµείται σε true αφού ισοδυναµεί µε 10, και στην εν λόγω µεταβλητή θα καταχωρείται η τιµή 10. Η ορθή διατύπωση εί if (x == 10) printf( \nτο x εί 10 ); 24
Άλλα κοινά λάθη: Η συνθήκη σε µία πρόταση if και η έκφραση ελέγχου σε µία πρόταση switch πρέπει να εί µέσα σε κανονικές παρενθέσεις. Πρέπει να υπάρχει η σωστή αντιστοιχία ανάµεσα στα if και τα else, π.χ. if (x > 0) sum = sum + x; printf( \ngreater than 0 ); else printf( \nless than 0 ); Ορθή διατύπωση: if (x > 0) {sum = sum + x; printf( \ngreater than 0 );} else printf( \nless than 0 ); 25
26