 Herzlich willkommen zurück zu Programmierparadigmen. Wir sind jetzt hier im zweiten von drei Teilen zum Thema Funktionale Programmiersprachen. Und was wir hier machen werden, ist uns die Sprache scheme, eine populäre funktionale Sprache ein bisschen genauer anzuschauen. Das wahrscheinlich zentralste Element von scheme und auch vielen anderen funktionalen Programmiersprachen ist die Function Application, also der Aufruf von Funktionen. In scheme drückt man das Ganze ganz einfach so aus, dass man ein paar von Klammern hat, also Klammer auf, Klammer zu und in diesen Klammern stehen dann zwei Sachen. Nämlich das erste was drin steht, ist die Funktion selbst. Das kann selbst auch wieder eine Expression sein, die dann schlussendlich zu einer Funktion evaluiert oder man nennt vielleicht einfach nur den Namen einer Funktion. Und das zweite sind dann die Argumente, die übergeben werden. Das heißt, ich habe ein Argument oder vielleicht auch mehrere Argumente, also innerhalb dieser Klammern kann ich zwei oder mehrere Dinge dann haben. Hier unten ist mal ein ganz einfaches Beispiel und zwar rufen wir hier die plus Funktion auf, die in der Sprache schon vordefiniert ist, also die müssen wir nicht explizit definieren, sondern die ist so definiert, wie man das auch erwarten würde. Und als Argumente übergebe ich dann 3 und 4, das Ganze mit den Klammern ringsrum, so dass das ein Funktionsaufruf auf ist. Und was dann rauskommt, ist der Wert 7 natürlich, weil wir hier eben diese plus Funktion auf 3 und 4 anwenden und 3 plus 4 ergibt ja 7. Etwas anders sieht es aus, wenn ich jetzt zweimal diese Klammer drum machen würde, denn dann würde ich nämlich zwei Funktionen aufrufen. Zum einen diese innere plus Funktion mit den Werten 3 und 4 als Argument, da kommt dann 7 raus und anschließend hätte ich die 7 mit den Klammern ringsrum, aber eben keinen Argument. Das heißt, was Schieben dann probieren würde, ist den Wert 7 als Funktion aufzurufen, ohne Argumente zu übergeben und weil 7 eben keine Funktion ist und insbesondere keine Funktion, die null Argumente nimmt, würde das dann ein Laufzeitfehler geben. Das heißt, in Schieben sind diese Klammern sehr wichtig, denn sie bedeuten eben Funktionsaufruf und das ist anders als in den meisten Sprachen, die wir bisher so gesehen haben, wo Klammern im Prinzip oft auch nochmal extra eingefügt werden können, um den Code vielleicht lesbar zu machen. In Schieben ist das nicht der Fall, denn diese Klammern sind immer ein Funktionsaufruf. Jetzt wissen wir, wie wir Funktionen aufrufen. Die Frage ist dann natürlich, wo kommen diese Funktionen überhaupt her? Teilweise sind Funktionen natürlich schon Teil der Sprache, wie wir gesehen haben das plus, aber natürlich möchte man auch selbst Funktionen erstellen und das macht man in Schieben und quasi allen anderen funktionalen Programmiersprachen auch mit Hilfe so genanter Lambda Expressions. Was diese Lambda Expressions machen, ist, dass sie im Prinzip auch wieder aussehen wie eine Funktion. Wir sehen hier unten ein Beispiel, also ich benutze da dieses spezielle Lambda Keyword und übergebe dann zwei Argumente an dieses Keyword oder an diese Lambda Funktion, nämlich ein Argument, was den formalen Parametern der Funktionen entspricht, die ich hier erstellen möchte. Also in dem Beispiel hier unten sage ich, diese Funktionen, die ich jetzt erstelle, bekommt ein formalen Parameter namens X. Und als zweites Argument an das Lambda übergebe ich dann den Body der Funktion, in dem Fall eine weitere Expression, die eben dieses X nimmt und dann mit sie selbst multipliziert. Was ich hier wieder tue, in dem ich eine weitere Funktion aufrufe, nämlich die Malfunktion, die Multiplikationsfunktion, an die ich dann die zwei Werte X und nochmal X übergebe. Das heißt, was ich hier schlussendlich in diesem Beispiel unten mache, ist, dass ich eine Funktion erstelle, die eine gegebene Zahl mit sie selbst multipliziert, also zum Beispiel eine Funktion, die man square nennen könnte. Jetzt haben wir gesehen, dass scheme diese beiden Grundzutaten jeder funktionalen Programmiersprache hat, nämlich eine Möglichkeit Funktionen zu definieren und eine Möglichkeit Funktion aufzurufen. Jetzt hatte ich vorhin gesagt, dass scheme eine nicht pure funktionale Programmiersprache ist. Was bedeutet, dass wir eben auch Assignments in der Sprache haben und diese Assignments führen schlussendlich zu bindings, also der Tatsache, dass ich einen Namen und ein Wert zueinander zuordne und sage, wenn ich diesen Namen verwende, meine ich damit diesen Wert. Und das macht man in scheme, wie wir in dem auffärben Beispiel ganz am Anfang schon gesehen haben, mithilfe dieses Let Keywords. Und was Let als Argumente erwartet, sind wieder zwei Sachen. Also wo rein so ein Taktisch betrachtet ist Let auch wieder nur eine Funktion, der ich Argumente übergebe. Und zwar folgendes, als erstes eine Liste von Namens Wertpaaren. Also das sind all die Dinge, die ich aneinander binden will, wo ich dann zum Beispiel sage, dieser Name bekommt diesen Wert und jener Name bekommt jeden Wert. Und als zweites dann eine weitere Liste, nämlich eine Liste von Expressions, die dann mithilfe dieser existierten Bindings und unter diesen existierten Bindings ausgewertet werden. Hier unten sehen wir mal ein Beispiel, das ich jetzt kurz erkläre. Wer möchte, kann auch gerne das Video hier schon mal anhalten und versuchen selber drüber nachzudenken, was daraus kommt. Ich erkläre es jetzt auf jeden Fall mal. Also wir rufen hier diese Let Funktion auf oder benutzen dieses Keyword, was Bindings erzeugt. Und das hat zwei Argumente, nämlich einmal diese vier Zeilen, die hier dann kommen, in der ich eine Liste von diesen Namens und Wertpaaren habe. Und anschließend dann eine Expression, die unter diesem Binding dann ausgewertet wird. So, was wir hier sehen, ist, dass vier Werte an jeweils einen Namen gebunden werden, nämlich wir sagen, dass A den Wert 3 hat, wir sagen, dass B ab hier den Wert 4 hat. Dann haben wir hier wieder unsere Lambda Expression, die wir vorhin schon gesehen haben und binden die jetzt an den Namen square. Das heißt, wir können diese Funktion jetzt verwenden, indem wir sie einfach mit diesem Namen square referenzieren. Und dann binden wir noch dieses existierende Plus, was ja eine Funktion ist, die Teil der Schemensprache bereits ist, an dieses Wort plus, so dass wir die Funktion dann mithilfe dieses Namens Plus auch aufrufen können. Und nachdem wir diese Binding dann erstellt haben, benutzen wir sie, indem wir diese Expression hier unten evaluieren und die liest man am besten von innen nach außen. Also, was hier passiert ist, wir rufen square von dem Wert der N-A, ist auf, dasselbe mit b, geben die Rückkabelwerte dieser beiden Aufrufe dann an die Funktion plus. Und all das, was da dann zurückkommt, geben wir an die Funktion square root, die schon Teil der Schemensprache ist und die wir hier nicht explizit definieren müssen. So, schauen wir mal an, was da rauskommt. Also, wir rechnen hier 3 mal 3, das ist 9, hier rechnen wir 4 mal 4, das ist dann 16. Dann addieren wir die zwei mithilfe der Plusfunktion, die einfach nur auf das eingebaute Plus meppt, haben also dann 16 plus 9 ist 25. Und rufen damit dann die square root Funktion auf und bekommen damit schlussendlich 5,0 als Wert zurück, also die Wurzel von 25. Man programmieren möchte man natürlich oft ausdrücken, dass unter bestimmten Bedingungen etwas so ist oder eben so. Und das kann man auch in Schieben machen, mithilfe von sogenannten conditional expressions. Und zwar einmal mithilfe dieses if keyword oder auch mithilfe dieses cont keyword. Schauen wir uns die mal ein bisschen genauer an. Also, if funktioniert so, dass wir auch wieder Argumente an das if selbst übergeben. Das ist nämlich als erstes Argument immer die Bedingung, die evaluiert werden soll. Und als zweites und drittes Argument übergeben wir dann, was passiert, welcher Wert evaluiert werden soll, wenn diese Bedingung wahr ist bzw. wenn sie falsch ist. Also, das zweite Argument ist für den Fall, dass das erste Argument zu wahr evaluiert wird. Und das dritte Argument für den Fall, dass das erste Argument zu falsch evaluiert wird. Hier unten sehen wir ein Aufruf dieser if Funktion bzw. es ist also syntaktisch eine Funktion und man kann es auch als Funktion begreifen. Aber gleichzeitig ist es auch ein eingebautes Keyword der Sprache scheme selbst. Und was wir hier machen, ist, dass wir als Bedingung übergeben, dass die kleiner Funktion auf 2 und 3 angewandt wird. Also, wir überprüfen, ob 2 kleiner als 3 ist. Und die Werte für den Wahr- und Falsch-Fahrt sind dann 4 und 5. Da 2 kleiner gleich 3 ist, kommt also hier schlussendlich 4 raus. Auf der rechten Seite sehen wir ein zweites Beispiel und zwar für diese andere Art, mit der ich conditional expressions in Schieben ausdrücken kann. Und zwar mit Hilfe von KONT, was eben nicht nur eine Bedingung die Werte falsch sein kann erlaubt auszudrücken, sondern eine multi-way conditional expression erlaubt, wo ich verschiedene Fälle abdecken kann. Also, das ist ein bisschen so ähnlich wie if mit else if und so weiter. In dem Fall schauen wir uns verschiedene Bedingungen an, nämlich zum einen, dass 3 kleiner als 2 ist und da noch noch, dass 4 kleiner als 3 ist. Und schlussendlich haben wir diesen else-Fahrt hier unten, der in dem Fall dann evaluiert wird, wenn die anderen Bedingungen alle nicht der Fall waren. Und in dem Beispiel ist das auch so. Und deswegen kommt hier schlussendlich dann 3 raus. Denn die erste Bedingung, dass 3 kleiner als 2 ist, ist ja nicht wahr. Und 4 ist auch nicht kleiner als 3. Deswegen nehmen wir dann hier den else-Wert und dieser gesamte Ausdruck hier, dieser gesamte Aufruf evaluiert dann zu 3. Eine interessante Frage bei jeder Programmiersprache ist ja, wie Typen da behandelt werden. Ist die Sprache statisch typisiert oder werden Typen erst zur Laufzeit überprüft und überhaupt berechnet. Und in Schieben ist die Antwort Letzteres. Also Schieben verwendet Dynamic Typing. Das heißt, die Typen werden erst zur Laufzeit überhaupt berechnet und dann auch überprüft. Schauen wir uns mal zwei Beispiele an, um das ein bisschen zu erläutern. Also in dem ersten Beispiel haben wir so ein If mit einer Bedingung hier vorne. Und je nachdem, ob die dann zu wahr oder falsch evaluiert, berufen wir die Plusfunktionen auf bestimmten Werten auf. Nämlich im Falle, dass das ganze zu wahr evaluiert auf 2 und 3, also 2 Integers. Und im anderen Falle auf 2 und diesem String fuh. Was jetzt hier passiert ist, dass je nachdem, welchen Wert A hat, ich entweder den Wert 5 bekomme. Nämlich dann, wenn ich in den True Branch hier reingehe. Oder ich versuche, 2 und diesem String fuh zu addieren, was in Schieben aber nicht geht, sondern mir ein Laufzeitfehler geben wird. Das heißt, je nachdem, welchen Wert A hat, kommt das eine oder das andere raus, aber eben erst zur Laufzeit, wenn wir dann tatsächlich dieses Stück Kot erreichen und der Wert von A auch feststeht. Und das ist ebenfalls Dynamic Typing ist und nicht schon statisch vor der Ausführung überprüft wird. Schauen wir mal noch das zweite Beispiel hier unten an. Was wir hier machen, ist, dass wir eine Lambda Expression wieder haben, in der wir eine Funktion definieren, die 2 Argumente A und B nimmt. Und dann schaut, ob A kleiner als B ist und in dem Fall dann A zurückgibt und ansonsten B zurückgibt. Also diese Funktion schaut sozusagen, was hier der kleinere Wert ist und deswegen nennen wir die auch Min und definieren die dann mithilfe dieses Define Keywords als eine Funktion, die man auch noch später aufrufen kann. Und was aus Typ sich das hier interessant ist, ist, dass wir hier einen impliziten Polymorphismus haben. Denn diese Funktion sagt natürlich nicht, welcher Typen A und B haben muss, aber sie wird implizit sowohl für Integers als auch für Floats funktionieren. Das heißt, wir schreiben die Funktion einmal hin und können die dann aber für mehrere Typen verwenden, weil die Sprache das dann erst dynamisch typisiert. So, um das Verständnis ein bisschen zu überprüfen, machen wir ein kleines Quiz. Und zwar in der Form von vier kleinen Schemenprogrammen, wo die Frage für Sie ist, welche dieser Programme evaluiert schlussendlich zu neun. Es könnten null Programme sein, es könnten noch alle vier sein oder irgendwas dazwischen. Und ich würde Sie bitten, an der Stelle das Video wieder kurz anzuhalten, zu überlegen, welche dieser Programme schlussendlich zu neun evaluiert, im Ilias abzustimmen und dann die Lösung anzuschauen. Schauen wir uns die Lösung mal an. Also, die ersten beiden Programme evaluieren tatsächlich zu neun, die letzten beiden aber nicht. Uns war aus folgenden Gründen. Also, bei Program 1 haben wir erstmal hier so eine Lambda-Expression, die eine Funktion definiert. Diese Funktion kriegt ein Argument x und berechnet dann x mal x und gibt das zurück. Und anschließend benutzen wir diese Funktion gleich und wenden sie auf den Wert 3 an. Und da 3 mal 3 gleich neun ist, kommt hier schlussendlich neun raus. Das zweite kleine Programm lesen wir am besten wieder von innen nach außen. Hier haben wir einmal die Berechnung, die 2 plus 4 berechnet, die hier dann 12 plus 3 unter 15... Ach ja, dann subtrahieren wir das zweite Ergebnis von dem ersten Ergebnis und da 15 minus 6 eben neun ergibt, kommt hier insgesamt auch neun raus. Das dritte ist ein bisschen tricky. Und da steht ja neun, aber außenrum sind ja eben diese Klammern, die bedeuten, dass wir eine Funktion aufrufen. Und so ähnlich wie bei dem Beispiel, was wir vorher schon mal gesehen hatten, würden wir hier also versuchen, die Funktion neun aufzurufen. Nun ist aber neun keine Funktion, sondern einfach nur ein Integer-Wert. Und deswegen gibt es dann hier einen Laufzeitfehler und es kommt eben nicht neun raus. Und schlussendlich das letzte Beispiel, da gibt es kein Laufzeitfehler, das evaluiert alles schön. Allerdings kommt schlussendlich acht und nicht neun raus. Denn 10 plus 0 bleibt ja 10, 4 minus 2 ist 2. Und wenn wir diese zwei Werte dann in diese Funktion, die wir hier mit der Lambda-Expression erstellen, reingeben, also den ersten Wert minus den zweiten Wert rechnen, 10 minus 2 kommen wir auf acht und eben nicht auf neun. So, nach dem kleinen Quiz schauen wir uns noch eine weitere Reihe von Features in Schemen an. Und zwar als nächstes mal die Listen. Ich hatte gesagt, in vielen funktionalen Programmiersprachen spielen Listen eine sehr, sehr zentrale Rolle und so natürlich auch in Scheme. Auf Listen werden eine ganze Reihe von Operationen definiert, die man da aufrufen kann. Und die drei Wichtigsten sind die drei Divehese, nämlich K, Kader und Kons. Was die machen ist folgendes. K nimmt eine Liste und extrahiert einfach das erste Element. Kader macht genau das Gegenstück dazu. Es nimmt eine Liste und extrahiert alle außer dem ersten Element. Und mithilfe von Kons kann ich einen Wert und eine gegebene Liste zusammenfügen, also Konkatenieren, um dann eine längere Liste auszubekommen. Und mithilfe dieser drei Operationen kann man eigentlich so ziemlich alles, was man mit Listen gern machen möchte, machen. Insbesondere kann man Listen sehr gut reklusiv damit bearbeiten, indem man sich das erste Element rausnimmt, damit etwas macht und dann den Rest der Liste an dieselbe Funktion wieder gibt oder vielleicht auch in eine andere Funktion reingibt und die Ergebnisse schlussendlich mit Kons dann verknüpft. Schauen wir uns hier unten mal drei kleine Beispiele dazu an. Bevor wir uns anschauen, was diese Beispiele genau machen und zu welchen werden die evaluieren, muss ich vielleicht kurz dieses Quotezeichen erklären, was hier überall verwendet wird. Was das macht, ist dem Schiem Interpreter sagen, dass das, was danach kommt, ein literales und eben nicht hier jetzt schon evaluiert werden soll. Also wir erstellen damit sozusagen eine Liste in dem Fall, zum Beispiel hier in dem ersten Beispiel eine Liste mit den Werten 234 und sagen, dass das eben eine Liste ist, aber kein Funktionsaufruf. Normalerweise, wenn ich die Klammern hätte, würde ich hier versuchen, die Funktion 2 aufzurufen mit den Argumenten 3 und 4, was dann aber wieder nicht gehen würde, und stattdessen wird hier einfach nur eine Liste erstellt. Also das macht das Quote. So, jetzt schauen wir uns die drei Operatoren an, die verwendet werden. Das K extrahiert das erste Element, also kommt hier 2 raus. Kadde wirft das erste Element weg und gibt den Rest der Liste zurück, wird also eine Liste zurückgeben, die 3 und 4 enthält und Konz concateniert ein Wert mit einer existierenden Liste, in dem Fall den Wert 2 mit der Liste, die wir hier erstellen, die dann 3 und 4 erhält. Also bekommen wir schlussendlich eine Liste raus, in der 2, 3 und 4 drin sind. Jetzt haben wir vorhin schon die Bindings angeschaut. Also wo man mithilfe des Lead Keywords für einen bestimmten Scope sagen konnte, dass dieser Wert an diesen Namen gebunden werden soll. Einen Schritt weiter gehen jetzt noch die Assignments, die wir uns jetzt anschauen werden. Denn hier kann ich sagen, dass ab einem bestimmten Zeitpunkt eine bestimmte Variable einen bestimmten Wert haben soll. Und über Variablen hinaus kann ich mit Hilfe von anderen Keywords, nämlich ZKader und ZK auch noch Teile von Listen manipulieren auf diese Art und Weise. Also was das Set, und da ist wichtig, dass immer ein Ausrufezeichen dahinter kommt, um zu sagen, hey hier passiert tatsächlich was, macht ist, dass es einfach einer bestimmten Variable ein Wert zuordnet, also so ähnlich wie den Assignment mit dem Istgleich in den meisten operativen Programmiersprachen auch funktioniert. Und analog dazu gibt es noch ZK und ZKader, die nämlich im Falle von ZK den ersten Wert einer Liste verändern und den überschreiben mit einem Wert. Und ZKader, was den Rest einer Liste überschreibt und quasi alles außerdem ersten Wert mit etwas anderem ersetzt. Schauen wir uns das Ganze mal an, wenn es benutzt wird. Also hier unten haben wir ein Beispiel. Das geht erstmal mit einem Lett los. Also wir schreiben in zwei Namen, nämlich X und L was rein. Wir sagen X wird den Wert 2 haben und L wird diese Liste sein, die wir hier mit der literalen Notation erstellen, nämlich die Liste, die aus A und B besteht. Und mit diesen Bindings haben wir dann hier eine Reihe von Assignments, die in dieser Reihenfolge, wie sie da stehen, diese Werte X und L noch weiter modifizieren. Und zwar zunächst hier in der Form, dass wir X überschreiben und da jetzt eine 3 reinschreiben, dann hier unten, indem wir List, also ZK aufrufen und damit den ersten Wert dieser Liste überschreiben, so dass dann C und D drin steht und zwar ist C und D in dem Fall auch wieder eine Liste, die wir hier als Listen literal erstellen. Das heißt, unsere Liste L wird in dem Moment aus der Liste CD gefolgt von dem Wert B bestehen. Und dann haben wir unten noch ein drittes Assignment, nämlich mit ZK, was jetzt den Rest dieser Liste, also nicht den Anfang, sondern den Rest, überschreibt und zwar mit dem Wert E. Und das heißt, an der Stelle hätten wir jetzt die Liste, die besteht aus CD, also einer anderen Liste, gefolgt von E. Und dann haben wir noch schlussendlich eine weitere Expression hier, die jetzt nach all diesen Assignments evaluiert wird und die fügt jetzt den Wert der in X ist und die Liste, die wir in L haben zusammen und was daraus kommt, schlussendlich ist dann 3 gefolgt von CD und dann E und das Ganze als Liste, denn X hat ja den Wert 3, weil wir das hier oben überschrieben hatten, dann hatten wir CD in die Liste L geschrieben und den Rest von L dann mit E überschrieben. Also die Liste war zu irgendeinem Zeitpunkt dann CD gefolgt von E und vor dieser Liste fügen wir mit Hilfe von diesem Cons jetzt noch die 3 ein und kommen dann deswegen schlussendlich auf diese Liste 3 CD, das Paar und dann E. Ein weiteres Sprachfeature, was ich hier kurz ansprechen möchte, ist das Sequencing und zwar mit Hilfe des Keywords beginn. Was das macht, ist, dass es an einer Stelle, wo in Schieben üblicherweise eben nur eine Expression erwartet wird, erlaubt mehrere Expressions nacheinander zu evaluieren. Also hier an dem Beispiel haben wir 2 Expressions, nämlich Display High und Display, was auch immer in der Variable N ist und wir möchten, dass das hier eins nach dem anderen evaluiert wird und machen das Ganze in einem Kontext, in einem Scope, wo wir N an den Wert There binden und was dann schlussendlich hier rauskommt, ist, dass dieses Stück Code High There ausgibt. Das heißt, mit diesem Beginn kann ich anstellen, wo das eigentlich nicht erwartet würde, mehrere Expressions nacheinander haben. In dem Beispiel könnte ich das auch ohne das Beginn erreichen, indem ich das einfach weglasse und die letzte Klammer dann auch weglasse und der Grund dafür ist, dass nach einem Let eh mehrere Expressions erwartet werden, die unter diesem Binding in dem Scope evaluiert werden. Aber das Beginn erlaubt uns, das Ganze dann auch an anderen Stellen zu machen, wo man üblicherweise eben nur genau eine Expression erwarten würde und ich aber sagen möchte, mach erst das, dann das und schlussendlich jenes. In einer funktionalen Programmiersprache braucht man im Prinzip keine Schleifen, denn man kann durch Funktionsaufrufe und recursive Funktionsaufrufe alles, was man mit Schleifen ausdrücken kann, im Prinzip auch ausdrücken, aber in der Praxis möchte man manchmal trotzdem Schleifen haben, weil es ganz einfach ein natürlicher Weg ist, bestimmte Algorithmen auszudrücken. In Schieben gibt es solche Schleifen und zwar unter anderem mithilfe des Do Keywords, was wir hier mal kurz anschauen wollen. Hier in dem Beispiel unten benutzen wir dieses Do Keyword und definieren da in dieser Do Schleife mithilfe dieser Trippel, die wir hier sehen, die Schleifenvariablen, die es innerhalb dieser Schleife gibt. Also wir haben ja nicht nur eine Schleifenvariable, sondern in dem Fall drei. Und zwar für jedes dieser Trippel, die wir hier haben, haben wir einmal eine Variable, die wir als Schleifenvariable verwendet wollen, dann einmal den initialen Wert, in dem Fall Null, den i dann am Anfang hat und dann eine Expression, die uns angibt, wie wir nach jeder Schleifeninteraktion diesen Wert i neu berechnen wollen. In dem Fall fügen wir immer einen weiteren Wert hinzu. Neben dieser Einschleifenvariable gibt es eben hier noch mehr, nämlich einmal die, wo wir sagen, wir haben noch eine Variable A, die mit Null initialisiert wird und nach jeder Iteration dann den Wert von B bekommt und dann noch die Variable B mit Eins initialisiert, den Wert wird dann immer als A plus B nach der jeweiligen Iteration berechnen. Neben diesen Schleifenvariablen brauchen wir natürlich auch irgendeine Bedingung, die uns sagt, wann wir aus der Schleife wieder rausgehen und das steht dann hier. Und zwar ist das, was wir hier sehen, zum einen die Bedingung, wann die Schleife beendet wird, in dem Fall, wenn i gleich endet. Und zum anderen als zweiten Wert hier noch, den Wert, den diese Schleife schlussendlich zurückgeben soll. Also alles in Schim hat ja einen Rückerbewert, also hat auch die Schleife einen Rückerbewert und das soll hier der letzte Wert von B dann sein. Und dann sehen wir hier unten schlussendlich noch den eigentlichen Body der Schleife, indem wir den aktuellen Wert von B ausgeben und dann noch ein Leerzeichen dazu ausgeben. Wenn man sich das jetzt ein bisschen anguckt, kann man vielleicht raten, was hier schlussendlich rauskommt. Wer will, kann da gerne nochmal kurz auf Pause klicken und mal kurz selber darüber nachdenken. Was hier schlussendlich rauskommt, ist nämlich, dass die ersten N Fibonacci-Zahlen berechnet und dann auch ausgegeben werden. In dem Fall rufen wir diese Funktion, in der das Ganze passiert. Also das ist ja hier eine Lambda-Expression. Auf mit dem Wert 5, also das wird quasi die Fibonacci-Zahlen bis 5 berechnen und dann auch ausgegeben. Wie man an den ganzen Beispielen mir selbst sehr gut sehen konnte, spielen Listen in Schim eine sehr, sehr wichtige Rolle und interessanterweise sind selbst Programme selbst wieder Listen oder können zumindest als Listen betrachtet werden. Was auch interessant ist, ist, dass in der Schim Söntags sowohl Listen als auch Programme genau dieselbe Söntags verwenden. Nämlich beide verwenden sogenannte S-Expressions. Eine S-Expression ist einfach ein String von Symbolen mit der gleichen Anzahl an Öffenden und schließenden Klammern, und zwar so, dass die jeweils balanciert sind, also dass jeder geöffnete Klammer an der richtigen Stelle dann auch wieder zugeht. Was man in Schim jetzt machen kann, weil Programme nämlich auch als Listen betrachtet werden können, ist, dass man in einem Programm eine Liste manipuliert, und diese Liste dann ein anderes Programm ist, welches man dann schlussendlich ausführt, und zwar macht man das mit dem Eval Keyword. Was so bisschen so ähnlich ist, wie zum Beispiel in Jarmas gibt, wo ich auch den String der Code enthält, evaluieren kann, und hier kann ich eben das Programm als Liste manipulieren, also nicht als String, sondern als Liste, und dann schlussendlich dieses Programm evaluieren. Hier unten sehen wir ein kleines Beispiel, wo wir das mal verwenden. Also hier wird das Eval aufgerufen, und zwar auf dem, was dann hier rauskommt, und da müssen wir uns mal anschauen, was das ist, was da rauskommt. Also als erstes konstruieren wir hier, wenn wir von innen nach außen lesen, eine Liste aus diesen zwei Argumenten, 2 und 3, die haben wieder diese Notation mit dem kleinen Backtick hier, um zu sagen, dass 2 und 3 eben literale sind, also keine Funktion, sondern den Wert 2, den Wert 3, und fügen die dann mithilfe dieses List keywords zu eine Liste zusammen, rufen dann den Konstoperator auf, indem wir vor diese Liste 2 und 3 noch den Wert Plus wieder als Literal packen, und geben diese Liste, die dann rauskommt, die also aus Plus 2 und 3 besteht, dann zu Eval. Eval wird das Ganze dann als Programm evaluieren, also das Programm Plus angewandt auf 2 und 3 berechnen, und dieser Funktionsaufruf ergibt schlussendlich 5, weil das natürlich das Ergebnis ist, wenn ich 2 plus 3 addiere. Also es ist ganz interessant, dass man hier Programme sozusagen selbst wieder manipulieren kann, was doch ein sehr mächtiges Konstrukt ist, weil man damit ja so dieses Metaprogramming betreiben kann, was aus anderen Sprachen wie zum Beispiel JavaScript und so etwas mehr vielleicht schon bekannt ist. Ja, und dann sind wir auch schon am Ende dieses 2. von 3 Teilen zum Thema Funktionalprogrammierung. Ich hoffe, Sie wissen jetzt ein bisschen besser Bescheid über Scheme. Es war natürlich jetzt nur eine ganz kleine Einführung in diese Sprache, es gibt noch eine ganze Reihe Features, die wir hier nicht betrachtet haben, aber das sollte einfach mal als konkretes Beispiel einer recht populären Funktionalprogrammiersprache dienen, damit Sie diese Features, die es da üblicherweise gibt, ein bisschen besser verstehen können, und wer Lust hat, kann das natürlich einfach mal ausprobieren und mal ein paar kleine Schemenprogramme schreiben, gern auch größere, um zu sehen, wie das Ganze denn praktisch funktioniert. Danke fürs Zuhören und bis zum nächsten Mal.