Skriptkapitel

Werbung
Kapitel 4
Iterationen
Anything that happens, happens. Anything
that, in happening, causes something else to
happen, causes something else to happen.
Anything that, in happening, causes itself to
happen again, happens again. It doesn’t
necessarily do it in chronological order,
though.
(Douglas N. Adams, Mostly Harmless)
4.1 Einführung
Motivierendes Beispiel
„Aufgabe“
Esse alle Pralinen in einer Pralinenschachtel!
Annahme: Ich weiß, wie ich eine Praline essen kann.
Bildquelle: BeautifulCataya, CC 2.0
121
M. Werner: Algorithmen & Programmierung
Variante 1
Esse die erste Praline von links in der ersten Reihe.
Esse die zweite Praline von links in der ersten Reihe.
..
.
Esse die Praline ganz rechts in der ersten Reihe.
Esse die erste Praline von links in der zweiten Reihe.
..
.
Esse die erste Praline von links in der letzten Reihe.
Esse die zweite Praline von links in der letzten Reihe.
..
.
Esse die Praline ganz rechts in der letzten Reihe.
• Problem: Algorithmus1 ist evtl. sehr lang
Variante 2
• Weise den Pralinen die Nummern 1 bis n zu
• Zähle von 1 bis n und esse die Praline mit der jeweiligen Nummer
• Anmerkung: Es findet eine Abbildung von der Menge der natürlichen Zahlen N auf
die Menge der Pralinen statt
Variante 3
• Zähle die Pralinen á n
• Zähle von 1 bis n und esse bei jeder Zahl jeweils eine Praline
• Anmerkung: Der Vorgang des Essens wird für eine vorher bestimme Zahl von
Wiederholungen durchgeführt
Variante 4
• Solange noch eine Praline da ist: Esse eine (beliebige) Praline
• Anmerkung: Der Vorgang des Essens wird für eine (zunächst) unbestimme Zahl
von Wiederholungen durchgeführt
1 Genau
122
genommen ist es in der dargestellten Form gar kein Algorithmus; vgl. Kapitel 1.
4. Iterationen I 4.2 Schleifen in C und Python
Unterschiedliche Sichtweisen
• Wiederholen...
– ...für eine bestimmte Anzahl von Wiederholungen
– ...für Elemente einer Menge
– ...solange eine Bedingung (nicht) erfüllt ist
• Alle Varianten sind aufeinander abbildbar
• Solche Wiederholungen heißen in Programmiersprachen Schleifen
• Schleifen gibt es in vielen (nicht allen!) Programmiersprachen
• Allgemein gibt es bei Schleifen immer etwas Invariantes/Konstantes und i.d.R. etwas
sich Änderndes
4.2 Schleifen in C und Python
• In C gibt es drei Arten von Schleifen:
– while-Schleife
– do-while-Schleife
– for-Schleife
• Alle Schleifen nutzen Bedingungen
• Jede davon kann2 die anderen simulieren
• Welche genutzt wird, ist also z.T. Geschmackssache
while-Schleife
• Die while-Schleife führt ein Statement solange aus, wie eine Bedingung wahr ist.
• Sie arbeitet wie folgt:
1. Es wird überprüft, ob der Ausdruck in der Klammer nach dem Schlüsselwort
while eingehalten wird
2. Falls ja, wird eine Anweisung nach diesem Ausdruck ausgeführt (á Schleifenkörper) und anschließen wieder zu 1. gesprungen
– Wenn die Schleife mehrere Anweisungen umfassen soll, müssen diese in einem
Block geklammert werden
2 ...ggf
unter Nutzung anderer Sprachkonstrukte...
123
M. Werner: Algorithmen & Programmierung
3. Falls nein, wird mit der ersten Anweisung nach dem Schleifenkörper fortgesetzt
Anweisung (Schleifenkörper)
while(i<10) i=i+1;
Bedingung
Beispiele:
/ * sqr .c -- Heron ’s method * /
# include < stdlib .h >
# include < math .h >
# include < stdio .h >
int main ( int argc , char ** args )
{
double x =1.0;
double y= atof ( args [1]);
while ( fabs (y -x * x ) >0.00001){
x =0.5 * ( x+y/x );
}
printf (" sqrt (% f) = % f .\ n " ,y , x );
return 0;
}
# include < stdio .h >
int main ()
{
int i =3;
while (i >0) {
printf (" Cthulhu !\ n " );
i=i -1;
}
return 0;
}
Schleifen und Felder
• Schleifen können zur Bearbeitung oder Belegung der Elemente eines Array zur Laufzeit
genutzt werden
124
4. Iterationen I 4.2 Schleifen in C und Python
• Beispiel:
# include < stdio .h >
enum { arraysize =12};
/ * constant for array size * /
int main ()
{
int xa [ arraysize ] ,i =0;
while (i < arraysize ){
xa [i ]= i * i;
i=i +1;
}
/ * 0 ,1 ,... ,11
*/
/ * square of index * /
i =0;
while (i < arraysize ){
/ * 0 ,1 ,... ,11
*/
printf (" Element %d of xa : % d \n" ,i , xa [i ]);
i=i +1;
}
return 0;
}
do-while-Schleife
• Die do-while-Schleife ähnelt der while-Schleife
• Unterschied: Die Bedingung wird nach einem Schleifenduchlauf geprüft
• Damit der Compiler weiß, dass eine Schleife beginnt, wird zu Schleifenbeginn das
Schlüsselwort do gesetzt
do i=i+1; while(i<10);
Schleifenkörper
Bedingung
• Wieder müssen bei mehr als einer Anweisung im Schleifenkörper diese in geschweifte
Klammern gesetzt werden
• do-while-Schleife werden immer dann eingesetzt, wenn der Schleifenkörper mindestens
einmal ausgeführt werden soll
125
M. Werner: Algorithmen & Programmierung
for-Schleife
• Typisch:
– Vor einer Schleife wird ein Wert initialisiert
– Im Schleifenkörper wird ein (meist derselbe) Wert geändert
• Die for-Schleife lässt beides explizit in der Schleifendeklaration zu
• Wieder: Mehr als ein Statement im Schleifenkörper muss geklammert werden
• Achtung: Der Schleifenkörper kann leer sein á einzelnes Semikolon
– Schleifenkörper im Schleifenkopf integriert
– Schlechter Stil, außer bei sehr einfachen Schleifen
Initialisierung
for(int i=0;i<10; i=i+1) printf("i=%d \n",i);
Bedingung
nach Schleifenkörper
Schleifenkörper
Beispiele für for-Schleife
/ * for .c -- loop with for * /
# include < stdio .h >
int main ( int argc , char * argv []){
for ( int i =0; i < argc ; i =i +1)
printf ("%d. argument : % s\ n" ,i +1 , argv [i ]);
return 0;
}
> ./for 23 foo Bar 42
1. argument: ./for
2. argument: 23
3. argument: foo
4. argument: Bar
5. argument: 42
• Es können einzelne Ausdrücke weggelassen werden...
126
4. Iterationen I 4.2 Schleifen in C und Python
/ * for2 . c -- loop with for , #2 * /
# include < stdio .h >
int main ( int argc , char * argv []){
int i =0;
for (; i < argc ; i= i +1)
printf ( "% d. argument : %s \n " ,
i +1 , argv [i ]);
return 0;
}
• ... oder auch alle Ausdrücke á Endlosschleife
/ * for - ever . c -- infinite loop * /
# include < stdio .h >
int main (){
for (;;)
printf (" The answer is 42.\ n" );
return 0;
// never reached
}
• Anmerkung: Vor C99 konnte in einer for-Schleife eine Laufvariable nur initialisert,
aber nicht deklariert werden
• Die Deklaration musste also vorher erfolgen
• Das obige Beispiel würde in ANSI-C nicht übersetzbar sein und müsste stattdessen so
aussehen:
C
/ * for .c -- loop with for , ANSI C * /
# include < stdio .h >
Version
int main ( int argc , char * argv []){
int i ;
for (i =0; i < argc ; i= i +1)
printf ( "% d. argument : %s \n " ,i +1 , argv [i ]);
return 0;
}
127
M. Werner: Algorithmen & Programmierung
Quelle: xkcd - A webcomic of romance, sarcasm, math, and language
http://xkcd.com/411/
break und continue
• Schleifen werden solange durchlaufen, wie die Schleifenbedingung gegeben ist
• Allerdings gibt es zwei Möglichkeiten, innerhalb des Schleifenkörpers den Kontrollfluss zu ändern:
break: Beendet die Schleife unabhängig von der Schleifenbedingung
continue: Startet sofort die nächste Auswertung der Schleifenbedingung und ggf. den
nächsten Schleifendurchlauf
Achtung!
Die Benutzung von break und continue ist eigentlich nicht notwendig und ein Indiz,
dass die Programmlogik nicht hinreichend durchdacht wurde.
Daher sollten break und continue nur zur Behandlung von Notfällen (Ausnahmen)
genutzt werden.
/ * reciprocal .c -- calculate reciprocal value of array elements * /
# include < stdio .h >
/ * a negative value indicates end of list * /
double f []={1.0 , .5 , 3.1415 , .33333 , 0.0 , 2.7182 , 42.23 , -1};
int main ()
{
int i;
for (i =0; ; i=i +1){
128
4. Iterationen I 4.2 Schleifen in C und Python
if ( f[i ] <0.0) break ;
if ( f[i ] <=0.0001) continue ;
f[i ]=1/ f[ i ];
}
for (i =0; f[ i ] >=0.0; i= i +1)
printf (" %d value : %f \n " ,i ,f [i ]);
return 0;
}
Geschachtelte Schleifen
• Schleifen können auch ineinander verschachtelt sein
• Dabei können sich die Schleifen auch beeinflussen
/ * for3 . c -- nested loops * /
# include < stdio .h >
int main ( int argc , char * argv []){
int goal ;
if ( sscanf ( argv [1] , "% d" ,& goal )!=1) return -1;
for ( int i =1; i <= goal ; i= i +1)
{
for ( int j =1; j <= i ; j =j +1)
printf ( "% d " ,i );
printf (" \n " );
}
return 0;
}
Schleifen in Python
• In Python gibt es zwei Arten von Schleifen:
– while-Schleifen
– for-Schleifen
• while-Schleifen ähneln ihren Pendants in C:
129
M. Werner: Algorithmen & Programmierung
i =10
while (i >0):
print ("i=" ,i)
i=i -1
• Wie immer in Python können Blöcke durch Einrückungen markiert werden
Schleifensteuerung
• Auch Python kennt die Schlüsselwörter break und continue
• Die Semantik ist gleich zu C
• Zusätzlich gibt es bei Python-Schleifen einen optionalen else-Zweig
á Wird nach der Schleife ausgeführt, wenn diese nicht abgebrochen wurde
def prim (x ):
y= int (x /2)
while y >=2:
if x % y == 0:
print (x , " has factor " , y)
break
y=y -1
else :
print (x , " is prim ")
• Mit Hilfe von break kann leicht die do-while-Schleife aus C nachempfunden werden
i =0
while True :
print ("i=" ,i)
i=i +1
if not (i <10): break
print (" Ende ")
for-Schleife in Python
• Die Syntax for-Schleife sieht zunächst ähnlich wie die while-Schleife aus
130
4. Iterationen I 4.2 Schleifen in C und Python
for i in C :
do _ something ( i)
• Die Semantik ist jedoch anders:
– C muss eine Sequenz oder Menge sein (d.h. String, Liste, Tupel, Menge, Dictionary)3
– Es werden i nacheinander die Elemente von C zugewiesen
# for . py -- loops over sequence objects
for x in [ ’ John ’, ’ Paul ’ ,’ Georg ’ ,’ Pete ’]:
print ( x)
• Dies funktioniert auch für gemischte Typen:
# for2 . py -- mixed types
T =( ’ Alpha ’ ,2 ,(1 ,2))
for x in T :
print (2 * x)
python for2.py
AlphaAlpha
4
(1, 2, 1, 2)
• Um die Semantik der C-for-Schleife nachzuempfinden, kann die range-Funktion genutzt
werden
range(start=0, end, step=1 )
Von
Bis
(nicht einschliesslich)
Schrittweite
»> range(5), range(2,5), range(2,10,2)
([0, 1, 2, 3, 4], [2, 3, 4], [2, 4, 6, 8])
»>
3 Genauer:
Es muss ein iterierbarer Typ sein.
131
M. Werner: Algorithmen & Programmierung
# for3 . py -- loops with range
for i in range (1 ,8):
for j in range (i ):
print (i , end = ’ ’)
print ()
Iteratoren in C
• Umgekehrt kann man auch den Python-Ansatz in C „nachempfinden“
/ * iter .c -- iterator with C * /
# include < stdlib .h >
# include < stdio .h >
static int ndx =0;
char * first () { return names [ ndx =0]; }
char * next (){
if ( names [ ndx ]== NULL ) ndx =0;
else ndx = ndx +1;
return names [ ndx ];
}
char * names []={ " John " ," Paul " ," Georg " ,
" Ringo " , NULL };
int main (){
for ( char * str = first (); str != NULL ;
str = next ())
printf ("%s " , str );
return 0;
}
• Solche Iteratoren sind z.B. in C++ verbreitet
á Dort aber eleganter (auch in C geht es schöner
132
)
4. Iterationen I 4.3 Iteration und Rekursion
4.3 Iteration und Rekursion
• Bisher wurden in Schleifen immer (implizite) Zuweisungen benutzt á Zustandsmodell
• Auch im funktionalen Modell sind Schleifen möglich
• Dort werden sie durch Rekursion realisiert
• Beispiel:
/ * recursion . c -- loops by recursion * /
# include < stdio .h >
void say _ it ( int i ){
if ( i ==0) return ;
else {
printf (" Cthulhu !\ n " );
say _ it (i -1);
}
}
int main (){
say _ it (3);
return 0;
}
Schleifenfunktion
• Dabei kann der Schleifenkörper von der „Schleifenmechanik“ getrennt werden, so dass
eine Schleifenfunktion möglich ist
• Wir nutzen hier einen Zeiger auf Funktionen
/ * loop - func .c * /
# include < stdio .h >
// Pointer to a void function with integer argument
typedef void ( * VFP )( int );
void loop ( VFP func , int start , int end ,
unsigned int step ){
if ( start <= end ) {
func ( start );
loop ( func , start + step , end , step );
133
M. Werner: Algorithmen & Programmierung
}
}
void lbody ( int param ){
printf ("%d ^2=% d\n" , param , param * param );
}
int main (){
loop ( lbody ,0 ,10 ,1);
return 0;
}
• Dieses Beispiel hat einige Einschränkungen:
– Der Typ der Schleifenkörper-Funktion ist festgelegt á Der Schleifenkörper kann
nur auf den Schleifenindex (und globale Variablen) zugreifen
– Es muss step> 0 gelten á Die Schleife kann nur aufwärts zählen
• Man kann diese Einschränkungen überwinden, aber dies würde den Code „aufblasen“
• Allgemeiner (und in der Anwendung eleganter) funktioniert dies in Python, das mit
λ-Funktionen (namenlosen Funktionen) ein Feature funktionaler Sprachen bietet
# loop - func . py -- loops by recursion
from sys import stdout
loop = lambda v ,e ,i ,f , args : \
e(v) and (f ( * (( v ,)+ args )) , \
loop (( lambda y: y+i )( v),e ,i ,f , args )) or 0
def printpow (i ,x ):
print (i ," ** " ,x ,"=" ,i ** x)
def printi (i ,b ,a ): stdout . write (b+ str (i )+ a)
loop (1 , lambda x: x <=4 ,1 , printpow ,(2 ,))
loop (10 , lambda x: x >=0 , -1 , printi , ("[" ,"] " ))
>python loop-func.py
1 ** 2 = 1
2 ** 2 = 4
3 ** 2 = 9
4 ** 2 = 16
[10] [9] [8] [7] [6] [5] [4] [3] [2] [1] [0]>
134
4. Iterationen I 4.3 Iteration und Rekursion
Anmerkung:
Wenn Sie dieses Beispiel nicht verstehen, können Sie es ignorieren – λ-Funktionen
werden in Kurs „Funktionale Programmierung“ behandelt
Äquivalenz
• Allgemein gilt:
Jede Schleife in einem Programm kann in eine rekursive Funktion umgewandelt
werden.
und
Jede (trivial-)rekursive Funktion kann mit Hilfe von Schleifen berechnet werden.4
• Nicht trivialerekursiv
ist z.B. die


