Η εντολή ανακύκλωσης while

Η εντολή ανακύκλωσης while είναι η δεύτερη εντολή ανακύκλωσης που βλέπουμε μετά τη for. Η while ελέγχει τη συνθήκη της (όπως και το if) και όσο η συνθήκη είναι True τότε εκτελούνται οι εντολές που υπάγονται στη while (οι οποίες είναι γραμμένες σπρωγμένες προς τα μέσα (indented) κάτω από τη while).

Στο παρακάτω πρόγραμμα τυπώνουμε τους αριθμούς από 1 έως και 10.

Χσρηιμοποιούμε μια μεταβλητή "μετρητή", την i που έχει αρχικά τιμή 1. Στη συνθήκη του while ελέγχουμε αν η μεταβλητή αυτή είναι \(\le 10\) και, αν αυτό συμβαίνει, μπαίνουμε στο loop και το i τυπώνεται και αυξάνεται κατά 1.

In [1]:
i=1
while i <= 10:
    print i,
    i += 1
1 2 3 4 5 6 7 8 9 10

Η εντολή ανακύκλωσης while χρησιμοποιείται φυσιολογικά (αντί για τη for) όταν δεν είμαστε εκ των προτέρων σίγουροι για το πόσες φορές θα εκτελέσουμε το loop. Στο παρακάτω πρόγραμμα διαβάζουμε ακεραίους από το χρήστη και τους αθροίζουμε κάθε φορά. Όταν ο χρήστης θέλει να σταματήσει τότε μας δίνει τον ακέραιο 0, η οποίος σηματοδοτεί για το πρόγραμμά μας ότι θέλουμε να σταματήσουμε.

Είναι φανερό ότι δε θα είχε νόημα εδώ να χρησιμοποιήσουμε την εντολή for μια και δε μπορούμε να ξέρουμε από την αρχή πόες φορές θα χρειαστεί να τρέξει η ανακύκλωση.

In [4]:
x = int(raw_input("Give an integer, 0 to finish: "))
s = 0

while x != 0:
    s += x
    print "The current sum is", s
    x = int(raw_input("Give an integer, 0 to finish: "))
    
print "Bye"
Give an integer, 0 to finish: 4
The current sum is 4
Give an integer, 0 to finish: 3
The current sum is 7
Give an integer, 0 to finish: 4
The current sum is 11
Give an integer, 0 to finish: 0
Bye

Έστω η συνάρτηση \(f(x) = x^3+x+1\). Θέλουμε, δοθέντος ενός ακεραίου \(y\ge 0\) να αποφασίσουμε αν υπάρχει ακέραιος \(x \ge 0\) τέτοιος ώστε \(f(x)=y\), και αν υπάρχει να βρούμε ποιος είναι. Αφού η συνάρτηση \(f(x)\) είναι γνησίως αύξουσα, ο τρόπος που ακολουθούμε για να το βρούμε είναι ότι αρχίζουμε από την τιμή \(x=0\) και, αυξάνοντας συνεχώς το \(x\) κατά 1, ελέγχουμε αν το \(f(x)\) είναι ακόμη \(< y\).

Όταν αυτό παύει να συμβαίνει τότε ξέρουμε ότι \(y \le f(x)\). Αν ισχύει η ισότητα έχουμε βρει τη (μοναδική) λύση μας, αλλιώς είμαστε σίγουροι (λόγω της μονοτονίας της συνάρτησης) ότι δεν υπάρχει λύση.

Αυτό υλοποιείται παρακάτω με μια ανακύκλωση while. Στη μεταβλητή f κρατάμε την τιμή \(f(x)\) για την τρέχουσα τιμή του \(x\) (που βρίσκεται στη μεταβλητή x).

In [5]:
y = int(raw_input("Give an integer: "))
                  
x = 0
f = 1

while f < y :
    print "Examining",x
    x += 1
    f = x*x*x + x + 1

if f == y:
    print "Solution is", x
else:
    print "No solution exists"
    
Give an integer: 1012
Examining 0
Examining 1
Examining 2
Examining 3
Examining 4
Examining 5
Examining 6
Examining 7
Examining 8
Examining 9
Examining 10
No solution exists

