Διάλεξη 23ης Νοεμβρίου 2016

Ακολουθίες. Μέθοδοι για ακολουθίες χαρακτήρων. Μορφοποίηση εξόδου

Έχουμε δει τέσσερις τύπους ακολουθιών: τις ακολουθίες χαρακτήρων (strings), τις πλειάδες (tuples), τις λίστες (lists) και τον τύπο range. Μια σταθερά τύπου string ορίζεται αναγράφοντας τους χαρακτήρες από τους οποίους αποτελείται μεταξύ μονών ή διπλών εισαγωγικών, όπως 'lemon' ή "orange". Οι λίστες κατασκευάζονται αναγράφοντας τα στοιχεία τους ανάμεσα σε αγκύλες, χωρισμένα με κόμματα: ['a', 2, 'cee', [3, 4]]. Τα στοιχεία μιας λίστας δεν είναι απαραίτητο να έχουν όλα τον ίδιο τύπο. Μια πλειάδα ορίζεται αναγράφοντας τα στοιχεία της μεταξύ παρενθέσεων. Η κενή πλειάδα γράφεται ως () ενώ η πλειάδα με μοναδικό στοιχείο τον χαρακτήρα 'a' γράφεται ως ('a',).

Μια ακολουθία τύπου range γράφεται στη μορφή range([start], stop[,step]) και αναφέρεται στην ακολουθία με όρους τους ακέραιους start, start + step, start + 2*step,.... Αν το όρισμα step είναι θετικός αριθμός ο τελευταίος όρος της ακολουθίας είναι ο μεγαλύτερος ακέραιος της μορφής start + i*step ο οποίος είναι αυστηρά μικρότερος του stop. Αν το όρισμα step είναι αρνητικός αριθμός ο τελευταίος όρος της ακολουθίας είναι το μικρότερο στοιχείο της μορφής start + i*step το οποίο είναι μεγαλύτερο από το stop. Όπως μαρτυρά και το συντακτικό της, μόνο το όρισμα stop είναι υποχρεωτικό. Μπορούμε να μετατρέψουμε ένα αντικείμενο τύπου range σε λίστα χρησιμοποιώντας τη σύναρτηση list().


>>> list(range(1,10,2))
[1, 3, 5, 7, 9]
>>> list(range(10,2,-1))
[10, 9, 8, 7, 6, 5, 4, 3]
>>> list(range(10))
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> list(range(1,0))
[]

Όλοι οι τύποι ακολουθιών υποστηρίζουν τις ακόλουθες λειτουργίες (s, t είναι ακολουθίες, n, i, j, k είναι ακέραιοι):

Αναφερόμαστε στις λειτουργίες index και count ως μεθόδους και στρέφουμε την προσοχή τπυ αναγνώστη στο συντακτικό τους, το οποίο είναι τυπικό όλων των μεθόδων για όλους τους τύπους ακολουθιών. Αναφέρουμε παρακάτω μερικές από τις δεκάδες μεθόδους που η υποστηρίζει η Python για τις ακολουθίες χαρακτήρων. Σε ότι ακολουθεί, s είναι μια ακολουθία χαρακρήρων.

s.capitalize(): επιστρέφει ένα αντίγραφο του s όπου ο πρώτος χαρακτήρας, αν είναι γράμμα, έχει γίνει κεφαλαίο γράμμα.


>>> s = 'maria'
>>> s.capitalize()
'Maria'
>>> 'john'.capitalize()
'John'
>>> '123'.capitalize()
'123'

s.center(width [,fillchar]): επιστρέφει ένα αντίγραφο του s στο κέντρο μιας ακολουθίας χαρακτήρων μήκους width με τους περιβάλλοντες χαρακτήρες ίσους με το fillchar, αν δίνεται, διαφορετικά κενούς χαρακτήρες. Οι μέθοδοι s.ljust(width [,fillchar]) και s.rjust(width [,fillchar]) επιτελούν παρόμοιες λειτουργίες.


>>> s = 'python'
>>> s.center(12)
'   python   '
>>> s.center(12, '-')
'---python---'
>>> s.ljust(12, '-')
'python------'
>>> s.rjust(12, '*')
'******python'

s.endswith(suffix[, start[, end]]): επιστρέφει True αν η ακολουθία s καταλήγει με τους χαρακτήρες suffix, διαφορετικά False. Τα προαιρετικά ορίσματα start και stop περιορίζουν το εύρος της αναζήτησης της κατάληξης suffix. Η μέθοδος s.startswith(prefix[, start[, end]]) επιτελεί παρόμοια λειτουργία.