y + 1
f (x, y) = f (x − 1, 1)


f (x − 1, f (x, y − 1))
Ackermann-Funktion:
⇐x=0
⇐ (x 6= 0) ∧ (y = 0)
sonst
• Schleifen und Rekursion sind also für die meisten praktischen Anwendungsfälle gleichmächtig
• In der Regel ist eine Schleife übersichtlicher
• Es gibt aber auch Fälle (vgl. Kapitel 9), in denen Rekursion die handlichere Lösung
bildet
• Einige Programmiersprachen kennen keine Schleifen, sondern nur Rekursionen
Aufgaben
Aufgabe 4.1
Was bedeutet in C die folgende Deklaration:
int* (*foo)(int,int*)
4 Genauer:
Primitiv-rekursive Funktionen lassen sich stets mit a priori beschränkten Schleifen berechnen;
bei sogenannten µ-rekursive Funktionen muss diese Einschränkung aufgegeben werden.
135
M. Werner: Algorithmen & Programmierung
Aufgabe 4.2
In einer Schale und in einem Eimer seien eine Anzahl von weißen und orangen
Tischtennisbällen, wobei soviel Bälle im Eimer sind, dass der folgende Algorithmus
stets durchgeführt werden kann:
1. Nehme zwei beliebige Bälle aus der Schale.
• Wenn sie die gleiche Farbe haben, werfe sie in den Eimer und nehme
einen orangen Ball aus dem Eimer und lege ihn in die Schale.
• Wenn sie unterschiedliche Farbe haben, lege den weißen in die Schale
zurück und werfe den orangen Ball in den Eimer.
2. Wiederhole, bis nur noch ein Ball in der Schale ist.
Wird tatsächlich stets ein Ball übrig bleiben? Wenn ja, kann etwas über seine
Farbe in Abhängigkeit zur ursprünglichen Anzahl der Bälle gesagt werden? Wenn
ja, was?
136
Herunterladen