Παρακάτω αφαιρούμε από μια λίστα όλα τα 2άρια που υπάρχουν μέσα. Η μέθοδος remove αφαιρεί μόνο το πρώτο 2άρι, οπότε χρειάζονται επανειλημμένες κλήσεις σε αυτήν, όσο εξακολουθεί να υπάρχει 2άρι στη λίστα.

In [8]:
L = input("Please give a list of integers: ")

while 2 in L:
    L.remove(2)
    
Please give a list of integers: [1, 2, 2, 3, 4, 5,2, 7]
[1, 3, 4, 5, 7]

Ένα λίγο περίπλοκο παράδειγμα. Μια μικρή βάση δεδομένων.

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

Αρχίζουμε με τη λίστα L που ορίζεται στην πάνω μεριά του προγράμματος. Αυτή η λίστα περιέχει όλη την πληροφορία που θέλουμε, είναι η "βάση δεδομένων" μας. Η λίστα L περιέχει ένα αντικείμενο για κάθε φοιτητή της μορφής

[ ΌνομαΦοιτητή, [Μάθημα1, Μάθημα2, ...]]

Για κάθε φοιτητή δηλ. περιέχει μια λίστα, έστω x, με δύο στοιχεία. Το πρώτο στοιχείο, το x[0] είναι ένα string, το όνομα του φοιτητή. Το δεύτερο στοιχείο, το x[1], είναι μια λίστα. Η λίστα αυτή περιέχει όλα τα μαθήματα στα οποία έχει εγγραφεί ο φοιτητής.

Έτσι, η λίστα L που δίνεται στο πρόγραμμα περιέχει όλες τις εγγραφές σε μαθήματα για 4 φοιτητές.

Το πρώτο κομμάτι του προγράμματος

students = []; courses=[]

for x in L:
    students.append(x[0])
    for y in x[1]:
        if y not in courses:
            courses.append(y)

βρίσκει τη λίστα students με τα ονόματα όλων των φοιτητών και τη λίστα courses με τα ονόματα όλων των μαθημάτων που εμφανίζονται. Και οι δύο αυτές λίστες έχουν ως αρχική τιμή την κενή λίστα. Έπειτα διανύουμε τη λίστα L και για κάθε στοιχείο της (για κάθε φοιτητή δηλ.) ενημερώνουμε τις δύο αυτές λίστες αυξάνοντάς τις κατά το όνομα του φοιτητή την πρώτη και κατά το όνομα του μαθήματος τη δεύτερη. Ειδικά για τη δεύτερη λίστα προσέχουμε να μη ξαναβάλουμε στη λίστα ένα μάθημα αν έχει ήδη μπει εκεί.

Ο κύριος σκοπός του προγράμματος αυτού είναι να "αντιστρέψουμε" κατά κάποιο τρόπο τη μορφή στην οποία είναι αποθηκευμένη η πληροφορία μας. Στη βάση L η πληροφορία μας είναι αποθηκευμένη κατά φοιτητή. Στη λίστα M που θα φτιάξουμε η πληφοροφορία θα είναι αποθηκευμένη κατά μάθημα, θα έχει δηλ. η λίστα M γραμμές της μορφής (μια για κάθε μάθημα)

[ ΌνομαΜαθήματος, [Φοιτητής1, Φοιτητής2, ...]]

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

Πρώτα αρχικοποιούμε τη λίστα M με το παρακάτω:

M = []
for x in courses:
    M.append( [x, [] ] )

Έτσι ώστε όλες οι γραμμές της M είναι πλέον της μορφής

[ ΌνομαΜαθήματος, []]

Έχουμε δηλ. βάλει τα ονόματα των μαθημάτων στη θέση τους αλλά οι λίστες με τους εγγεγραμμένους φοιτητές είναι αρχικά κενές, και θα πρέπει να τις γεμίσουμε. Αυτό γίνεται με τον κώδικα

for x in L:
    name = x[0]
    stcourses = x[1]
    for y in stcourses:
        for z in M:
            if z[0]==y:
                z[1].append(name)
                break

Ο κώδικας αυτός έχει αρκετό "βάθος", αφού έχει μια ανακύκλωση for που περιέχει μια άλλη ανακύκλωση for η οποία περιέχει με τη σειρά της μια άλη ανακύκλωση for.