>>> s = 'really long string'
>>> s.endswith('ing')
True
>>> s.endswith('str', 10, 15)
True
>>> 'Manchester by the sea'.startswith('che')
False

s.find(sub[, start[, end]]): επιστρέφει τον δείκτη της πρώτης εμφάνισης του string sub στην ακολουθία s, διαφορετικά -1. Τα προαιρετικά ορίσματα start και end περιορίζουν το εύρος της αναζήτησης στο κομμάτι s[start:end]. Η μέθοδος s.rfind(sub[, start[, end]]) επιστρέφει τον δείκτη της τελευταίας εμφάνισης του string sub στην ακολουθία s


>>> s = 'needle in a haystack'
>>> s.find('hay')
12
>>> s.find('needle', 5)
-1
>>> 'Mississippi'.rfind('ssi')
5

s.join(seq): επιστρέφει μια ακολουθία χαρακτήρων αποτελούμενη από την συνένωση των στοιχείων της ακολουθίας seq. Τα στοιχεία της ακολουθίας ενώνονται με την ακολουθία s.


>>> ' '.join('maria')
'm a r i a'
>>> '-'.join(['python', 'is', 'fun'])
'python-is-fun'

s.split([sep[, maxsplit]]): επιστρέφει μια λίστα αποτελούμενη από τα μέρη στα οποία χωρίζει την ακολουθία χαρακτήρων s το sep. Αν ο ακέραιος maxsplit δίνεται τότε επιστρέφονται ακριβώς maxsplit+1 στοιχεία. Η μέθοδος s.rsplit([sep[, maxsplit]]) χωρίζει την ακολουθία s ξεκινώντας από δεξιά.


>>> s = 'four score and sever years ago'
>>> s.split(' ')
['four', 'score', 'and', 'sever', 'years', 'ago']
>>> s.split(' ', 2)
['four', 'score', 'and sever years ago']

s.splitlines([keepends]): επιστρέφει μια λίστα αποτελούμενη από τις γραμμές της ακολουθίας χαρακτήρων s. O χαρακτήρας '\n δεν συμπεριλαμβάνεται εκτός και αν το όρισμα keepends δίνεται και είναι ίσο με True.


>>> s = 'It was the best of times.\nIt was the worst of times.\nIt was the age of wisdom.\nIt was the age of foolishness.\n'
>>> s.splitlines()
['It was the best of times.', 'It was the worst of times.', 'It was the age of wisdom.', 'It was the age of foolishness.']
>>> s.splitlines(True)
['It was the best of times.\n', 'It was the worst of times.\n', 'It was the age of wisdom.\n', 'It was the age of foolishness.\n']

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


>>> 'www.tem.uoc.gr'.partition('.')
('www', '.', 'tem.uoc.gr')
>>> 'www.tem.uoc.gr'.rpartition('.')
('www.tem.uoc', '.', 'gr')
>>> 'www.tem.uoc.gr'.partition(',')
('www.tem.uoc.gr', '', '')

s.replace(old, new[, count]): αντικαθιστά κάθε εμφάνιση του old με το new. Αν το προαιρετικό όρισμα count δίνεται τότε οι αντικαταστάσεις εκτελούνται το πολύ count φορές.


>>> s = 'My number is 2810-39-3709'
>>> s.replace('-', '.')
'My number is 2810.39.3709'

s.strip([chars]): αφαιρεί τους χαρακτήρες chars από την αρχή και το τέλος της ακολουθίας χαρακτήρων s. Αν η ακολουθία chars δεν δίνεται αφαιρούνται οι κενοί χαρακτήρες. Οι μέθοδοι s.lstrip([chars]) και s.rstrip([chars]) αφαιρούν τους χαρακτήρες chars από την αρχή, αντίστοιχα από το τέλος της ακολουθίας s.


>>> s = 'A lot of spaces, left and right   '
>>> s.strip()
'A lot of spaces, left and right'
>>> 'madam'.lstrip('m')
'adam'
>>> 'madam'.rstrip('am')
'mad'

Ο ενδιαφερόμενος αναγνώστης καλείται να αναζητήσει περισσότερες πληροφορίες για τις παραπάνω μεθόδους, γράφοντας, για παράδειγμα, help(sṫr.find) στη γραμμή εντολών της Python, όσο και για τις μεθόδους s.isalnum(), s.isalpha(), s.isdecimal(), s.isdigit(), s.isidentifier(), s.islower(), s.isnumeric(), s.isprintable(), s.isspace(), s.isupper(), s.lower(), s.upper(), s.zfill(width) και s.swapcase() τις οποίες δεν θα συζητήσουμε εδώ.

Μορφοποίηση εξόδου

Η Python3 παρέχει τη μέθοδο s.format() η οποία μορφοποιεί τα ορίσματά της σύμφωνα με τις οδηγίες που αναγράφονται στην ακολουθία χαρακτήρων s και επιστρέφει μια καινούργια ακολουθία χαρακτήρων η οποία περιέχει τη ζητούμενη κωδικοποίηση. Εξηγούμε και παραθέτουμε παραδείγματα.

΄Έστω ότι θέλουμε να φτιάξουμε τον πίνακα τιμών των συναρτήσεων $x^2$ και $x^4$ για τους πρώτους 20 φυσικούς αριθμούς. Μπορούμε, φυσικά, να γράψουμε


for i in range(1,21):
    print(i, i**2, i**4)

και να πάρουμε το αποτέλεσμα


1 1 1
2 4 16
3 9 81
4 16 256
5 25 625
6 36 1296
7 49 2401
8 64 4096
9 81 6561
10 100 10000
11 121 14641
12 144 20736
13 169 28561
14 196 38416
15 225 50625
16 256 65536
17 289 83521
18 324 104976
19 361 130321
20 400 160000

Το μόνο πρόβλημα στον παραπάνω πίνακα είναι η στοίχιση των αριθμών. Προφανώς θα παίρναμε ένα αισθητικά καλύτερο αποτέλεσμα αν στοιχίζαμε την πρώτη στήλη σε ένα πλάτος πεδίου 2, τη δεύτερη στήλη σε ένα πλάτος πεδίου 3 και την τελευταία στήλη σε ένα πλάτος πεδίου 6. Ένας τρόπος να δώσουμε αυτές τις οδηγίες μορφοποίησης στην Python είναι ο παρακάτω:


for i in range(1,21):
    print('{:2d}\t{:3d}\t:{:6d}'.format(i, i**2, i**4)

Το αποτέλεσμα αυτής της μορφοποίησης είναι


 1	  1	     1
 2	  4	    16
 3	  9	    81
 4	 16	   256
 5	 25	   625
 6	 36	  1296
 7	 49	  2401
 8	 64	  4096
 9	 81	  6561
10	100	 10000
11	121	 14641
12	144	 20736
13	169	 28561
14	196	 38416
15	225	 50625
16	256	 65536
17	289	 83521
18	324	104976
19	361	130321
20	400	160000

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

Ο πρώτος κωδικός μορφοποίησης είναι ο {:2d} ο οποίος, εναλλακτικά, μπορεί να γραφεί και ως {0:2d}, με το ψηφίο μηδέν να αναφέρεται στον αριθμό του ορίσματος προς μορφοποίηση (θυμόμαστε ότι η Python αριθμεί ξεκινώντας από το μηδέν). Μεταξύ των κωδικών μορφοποίησης υπάρχει ο χαρακτήρας εσοχή (tab) '\t' απλά και μόνο για να αφήσει ένα αριθμό από κενούς χαρακτήρες μεταξύ των στοιχείων που εκτυπώνονται. Ο κώδικας 2d αποτελείται από ένα αριθμό, το πλάτος του πεδίου στο οποίο θα τυπωθεί το προς μορφοιποίηση στοιχείο και τον χαρακτήρα d ο οποίος δηλώνει τον τρόπο αναπαράστασης του στοιχείου που μορφοποιείται. Ο χαρακτήρας d αφορά στους ακέροιους αριθμούς στο δεκαδικό σύστημα (decimal integers). Άλλοι κωδικοί, κατάλληλοι για ακεραίους είναι οι b για εκτύπωση στο δυαδικό σύστημα (binary system), o για εκτύπωση στο οκταδικό σύστημα (octal), x ή X για εκτύπωση στο δεκαεξαδικό σύστημα (hexadecimal) και n για εκτύπωση πάλι στο δεκαδικό σύστημα αλλά με εμφανή τον χαρακτήρα για το διαχωρισμό χιλιάδων. Ως παράδειγμα, εκτυπώνουμε τους αριθμούς από το 32 ως το 49 στο δεκαδικό, οκταδικό, δεκαεξαδικό και δυαδικό σύστημα χρησιμοποιώντας τη μορφοποίηση '{:2d}\t{:2o}\t{:2x}\t{:7b}'. Μπορούμε, βεβαίως, να παραλέιψουμε το πλάτος του πεδίου και να γράψουμε, για παράδειγμα, {:d}. Σε αυτή την περίπτωση θα επιλεγεί, για κάθε αριθμό προς μορφοποίηση το κατάλληλο πλάτος πεδίου.


32	40	20	 100000
33	41	21	 100001
34	42	22	 100010
35	43	23	 100011
36	44	24	 100100
37	45	25	 100101
38	46	26	 100110
39	47	27	 100111
40	50	28	 101000
41	51	29	 101001
42	52	2a	 101010
43	53	2b	 101011
44	54	2c	 101100
45	55	2d	 101101
46	56	2e	 101110
47	57	2f	 101111
48	60	30	 110000
49	61	31	 110001

Για τους πραγματικούς αριθμούς, ακριβέστερα για τους αριθμούς κινητής υποδιαστολής (floating point numbers) η Python παρέχει τους κωδικούς μορφοποίησης e, E, f, D, g, G, n και %. Οι λεπτομέρεις έχουν ως εξής: οι κωδικοί f, F εκτυπώνουν 6 δεκαδικά ψηφία μετά την υποδιαστολή, σε ένα πλάτος πεδίου ανάλογο με μέγεθος του αριθμού που τυπώνεται. Το πλάτος πεδίου και ο αριθμός των δεκαδικών ψηφίων μετά την υποδιαστολή μπορεί να δοθεί πριν τον κωδικό f όπως στην περίπτωση {:8.5f}, όπου χρησιμοποιείται ένα πλάτος πεδίου 8 και 5 δεκαδικά ψηφία. Για παράδειγμα, οι εντολές


for i in range(1,13): print('{:2d}--{:f}--{:6.3f}'.format(i, 1/i, 1/i))

θα εκτυπώσουν


 1--1.000000-- 1.000
 2--0.500000-- 0.500
 3--0.333333-- 0.333
 4--0.250000-- 0.250
 5--0.200000-- 0.200
 6--0.166667-- 0.167
 7--0.142857-- 0.143
 8--0.125000-- 0.125
 9--0.111111-- 0.111
10--0.100000-- 0.100
11--0.090909-- 0.091
12--0.083333-- 0.083

Οι κωδικοί e, E εκτυπώνουν τα αντίστοιχα ορίσματα χρησιμοποοιώντας τον επιστημονικό συμβολισμό. Είναι δυνατόν να καθορίσουμε τόσο το πλάτος πεδίου όσο και τον αριθμό των δεκαδικών ψηφίων. Αν αυτός δεν δοθεί τότε τυπώνονται 12 δεκαδικά ψηφία μετά την υποδιαστολή. Για παράδειγμα, χρησιμοποιώντας τον κωδικό μορφοποίησης {:15.8e} για να εκτυπώσουμε τους αριθμούς $\sin 1, \sin 2,\ldots, \sin 12$, λαμβάνουμε:


 8.41470985e-01
 9.09297427e-01
 1.41120008e-01
-7.56802495e-01
-9.58924275e-01
-2.79415498e-01
 6.56986599e-01
 9.89358247e-01
 4.12118485e-01
-5.44021111e-01
-9.99990207e-01
-5.36572918e-01

Ο κωδικός % πολλαπλασιάζει το αντίστοιχο όρισμα με το 100 εκτυπώνει χρησιμοποιώντας τον κωδικό μορφοποίησης f ακολουθούμενο από το σύμβολο %. Οι κωδικοί g, G μοροφοποιούν το αντίστοιχο όρισμα είτε όπως ο κωδικός f είτε όπως ο κωδικός e, ανάλογα με το μέγεθός του. Ο κωδικός μορφοποίησης n είναι ανάλογος αλλά εκτυπώνει, επιπλέον, το σύμβολο διαχωρισμού των χιλιάδων.

Τέλος, για τις ακολουθίες χαρακτήρων μπορούμε να χρησιμοποιήσουμε τους κωδικούς μορφοποίησης {:<w}, {:>w}, {:^p} για να εκτυπώσουμε το αντίστοιο όρισμα σε ένα πλάτος πεδίου w, στοιχισμένο αριστερά, δεξιά ή στη μέση, αντίστοιχα.


for i in range(1,10): print('{0:>{width}}'.format('*', width = i)

*
 *
  *
   *
    *
     *
      *
       *
        *

Δείτε και το πρόγραμμα για τον πίνακα της προαπαίδειας.