 Herzlich willkommen zurück zu Programmierparadigmen. Wir sind hier im Modul zum Thema Kontrollfluss und das ist Teil 4, in dem es um Iterationen gehen soll. Also quasi der Frage, wie können wir in einem Programm eigentlich Dinge mehrmals machen, zum Beispiel indem wir Schleifen verwenden oder über die Elemente in irgendeiner Form von Datenstruktur iterieren. Diese Iterationskonstrukte, die man in quasi jeder Programmiersprache hat, sind sehr, sehr wichtig und eigentlich essentiell um wirklich interessante Programme aufschreiben zu können. Der Grund ist, dass man, wenn man diese Konstrukte nicht hätte, eigentlich nur so viel Berechnung oder Arbeit wirklich machen kann in dem Programm, wie das Programm groß ist, also wie der Source Code des Programms groß ist. Und der einzige Weg quasi mehr zu tun als die Länge des Programms hergibt, ist indem man Dinge mehrmals tut und dafür braucht man irgendeine Form von Iterationen. In den meisten Sprachen gibt es eigentlich zwei grundlegende Formen von Schleifen, die einem erlauben Iterationen zu implementieren. Das eine ist, sind Schleifen, die durch irgendeine Form von Aufzählung, also Inumeration kontrolliert werden, indem ich zum Beispiel durch eine Endliche Menge durchgehe und jedes Element dieser Menge einmal besuche und dafür dann irgendwas mache. Die zweite Art sind logisch kontrollierte Schleifen, indem ich irgendeine Bedingungen habe, die ich als logischen Ausdruck darstellen kann und die Iterationen so lange läuft bis dieser bulge Ausdruck schlussendlich zu false evaluiert. Schauen wir uns die erste Form dieser Schleifen, nämlich dieser aufzählungsbasierten Schleifen mal zunächst an. In ihrer einfachsten Form besteht so eine Schleife eigentlich einfach nur aus drei Dingen, nämlich irgendeinen Stadtwert, irgendeinen Endwert, also hier Bound genannt, und eine Beschreibung, wie groß die Schritte sind, die mich vom Stadtwert zum Endwert bringen. Hier sind mein Beispiel von solchen Schleifen in zwei Programmiersprachen, die heutzutage gar nicht mehr so viel verwendet werden, aber wer das aus anderen Sprachen kennt, kann das vermutlich auch dann auf seine jeweilige Lieblingssprache mapeln, sofern es das dann in der Sprache überhaupt gibt. Also hier in dem Vortranenbeispiel haben wir so eine Du-Schleife, Du-Enddu, das ist quasi der Start und Ende von der Schleife, und was wir dann hier haben, ist zum einen dieser Stadtwert, nämlich 1, dann der Endwert, nämlich 10, und dann die Schrittgröße, nämlich 2. Und was diese Schleife schlussendlich machen wird, ist iterieren, und zwar so, dass wir fünf Iterationen insgesamt haben, bei denen i den Wert 1, 3, 5, 7 und 9 hat, also quasi alles, was zwischen 1 und 10 liegt, in zweier Schritten gerechnet, so dass wir schlussendlich aber nie größer als 10 sind. Und auf der rechten Seite hier sehen Sie im Prinzip genau dasselbe Beispiel mal in einer anderen Sprache geschrieben, wo die Sündtags ein bisschen anders ist, aber die Grundidee ist dieselbe, wir sagen es geht bei 1 los, geht bis 10 und wir gehen immer zwei Schritte voran, und was ich dadurch zeigen will, ist einfach nur, dass es natürlich verschiedene Sündtags gibt, um das auszudrücken, diese Grundidee einer Schleife, die ich durch so einen Triple angib, aber in vielen Sprachen existiert. Wenn irgendein Sprachkonstrukt oder so eine Familie von Sprachkonstrukten in vielen Sprachen existiert, dann gibt es natürlich ja immer verschiedene Varianten und die Details variieren so ein bisschen von Sprache zu Sprache und genauso ist das auch wieder hier. Und hier sind so eine Reihe von Fragen, die je nachdem welche Sprache man jetzt genau nimmt, so oder auch anders beantwortet werden kann. Eine Frage ist, ob man in der Mitte der Schleife aus dieser Schleife oder in der Mitte der Iteration aus der Schleife auch raus kann. Also kann ich, wenn ich eigentlich von 1 bis 10 gehen wollte, nach 5 auch sagen, so jetzt reicht es, und ich hier raus in manchen Sprachen geht das, zum Beispiel mit sowas wie einem Break Statement, in anderen Sprachen geht das aber nicht. Eine zweite Frage ist, ob man die Schleifenvariable, also das I in dem Beispiel, die ich gerade gesehen habe, zwischendurch verändern kann. Also kann ich, wenn ich im Schleifenbody bin, zum Beispiel das I auf irgendein Wert setzen und wenn ja, was bedeutet das dann? Bedeutet das, dass mein I wirklich wieder zurückgesetzt ist oder überschreibe ich vielleicht nur den lokal sichtbaren Wert innerhalb dieser Iteration und innerhalb des Schleifenbodies und hier variieren die Antworten auch wieder ein bisschen von Sprache zu Sprache und man muss einfach wissen, was das bedeutet, wenn man das in dem Programm aufschreibt. Eine dritte Frage ist, ob die Werte, die verwendet werden, um die Grenzen zu berechnen, während der Iteration verändert werden können. In dem Beispiel, in dem wir gerade hatten, standen die Grenzen einfach als Literale drin, also wir haben gesagt, es geht von 1 bis 10. Ich könnte aber auch sagen, es geht von 1 bis n und was passiert dann, wenn ich das n innerhalb der Schleifeniteration verändere und ist das überhaupt möglich? Das ist auch eine Frage, wo verschiedene Sprachen verschiedene Antworten geben und schlussendlich die Frage, ob ich überhaupt einen Zugriff auf diese Schleifenvariable habe und wenn ja, ob dann nur innerhalb der Iteration oder auch hinterher, auch hier gibt es verschiedene Antworten. So, jetzt haben wir diese einfachen aufzählungsbasierten Schleifen gesehen, bei denen man einfach nur dieses Trippel aus Stadt, Ende und Schrittgröße angibt. Etwas interessanter oder zumindest kompliziertere Varianten sind dann Iteratoren. Die Idee von Iteratoren ist, dass wir auch eine Aufzählung haben und dadurch eine Schleife beschreiben und zwar jetzt, indem wir über irgendeine Menge oder über irgendeine Sequenz von Werten iterieren, so dass jedes Element in dieser Menge oder dieser Sequenz genau einmal besucht wird. Also ein Beispiel, wo das nützlich ist, ist, wenn ich zwar schon eine Baumstruktur habe und jeden Knoten innerhalb dieses Baums genau einmal besuchen möchte oder wenn ich irgendeine Form von Collection habe, also einfach eine Liste oder eine Menge von Werten und für jedes Element innerhalb dieser Collection irgendetwas tun will. All das sind Situationen, wo ich ein Iterator möchte, weil der mir einfach erlaubt, all diese Elemente oder all diese Knoten einmal zu besuchen. Der große Vorteil davon, dass man das als Iterator programmiert und sich eben nicht jedes Mal Gedanken macht, wie genau ich jetzt über all diese Elemente iterieren kann, ist, dass ich zwei Arten von Algorithmen voneinander abtrennen kann. Zum einen dieser Algorithmus, der eben die Werte aufzählt und für jeden dieser Werte dann irgendetwas macht und der zweite Algorithmus, der die Werte dann tatsächlich benutzt und diesen eigentlich zwei verschiedene Paar Schuhe und der Vorteil an Iteratoren ist, dass man die Algorithmen getrennt voneinander entwickeln kann und zum Beispiel oft den Algorithmus, der das eigentliche Aufzählen macht, wiederverwenden kann, weil den schon irgendjemand anders für uns programmiert hat. Es gibt in der Praxis drei Arten von diesen Iteratoren, die so in manchen Sprachen verfügbar sind. In manchen Sprachen gibt es alle drei. In vielen Sprachen gibt es aber auch nur eine Teilmenge davon. Und das sind zum einen die sogenannten echten Iteratoren, dann die Iteratoren Objekte und schlussendlich die Variante, dass wir First Class Functions benutzen, um über Datenstrukturen zu iterieren und wir schauen uns alle drei von diesen Arten jetzt ein bisschen genauer an. Fangen wir mal an mit den sogenannten echten Iteratoren. Die Grundidee ist, dass wir den Algorithmus, der die Aufzählung der eigentlichen Werte betreibt, so implementieren, dass das im Prinzip einfach nur eine Subroutine oder Funktion ist, die aber diese besonderen Yield-Statements enthält. Und was sie Yield macht, ist, dass jedes Mal, wenn ich Yield aufrufe, diese Funktion so ein bisschen zurückkehrt und quasi ein weiteres Element zurückgibt, aber dann kommt die Kontrolle trotzdem schlussendlich wieder in diese Funktion rein, nämlich dann, wenn das Element bearbeitet wurde. Sprachen, in denen diese echten Iteratoren häufig verwendet werden und zur Verfügung stehen, sind zum Beispiel Python, Ruby oder auch C-Sharp. In Java gibt es die in der Form aktuell noch nicht, aber selbst wenn ihr die jetzt nicht kennt, solltet ihr auf jeden Fall nach ausprobieren und in der Übung wird es da wahrscheinlich auch eine Möglichkeit zu geben. Um mal zu sehen, wie diese Iteratoren dann verwendet werden, habe ich hier ein Beispiel aus Python. Was wir hier sehen, ist einfach eine Vorloop, in der wir von ja durch bestimmte Zahlenwerte iterieren, nämlich von irgendeinem ersten Wert bis zu irgendeinem letzten Wert mit einer gewissen Schrittgröße. Also das sieht so ein bisschen aus wie diese Triple-Loops, die wir vorhin schon gesehen haben. Bloß, dass das jetzt eben kein Sprach-Feature direkt ist, sondern dass wir diesen Range-Iterator benutzen, der eigentlich nur eine Funktion ist, die intern die Zahlen zwischen first und last alle aufzählt, immer mit step als Abstand. Und für jede dieser Zahlen einmal yield auf Ruf, sodass wir dann schlussendlich hier in dieser Vorloop genau diese Zahlen jeweils als i bekommen und das Ganze dann in Form dieser Schleife benutzen können. So, damit man so ein Iterator jetzt so schön einfach in der Schleife, wie gerade eben gesehen, benutzen kann, muss natürlich irgendwo der eigentliche Iterator implementiert sein und das ist wie gesagt eine Funktion, die dieses yield-Statement einmal oder eine Regel mehrmals aufruft. Ein Beispiel, das hier mal gegeben in Form einer Python-Klasse, die ein binären Baum implementiert. Also ein binärer Baum steht einfach nur aus Knoten, die jeweils wieder Kinder haben und er ist binär, weil jeder Knoten maximal zwei Kinder hat. Jeder von diesen Knoten speichert irgendwelche Daten, und zwar in diesem Data-Field. Und außerdem haben wir hier zwei Felder für das Kind auf der linken Seite und für das Kind auf der rechten Seite. Praktisch würde so eine Klasse natürlich noch andere Methoden haben, zum Beispiel um neue Elemente einzufügen, zu löschen oder Elemente nachzuschauen oder zu suchen, aber die sind jetzt hier mal nicht gezeigt, weil die nicht relevant sind, sondern was relevant ist, ist diese Methode hier, die durch all die Elemente in diesem Binärbaum durchgeht und für jedes Element im Binärbaum die jeweiligen Daten zurückgeht. Man könnte also mit Hilfe dieser Pre-Order-Funktion dann sozusagen ganz einfach durch die Daten, die in diesen Baum gespeichert sind, durchiterieren, und zwar in der Pre-Order, was also bedeutet zuerst die Eltern und anschließend die Kinder besuchen. Das Ganze läuft so, dass wir hier drei Fälle abdecken. Zum einen schauen wir, ob der aktuelle Knoten, in dem wir gerade sind, tatsächlich irgendwelche Daten enthält. Und wenn das so ist, also das nicht nann ist, dann geben wir diese Daten zurück, und zwar nicht einfach mit Return, weil dann, wenn wir ja schon fertig mit der Methode, sondern mit Yield, und Yield gibt dann sozusagen, wenn dieses Pre-Order in der Schleife benutzt wird, ein Element für eine Iteration zurück. Außerdem, aktuellen Element wollen wir natürlich in die Kinder links und rechts reinschauen, und das tun wir hier. Also wenn das Kind auf der linken Seite existiert, dann rufen wir rekosiv diese Pre-Order-Methode für dieses linke Kind auf, nutzen also sozusagen den Iterator, der Pre-Order ja bereitstellt selbst, um durch die Daten in dem Unterbaum auf der linken Seite zu iterieren. Und für jedes Datenelement D, was wir hier kriegen, geben wir das Element dann jeweils mit dem Yield auch wieder zurück. Und hier unten dann dasselbe nochmal mit dem rechten Teilbaum, wo wir einfach durch all die Kinder auf der rechten Seite genauso iterieren und die auch wieder zurückgehen. Und was diese Methode dann also insgesamt macht, ist wirklich alle Knoten in diesem Binärbaum einmal besuchen und jeweiligen Daten-Elemente, die da sind, zurückgehen. So, diese echten Iteratoren sind also ein Weg, wie man Iteratoren implementieren kann. Ein anderer, der auch in vielen Sprachen sehr beliebt ist, zum Beispiel in Java und C++ sind Iteratoren-Objekte. Das sind ganz normale Objekte, bloß haben die eben spezielle Methoden, die helfen durch Datenschruktur zu iterieren, und zwar genau genommen drei Methoden, nämlich einmal, um die Iteration zu initialisieren, also sozusagen alles zu tun, damit es dann losgehen kann. Dann einmal, um den jeweils nächsten Wert innerhalb der Datenstruktur über die wir iterieren zu erhalten und schlussendlich irgendwas womit ich schauen kann, ob ich schon am Ende bin, sodass ich quasi diesen nächsten Wert immer nur dann abfrage, wenn ich weiß, dass ich doch nicht am Ende bin. Hier unten sieht man mal ein Beispiel, wie das Ganze dann benutzt wird. Also wir erstellen diesen Iterator mithilfe der Methode, die den Iterate initialisiert. Das ist also in dem Fall hier dieser Iterator-Aufruf, wobei das C einfach irgendeiner Collection ist oder irgendeine Datenstruktur, die eben iterabel ist, also diesen Iterator-Methode anbietet. Dann benutzen wir hier diese next Methode, um das jeweils nächste Element innerhalb des schleifen Bodies auszulesen und dann um festzustellen, ob wir nochmal in die Schleife reingehen, haben wir außerdem noch diesen Aufruf zu Hasnext, bei dem wir überprüfen, ob es noch einen weiteren Wert gibt. Und was wir da insgesamt einfach bekommen, ist eine Schleife, die über all die Elemente dieser Collection namens C iteriert. Was ich hier jetzt aufgeschrieben habe, ist, wie man es quasi in Java gemacht hat, bevor es Java 5 gab. Seit Java 5 kann man das Ganze auch noch ein bisschen kürzer und eleganter hinschreiben, nämlich mithilfe dieser Vorloop, bei der ich einfach sage, ich möchte für jedes Element E in der Collection C etwas machen. Und was das Ganze macht, ist letztendlich genau derselbe, was man auch auf der linken Seite sieht. Bloß eben, dass man nicht explizit das Iterator-Objekt erstellen und immer überprüfen muss, ob es noch weiteren Wert gibt und so. Sondern das wird alles dann automatisch durch den Compiler erledigt. Schauen wir uns auch mal wieder an, wie das Ganze dann implementiert würde, weil um so ein Iterator zu benutzen, kann man sich irgendwer auch die entsprechende Klasse und die entsprechenden Methoden dafür implementieren. Und hier nehmen wir wieder das Beispiel eines binären Baumes. Allerdings diesmal in Java implementiert. Also das ist eine Klasse, die so ein Element in dem binären Baum repräsentiert. So ähnlich wie gerade eben auch schon in den Python-Beispiel, gibt es drei Felder, nämlich einmal ein binären Baum, der das Kind auf der linken Seite darstellt, dann dasselbe für die rechte Seite und zum Schluss endlich den eigentlichen Wert, der hier gespeichert ist. Dieses T ist irgendein Typ, der hier oben als Typparameter auftaucht und quasi erlaubt zu sagen, dass man binary trees von irgendein beliebigen Typ T haben kann. Zum Beispiel binary trees, die Strings speichern oder andere, die Listen speichern oder was auch immer. Um tatsächlich anzuzeigen in Java, dass dieser binary tree eine Datenstruktur ist, über die man iterieren kann und also so eines iterator Objektes, muss sich das Interface iterable implementieren und was dieses Interface dann erwartet oder quasi erzwingt, ist, dass wir diese Methode iterator anbieten, die schlussendlich ein iterator über diese Datenstruktur kreiert. In dem Fall ist der iterator in der Klasse tree iterator implementiert und die sehen wir dann hier unten, also die ist ja einfach als innere Klasse innerhalb dieser binary Klasse implementiert und dieser iterator selbst implementiert ein weiteres Interface, nämlich iterator und was das Interface jetzt verlangt, ist, dass wir diese drei Methoden hier anbieten, von denen die ersten zwei genau die sind, die ich abstrakt auch schon beschrieben hab, nämlich einmal Hasnext, um zu überprüfen, ob es da noch ein weiteres Element gibt. Also was der Code hier machen würde, um, ob wir denn jetzt schon alle Elemente, dieses Knotens, sorry, alle Elemente, dieses Binärenbaums besucht haben oder nicht. Und zum anderen gibt es die nächste Methode, die dann immer das jeweils nächste Element zurückgibt in irgendeiner Reihenfolge, die hier der Implementierung obliegt. Die dritte Art des Iterations, die ich vorhin schon kurz erwähnt habe, ist mithilfe von sogenannten first class functions. Hier ist die Idee, dass wir diese Aufteilung auch wieder haben in einmal den Code, der beschreibt, was ich eigentlich mit jedem Element machen möchte. Und dafür gibt es dann eine Funktion, die eben genau das macht. Diese Funktion bekommt ein Element und macht damit irgendwas. Und auf der anderen Seite den Algorithmus, der das eigentliche aufzählende Elemente macht. Und das ist in dem Fall eine weitere Funktion, die eben diese erste Funktion als Parameter bekommt und diese erste Funktion dann auf jedem Element aufruft. Und das Ganze nennt sich first class functions, weil die Funktion hier selbst als Argumenti übergeben werden kann, also first class Citizens in dieser Sprache sind. Eine Sprache, wo das funktioniert ist Scheme. Allgemein geht das in den meisten funktionalen Programmiersprachen, und wie wir gleich sehen werden, auch noch in ein paar anderen. Schauen wir uns erstmal das Schemenbeispiel ein bisschen genauer an. Also was wir hier haben, ist eine Funktion, die durch alle Werte zwischen low und high iteriert mit einem Abstand von jeweils step und für jeden dieser Werte diese andere Funktion f aufruft. Um die Scheme so ein bisschen genauer zu erklären, wir kommen da in dem späteren Modul der Veranstaltung dann auch nochmal drauf zurück. Was wir hier sehen ist zum einen die Definition von dieser Funktion die vier Argumente erhält, nämlich low, der Wert wo es losgehen soll, high, der Wert bis wohin wir eschalieren wollen, step, der Abstand mit dem wir jeweils uns fortbewegen wollen. Also das ist so ähnlich wie das Triple, was wir jetzt schon ein paar Mal gesehen haben. Und dann f die Funktion, die ich auf jedes Element durch das ich iteriere anwenden möchte. Und was dann hier passiert, ist, dass wir f jeweils mit dem nächsten Element aufrufen. Und dann braucht man natürlich noch die eigentliche Iteration der Sprachen hier durch Rekursion implementiert, indem wir nämlich diese up to by Funktion rekursiv nochmal aufrufen um dann die restlichen Elemente die bis high noch verbleiben auch noch abzuarbeiten. In dem wir in dem Fall hier auf das low step hinzu addieren, also dieses plus low und step bedeutet einfach nur addiere low in step und übergebe das als erstes Argument an up to by. Und dadurch kommen wir dann wieder in diese Funktion hier oben rein. Und das Ende der Schleife ist sozusagen durch dieses if hier gegeben, wo wir schauen, ob low wirklich noch kleiner gleich unserem high wert ist. Und wenn nicht, dann hören wir einfach auf, weil wir rekursiv nichts weiter aufrufen. Diese Idee des Iteriens mit first class Functions kommt wie gesagt ursprünglich aus der Ecke der Funktionalen Programmiersprachen. Da gibt es die schon sehr lange. Vor einigen Jahren ist das Ganze aber auch in anderen Sprachen immer mehr populär geworden und mittlerweile gibt's diese first class Functions insbesondere zum Iterien auch in vielen anderen populären Programmiersprachen. Hier sind mal zwei Beispiele aufgezählt, wo das Ganze über die Sprache beziehungsweise über eine Bibliothek dann jeweils auch möglich ist. Also zum einen erstmal Java, wo ich irgendeine Menge habe und jetzt für jedes Element in dieser Menge etwas tun möchte, aber nur wenn eine bestimmte Bedingung erfüllt ist und das kann ich zum Beispiel tun, indem ich mir so ein Stream hier aufrufe, der mir sozusagen die Menge aller Elemente in dieser Menge gibt und dann Filter aufrufe und hier übergebe ich jetzt diese andere Funktion das ist sozusagen die first class Function wo ich sage, gib mir ein Element und für jedes dieser Elemente schaue ich ob die Property von diesem Element größer als 5 ist und nur dann interessiere ich mich für dieses Element. In der Praxis würde man dann hier dahinter noch mehr schreiben und dann für jedes Element das eben diese Eigenschaft erfüllt noch etwas anderes machen und zum Beispiel irgendeine Berechnung ausführen. So ähnlich kann man das Ganze zum Beispiel auch in JavaScript aufschreiben. Hier haben wir auch wieder eine Datenstruktur in dem Fallen in Array und würden dann direkt darauf diese Filter Methode aufrufen, der wir auch wieder eine zweite Funktion übergeben und diese zweite Funktion auch oft Callback genannt sagt auch wieder, ich nehme ein Element E als Parameter und überprüfe dann darauf diese Bedingung und nur die Elemente, die diese Bedingung erfüllen sollen von Filter schlussendlich zurückgegeben werden und genauso wie eben oberen Beispiel würde ich mit diesen Elementen die diese Bedingung erfüllen und diese natürlich noch was anderes machen. Also was hätte ich im Anfang gesagt es gibt zwei Arten von Iterationskonstrukten zum einen dieser aufzählungsbasierten Iterationskonstrukte die wir uns gerade im Detail angeschaut haben und die andere Art die wir jetzt hier einfach nur noch ganz kurz anschauen möchten ist die wo die Schleife mithilfe einer logischen Bedingung kontrolliert wird ich also so lange iteriere bis eine bestimmte logische Bedingung erfüllt ist. Dieses Grundkonzept gibt es in ziemlich vielen Programmiersprachen und weil das die meisten vermutlich kennen werde ich da auch gar nicht so viel zu sagen vielleicht nur so viel, dass es da eigentlich immer drei Formen von gibt die sich danach unterscheiden wo genau diese logische Bedingung jeweils überprüft wird nämlich zum einen kann man das vor dem Ausführen der Schleife machen also pre-test und ein Beispiel dafür ist so eine While Loop wo ich sozusagen am Anfang einmal überprüfe wenn ja, gehe ich in den Schleifenbody rein und danach jede iteration schaue ich wieder nach ob diese Bedingung immer noch wahr ist die zweite Variante ist, dass ich in der Mitte der Schleife irgendwo schaue das kann ich zum Beispiel in vielen Sprachen so aufschreiben dass ich hier so eine Endlosschleife hinschreibe die eigentlich jetzt erstmal unendlich oft iterieren würde ich dann aber in der Mitte der Schleife irgendwo eine Bedingung überprüfe und wenn diese Bedingung wahr ist in dem ich zum Beispiel Break-off rufe die dritte Variante ist Post-Test also dass ich nach dem Ausführen des Schleifenbodies jeweils überprüfe ob meine Bedingung wahr oder falsch ist zum Beispiel kann ich das mit so einer Dual-Schleife machen wo ich in jedem Fall einmal den Schleifenbody ausführen würde und anschließend schaue ob die Bedingung wahr ist und wenn sie wahr ist, gehe ich wieder in die Schleife rein in die Musik gegebenfalls noch ein paar Mal aus so zum Abschluss dieses Teils der Vorlesung zum Thema Iteration noch ein kleines Quiz wie immer ist das gedacht, damit sie überprüfen können was da jetzt so hängen geblieben ist und vielleicht auch ein bisschen vergleichen können mit dem was andere so verstanden haben und zwar läuft es wieder so dass ich hier vier Aussagen habe von denen manche vielleicht stimmen manche auch nicht stimmen und dann wieder mal anzuhalten und zu schauen welche dieser Aussagen stimmen und anschließend im Ilias darüber abzustimmen was von diesen Aussagen korrekt ist und was nicht korrekt ist so, dann schauen wir uns mal die Lösung an also von diesen vier Aussagen sind die ersten zwei leider falsch und zwar war das erste ja das iteratorene Form von logisch kontrollierten Schleifen sind, das stimmt eben nicht sondern da gibt es keine logische Bedingung sondern ein iterator iteriert einfach so lange wie es noch Elemente in einer bestimmten Datenstruktur gibt und das ist eben genau diese andere Gruppe von Schleifen über die wir hier gerade gesprochen haben der zweite Satz sagt, dass einer dieser echten iteratoren jedes mal wenn er aufgerufen wird ein Element zurück gibt das stimmt so auch nicht, denn diese echten iteratoren werden eigentlich nur einmal aufgerufen und zwar in einer for loop und die for loop macht dann die eigentliche iteration also wenn ich diesen iterator mehrmals aufrufe, dann würde ich mehrmals wieder von vorne anfangen zu iterieren und das ist aber nicht die Art und Weise wie man diese echten iteratoren hier benutzt der dritte Satz der stimmt, denn iteratoren Objekte haben tatsächlich eine Methode, mit der ich dann jeweils das nächste Element erhalten kann und schlussendlich der letzte Satz stimmt auch, wenn ich first class functions habe und mit denen iteriere so wie wir das an dem Schienbeispiel auch an dem java und javascript zum Beispiel gesehen haben, dann brauche ich dafür keine for loop, weil die iteration nämlich innerhalb dieser Funktion implementiert ist, die das zum Beispiel über recursion erledigen kann ja und damit sind wir auch schon wieder am Ende von diesem Teil der Vorlesung zum Thema iteration ich hoffe, Sie haben ein bisschen was gelernt wie diese iterationssprachkonstrukte aussehen und ja, dann kommt noch ein fünfter Teil innerhalb dieses Moduls zum Thema Vollfluss. Damit erstmal Danke fürs Zuhören und bis zum nächsten Mal.