Η στρατηγική μας είναι ότι διανύουμε τη λίστα L (εξωτερικό for loop), και για κάθε στοιχείο (γραμμή) x της L που βρίσκουμε κρατάμε στις μεταβλητές name και stcourses το όνομα του φοιτητή και τη λίστα με τα μαθήματά του. Έπειτα για κάθε ένα από τα μαθήματα του φοιτητή, έστω y (αυτό γίνεται στο μεσαίο for loop), πρέπει να προσθέσουμε το όνομα του φοιτητή στη λίστα του μαθήματος y μέσα στη μεγάλη λίστα M. Εδώ είναι που χρειάζεται το εσωτερικό for loop όπου διανύουμε τα στοιχεία z της λίστας Μ αν το στοιχείο αυτό της M που έχουμε αντιστοιχεί στο μάθημα y (το ελέγχουμε με z[0]==y) τότε προσθέτουμε στη λίστα αυτού του στοιχείου z[1] το όνομα name του φοιτητή.

Η λίστα M που φτιάξαμε τυπώνεται με τον κώδικα:

print "List of courses and registered students follows:"
for x in M:
    print x

Στο επόμενο κομμάτι κώδικα

reg = 0
print "---"
print "Number of registered students per course:"
for x in M:
    print x[0], len(x[1])
    reg += len(x[1])
    
print "Number of (student, course) pairs:", reg

τυπώνουμε κατ'αρχήν το πόσοι φοιτητές είναι εγγεγραμμένοι σε κάθε μάθημα και, ταυτόχρονα, στο ίδιο for loop υπολογίζουμε το συνολικό αριθμών ζευγών (φοιτητής, μάθημα στο οποίο εγγράφτηκε) που υπάρχει στη μεταβλητή reg.

Στο τελευταίο κομμάτι κώδικα:

name1 = raw_input("Give name of course No 1: ")
name2 = raw_input("Give name of course No 2: ")

diff = []
for x in L:
    if name1 in x[1] and name2 not in x[1]:
        diff.append(x[0])
print diff

Ζητάμε από το χρήστη τα ονόματα δύο μαθημάτων και βρίσκουμε στη λίστα diff τα ονόματα όλων των φοιτητών που έχουν εγγραφεί στο πρώτο μάθημα αλλά όχι στο δεύτερο.

In [6]:
L = [
     [ "Mihalis", ["Prog1", "Calc1", "Foundations"]],
     [ "Maria", ["Prog1", "Calc1", "Physics"]],
     [ "Yannis", ["Calc1", "Physics", "Foundations", "Prog2"]],
     [ "Eleni", ["Calc1", "Physics", "English I"]]
    ]

students = []; courses=[]

for x in L:
    students.append(x[0])
    for y in x[1]:
        if y not in courses:
            courses.append(y)
    
M = []
for x in courses:
    M.append( [x, [] ] )
    
for x in L:
    name = x[0]
    stcourses = x[1]
    for y in stcourses:
        for z in M:
            if z[0]==y:
                z[1].append(name)
                break

print "List of courses and registered students follows:"
for x in M:
    print x

reg = 0
print "Number of registered students per course:"
for x in M:
    print x[0], len(x[1])
    reg += len(x[1])
    
print "---"
print "Number of (student, course) pairs:", reg

name1 = raw_input("Give name of course No 1: ")
name2 = raw_input("Give name of course No 2: ")

diff = []
for x in L:
    if name1 in x[1] and name2 not in x[1]:
        diff.append(x[0])
print diff
List of courses and registered students follows:
['Prog1', ['Mihalis', 'Maria']]
['Calc1', ['Mihalis', 'Maria', 'Yannis', 'Eleni']]
['Foundations', ['Mihalis', 'Yannis']]
['Physics', ['Maria', 'Yannis', 'Eleni']]
['Prog2', ['Yannis']]
['English I', ['Eleni']]
Number of registered students per course:
Prog1 2
Calc1 4
Foundations 2
Physics 3
Prog2 1
English I 1
---
Number of (student, course) pairs: 13
Give name of course No 1: Calc1
Give name of course No 2: Prog1
['Yannis', 'Eleni']

In []: