Σημειωματάριο διάλεξης 12 Φεβ. 2015

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

In [1]:
x=2
y=3
print x, y
2 3

Εκτός από τους αριθμούς (integers και floating point) και τα strings οι λίστες είναι από τις πιο βασικές και χρήσιμες μορφές δεδομένων στην python.

Στο επόμενο παράδειγμα ορίζουμε μια λίστα l στην οποία βάζουμε 4 αντικείμενα. Τα τρία πρώτα είναι αριθμοί και το τελευταίο είναι μια λέξη (string). Έπειτα τυπώνουμε τη λίστα. (Υπάρχουν κάτι προβλήματα αν το string περιέχει ελληνικά (unicode χαρακτήρες).)

In [49]:
l=[1, 2, 3.2, "Mihalis"]
print l
[1, 2, 3.2, 'Mihalis']

Εδώ βλέπουμε για πρώτη φορά την ανακύκλωση for πάνω σε μια λίστα. Η μεταβλητή w στο for w in l: διατρέχει όλα τα στοιχεία της λίστας l από το πρώτο έως το τελευταίο. Η εντολές που ελέγχονται από το for είναι αυτές που εμφανίζονται ακριβώς από κάτω και σπρωγμένες (indented) λίγο πιο μέσα. Αυτές εκτελούνται μια φορά για κάθε διαφορετικό w που παράγεται από το for.

In [5]:
l=[1, 2, 3.2, "Μιχάλης"]
for w in l:
    print w
1
2
3.2
Μιχάλης

Μια λίστα όπως η m που φαίνεται παρακάτω μπορεί να περιέχει στοιχεία οποιουδήποτε είδους, ακόμη και άλλες λίστες. Εδώ το 4ο στοιχείο της λίστας m είναι η λίστα [1, 2].

Το κόμμα (,) που υπάρχει στο τέλος της print w, λέει στην print να μην αλλάξει γραμμή μετά την εκτύπωση της μεταβλητής w. Η default συμπεριφορά της print είναι να αλλάζει γραμμή μετά από κάθε εκτύπωση. Αν τελειώνει με κόμμα τότε απλά τυπώνει κι ένα λευκό χαρακτήρα (space) μετά από κάθε εκτύπωση.

In [7]:
m = [1, 2, 3, [1, 2]]
for w in m:
    print w,
    
1 2 3 [1, 2]

Η συνάρτηση range(N) επιστρέφει μια λίστα που αρχίζει από το 0 και φτάνει έως και το N-1. Η λίστα αυτή έχει μήκος N.

In [10]:
range(11)
Out[10]:
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

Στο επόμενο τυπώνουμε τα τετράγωνα όλων των αριθμών από 0 έως 10. Το κάνουμε αυτό διανύοντας με τη μεταβλητή x τη λίστα που παράγει η range(11).

In [12]:
for x in range(11):
    print x**2,
0 1 4 9 16 25 36 49 64 81 100

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

In [13]:
print range(11)**2
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-13-a43593f3ecbb> in <module>()
----> 1 print range(11)**2

TypeError: unsupported operand type(s) for ** or pow(): 'list' and 'int'

Αν θέλουμε να τροποποιήσουμε το πρόγραμμα που παράγει τα τετράγωνα ώστε να μη παράγει το τετράγωνο του 0, τότε χρησιμοποιούμε ένα if μέσα στο for. Το if αυτό ελέγχει αν ο αριθμός x είναι θετικός και μόνο τότε τυπώνει το τετράγωνό του. Έτσι δεν τυπώνεται ο αριθμός 0.

In [17]:
for x in range(11):
    if x>0:
        print x**2,
1 4 9 16 25 36 49 64 81 100

Η συνάρτηση range δουλεύει και με τη μορφή range(a,b) όπου a, b είναι δύο ακέραιοι, a < b, και τότε παράγει τη λίστα [a, a+1, ..., b-1].

In [14]:
range(3,10)
Out[14]:
[3, 4, 5, 6, 7, 8, 9]
In [16]:
range(-4, 10)
Out[16]:
[-4, -3, -2, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

Οπότε μπορούμε να αποφύγουμε την εκτύπωση του 0 στη λίστα των τετραγώνων χρησιμοποιώντας range(1,11) αντί range(0,11).

In [21]:
for x in range(1,11):
    print x**2,
1 4 9 16 25 36 49 64 81 100

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

Ξεκινάμε με την πρώτη γραμμή import math η οποία "φορτώνει" τη βιβλιοθήκη math της python, βιβλιοθήκη που περιέχει κάποιες μαθηματικές συναρτήσεις που είναι πολύ χρήσιμες. Από τη βιβλιοθήκη math θα χρησιμοποιήσουμε εδώ μόνο τη συνάρτηση math.sqrt που υπολογίζει την τετραγωνική ρίζα ενός μη αρνητικού πραγματικού αριθμού. (Για να δείτε τι άλλες συναρτήσεις ορίζει η βιβλιοθήκη math μπορείτε, αφού τη φορτώσετε, να δώσετε την εντολή help("math").)

Η γραμμή l = [-1, 2, -6, 3.4, 3.5, -100] ορίζει τη μεταβλητή l να είναι μια συγκεκριμένη λίστα αριθμών.

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

Στο for x in l: η μεταβλητή x διατρέχει τη λίστα και μέσα στην ανακύκλωση ελέχουμε στο if αν είναι μη αρνητικό. Αν είναι τότε απλά τυπώνουμε την τετραγωνική του ρίζα που υπολογίζεται με την κλήση συνάρτησης math.sqrt(x). Αλλιώς (στο else:) εκτελούμε τις δύο εντολές print 0 και μετά n=n+1. Αν δηλ. ο αριθμός x είναι αρνητικός τότε τυπώνουμε 0 (αντί τετραγωνικής ρίζας) και επίσης αυξάνουμε το n κατά ένα, ώστε να εξακολουθήσει να σημαίνει πόσους αρνητικούς αριθμούς έχουμε δει μέχρι στιγμής.

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

In [26]:
import math
l = [-1, 2, -6, 3.4, 3.5, -100]
n = 0
for x in l:
    if x>=0:
        print math.sqrt(x),
    else:
        print 0,
        n = n+1
print
print n, "negative numbers were found."       
0 1.41421356237 0 1.84390889146 1.87082869339 0
3 negative numbers were found.

Στο επόμενο λύνουμε το ίδιο πρόβλημα αλλά με μια παραλλαγή στο πώς μετράμε το πλήθος των αρνητικών αριθμών. Συγκεκριμένα, η μεταβλητή n έχει τώρα διαφορετικό νόημα. Κατ' αρχήν θέτουμε με την εντολή n = len(l) τη μεταβλητή αυτή ίση με το μήκος της λίστας (η συνάρτηση len επιστρέφει το μήκος της λίστας που της δίνουμε ως όρισμα και δουλεύει επίσης και με strings, ώστε, π.χ., len('abc123') ισούται με 6). Έπειτα, όπως διανύουμε τη λίστα, όποτε συναντήσουμε μη αρνητικό αριθμό (μέσα στο if δηλ.) αφαιρούμε από την n το 1, ώστε στο τέλος της ανακύκλωσης for να ισούται το n με το πλήθος των αρνητικών αριθμών στη λίστα.

Παρατηρούμε εδώ ότι αφαιρούμε το 1 από τη μεταβλητή n όχι με την εντολή n = n -1 (που θα ήταν εξίσου σωστή εδώ), αλλά με χρήση του ειδικού τελεστή -= οποίου το νόημα είναι "αφαίρεση από το αριστερό μέλος το δεξί μέλος, αφού το υπολογίσεις". Υπάρχει και αντίστοιχος τελεστής += όπως και οι τελεστές *=, /= που κάνουν τα ευνόητα. Για παράδειγμα αν δώσουμε τις εντολές

x=3; y=2
x *= 4
y /= 3.0
print x, y

θα πάρουμε ως αποτέλεσμα

12 0.666666666667
In [30]:
import math
l = [-1, 2, -6, 3.4, 3.5, -100]
n = len(l)
for x in l:
    if x>=0:
        print math.sqrt(x),
        n -= 1
    else:
        print 0,
print
print n, "negative numbers were found."
        
 0 1.41421356237 0 1.84390889146 1.87082869339 0
3 negative numbers were found.

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

Η νέα αυτή λίστα είναι στη μεταβλητή neg στην οποία δίνουμε αρχική τιμή (την κενή λίστα) με την εντολή neg = []. Μέσα στην ανακύκλωση, στο else, όποτε βρούμε ένα αρνητικό x το προσθέτουμε στη λίστα neg το στοιχείο αυτό με την εντολή

neg.append(x)

Αν xyz είναι το όνομα μιας λίστας και obj είναι ένα αντικείμενο τότε η εντολή xyz.append(obj) προσθέτει στo τέλος της λίστας xyz το αντικείμενο obj. Το ίδιο θα μπορούσε να επιτευχθεί με τη χρήση του τελεστή + που για λίστες δε σημαίνει φυσικά πρόσθεση αριθμών αλλά συγκόλληση (concatenation) των δύο λιστών που είναι αριστερά και δεξιά του. Η εντολή δηλ. xyz.append(obj) πετυχαίνει το ίδιο αποτέλεσμα με την εντολή xyz = xyz + [obj], συγκολλούμε δηλ. τη λίστα xyz με τη λίστα [obj], η οποία είναι η λίστα με ένα ακριβώς στοιχείο ίδιο με το obj.

Αφού τελειώσει το πρώτο for τυπώνουμε με τα δύο πρώτα print όπως και πριν, το πόσους αρνητικούς αριθμούς βρήκαμε.

Με το τρίτο print τυπώνουμε, χωρίς να αλλάξουμε γραμμή, ένα προκαταρκτικό μήνυμα ("The negative numbers I found are:") μετά από το οποίο θα τυπωθούν όλοι οι αρνητικοί αριθμοί που βρήκαμε διανύοντας τη λίστα l και που αποθηκεύσαμε στη λίστα neg.

Αρχικά είχαμε γράψει το κομμάτι κώδικα όχι όπως φαίνεται παρακάτω (μετά τη γραμμή print "The negative numbers I found are:",) αλλά ως εξής:

for w in neg:
    print w, ",",

το οποίο μας τύπωνε κανονικά τους αριθμούς που έχουμε βάλει στη neg ως εξής:

The negative numbers I found are: -1 , -6 , -100 ,

Από αισθητική άποψη υπάρχουν τα εξής δύο προβλήματα. Πρώτον δε θέλουμε να τυπώνουμε κόμμα μετά το τελευταίο στοιχείο της λίστας και, δεύτερον, δε θέλουμε να τυπώνεται κενός χαρακτήρας πριν από κάθε κόμμα.

Έτσι λοιπόν πρέπει κατ' αρχήν να γνωρίζουμε, πριν από κάθε εντολή print αν πρόκειται να τυπώσουμε το τελευταίο στοιχείο της λίστας (οπότε δε θα τυπώσουμε και το κόμμα ή όχι (οπότε τυπώνουμε). Για να το πετύχουμε αυτό χρησιμοποιούμε τη μεταβλητή i, της οποίας το νόημα είναι ότι μας λέει ανά πάσα στιγμή πόσα από τα στοιχεία της λίστας έχουμε τυπώσει (γι' αυτό και αυξάνεται κατά 1 στην τελευταία γραμμή που υπάγεται στο for). Η συνθήκη i<len(neg)-1 ισχύει όσο δεν έχουμε ακόμη τυπώσει το προτελευταίο στοιχείο της λίστας. Αυτή ακριβώς είναι λοιπόν η συνθήκη που, αν ισχύει, πρέπει να τυπώσουμε κι ένα κόμμα μετά από το στοιχείο που γράφουμε. Η τελευταία φορά που θα ισχύσει αυτό το if είναι όταν θα τυπωθεί το προτελευταίο στοιχείο της λίστας. Το αντίστοιχο else ισχύει μόνο όταν τυπώνουμε το τελευταίο στοιχείο της λίστας.

Για να λύσουμε το δεύτερο πρόβλημα που αναφέραμε παραπάνω (για να μη τυπώνουμε δηλ. κενό χαρακτήρα πριν το κόμμα) θα πρέπει να αποφύγουμε την default συμπεριφορά της print η οποία τυπώνει κενά ανάμεσα στα ορίσματά της. Ο τρόπος που γίνεται αυτό είναι με ένα formatting string, που είναι το "%.3f," στην εντολή print. Το νόημα του κομματιού %.3f είναι ότι θέλουμε να τυπώσουμε ένα πραγματικό αριθμό (εξ ου το f) σε τρία δεκαδικά ψηφία (εξ ου το .3) και το ποιο αριθμό τυπώνουμε το λέμε μετά το % που ακολουθεί το formatting string και που εδώ είναι η μεταβλητή w που διανύει τη λίστα neg.

Μπορείτε να δείτε μερικά παραπάνω πράγματα για formatting εδώ.

In [48]:
import math
l = [-1, 2, -6, 3.4, 3.5, -100]
n = len(l)
neg= []
for x in l:
    if x>=0:
        print math.sqrt(x),
        n -= 1
    else:
        print 0,
        neg.append(x) # μπορεί να γίνει και με την εντολή: neg = neg + [x]
print
print n, "negative numbers were found."
print "The negative numbers I found are:",
i=0
for w in neg:
    if i<len(neg)-1:
        print "%.3f," % w,
    else:
        print "%.3f" % w
    i += 1
0 1.41421356237 0 1.84390889146 1.87082869339 0
3 negative numbers were found.
The negative numbers I found are: -1.000, -6.000, -100.000