und ein paar Worte zu der Prüfung für diese Veranstaltung sagen.
Da gibt es eine kleine Ungewissheit. Ich habe gerade nochmal
mit Frau Dreschler gesprochen, aber sie ist da auch noch nicht weiter.
Das betrifft den Termin der Wiederholungsprüfung. Also das sind die Daten,
die offiziell auf der offiziellen Seite des Studienbüros stehen.
Frau Dreschler hatte mich gefragt, ob wir die funktionale Prüfung
ist immer am Vormittag. Die Logikprogrammierung am Nachmittag terminiert,
ob für den 5.3. die Termine geswappt werden könnten.
Also wenn das passiert, Bis jetzt ist es noch nicht passiert,
dann würde das auf 10.30 Uhr vorrutschen.
Also dieses Fragezeichen hier soll heißen, wer den zweiten Termin anpeilt,
möge sich bitte vorher noch mal informieren, wann die Uhrzeit genau ist.
Es ist wahrscheinlich kein Beinbruch, wenn sie zu der Uhrzeit hinkommen.
Ich werde dafür sorgen, dass dort auch Klausurbögen für die Logikprogrammierung vorhanden sind,
sodass also Irreläufer nicht um ihre Prüfungsmöglichkeit gebracht werden.
Dann ist noch niemand anwesend, der Fragen zur Logikpromierung
beantworten kann
Gut, das ist die Terminfrage. Die Bedingungen dafür sind,
es gibt keine Voraussetzungen für die Teilnahme an der Prüfung,
also der erfolgreiche Erwerb des Scheins,
ist nicht Voraussetzung für die Teilnahme an der Prüfung.
Er ist aber sehr wohl eine Teilprüfungsleistung, die absolviert werden muss,
um insgesamt die Prüfung bestehen zu können. Es ist eine unbenotete Teilprüfung,
aber der Schein ist eine unbenotete Detailprüfungsleistung,
der nicht in die Bewertung einfließt und die Kriterien hatte ich ja ganz am Anfang vorgestellt,
die haben sich auch nicht geändert, regelmäßige Teilnahme,
das ist sozusagen The Buttomline,
50 Prozent der Übungspunkte in den Einreichungen und dann noch das zweimalige Vortragen von Lösungen vor der Gruppe.
Sie müssen über Stine angemeldet sein. Also bitte sorgen Sie
dafür, dass das noch rechtzeitig passiert.
Soviel ich weiß, ist immer der Freitag,
dass die beiden Prüfungstermine sind jeweils am Dienstag, exakt vier Wochen auseinander.
Soviel ich weiß, ist immer der Freitag davor,
die letzte Möglichkeit, sich anzumelden, abzumelden oder umzumelden.
Im Studienbüro, nicht im Studienbüro, über Stine natürlich.
Wenn Krankheitsfall vorliegt, dann besorgen Sie sich bitte ein,
ärztliches Attest vor Beginn der Prüfung. Das ermöglicht auch,
wenn sie unter den Bedingungen der Fristenregelung studieren, möglicherweise eine Fristverlängerung.
Ich weiß nicht, ob es schon jemanden gibt,
der unter den Bedingungen der Wiederholungsregelung jetzt wieder studiert.
Das hat sich ja gerade geändert. Sollte eigentlich noch nicht der Fall sein,
dann ermöglicht das Attest natürlich einen weiteren Versuch wahrzunehmen.
Wenn Sie die Prüfung aus Krankheitsgründen abbrechen müssen,
dann suchen Sie unverzüglich einen Arzt auf und legen das Attest
im Studienbüro vor
Ich bitte sie jeweils 15 Minuten vor Beginn der Klausur da zu sein,
damit man möglicherweise noch, damit sie rechtzeitig ihren Platz einnehmen können,
damit Ruhe einkehrt, damit wir wirklich pünktlich beginnen können.
Bringen Sie bitte mit, einen Ausweis mit Lichtbild,
damit man sie personell identifizieren kann, plus den Studierendenausweis,
aus dem die Materikeln nochmal hervorgeht.
Bringen Sie auch mit, falls Sie die zwei Stunden sonst nicht überleben,
irgendetwas, was Sie körperlich so weit fit hält,
dass Sie die Prüfung überstehen können.
Ja,
danke für den Hinweis
Die Prüfung, die wir machen, sind ja relativ harmlos,
eine gute Klausur in anderen Fächern.
Es dauert üblicherweise vier Stunden. Da ist die Sache schon etwas kritischer,
aber das muss jeder individuell für sich entscheiden,
inwieweit er da etwas braucht, um sich auf den Beinen halten zu können.
Ja, die immer wieder die wählerkehrende Frage nach den erlaubten Unterlagen,
ich mache da keinerlei Einschränkungen, bringen sie mit,
was immer sie glauben, was ihnen helfen könnte, die Klausur zu bestehen.
Ich werde das nicht kontrollieren, was sie da haben.
Die einzige Einschränkung sind elektronische Geräte, die dazu dienen könnten,
Kommunikation nach draußen herzustellen oder auf denen ein Prolog-Interpreter läuft.
Also alles, was irgendwie im Bereich Mobiltelefon oder Computer
angesiedelt ist, das ist nicht erlaubt als Hilfsmittel.
Ansonsten ist es mir egal, was sie nutzen. Das ist eine eine scheinbare Großzügigkeit,
ist es aber in Wirklichkeit gar nicht, die Erfahrung zeigt,
dass es keinen Sinn macht in der Klausur anzufangen zu recherchieren.
Ich werde ihnen, und da komme ich gleich noch dazu,
eine Liste,
die häufig oder der für die Lösung in der Klausur benötigten Prädikate für den Logikteil und Funktionen für den funktionalen Teil,
die Liste werde ich ihnen zur Verfügung stellen mit einer Kurzbeschreibung,
wo sie daraus entnehmen können,
was die Dinger tun und wie die Argumentreihenfolge ist und
so weiter und so fort
Sodass sie also relativ schnell auf solche Informationen zugreifen können und
nicht erst irgendwo in irgendwelchen Manuals anfangen müssen zu suchen.
Dafür wird die Zeit in der Klausur nicht ausreichen.
Also zweites Argument ist, also das Zeitargument, das zweite Argument ist,
die Lösung steht typischerweise nicht in irgendwelchen Unterlagen drin.
Weder in den Übungsaufgaben, die wir gelöst haben,
noch im Vorlesungsskript oder in irgendwelchen Büchern.
Ich habe genügend viel Fantasie, um mir immer mal neue Sachen auszudenken.
Also das müssen sie entscheiden, was ihnen helfen kann.
Ich habe da schon die beeindruckendsten Sachen gesehen,
dass das Skript mit einem umfangreichen Registerapparat versehen war,
damit man den Schnellzugriff zu den relevanten Teilen hat und so weiter.
Das ist, kann jeder individuell handhaben.
Während der Klausur dürfen sie Notizen nur auf dem Klausurbogen vornehmen,
auf dem Klausurbogen wird genügend Platz sein,
um so etwas zu tun, falls der Platz wirklich nicht ausreichen sollte,
werden wir eine Ersatzlösung dafür finden.
Wenn nicht irgendjemand dabei ertappe,
dass er sich Notizen für den Weg nach Hause mitnehmen will,
dann gilt das auch als möglicher Betrugsversuch
Die Dauer der Prüfung ist 120 Minuten, der Mix der Aufgaben,
der dort, ach so, ja, wie üblich die Regelung,
50 Prozent der Punkte sind zum Bestehen erforderlich.
Der Mix der Aufgaben ist ungefähr, dass zu 15 Prozent,
also legen Sie mich nicht bitte auf,
ich stelle nach dem Komma fest dabei,
aber so die Größenordnung soll das andeuten,
Konzepte, was ist was, wozu braucht man das,
warum braucht man das,
was ist der Unterschied zwischen A und B und dergleichen mehr,
Fragen dieser Art
Dann 15 Prozent Anwendung von technischen Grundfertigkeiten,
also unifizieren Sie verschiedene Therme miteinander,
werten Sie einen funktionsalen Ausdruck aus, was ist der Wert davon,
stellen Sie Anfragen an die Datenbank und dergleichen mehr.
15 Prozent eher passive Programmanalyse, also irgendein Stück Text,
Programmtext ist gegeben und Ihr Programmverständnis soll überprüft werden.
Und das ist ein Feature und kein Bug. 55 Prozent sind dann tatsächlich
aktive Programmieraufgaben, wo Sie irgendein Stück Programmcode mal produzieren müssen.
Und das ist Größenordnung her tatsächlich bewusst so angelegt,
wenn sie an keiner Stelle in der Lage sind,
aktiv ein Stück Programm zu entwickeln,
dann werden sie die Prüfung nicht bestehen.
Das ist wirklich eine bewusst eingezogene Schwellschwelle,
nur durch das Beantworten von Definitionsfragen oder dergleichen.
Ich stelle sowieso keine Definitionsfragen. Aber nur auf der konzeptuellen Ebene,
also was weiß ich, was ist der Unterschied zwischen einer
Special Form Expression und einer Funktion oder so etwas.
Nur auf dieser Ebene wird die Prüfung
nicht zu bestehen sein
Das ist also der Kern der Prüfung, des Prüfungsinhaltes,
aktive Programmentwicklung. Das Ganze natürlich in sehr,
sehr einfachen Anwendungen ist mir vollkommen klar,
dass wenn man nicht die Unterstützung einer Programmentwicklungsumgebung hat,
dass man da einen schweren Stand hat,
dass man insbesondere nicht in der Lage sein wird,
hundertprozentig so ein taktisch korrekter Programme zu schreiben.
Es hat auch noch nie jemand wegen eines Klammerfehlers oder wegen
einer fehlenden, schließenden Klammer Punktabzug bekommen in seiner Klausur.
Allerdings muss man natürlich dort den Vorbehalt machen,
die Klammersetzung ist teilweise Semantik beeinflussend
In dem Augenblick, wo eine falsche Interpretationsmöglichkeit entsteht,
dann sehe ich mich schon gezwungen, das irgendwie zu berücksichtigen.
Aber wenn offensichtlich ist, dass da nur ein Flüchtigkeitsfehler,
ein fehlendes Komma oder irgend so etwas aufgetreten ist,
na da, das kann ich in dem Kontext nicht verlangen,
dass man da die Ruhe mitbringt, dass ob wir Stimmigkeitsüberprüfen.
Ich würde lieber die Prüfung in einer Programmierumgebung machen,
aber dafür fehlt uns die Infrastruktur.
Die Klausur selber besteht aus sechs Teil auf,
also sechs Aufgaben von denen 50 mit der Logikprogrammierung befassen,
sowohl aktiv als auch passiv und auf
Und ein Anteil funktionale Programmierung, das ist rein passiv,
was dort passieren wird. Also nur ein Programm analysieren.
Und sie werden auf dem letzten Übungszettel, der hängt seit gestern im,
nein, der wird heute wahrscheinlich ins Netz gehangen.
Auf dem letzten Übungszettel können sie das sehen, der besteht aus drei Aufgaben.
Erstens ist, zu was wertet ein Ausdruck aus? Ja,
das ist also so ungefähr wie, was bewirkt ein Prolog-Ausdruck?
Ist das dann auf der funktionalen Ebene? Das zweite ist,
es sind Funktionsdefinitionen gegeben und sie sollen beschreiben,
was die tun
Und sie sollen das Ganze, um zu zeigen,
dass sie verstanden haben, was die tun,
in ein äquivalentes Prologprogramm überführen. Das ist wieder aktives Programmieren,
aber eben auf der Logikprogrammierungsseite, nicht auf der funktionalen Seite,
auf der funktionalen Seite wird nur das Programmverständnis überprüft.
Und die dritte Sorte von Aufgaben,
die auf dem Übungszettel noch drauf ist,
sie wird es in der Klausur nicht geben,
nämlich programmieren sie selber irgendeine einfache Funktion.
Das kann ich nicht erwarten,
dass man nach so kurzer Zeit tatsächlich und nur einer
einmaligen Übung tatsächlich in der Lage ist,
aktiv nicht triviale Sachen aus dem Stehgreif
zu produzieren
Gut, zur Prüfungsvorbereitung empfehle ich Ihnen die begriffliche Basis nochmal zu überprüfen,
dass Sie wirklich in den Grundbegriffen relativ sattelfest sind,
also was ist Unifikation, was ist eine Relation,
was ist ein Fakt, eine Regel und dergleichen mehr.
Das Gleiche gilt auch für die funktionale Salbe,
dass sie dann sich Übungsaufgaben nochmal angucken, die nacharbeiten,
modifizieren und schauen, was sind die,
sind mögliche leicht abgewandelte Formen dieser Aufgabe,
das ist eine beliebte, ein beliebter Trick in der Klausur,
dass man die Fähigkeit überprüft von einer gegebenen Lösung eine modifizierte Lösung abzuleiten.
Und ich gebe Ihnen eine Probeklausur, die hänge ich auch demnächst ins Netz.
Diese wird üblicherweise oder löst üblicherweise schwere Kontroversen aus,
weil sie immer mit Kommentaren versehen wird, wie ja,
die war ja einfach.
Und die Klausur hingegen war extrem schwer. Die Empirie sagt
etwas anderes. Diese Probeklausur ist dadurch zustande gekommen,
dass sie mal eine reale Klausur war,
die wir in dieser Form nie wieder gewagt haben,
jemanden anzubieten, weil die Ergebnisse desaströs waren.
Das war das einzige Mal, wo wir den Bewertungsmaßstab verändern mussten.
Und das ist über die letzten 98, also 15 Jahre,
sagen wir 14 Jahre. Einmal mussten wir den Bewertungsmaßstab ändern
und das war diese Klausur. Da hatte sich sowohl,
damals war das noch eine gemeinsame Klausur,
hatte sich sowohl Frad Reschler für die funktionale Programmierung,
als auch ich mich für die Logikprogrammierung heftig verschätzt.
Und zwar an Stellen, wo wir auch hinterher noch ratlos
davor gestanden haben und uns gefragt haben, wieso eigentlich,
was ist da so schief gelaufen?
Also das ist empirisch ein ziemlich dicker Brocken mal gewesen. Ich glaube,
dass diese Einschätzung, dass die sehr einfach ist und die reale Klausur sehr schwer ist,
dass die Einschätzung daher kommt,
dass man eben die Probeklausur unter entspannten Bedingungen sich vornimmt,
auf dem Sofa sitzend bei schöner Musik und einem Glas Wein und hinterher nur abnickt,
ja, ja, ja, das kann ich alles.
Während in der Klausur man doch unter psychischer Anspannung vor den
Aufgaben sitzt und da möglicherweise in Schwierigkeiten läuft.
Also ich kann nur empfehlen, machen Sie das unter realitätsnahen Bedingungen,
stellen Sie sich eine Uhr daneben, schauen Sie,
wie Sie mit der Zeit zurechtkommen, die ganze Klausur,
da steht auch auf dem Klausurbogen so drauf,
wird mit sechs Aufgaben, A10 Punkte.
Das modifiziere ich manchmal leicht nach oben oder nach unten,
aber insgesamt gibt es immer 60 Punkte.
Für 120 Minuten heißt pro Punkt haben sie zwei Minuten zur Verfügung. Das heißt,
ihr könnt also schon bei der Aufgabe sehen,
wenn es dafür zwei Punkte gibt und sie kommen in vier Minuten nicht klar,
dann lohnt es sich nicht, auf die vier Punkte noch mehr Zeit zu werfen.
Also dieses Zeitmanagement kann man bei der Gelegenheit auch nochmal ein bisschen trainieren.
Gut, das sind auch schon die ganzen Bemerkungen,
die ich dazu machen wollte. Wenn Sie Fragen haben,
gerne können Sie die loswerden jetzt oder auch später,
ist egal
Nö, keine Fragen. Gut, dann kommen wir zum
eigentlichen Gegenstand der Veranstaltung wieder. Zur Erinnerung,
wir sind dabei, uns um funktionale Programmierung zu kümmern.
Wir hatten uns angeguckt, die Trivialfälle eines Auswertungsmechanismuses,
also Konstanten, die zu sich selbst auswählen,
das sind im Wesentlichen die Zahlen und die bullschen Werte gewesen
Ja, das ist eigentlich alles, was wir bisher gemacht haben.
Wir müssen uns jetzt als nächstes angucken, vajablen Behandlung.
Und als die weiteren Schritte dann Funktionen und die Ausnahmefälle von Funktionen,
also Special Form Expressions. Und wenn wir das haben,
dann haben wir einen Interpreter, der mit funktionaler Programmierung,
den wir benutzen können, um funktional zu programmieren.
Und das ist das Ziel für heute, dass wir diesen Punkt erreichen.
Wenn wir über Vajablen reden im Bereich der Funktionalen Programmierung,
dann müssen wir etwas einführen, was von der Logikprogrammierung drastisch abweicht,
was sie aber aus praktisch allen anderen Programmiersprachen kennen,
wo die Logikprogrammierung einen Sonderstatus hat.
Das ist nämlich
der Gültigkeitsbereich von Vajablen
In der Logikprogrammierung hatten wir ja immer die Regel der Gültigkeitsbereich der Variablen,
ist die Klausel. Alles, was über die Klauselgrenze hinweggeht
, ist eine neue Variable. Also wenn eine Variable,
der gleiche Variable in unterschiedlichen Klauseln auftaucht, dann sind das unterschiedliche Variablen.
Was wir immer dann gesehen haben, wenn wir den Dresser angeworfen haben,
wenn man nämlich in die Innereien des Systems guckt,
die Namen auf der Oberfläche werden nur zur Kommunikation mit dem Nutzer verwendet.
Intern gibt es Gensüme, um sicherzustellen,
dass tatsächlich die Variablen unterschiedlich sind, die dort verwendet werden.
Das ist hier anders. Hier gilt also die Blockstruktur,
wie in jeder guten blockorientierten Sprache, dass ich mit bestimmten Konstrukten,
Umgebungen aufmache, in denen die um Variablen gültig sind.
Und die sind dann nur innerhalb dieser Umgebung gültig.
Eine solche, das Konstrukt, was eine solche Umgebung aufmacht,
ist ein ganz simpler Funktionsaufruf. Jeder Funktionsaufruf eröffnet eine neue Umgebung,
definiert dort lokale Variablen und die sind nur in dieser Umgebung gültig.
Wenn die gleiche Variable außerhalb dieser Umgebung auftaucht, ist es auch eine andere Umgebung.
Aber da das jetzt Funktionsaufrufe natürlich eingebettet sein können,
ist das eine hierarchische Struktur und ich muss in dieser hierarchischen Struktur gucken,
welche Variable hat zu welchem Zeitpunkt eigentlich welche in welche Referenz,
auf welche Variable wird dort eigentlich verwiesen.
Und wenn ich also diese Frage lösen will,
über welche Variable reden wir hier jetzt gerade,
muss ich sozusagen in dieser Hierarchie, also in diesem Baum,
eine Buttom-Up-Suche durchführen, bis zu der Umgebung,
wo die Bayable mal eingeführt worden ist.
Und sie ist dann sichtbar in allen auch untergeordneten Variablen,
solange dort nicht die gleiche Variable nochmal halt der gleiche vaiablen Name
nochmal eingeführt wird, dann wäre es eine andere Variable.
Es gibt weitere Umgebungen, mit denen man zusätzliche Variablen einführen kann, die nicht...
Da habe ich mich an. An mir selbst verschluckt.
Es gibt weitere Umgebungen, die können zusätzliche Variable einführen,
die aber nicht formale Parameter einer Funktion sind.
Und das sind die Konstrukte der Latt-Umgebung. Latt hat also immer die Struktur,
dass es nach dem Funktionsnamen oder wir werden auch sehen,
es ist eine Special Form Expression,
dass sie hier Eine Liste von Name Wertpaaren enthält,
die es hier mit, Klammer zu,
hier mit Klammer auf, abgegrenzt.
Jedes Name wertbar ist eine zweielementige Liste aus einem vajablen Bezeichnung und dem zugehörigen Wert.
Und dann als letztes ein Körper enthält, wo als drittes,
sorry, erste Listen-Element, zweites Listen-Element, ist diese Liste von Name Wertpaaren.
Und das dritte Element ist das dritte und alle weiteren,
muss man genauer sagen, ist der Körper der Lett-Umgebung,
das sind die Funktionsaufrufe, die dann unter diesen vajablen Bindungen hier tatsächlich ausgeführt werden.
Ja, also hier wird nichts anderes gemacht als eine
neue Umgebung aufgemacht. Es werden neue Bajablen eingeführt,
denen wird jeweils ein Initialwert zugewiesen und dann wird in dieser Umgebung mit diesem vajablen Bindungen,
die dort vorgenommen worden sind, irgendeine Verarbeitung durchgeführt.
Es gibt, das ist eine ganze Familie,
eine Variante ist Lettstern, Lettstern wird immer dann verwendet,
wenn ich innerhalb dieser Name Wertpaarliste hier mich auf eine vajable beziehe,
nein, hier den Wert einer vajablen Abfrage, die weiter vorn, der Wert der vorn erst eingeführt worden ist.
Also wenn es innerhalb dieser Liste Abhängigkeiten gibt,
dann muss man Lettstern verwenden, wenn denn der Funktionsaufruf hier rekursiv ist,
dann muss man Lett Dreck verwenden
Ja, das ist eine ganze Familie mit unterschiedlicher Leistungsfähigkeit.
Hier also ein Beispiel für den Umgang mit Umgebungen und das Phänomen der Verschattung.
Und der Verschattung verstehen wir jetzt also immer, dass eine neue Umgebung,
die eingeführt wird durch einen Funktionsaufruf, die alten Variablen verschattet,
weil sie die Variable mit dem gleichen Namen als Subumgebung einführt.
Also wir sind hier ganz Anfang in der globalen Umgebung des
Scream-Systems und definieren jetzt eine Variable X für den WR3,
eine Variable Y auf dem Wert 4.
Und wir definieren eine Funktion Pythagoras,
die lokal zwei Variablen XY einführt
Ja. Die Funktion Pythagoras hat eine Letztstern-Umgebung und diese Letztstern-Umgebung definiert drei Namen,
nämlich Square, X2 und Y2. Square ist eine Funktionsdefinition,
das sehen wir an diesem Lamm da hier.
Die nimmt ein Argument und quadriert es.
Daher der Name Square. Und X2 wird jetzt benutzt,
wird jetzt auf den Wert gebunden, Quadrat,
also Aufruf dieser Funktion, von diesem Wert X,
der hier oben als formaler Parameter übergeben wird.
Und Y2 analog dazu, auch das Quadrat nur diesmal aus
der zweiten Argumentstelle genommen. Das sind die drei Variablenbindungen,
die hier vorgenommen werden. Und hier sehen wir,
da ist intern eine Abhängigkeit vorhanden, die Funktion Square,
die ich hier gerade definiert habe, die wird hier bereits benutzt.
Und wegen dieser internen Abhängigkeit, hier benutze ich eine Variablenbindung,
die ich hier erst hergestellt habe, wegen dieser Abhängigkeit,
muss ich also nicht letzt, sondern letzt Stern verwenden.
Aber das sind Feinheiten, die sind auch nicht prüfungsrelevant. So,
und jetzt zum Schluss in dieser globalen Umgebung,
hier habe ich also nichts anderes gemacht als Definitionen vorgenommen,
in dieser globalen Umgebung rufe ich jetzt diese Funktion die hier definiert worden ist,
auf mit zwei Argumenten.
Das eine ist nämlich X plus 3 und das andere ist Y plus 2.
Und jetzt müssen wir uns fragen, was wird hier eigentlich benutzt?
Über welches X reden wir denn?
Wir haben hier oben in der globalen Umgebung X und Y
und wir haben in dieser lokalen Umgebung auch X und Y.
Ich hätte es eigentlich noch brutaler machen sollen,
dass sich das X und Y nochmal in dem Lat verwendet.
Dann wäre es richtig spannend geworden. Hier ist die Variante in unserem Peace-Geam-Dialekt,
das ist nur die Pro-Log-Listen-Notation für das Gleiche,
was wir eben in der Lisp-Notation gesehen haben
Das ist. Und ich habe hier den Star ausgeschrieben und nicht als Stern verwendet,
um alle syntaktischen Ärgerlichkeiten von Prolog zu vermeiden,
weil Prolog über die Operatoren natürlich sehr viel möglicherweise Belecht hat.
So, was wir jetzt haben ist, wie eine globale Umgebung,
das ist also die hier Außen eingezeichnet,
in der haben wir drei Namen benutzt, nämlich X,
Y und Pythagoras.
Das sind die drei, die hier stehen. Pythagoras hat
eine Funktion als als Wert X und Y sind hierdurch,
dass die feinen auf 3 und 2,
also auf numerische Konstanten gesetzt worden.
Und wir haben in dieser globalen Umgebung natürlich auch alle Standardnamen zur Verfügung.
Also etwa auch alle Funktionsnamen, die in Steam standardmäßig vordefiniert sind.
Sie erinnern sich, das hatte ich vor die Woche gesagt,
da gilt das Verbot der Wiederverwendbarkeit.
Also was einmal als Funktionsname benutzt wurde, hat einen Wert.
Und den kann ich natürlich überschreiben,
aber das sollte ich tun,
dies nicht tun
Gut, das ist die globale Umgebung mit diesen drei Variablen.
Und jetzt mache ich hier durch diesen Aufruf eine neue Umgebung auf.
Durch diesen Aufruf wird jetzt die neue Umgebung von Pythagoreis
eröffnet und die für zwei neue Variablen ein,
nämlich X und Y. Und in der Umgebung von Pythagoras wird jetzt eine neue Umgebung aufgemacht durch das Letztstern.
Und die führt die Namen Square X2 und Y2er ein.
Und wenn jetzt also in dieser Lett-Umgebung hier dieser Aufruf erfolgt,
dann muss ich in der Lett-Umgebung gucken,
wo sind die Variablen X2 und Y 2 und sehe,
aha,
über die beiden wird hier gerade geredet
Wenn ich hier innen drin jetzt über X und Y rede,
ja, hier an dieser Stelle, dann kann ich in
der Latt-Umgebung gucken und da gibt es kein X und Y.
Also muss ich eine Umgebung weiter nach oben gehen und
da gibt es ein X und Y. Das heißt,
dieses X und Y hier wird aus der Pythagoras-Umgebung genommen.
Und wenn in diesem Putagoras-Aufruf hier von X und Y die Rede ist,
dann ist das das X und Y aus der globalen Ebene.
Also durch den Aufruf von Pythagoras werden X und Y,
die hier global mal definiert worden sind, verschattet,
durch die lokalen Bajablen X und Y
der Potagoras-Funktion
Also, die korrekte Sprechweise ist,
der Aufruf dieser Funktion verschattet die globalen Variablen X und Y.
In Klausuren oder wo auch immer kriege ich oftmals die Formulierung,
dass die Definition dieses Pythagoras das verschatten würde, mitnichten.
Überhaupt nicht. Die Definition tut gar nichts anderes,
als diese vajable Pythagoras auf den Wert dieser Funktion zu setzen.
Da wird nichts verschattet, überhaupt nichts sein.
Wenn ich hier die Funktion Pythagoras konstruieren würde,
ich muss ja hier nicht einen Lamdeausdruck hinschreiben,
sondern ich könnte ja hier, also ein Funktions- Körper.
Ich könnte ja hier auch Aufrufe hinschreiben,
die mir eine Funktion überhaupt erst konstruieren.
Dann könnte es passieren, dass eine Verschattung auftritt.
Aber durch eine einfache Definition wird nur eine Variable auf einen Wert gesetzt.
Da wird nichts verschattet. Erst der Aufruf,
der Aufruf eröffnet diese neue Umgebung und diese neue Umgebung verwendet die gleichen variablen Bezeichner,
wie die hier außen auch schon verwendet worden sind.
Und damit verschatten diese beiden, die, die hier außen verwendet worden sind.
So, und diesen Mechanismus müssen wir jetzt implementieren und
das macht einen guten Teil unseres Scream-Interpreters aus.
Ich habe zwei Prädikate eingeführt, Environment 3-stellig und Environment 2-stellig,
Environment 3-stellig sagt, ich bin eine bestimmte Umgebung,
hier EA11 und ich kenne eine Variable X und die hat
den Wert
Ja, oder hier eben die vajable Y und mit dem Wert 4
und die vajable Square, die einen solchen Lamderausdruck hat,
als Wert. Und tatsächlich ist es kein lammender Ausdruck,
wir werden gleich noch kommen, es ist was anderes,
was an der Stelle steht, aber das kommt jetzt fünf Minuten.
Und da muss sich natürlich die hierarchische Einbettung der Umgebungen irgendwie
modellieren und das macht dieses zweistellige Environment-Prädikat, was also sagt,
ich bin E12 und ich bin eine untergeordnete Umgebung von E11.
Also ich erbe von E11 alle Variablen,
solange ich nicht selbst die gleichen Variablenbezeichner nehme und dadurch Verschattung eintritt
Also das ist eine Vererbungsbeziehung für die Variablen der übergeordneten Umgebung,
die man dort hat. Und natürlich müssen wir irgendwie definieren,
was die oberste Umgebung sein soll.
Und die habe ich ja Global genannt.
Kann man anders machen. Also das ist eine Vereinbarungssache,
mit dem Global da unten. Damit habe ich eine Baumhierarchie,
in der ich jetzt Umgebung eintragen kann,
weil immer ein neuer Funktionsaufruf kommt,
muss die für den eine Umgebung eröffnet werden in dieser Hierarchie.
Dort müssen die lokalen Variablen eingetragen werden in Form dieser
dreistelligen Environment-Predikate mit gegebenenfalls dem Wert,
mit dem der Aufruf erfolgt ist
Okay, und da müssen wir natürlich vereinbaren, dass das Ganze dynamisch sein muss.
Ich muss es verändern können, ich muss neue Eintragungen machen können.
Und es muss initial leer sein, das heißt,
ich mache vorsichtshalber mal ein Retract All auf beide Prädikate damit.
Am Anfang der Verarbeitung meine Datenbank sauber ist. Und dann
brauchen wir ein paar Prädikate, um genau die Semantik umzusetzen.
Lassen Sie sich nicht stören. Hier ist noch eine Menge Housekeeping dabei,
die nur dazu dient, um nachzuvollziehen,
was macht ihr Apparat eigentlich,
damit man ein bisschen was aus den Innereien sehen kann
Die ihre eigentliche Semantik ist einfacher. Also Get Value macht nichts anderes.
Ich habe eine Variable gegeben, ich habe die Umgebung gegeben,
aus der ich den Variablen wert haben will.
Und es gibt mir den Wert zurück.
Das ist die Idee hier dabei. Und dann macht es eben
nichts anderes, als dass es dieses dreistellige Environment-Prädikat abfragt.
Also was ist der Wert dieser Variablen in dieser Umgebung?
Und wenn das erfolgreich war, bin ich fertig,
dann kenne ich den Wert. Wenn das nicht erfolgreich war,
dann gibt es diese Variablen der Umgebung nicht.
Ja, dann muss ich eine Variable weiter nach oben gehen.
Dann nehme ich hier diese Super Environment und hole mir
den Wert aus der Super Environment
Und das Ganze rekursiv eben, wenn er auch dort nicht da ist,
geht es eben wieder eine Ebene weiter nach oben,
bis ich die Variable finde.
Oder, oder das Ganze wird nur so lange gemacht,
solange ich noch nicht in der obersten Ebene bin.
Wenn ich in der obersten Ebene angekommen bin,
dann kenne ich die Variable nicht, dann müsste eigentlich,
ah ja, das ist kein Housekeeping, ja,
sorry, das ist die Fehlernachricht, die geworfen wird.
Wenn es also in keiner der Umgebungen einen Wert dafür gibt,
dann wird der Fehler geworfen,
dass es keinen Wert dafür gefunden werden konnte
Wenn ich eine neue Umgebung aufmachen will, muss die eingetragen werden
in die Hierarchie. Ich habe dann also eine Umgebung gegeben,
die möchte ich erweitern durch eine neue Umgebung.
Und ich möchte in der neuen Umgebung für eine Liste von Variablen die Werte eintragen.
Dann muss ich der neuen Umgebung erst mal einen Namen geben,
die Environments habe ich immer mit E anfangen lassen.
Also das ist dann ein Gensüm,
E-E-188. Unter diesem E-188 trage ich jetzt das Ganze,
oder dass ich trage, diese neue Umgebung als Subumgebung der aktuellen,
wo ich es eintragen will, ein.
Und ich muss dafür sorgen, dass die Variablen gebunden werden.
Und zwar in dieser neuen Umgebung. Und dieses Binden der Variablen
ist einfach ein Synchroner-Durchlauf durch die Variablen-Liste und durch die Werteliste.
Ja, also hier ist die Variablen-Liste. Hier ist die Werteliste,
bis beide leer sind. Wird immer der Ersteintrag hier genommen,
der erste Eintrag hier genommen und es wird diese Wertebindung in
der aktuellen Umgebung eingetragen, nämlich gibt dieser Variablen diesen Wert.
Und das wird eben rekursiv gemacht,
bis die Listen leer sind und dann stehen alle Environment,
diese dreistelligen Environment-Prädikate da drin.
Also hier oben wird das zweistellige Environment-Prädikat gesetzt.
Hier werden die Wertebindungen, also die dreistelligen,
Jetzt kann ich auch innerhalb einer Umgebung natürlich Variablenwerte verändern.
Ich habe das Gäste, nein, Gäste, ja,
irgendwie jetzt in letzter Tage mal gegoogelt und durch Zufall bin
ich auf eine Seite gekommen, wo drin stand,
ich weiß nicht, ob Frau Draschler Fischer das gefällt,
Scream ist eine imperative Sprache mit einem funktionalen Kern.
Das ist doch etwas überraschend, weil wir das hier benutzen,
um funktionale Programmierung zu betreiben. Das fand ich eine ziemlich harte Aussage,
dass man tatsächlich den emperativen Charakter hier in den Vordergrund stellt und der ist nämlich genau hier da,
wenn ich eine Variable auf einen Wert setzen kann,
unsere Defeindprädikate, da sind Wertezuweisungen, die ich mache.
Damit ändere ich den Zustand der Maschine und Das hat imperativen Charakter.
Gut, also definieren einer Variablen heißt, ich muss schauen,
ob es einen Wert dieser Variablen bereits gibt.
Wenn es ihn gibt, dann muss aber entfernt werden.
Und in jedem Fall muss der neue Wert in die Datenbank
eingetragen werden. Das ist relativ simpel. Und das Setzen
einer Variablen ist nur eine leichte Modifikation da drin.
Dann wird nämlich, das Setzen der Variablen setzt voraus,
dass die Variable vorher schon mal eingeführt worden ist,
auf irgendeine Art und Weise.
Und ich muss nur den alten Wert beseitigen, das macht das Retrapt.
Und ich muss den neuen Wert eintragen, das macht das Iss hört.
Und falls denn kein Alter, wer hat existiert?
Dann muss ich wieder einen Fehler werfen, der sagt,
da wird versucht, eine Variable zu modifizieren, die es gar nicht gibt.
Das ist ein Unterschied zwischen dem Definieren einer Variablen und dem Setzen eines Variablen Wertes,
der bis auf die Oberfläche des Geam-Systems durchgedrückt wird.
Das ist nämlich der Aufruf von Defyen und das ist der Aufruf von Set Bang.
Set Bang ist also tatsächlich das Ändern eines Variablen Wertes,
eine Update-Operation, die den Zustand der Maschine verändert und deshalb
also hier hinten auch dieses Ausrufezeichen,
Achtung,
hier wird mit Nebeneffekten gearbeitet
So, und nun brauchen wir nur noch eins,
dass wir nämlich syntaktisch erkennen, was ist eigentlich eine Variable und Variablen,
sind die Atome in Scream.
Dadurch, dass diese Klausel hier eingeordnet ist in die ganze
Liste der Klausel und wir die Konstanten bereits behandelt haben.
Also at no attem sind die nicht numerischen. Also ich teste,
ob das der Ausdruck, den ich bekomme,
ein nicht numerisches Atom ist, das ist genau dieser Aufruf hier.
Wenn das der Fall ist,
dann muss ich das hier hinten anwenden und nur das,
ich mache ein funktionale Auswertung und dann rufe ich einfach mein Get Value-Prädikat auf,
gib mir in der aktuellen Umgebung den Wert dieser Expression,
nämlich dieses Atoms, als Ergebnis zurück.
Und nun können wir die ersten Interaktionen mit unserem Scream-System machen.
Wir definieren hier die Variable 5 in der globalen Umgebung.
Quatsch. Die Variable X in der globalen Umgebung setzen wir
auf den Wert 5 und wir können als nächstes abfragen,
welchen Wert hat denn X in der globalen Umgebung?
Ja, das ist die erste einigermaßen sinnvolle Interaktion,
die wir mit unserem Scream-System machen können
Noch nicht besonderlich spannend, aber stay tuned, es kommt noch mehr.
Der wesentliche Teil ist jetzt natürlich die Abarbeitung von Funktionen.
Bis jetzt haben wir nur Konstanten und Vagablen uns angesehen,
jetzt müssen wir irgendwann mal zu den Funktionen kommen und
Funktionen werden in Skim auf eine uniforme Art und Weise,
eine sehr einfache Art und Weise ausgewertet, nämlich in zwei Schritten.
Der erste Schritt heißt Evil, der zweite Schritt heißt
Apply und dieser zweistufige Prozess wird für jede Funktion durchlaufen.
Funktionen haben immer den Aufbau, es ist eine Liste,
Das erste Argument, oder die Kommas sollten natürlich verschwinden hier.
Wir haben Scheme-Listen und keine Prologlisten. Also denken Sie sich
mal bitte, die Kommas weg hier.
Das erste Listen-Element ist jeweils der Funktionsname und die restlichen Listelemente
sind, die Argumente für die Funktion.
Und es gibt durchaus auch Funktionen, deren Stelligkeit erliebig ist,
wo ich beliebig viele Argumente dazuliefern kann.
Die Restliste hier kann ja auch beliebig lang sein.
Es gibt natürlich auch Funktionen fester Stelligkeit.
Wir werden Beispiele für beides sehen
Jetzt wird, wenn das ausgewertet wird,
ist der erste Schritt, dass sich alle, alle,
und zwar ohne Unterschied, alle Listenelemente auswertet.
Ich stecke alle in die Auswerteprozedur hinein und frage,
was ist der Wert hiervon? Was ist der Wert hiervon
? Was ist der Wert hiervon? Ich erwarte natürlich,
dass die Auswertung des ersten Elementes, weil das der Funktionsname ist,
mir die Funktion liefert, die ich anwenden muss.
Also der Wert des ersten Agumentes, des ersten Elementes muss ein Landerausdruck sein.
Und die restlichen sind dann die aktuellen Parameter, mit denen
ich den Funktionsaufruf erfüllen kann. Und der zweite Schritt ist dann,
nimm diesen Landerausdruck, den du hier gekriegt hast,
für das erste Element und wende ihn auf die aktuellen Parameter an und berechne den Funktionswert für den Gesamtausdruck.
Das ist die Standardvereinbarung. Also werte alle Dinge aus.
Das ist natürlich etwas, was wir nicht immer wollen.
Das kann in Einzelfeld und wir werden die Fälle gleich sehen, zu Ärger führen.
Wir müssen in Einzelfällen Abweichungen von dieser Standard-Auswertungsreihenfolge zulassen.
Wir können das nicht auf diese uniforme Art und Weise in jedem Falle machen.
Und für diesen Fall gibt es Special Form Expressions.
Special Form Expressions sind also solche, wo von der Standard-Auswertungsreihenfolge abgewichen wird.
Und die erste Abweichung ist, der Funktor Einer Special Form Expression wird nie ausgewertet.
Der ist immer vor der Auswertung geschützt grundsätzlich.
Während hier der Funktoren als erstes mit ausgewertet wird.
Und der Wert dessen muss die Funktion sein, die ich anschließend anwende.
Das ist schon mal der erste große Unterschied zwischen einer Special
Form Expression und einer Funktion.
Also hier, der Funktor ist immer vor Auswertung geschützt.
Das ist der Funktions- oder der Name dieser Special Form Expression.
Und wie die Argumente behandelt werden, da müssen wir vier Fälle unterscheiden.
Und die vier Fälle kommen jetzt. Der einfachste Fall ist
, keines der Argumente wird ausgewertet
Ich kann nur ein Beispiel davon, das ist Quote.
Quote schützt also den Ausdruck, der hier hinten erscheint,
vor Auswertung. Wenn wir also irgend so etwas haben,
dass wir einen Ausdruck in unser Geam-System reingeben,
wie X, dann wird dieses X ausgewertet.
Und wenn wir gar nicht von der Variablen X sprechen wollen,
sondern von dem Wert, von den konstanten X,
dann müssen wir das vor Auswertung schützen.
Also genau umgekehrt, als in Prolog,
in Prolog haben wir die Variablen,
sind taktisch markiert,
hier markieren wir
die Konstanten
so taktisch
Durch dieses Quote und ich hatte, glaube ich, das letzte Mal schon gesagt,
da gibt es eine Kurzfassung davon mit dem Hochkomma davor.
Das ist semantisch das Gleiche, das ist nur eine Kurzschreibweise.
Also Quote verhindert, dass die Argumente überhaupt,
dass das einzige Argument, was wozu lässt, ausgewertet wird.
Anderer Fall ist natürlich, ich habe hier hinten eine Liste,
also wenn ich hier so etwas habe, wie Fu,
X, Y, wenn ich das so stehen habe,
wird eben hier der Funktionsdefinition Auswertung ermittelt und dann auf die
vaiablen Belegungen für X und Y angewendet.
Wenn ich das nicht möchte, wenn ich von der Liste fuhr XY spreche,
auch dann muss ich das Ganze durch Quote vor der Auswertung schützen.
Also Standardinterpretation für Listen ist, ich bin eine Funktion,
wenn ich das nicht will, Quote anwenden.
Standardinterpretation für nicht lumerische Atome ist, ich bin eine vaiable,
wenn ich das nicht will, wenn es eine Konstante sein soll,
muss ich das durchaus vor der Auswertung schützen.
Das ist der erste Fall. Der zweite Fall ist,
die Argumente werden nur teilweise ausgerechnet, ausgewertet.
Das ist immer bei in diesen Variablen Sätzen Definitionen davor.
Also dieses Set Bang, was eine Variable auf den Wert eines Ausdrucks setzt.
Ich möchte natürlich diesen Ausdruck hier hinten auswerten können, um eine Berechnung durchzuführen.
Und der Wert dieses Ausdrucks soll an diese Variable gespeichert werden.
Also die Variable darf nicht ausgewertet werden, aber dass sie hinten sehr wohl.
Also teilweise auswerten, hier wird nicht ausgewertet, hier wird ausgewertet.
Und genauso verhält es sich bei Define. Der Name,
der hier steht,
das kann also der Funktionsname sein oder der vajablen Name,
der wird nicht ausgewertet,
aber hier hinten,
dieser Ausdruck wird
sehr wohl ausgewertet
Also teilweise Auswertung und zwar nur hier hinten.
Das ist der zweite Fall. Der dritte Fall ist,
wir wissen von vornherein gar nicht, welche Argumente ausgewertet werden,
sondern das ergibt sich erst im Verlauf der Berechnung.
Das ist etwa bei den Konditionalen der Fall.
Natürlich will ich immer den Wert des Konditionals wissen.
Ich möchte wissen, ob das hier zu False oder Truh
auswertet. Denn bei Truh muss ich dieses auswerten.
Bei False muss ich jenes auswerten. Ich darf also hier
auch nicht diese Standard-Auswertungsreihenfolge anwenden, werde erstmal alles aus.
Denn wenn in einem dieser Zweige Nebeneffekte drin sind,
dann werden diese Nebeneffekte ja bereits ausgeführt bei der Gelegenheit
Ja, also Schreibbefehle beispielsweise. Ausgabe von Ergebnissen auf die
Konsole. Und ich möchte ja aber erst mal ermitteln,
welchen dieser Zweige ich überhaupt ausführen soll.
Das heißt, ich muss das hier auswerten und in Abhängigkeit
von dem Ergebnis hier wird entweder das oder das ausgewertet.
Also etwas, was ich erst dynamisch im Verlauf der Berechnung
ergibt, wo ich tatsächlich aktiv werden muss.
Aber grundsätzlich ist es eben auch so, es darf nicht alles blind ausgewertet werden.
Das ist die Weisheit und das macht es zu einer Special Form Expression.
Das Gleiche gilt also für und, und, oder.
Auch dort Guilty, dort hat man sich eine,
auch wenn eine links-rechtsverarbeitung geeinigt, die Ausdrücke,
die hier hinten drin stehen und, und, und,
oder sind die ersten Beispiele für Funktionen mit beliebig vielen Argumenten.
Also hier können beliebig viele Ausdrücke stehen, die erstmal ausgewertet werden müssten.
Und das End, nein, beide werden nur so lange durchgeführt,
bis der Funktionswert ermittelt ist. Also das End wird so lange ausgewertet,
bis ich die das erste Mal ein Falls finde,
denn dann weiß ich,
dass das Endfalls ist und das Ohr wird so lange ausgewertet,
bis ich das erste Mal ein Truf finde.
Wenn ich das Truh gefunden habe, dann weiß ich,
dass der Ohrausdruck auch Truh ist. Und alle mit anderen,
die weiter hinten liegen, brauche ich gar nicht anzufassen.
Also auch ihr, teilweise Auswertung und welche ich auswerte,
hängt davon ab, wie die Auswertung des jeweils vorangegangenen Ausdrucks ausgegangen ist.
Also ob dort To oder Falschfalls ermittelt wurde. Der vierte Fall,
den werden wir hier nicht behandelt, der vierte Fall ist Transformationsansatz.
Ich nehme also den Ausdruck und wandle ihn an einen anderen Ausdruck
um und den schicke ich dann wieder in die Auswertung in das System hinein.
Das passiert etwa bei den Kont-Ausdrücken. Also das ist ein
verallgemeinertes Konditional und das wird in solche IF-Ausdrücke,
wie wir es hier oben haben,
umgewandelt und die werden dann tatsächlich abgearbeitet
Also das ist auch implementationsabhängig, da können sich die Systeme auch
anders unterschiedlich verhalten. Aber das ist zum Beispiel ein,
sagen wir es so rum, es ist eine Möglichkeit,
wie ich diesen Transformationsansatz anwenden kann oder ein Gebiet,
wo ich das anwenden kann.
So, und jetzt müssen wir der Reihe nach durch.
Das sind Ausnahmefälle zu dem allgemeinen Funktionsauswerten, die müssen wir einzeln implementieren.
Da beißt die Maus keinen Faden ab, da müssen wir ran.
Also das erste ist erster Fall, die Argumente werden
überhaupt nicht ausgewertet. Das einzige, was wir hatten,
war diese zweistellige Liste mit Quote und einem Ausdruck.
Und der Interpreter ist ganz simpel dafür,
wenn ein solcher Ausdruck vorliegt, dann ist sein Wert,
dass das,
was hier hinten steht
Ohne dass ich irgendetwas getan habe damit. Also,
wenn ich so etwas sage, werde mir Quote X in
einer globalen Umgebung aus. Das ist genau dieses hier,
dann soll der Wert dann das X sein.
Und das sehen wir hier hinten. Also diese Prolog-Variable X
wird jetzt auf die Scream-Konstante X gebunden. Wir müssen hier immer unterscheiden,
sind wir auf der Meta-Ebene, der Ebene des Meta-Interpreters,
also reden wir jetzt über Prolog-Konstrukte und das hier ist ein Prolog-Konstrukt.
Oder reden wir über ein Scream-Konstrukt,
das ist dieser Ausdruck hier
Und der Wert dieser Variablen, das ist wieder ein Scream-Konstrukt,
das ist wieder auf der Basisebene.
Das muss man immer sauber auseinanderhalten. Dann der Fall,
Argumente werden teilweise ausgewertet. Da müssen wir wieder verschiedene Varianten unterscheiden.
Entweder das zweite Listen-Element ist ein Atom, dann wird eine einfache Variablenbindung vorgenommen.
Und zwar müssen wir ja hier hinten das auswerten.
Das hier sollen wir nicht auswerten. Und genau das steht
hier. Wir werten also diese Value Expression hier hinten aus
in der Umgebung, in der wir uns gerade befinden.
Das ist die Umgebung. Und wir kriegen einen Wert zurück,
der dann als Wert dieser aus, der an die Variable gebunden wird.
Dies dieser Wert hier ist nicht der Wert des diffeinen Ausdrucks.
Der Wert ist die feinen Ausdrucks,
ist laut Revised Report on Scream, Andi Feind.
Ich habe hier einfach okay reingeschrieben.
Also der Wert dieses De feinen Ausdrucks ist, okay,
das werden wir dann gleich in den Interaktionen sehen.
Andi Feind heißt, jeder Implementierer kann machen, was er will.
Das sind solche Stellen, wo die Definition,
falls Gim nicht strikt ist
Ja, und wenn wir diesen Wert haben,
wird also mit unserem Prädikat,
was wir vorhin gerade definiert haben, nämlich die Fine Variable,
dieser Wert, an diese Variable in dieser Umgebung gebunden.
Wenn das zweite Element, Listenelement eine Liste ist,
dann ist das einer, die Kurznotation für Funktionsdefinitionen.
Da gucken wir uns nochmal das Beispiel an,
was wir vorhin gerade hatten. Hier ist es.
Das ist diese Notationsweise. Die feiern auf der ersten Listenposition
und hier ist eine Liste auf der zweiten Listenposition.
Das heißt, hier definiere ich jetzt
eine Funktion
Oder ich setze die vaiable Pythagoras auf den Ausdruck Lamda von
XY und dann mit diesem Körper hier hinten.
Das ist die Kurznotation. Also wenn das eine Liste ist,
dann ist es, wird keine Variable auf den Wert gesetzt,
sondern wird eine Funktion definiert, die an einen Namen gebunden wird.
Und das ist jetzt ein Sonderfall. An der Stelle müssen wir ein bisschen genauer hingucken. Das,
was jetzt hier passiert und warum das passiert,
die Begründung kommt gleich, ist, dass wir jetzt eine Variable definieren,
das ist das ist das erste Element dieses Listenausdrucks,
was nach dem Defeien kommt.
Also neben Beispiel, was wir hatten,
Pythagoras, die Variable Pythagoras wird jetzt auf diesen Ausdruck gebunden
In dieser Umgebung jeweils und es wird dieser Funktionskörper,
der wird hier eingetragen und die Liste der Parameter,
die ja nach dem Pythagoras kamen,
das ist also die Liste, die noch XY enthält,
die wird hier an die zweite Listeposition eingetragen.
Das ganze Ding, was hier erzeugt wird,
wird eine Closure genannt und die Closure besteht im Prinzip aus zwei Sachen.
Das ist nämlich einerseits die Funktionsdefinition bestehend aus den Argumenten
der Funktion und dem Körper der Funktion plus die Umgebung,
in der die Funktion erzeugt worden ist
Und das ist ein Trick, der gemacht wird,
um die Informationen, die in der Umgebung drinsteckt und die
möglicherweise hier in dieser Funktionsdefinition benutzt wird, mitzutransportieren.
Ansonsten geht Informationen über den Entstehungs-, über den Definitionszeitpunkt der Funktionen verloren.
Wir gucken uns das gleich noch genauer an. Hier die Beispiele also,
ich definiere in der globalen Umgebung wieder die Variable X auf dem BERT 5 des,
der Interpreter sagt mir, okay dazu,
nach dem Motto,
ich habe es zur Kenntnis genommen,
das ist der Wert dieses Defeinausdrucks und der Wert der Variablen
Klein X ist also der Wert von 5. Davon ist der Wert 5.
Davon können wir uns überzeugen, indem wir ihn abfragen,
in der globalen Umgebung, wo wir das gemacht haben,
was ist der Wert von X,
es ist die Wert 5. Wir können, ja,
da nehme ich jetzt was voraus, dass das bereits implementiert ist.
Das ist es natürlich noch nicht, das müssen wir
noch machen, aber das ist schon mal eine Vorwegnahme.
Ich definiere die Vajable Y auf den Wert dieses Ausdruckes und wir nehmen mal an,
die Addition sei bereits definiert, was es noch nicht ist,
kommt noch,
dann wird dieser Ausdruck ausgewertet und der Wert wird der Vajablen Y zugewiesen,
wenn wir jetzt also Y abfragen,
kriegen wir als Wert 3 heraus
Oder wir haben hier eben eine Liste, das heißt,
wir definieren die Funktion Quadrat von X mit diesem Funktionskörper,
nimm X, multipliziere X mit sich selbst,
dann teilt uns das hinterher mit,
wenn wir den Wert von Square wissen wollen,
wir haben diese Closure vorgefunden.
Also, bestehend aus der Funktionsdefinition Square hat diese Argumente,
hat diesen Funktionskörper und ist definiert worden in der globalen Umgebung.
Das heißt, alle Vagablenbindungen, die in der globalen Umgebung
mal existierten, können wir nach wie vor wiederfinden.
Wir müssen nur in unserer Umgebungshierarchie in die Erzeugungsumgebung hineingehen und
die Vagablen von dort wieder uns holen, wenn wir sie brauchen.
Kommen wir noch dazu, wann wir die brauchen. Ja,
zweiter Fall war noch die, das Set Bang,
also das Überschreiben eines vajablen Wertes, das ist eben auch relativ simpel.
Wir hatten ja das Set variable bereits definiert und das wird hier einfach aufgerufen. Das heißt,
wir definieren jetzt hier x zu 5 beispielsweise,
wir können dann anschließend das Set Bang aufrufen und sagen,
jetzt soll das x,
aber sechs sein und danach hat das x tatsächlich den Wert 6. Ja,
also wir haben einen Wert von 5 auf 6 geändert und
können uns davon überzeugen,
dass das tatsächlich
der Fall ist
Fall 3 ist das konditional und das müssen wir wieder ausimplementieren.
Also wenn der Ausdruck lautet, dreistellig, falls dieses wahr ist, dann mach jenes.
Oder falls dieses wahr ist, dann mach dieses, andererfalls mach jenes.
Also das dreistellige und das vierstellige IF müssen wir voneinander unterscheiden.
Wenn es ein dreistelliges IF ist,
dann müssen wir natürlich das Prädikat ja,
also das konditional auswerten und Zu einer bullschen Wert,
also zu True oder Falls natürlich, in einer bestimmten Umgebung.
Immer alles umgebungsabhängig. Und wir müssen anschließend testen,
falls dieses wahr ist. Und jetzt müssen wir wieder
genau überlegen, dieser Bullsche Wert ist ein Scheme-Bullsche-Wert.
Das ist kein Prolog-Bullsche-Wert. Ja, das muss man
sauber auseinanderhalten. Hier haben wir den Base-Level-Wert ermittelt.
Jetzt wollen wir aber auf der Prolog-Ebene wissen,
ist dieser Base-Level-Wert denn True oder Falls.
Und das heißt, wir müssen irgendwo noch abgespeichert haben,
was eigentlich im Prolog-Sinne ein True ist und was im Prolog-Sinne
ein Falls ist
Das macht dieser Aufruf von Two. Und da haben wir
eben einfach definiert, dass dieses Scheme, Schweinegatter-Tee,
ein Prolog-True ist und das Geam Schweinegatter-F, ein Prolog-False ist.
Das wird hier abgefragt. Und wenn das wahr ist,
dann wird der Cons-Teil die Konsequenz hier ausgewertet.
Wieder in dieser Umgebung. Sie gibt uns
einen bestimmten Wert und der Wert wird nach außen mitgeteilt.
Das ist der dreistellige Fall fürs IF,
der vierstellige Fall ist jetzt eben nur ganz einfach,
dass hier hinten noch der Els-Zweig dazukommt
Also wenn das wahr war, die Auswertung des Konditionals,
dann mache den Konsequenzzweig, andernfalls mache den Alternative Zweig.
Das ist ja unsere bekannte Prologue If Venels Konstruktion.
Die kümmern an der Stelle einfach übernehmen. Und damit
sind wir jetzt in der Lage, auch konditionale auszuwerten.
Also wir definieren hier x mal wieder auf den Wert 5.
Wir testen jetzt, ob x gleich 5 ist.
Auch hier tun wir wieder so, als ob das die
Equality hier bereits implementiert ist, ist sie noch nicht.
Machen wir noch. Aber wenn wir sie denn hätten,
würde hier getestet, ob x gleich 5 ist.
Falls ja, dann yes, falls 9, dann no.
Und wir kriegen eben hier tatsächlich yes heraus,
weil wir eben x vorher auf 5 gesetzt hatten
So, also wir sind auch in der Lage, einfache konditionale auszuwerten.
Und die, den vierten Fall der Transformation,
das hatte ich ja schon angekündigt, dem behandeln wir hier nicht.
So, und jetzt kommen wir tatsächlich zum Kern,
zum funktionalen Kern von Scheme.
Und das erste ist die primitiven Funktionen. Wir hatten
schon gesehen, addiert haben wir schon mal benutzt.
EQ hatten wir schon mal benutzt. Und von der Sorte
gibt es eine ganze Reihe eingebauter Funktionen und die müssen wir
jetzt eine nach der anderen implementieren.
Da hilft nichts
Also, was passiert hier? Wir haben einen Funktionsausdruck mit
einem Funktionsnamen und seinen Argumenten. Wir müssen uns die Funktionsdefinition
erstmal holen für den Funktionsnamen, den wir hier haben.
Und wenn denn diese Funktionsdefinition eine primitive, vordefinierte Funktion ist,
dann müssen wir eben hier dieses Apply Primitive anwenden.
Also, wir holen uns dann die Liste der Werte,
indem wir für die Operanten in der gegebenen Umgebung
die Operantenwerte ermitteln.
Wir haben dann hier
sozusagen eine Liste von Werten
Und dann haben wir die Valiablen-Liste und die Werte, die
den Bajablen zugeordnet sind. Und dann können wir dieses Apply
Primitive machen. Das ist wieder nur in diesem Falle tatsächlich
nur für Tracing-Zwecke noch ein Rest, den ich nicht rausgestrichen habe.
Gut, was wir brauchen, sind jetzt die Definitionen der primitiven Prädikate.
Also wenn das ein Plus gewesen ist, dann haben wir
hier die Argumente und hier haben wir die Werte dazu.
Nein, Quatsch, dann haben wir hier die Argumente,
die wir eben aufgesammelt haben. Das ist ja hier Die Liste der Werte,
die wir hier ermittelt haben, was ist die Liste,
was sind die Werte dieser Argumente in der gegebenen Umgebung?
Die haben wir hier übergeben an der zweiten Argumentstelle.
Und die nehmen wir jetzt, wir sollen addieren,
alles, was hier drinsteht, an Werten.
Und das ist eben ein Samlest. Auf der Prolog-Seite.
Jetzt wird hier sozusagen eine Scream-Funktion, wird jetzt verbunden mit einer Prologsemantik.
Das ist die Basis des Ganzen, dass das Bootstopping überhaupt funktioniert.
Genau das Gleiche für die Multiplikation,
wenn das Multiplikation ist, hier haben wir die Werte,
die eine Liste von Werten, die zum Multiplizieren ist,
also müssen wir sowas wie eine Multiplikation einer Liste anwenden.
Die gibt es nicht in Prolog, die müssen wir definieren,
aber wir wissen ja inzwischen, wie wir das machen müssen.
Das ist ja kein Kunst, kein Geheimnis mehr.
Wenn das Ganze das Equality ist, dann ist das in diesem Falle keine,
und hier sehen wir, dass diese Liste beliebig lang sein kann.
Wir können über das Summlist für beliebig lange Listen die Werte ermitteln.
Hier bei der Equality haben wir genau zwei Argumente gegeben.
Ist A gleich B. Und dann wird das eben auf das entsprechende Prolog abgebildet,
das eben geguckt wird, sind die Dinge Pointer gleich, heißt das.
Das ist der, also zum Beispiel für numerische Ausdrücke.
Und wenn das der Fall ist,
dann gibt es den Prolog, wer tut zurück oder
ansonsten den Prolog wert False
Hier noch eine Kleinigkeit ins Scheme werden die meisten dieser Testprädikate,
also EQ, Per, Null und Attem, werden mit Fragezeichen geschrieben.
Also tatsächlich müsste das Steam-Programm dann eben so aussehen,
dass dort EQ-Fragezeichen, was weiß ich, XY steht.
Dieses Fragezeichen habe ich auch aus den taktischen Gründen immer weggelassen,
um gar nicht erst den Ärger zu bekommen
Okay, das sind die drei Funktionen gewesen. Zwei,
nein, die drei grundlegenden Funktionen für die Listenbehandlung sind K,
CDR und Cons. K und CDR sind eigentlich durch nichts erklären,
als durch eine alte Rechnerarchitektur,
auf der die ersten List-Systeme in den frühen 50 Jahren implementiert worden sind,
um deutlich zu machen, was das für historische Linien sind.
Das ist also eine der längsten kontinuierlichen Entwicklungslinien in der Informatik.
Das ist irgendwie ein Dekrementregister, das habe ich Content of Dekrementregister
, heißt das ja. Ich weiß gar nicht mehr,
was dieses K das A, wofür das steht.
Habe ich vergessen. Also das ist irgendein, sind Assembler-Befehle
in irgendeiner Maschinenarchitektur, die es heute längstens mehr gibt gewesen.
Die Interpretation im Sinne der Listenverarbeitung ist, dass der K immer
das erste Listen-Element ist und der CDR ist immer die Restliste.
Also mit anderen Worten, wenn wir unsere Prolog-Notation haben,
dann ist das hier der K der Liste und das
hier ist der CDR der Liste
Und das bis heute keiner einen Anlauf genommen hat,
um daran irgendetwas zu ändern,
ist einzig und allein der Tatsache geschuldet,
dass das auch wieder eine ganze Familie von Funktionen ist,
wenn man nämlich das erste Listen-Element nehmen will,
dann muss man K von X schreiben, also K von,
sagen wir mal, L der Liste.
Wenn ich das zweite haben will, was muss ich schreiben?
Das zweite Listen-Element haben, ja. Ja,
das ist bereits die Kurzfassung, die Langfassung lautet,
ich möchte den K von der Restliste von Erbs,
ja,
Von der Liste L haben, das soll ein kleines Elmer sein,
aber es ist auch egal. Und dafür gibt es eine
Kurznotation in den Lesb-Sprachen, die lautet dann Cadre von El. Ja,
und das gibt es bis zu einer Einbettungstiefe von drei,
typischerweise, also C-A-D-D-R, ist dann das dritte Element,
oder C-D-D-R ist die Restliste der Restliste,
der Restliste und so weiter und so fort.
Und weil sich das so schön schachteln lässt,
dass sich immer das C und das R vorne lasse und hier dazwischen nur sage,
einmal K, einmal CDR und so weiter,
Deshalb hat man es bei dieser Notation gelassen.
Hier sind also nur die untersten Elemente, tatsächlich implementiert der K.
Und da sehen wir hier genau diese Entsprechung. Der K ist also
genau dieser Teil vor dem senkrechten Strich auf der Prolog-Ebene.
Und der CDR ist genau dieser Teil hinter dem senkrechten Strich auf der Prolog-Ebene.
Das sind die Zugriffsfunktionen und die Konstruktorfunktion ist dann eben das Cons.
Und das Cons macht eine solche, baut eine List auf,
indem es eben genau eine solche Zelle bildet.
Und das erste Argument hier einträgt und das zweite Argument hier einträgt.
Und das sehen wir hier. Wir kriegen zwei Argumente X und Y.
Das erste Argument wird vor dem senkrechten Strich eingebaut.
Das zweite Argument wird hinter dem senkrechten Strich eingebaut. Ja,
und damit haben wir die Grund letztlichen Sachen für die Listenverarbeitung zur Verfügung.
Was wir noch brauchen, ist natürlich, testen, ob irgendetwas eine Liste ist.
Und da gibt es hier, wie auch in Prolog,
eben die Möglichkeit, nur an der obersten Ebene zu gucken.
Also sie erinnern sich an die Matroschkas, beispielsweise,
ich gucke nur von draußen drauf, ob es eine Matroschka ist,
wie wenn ja, bin ich zufrie Frieden oder die Variante,
dass sich die Liste rekursiv abbaue, bis auf das innerste Element.
Und erst, wenn alles bis ganz hinten Listen-Charakter hat,
sage, das ist eine Liste, dieses Per,
wie der Name schon sagt, schaut nur auf die oberste Ebene,
ob das ein gepunktetes Paar ist.
Wenn ja, es ist zufrieden, es macht keinen rekursiven Abbau der Liste.
Kann man natürlich definieren, wie man das macht,
wissen sie. Also diese schaut nur hier,
ob dieses Argument eine gepunktetes Paar ist und das wird hier getestet.
Wenn dieses Argument eingepunktetes Paar ist, dann lieferbar zurück,
ansonsten lief er Fouls zurück.
Also das ist wieder eine bulsche Funktion,
die ein Wahrheitswert liefert
Null testet, ob es die leere Liste ist. Also das,
was hier gegeben ist, muss eine leere Liste sein,
wenn das der Fall ist, dann war ansonsten falls Falls.
Also auch wieder eine Bullshit-Funktion und die Bullshit-Funktionen werden eben in Scream meist,
nein, die Bullshit-Funktionen werden alle mit Fragezeichen geschrieben,
aber nicht jeder Test wird mit Fragezeichen geschrieben.
Es gilt nämlich die generelle Annahme, dass alles, was nicht,
alles, was nicht Falls ist, wird als True interpretiert.
Ja, so rum ist es. Und deshalb kann ich also
auch Testfunktionen schreiben, die mir andere Werte als True zurückliefern,
die aber dennoch als in einem Konditional als True-Wert interpretiert werden.
Den Wert dieser Funktionsdefinition
Oder wir können ihn eben als Wert an eine Argumentstelle übergeben.
Also es gibt beispielsweise die Funktion Apply, die sagt,
diese Funktion hier soll auf diese Argumente angewendet werden.
Dann übergeben wir hier sozusagen eine Funktionsdefinition an diese andere Funktion. Oder sie können eben als Funktionswert auftreten,
indem ich eben hier unten sage, Liste,
List baut eine Liste auf aus beliebig vielen Elementen,
und zwar in die Liste soll aufgenommen werden, als erstes,
Quote Lamda, das Quote dient nur dazu,
um die Auswertung von Lamda zu verhindern,
sonst wird es zwei Jable betrachtet, was ich möchte ist,
dass ich jetzt tatsächlich Aussichtslos, komme ich nicht hin.
Was ich möchte, ist, dass ich tatsächlich die konstante Lamm
da in die Hand bekomme und deshalb muss ich es vor Auswertung schützen.
Also was das List erzeugt als Funktionswert, also dass nur der eingebettete Ausdruck,
der hier mit der Klammer beginnt und der hier mit dieser Klammer endet,
der erzeugt als erstes das Element Lamm da.
Das ist dieser Teil hier. Als zweites auch gequotet, eine Liste,
die XY enthält, Und als drittes ebenfalls gequotet ein Ausdruck,
den ich nicht mehr hingeschrieben habe, das SQS-QR-T von... Ja,
das macht jetzt keinen Sinn.
Das wäre das Gewerroot von zwei Sachen. Was ich eigentlich
dort haben wollte, haha, das war der Square,
was man vorhin gerade definiert haben.
Das macht auch keinen Sinn. Gut, also irgendeine Funktion,
bam, bam, bam, bam.
So ein Ausdruck wird hier erzeugt
Dynamisch. Und das ist der Wert des ersten Listen-Elements.
Das ist also die Funktionsdefinition und die wird jetzt auf die restlichen Listen-Elemente angewendet.
Also hier wird dynamisch eine Funktion erzeugt und sofort auf die Argumente hier angewendet.
Das macht natürlich keinen Sinn, wie in diesem Fall,
wenn ich die Funktion aus Konstanten erzeuge.
Das macht natürlich nur Sinn, wenn also diese Teilelemente,
die ich hier habe, jetzt wirklich dynamisch generiert werden,
in Abhängigkeit von irgendwelchen Berechnungsergebnissen.
Das ist dann sozusagen wieder die Ebene,
wo ich dann auch den Weg zur Metaprogrammierung und
so weiter frei habe
Das Problem, was ich schon angedeutet habe, ist,
dass Funktionsdefinitionen zwei Sorten von Variablen enthalten können.
Das allen sind die gebundenen Variablen, die sind überhaupt kein Problem.
Also wenn ich in einer Funktionsdefinition nur Variablen verwende,
hatte ich hier eine. Ja, hier,
wenn ich in dieser Funktionsdefinition hier nur die Variablen X und
Y verwende und genau das passiert hier.
Ich verwende entweder eingebaute Funktionen plus Square Root, Exponent und so weiter.
Ach so, das hatte ich damit gemeint.
Diesen Ausdruck, den wollte ich hier unten nochmal hinhaben.
Okay, gut
An Ajablen verwende ich nur X und Y. Das sind
genau diese hier. Und wenn die in der Liste der
formalen Parameter der Prozedur auftreten, dann sind sie gebunden.
Wenn ich jetzt hier im Körper dieser Funktion noch ein Set verwenden würde,
dann wäre das eine freie Variable, die nicht gebunden ist.
Und der Wert dieser freien Variablen würde dann aus der lokalen
Umgebung genommen werden, in der dieses Pytagoras ausgeführt wird.
Das ist möglicherweise falsch. Wenn der Programmierer so etwas verwendet hat,
meint er wahrscheinlich den Wert von Z,
der in der Umgebung gebunden war,
als die Funktion definiert worden ist
Und das sind zwei Sachen. Ich muss unterscheiden, zwischen der Umgebung,
in der die Funktion erzeugt worden ist, peddy fine.
Und in der Umgebung, in der die Funktion ausgeführt wird
, das sind die beiden Welten.
Und die können sich unterscheiden. Und wenn die eine freie Variable habe,
dann kriegt die Variable den Wert der Ausführungsumgebung und hat nicht mehr
den Wert der Erzeugungsumgebung.
Als ich noch programmieren gelernt habe, dann wurde so etwas durch Programmierhygiene geregelt.
Da hieß es eben ganz einfach,
das ist dirty,
das macht man nicht
Den Dreck steckt man nicht in den Mund. So ist man
da umgegangen. Bei Scream geht man den Schritt weiter und
sagt, ich muss verhindern, dass das passieren kann.
Oder, nein, falsch, ich muss sicherstellen,
dass zum Ausführungszeitpunkt die Vayablenbindung der Erzeugungsumgebung immer noch verfügbar sind.
Und das ist der Grund für die Closures.
Ich packe jetzt also in einer Closure die Vayable Eddiem,
die Funktionsdefinition mit ihren freien Vayablen plus die Erzeugungsumgebung,
in der die Funktion erzeugt worden ist.
Die packe ich zusammen. Und das nenne ich eine Closure
. Und wenn ich jetzt die Funktion auswerte,
dann kann ich variablen Umgebung, in der das Ganze erzeugt worden ist,
auch für die freien und variablen wieder rekonstruieren.
Das ist der Trick dabei. Und dafür brauchen wir
die Closures. Genau, das ist die Lösung dazu.
Also wir haben variablen Bindungen zum Zeitpunkt der Funktionsdefinition.
Und wir haben variablen Bindungen zum Zeitpunkt der Funktion.
Ausführung. Und wir wollen zum Zeitpunkt der Funktionsauswertung sicherstellen,
dass wir die gleichen variablen Bindungen haben,
wie wir bei der Definition, die auch hatten.
Also das, was sie vorhin als Programmierhygiene genannt habe,
hieß also, freie Variable verwendet man nicht.
Das ist die Ausbuchstabierung gewesen.
Das macht man nicht
Und hier ist es so, wir können durchaus freie Variablen verwenden.
Das Schiebensystem sorgt uns dafür, dass die originale vaiablen Wertebindung
auch zum Ausführungszeitpunkt wieder zum Verfügung steht.
Also wir werten die Funktionsdefinition in einer Umgebung aus und wir
nehmen dann eben die in diesem Falle nicht die aktuelle Umgebung,
sondern die Umgebung,
in der die Funktion
mal erzeugt worden ist
So, und das müssen wir einfach wieder einbauen.
Einfach ist gut in unser System. An der Stelle habe ich
mich ganz schön verhaspelt. Wir haben jetzt also so etwas,
und das war vorhin schon da, ein Defeinenausdruck,
wo wir jetzt hier einen Pattern,
einen Aufruf Pattern aus Funktionsname und Argumenten haben.
Und hier einem Körper der Funktion natürlich. Und wir tragen
jetzt unter der Variablen, also unter dem Funktionsnamen,
diese Closure hier ein.
Und die Closure enthält sowohl die Argumente als auch den Körper
der Funktion,
aber eben auch die Erzeugungsumgebung
Gut, und das ist eigentlich schon alles, was
dort gemacht wird. Ja, und wenn wir jetzt,
also die Definition dieser Funktion jeweils wieder abfragen,
dann haben wir hier hinten eben die ursprüngliche Umgebung wieder zur Verfügung,
in der sie erzeugt worden ist.
Gut, danke wir das jetzt, glaube ich,
machen, dass wir die Auswertung machen.
Also wir haben eine Funktion bestehend aus einem Funktor und also
einem Funktionsnamen und seinen Argumenten in einer bestimmten Umgebung auszuwerten.
Wir werden als erstes den Funktionsnamen aus und erwarten natürlich,
dass er als Wert eine Closure uns liefert
Das muss sichergestellt sein. Oder haben wir eben immer wieder
eine Funktion definieren, wird ein Closure unter dem Namen eingetragen.
Also dann kennen wir die Funktionsdefinition. Dann brauchen wir noch
die Liste der Werte, die an den Argumenten hier dranhängen.
Das sind also die operanten Values, die wir hier haben.
Anschließend, dass wir eine neue Umgebung aufmachen. Wir müssen also eine neue Umgebung aufmachen,
in der wir die Parameter, also die formalen,
die Argumente der Funktion mit den entsprechenden Werten versehen,
die wir aus dem Aufruf kennen.
Und wir müssen eben die neue Umgebung als Subumgebung der
ursprünglichen Umgebung eintragen. Und anschließend kommen wir eben hin,
dass wir den Körper in der neuen Umgebung,
die wir eben kreiert haben, auswerten können.
Und wenn wir das gemacht haben, ist die Umgebung,
die wir hier erzeugt haben, die wir hier erzeugt haben,
nutzlos geworden, die brauchen wir nicht mehr,
die hat ihre Schuldigkeit getan und wir können alle Eintragungen,
die wir dafür in der Datenbank erzeugt haben,
wieder beseitigen
Also die Umgebung wird praktisch beim Betreten der Funktion aufgemacht und beim Verlassen der Funktion wieder geschlossen.
Das sind die Hilfsprädikate, die wir hier haben. Also ein,
das erste ist, gib mir die Liste der Werte,
da gehe ich eben einfach die Liste der Ausdrücke,
die für die Argumente angegeben sind, durch,
ermittle deren Wert und trage das in die Liste der Werte ein und anschließend,
ach so, nee, das Ganze mache ich rekursiv,
indem ich die Liste durchgehe.
Und das ist, das Evil Sequence ist deshalb nötig,
weil ich Weil im Körper einer Funktion nicht nur ein Ausdruck stehen kann,
sondern beliebig viele Ausdrücke stehen können.
Und der Wert der Funktion ist immer der Wert des
letzten Funktionsaufrufes, den ich gemacht habe.
Also ich kann im Körper einer Klausel, nein,
einer Funktion natürlich auch beliebig viele Ride-Befehle hintereinander schreiben.
Das muss möglich sein. Ansonsten könnte ich viele Sachen nicht programmieren.
Und deshalb mache ich also hier das Auswerten einer Sequenz.
Ich muss alle Elemente der Sequenz auswerten,
aber der Wert ist dann tatsächlich der des letzten Ausdruckes.
Wenn die Liste nur noch einen Ausdruck enthält,
dann ist das, was sich bei der Auswertung dieses Ausdrucks
ergibt,
der Gesamtwert
Für alle anderen werde ich das Ding zwar aus,
aber ich schmeiße den Wert weg, der interessiert mich gar nicht.
Das ist das, was das Evil Sequence macht.
Also wenn der Körper einer Klausel aus mehreren funktionalen Aufrufen besteht,
arbeite sie der Reihe nach ab und behebehalte nur den Wert des letzten Aufrufes.
Der wird nach draußen mitgeteilt. Achso, ja,
damit haben wir jetzt alles zusammen, was wir brauchen.
Das sind die Innereien unseres Stream-Interpreters. Mehr brauchen wir nicht
. Jetzt können wir hingehen und das Ganze tatsächlich anwenden zur
funktionalen Programmierung
Jetzt gehen wir auf die, tatsächlich auf die Scheme-Ebene,
die Abbildung von Scheme auf Prolo Camber. Was ich jetzt immer noch mache,
ist die Gegenüberstellung einer Skim-Definition und einer einigermaßen äquivalenten Produktdefinition.
Und Dinge dieser Art werden auch in der Klausur auftauchen,
dass ich ihnen eine solche Skim-Definitionen gebe, frage, was macht die eigentlich?
Und sie sollen dann die entsprechende Semantik in Prolo-Notationen,
also als Produktdefinition angeben. Also Propalist ist das,
was sie vorhin angekündigt habe, dass per,
das per Prädikat testet ja nur,
ob auf der obersten Ebene ein gepunktetes Paar vorliegt,
geht also eine Liste jetzt bis zum entbitteren Ende durch und schaut nach,
ob die auch zum Schluss richtig aufhört.
Also ob zum Schluss ein Listenende-Marker drin ist und der Listenende-Marker ist
genau der gleiche wie in Prolog, nämlich die leere Liste.
So, also was macht Populiste? Es ist eine Funktion von
einem Argument. Es testet und Equal ist jetzt nicht nur Pointergleichheit,
sondern Equal ist Strukturgleichheit. Equal geht also auch listenrekursiv durch
bis zum bitteren Ende und guckt, ob die gleich sind.
Also vergleicht jetzt Das ist natürlich an dieser Stelle overkill,
weil ich ja hier nur auf die leere Liste vergleiche.
Da ist nichts, muss ich nichts rekursiv durchgehen. Also guckt,
ob ich das, was ich dort bekommen habe,
die leere Liste ist, wenn das die leere Liste ist,
dann bin ich hinten am Listenende angekommen und dann habe ich offensichtlich es geschafft,
eine Liste vollständig rekursiv abzubauen.
Ich kann also True zurückgeben als Funktionswert. Falls das nicht
die leere Liste ist, dann muss ich testen,
ob auf der obersten Ebene ein gepunktetes Paar vorliegt.
Und wenn das ein gepunktetes Paar ist, dann muss ich nachweisen,
dass das der CDR, also die Restliste dieser Liste,
wieder eine Popaliste ist.
Das ist der Rekurs über Aufruf
Und wenn das kein Paar ist, was dort da ist,
und es war auch keine leere Liste, dann muss ich Fords zurückgeben.
Dann stimmt mit der Liste irgendetwas nicht, dann ist sie nicht wohl aufgebaut.
Denn die Listendefinition ist ja eben gerade entweder,
es ist ein gepunktetes Paar, mit einer Liste als zweiten Listenelement,
oder es ist die leere Liste.
So ist ja die Funktion definiert, ja. Guter Hinweis,
ja, Overkill, ja, es schadet nicht,
aber es nutzt an der Stelle auch nichts.
Wir hatten ja genau definiert,
Truh und Fords werten zu sich selber aus,
und da muss ich kein Quote mehr setzen,
ja,
Das muss nicht sein. Okay, das ist also
der erste einfache Beispiel für eine rekursive Funktion.
Und das kann man jetzt eben vergleichen mit der Prolog-Definition.
Also die Lehreliste ist natürlich eine Propper-Liste und wenn die Restliste
eines gepunkteten Paares wieder eine Propper-Liste ist, dann ist das Gesamte auch eine Propper-Liste.
Das ist also die Übersetzung des Ganzen nach Prolog.
Gut, und dann kann man sich jetzt eben überlegen,
ja, gibt es da Unterschiede?
Gibt es Gemeinsamkeiten dazwischen zwischen diesen verschiedenen Definitionen? Es gibt
immer eine triviale einen trivialen Unterschied zwischen den beiden Definitionen,
der praktisch generell gilt.
Das ist der Unterschied zwischen funktionaler Programmierung und Logikprogrammierung.
Das kann man trivialerweise an der Stelle immer sagen.
Was ist der Unterschied zwischen der Semantik, dieses Logikprogramms und dieses funktionalen Programms?
Ja klar, das Ding kann ich in beiden Richtungen verwenden.
Das kann ich nur benutzen, um für eine gegebene Liste
zu testen, ist sie eine Liste ja oder nein.
Das Ding kann ich benutzen,
um beliebige Listen zu erzeugen
Ob das sinnvoll ist oder nicht, ist die zweite Frage.
Das ist ja überhaupt nicht das Problem. Aber ich kann immer
die Prolog-Programme in der anderen Richtung betreiben. Möglicherweise erzeugen sie Fehler,
terminieren nicht oder irgendetwas, aber ich kann es immer machen.
Gut, wir gucken uns weitere Sachen an.
Das ist ja ein Beispiel, was ich ganz zum Anfang
an der Vorlesung mal schon hingestellt hatte.
Member, Member testet also, ob ein Element in einer
Liste ist. Das ist also ein Testprädikat,
was ohne Fragezeichen geschrieben wird,
weil es nicht notwendigerweise
bullsche Werte zurückliefert
Und tatsächlich das Erste tut, dass das Zweite tut es nicht unbedingt.
Wir können den Unterschied gleich sehen. Also das Erste könnten
wir mit Fragezeichen schreiben. Das Zweite müssten wir ohne Fragezeichen
schreiben. Üblich ist also die zweite Definition,
die es in den meisten Scam-Systemen, die, die verwendet wird.
Wir gucken uns erst die erste an.
Also wir kriegen ein Element, wir kriegen eine Liste.
Wir testen, ob die Liste leer ist. Das ist übrigens
eine Alternative zu diesem Test, den ich vorher hier verwendet habe.
Hier habe ich eben auf Equal leere Liste getestet.
Und hier nehme ich jetzt tatsächlich dieses eingebaute Prädikat,
was war fürhin gerade definiert haben,
das verwende ich hier, also ist,
dass die leere Liste,
dann gib Falls zurück
Dann hast du die Liste vollständig rekursiv abgearbeitet, aber das Element nicht gefunden.
Falls es noch nicht die leere Liste ist,
dann schaue, ob das gesuchte Element der Kaderliste ist.
Das ist dieser Aufruf hier. Wenn es der Kader der Liste ist,
dann hast du das gesuchte Element gefunden und gibt Trut zurück.
Und ansonsten, falls du das noch nicht gefunden hast,
dann musst du eben das Ganze rekursiv aufrufen mit der Restliste.
Also ist das Element in der Restliste enthalten. Damit haben wir
die drei Fälle. Also wir haben die Liste erfolglos durchgearbeitet,
das Element nicht gefunden. Wir haben das Element gefunden,
das ist dieser Fall und wir haben das Element noch nicht gefunden,
aber es könnte noch kommen,
das ist der dritte Fall
Und der Unterschied hierzu, der besteht an einer einzigen Stelle,
dort, wo ich hier True zurückgegeben habe,
gebe ich hier die Restliste zurück.
Und damit ist es kein bullshites Prädikat mehr. Es gibt nicht mehr Funktionswerte,
nicht mehr nur noch Wahrheitswerte zurück als Funktionswert. Im Fehlerfalle ja,
aber im Erfolgsfalle gibt es mir die Restliste zurück,
wo das gesuchte Element, uh, das müsste ich jetzt,
das müsse ich, ich habe hier wahrscheinlich kein Scream drauf.
Wenn jemand ein Scream-System da hat,
es ist wirklich die Liste, die übrig bleibt,
nachdem ich das Element gefunden habe,
oder ist es die Liste,
wo das erste Element
das gesuchte ist
Das ist das andere. Ah, sorry, dann ist
die Definition hier falsch. Dann muss an dieser Stelle ein Konsk-A-CDR-CDR-Proteste stehen.
Sorry, danke. Das ist offensichtlich dann falsch gemacht hier.
Also das ist die übliche Definition. Das erste Element dieser Liste,
die zurückkommt, ist das Gesuchte. Dass diese erweiterte Definition,
wenn ich also dort nicht den Wahrheitswert zurückgebe,
sondern die restliche Liste, erlaubt ein paar zusätzliche Verwendungsweisen dieser Funktionen.
Okay, das, was hier unten steht,
ist jetzt nur die Übersetzung der ersten Definition.
Sie können sich auch überlegen, wie die Definition der zweiten
aussehen müsste
Ja, also das ist die Übernahme der ersten,
wenn denn das gesuchte Element, das erste der K ist,
das ist also genau dieser Fall hier,
dann ist das Ganze erfolgreich und der zweite ist,
wenn ich es noch nicht gefunden habe,
dann muss ich es in der Restliste suchen,
das ist dieser rekursive Aufruf hier unten.
Wie würde das,
die Signatur des Prädikats im Prolog aussehen,
wenn ich
die zweite Definition realisieren will
Wie viel stelle ich es, das Präkat?
Also die zweite Definition heißt, ich gebe ein Element rein,
ich gebe eine Liste rein und ich kriege die Liste zurück,
wo das gesuchte Element das erste Element der Liste ist.
Damit habe ich die Antwort gesagt, wir brauchen drei Argumente,
nicht zwei. Member ist aber in Prolog eben nur für
Bullshit Werte definiert und deshalb ist es zweistellig,
weil ich sozusagen den bullischen Wert über den Erfolg oder Misserfolg
der Prädikatsanwendung rausbekomme
Wenn ich aber jetzt tatsächlich diese Restliste sehen will,
brauche ich ein drittes Argument, auf der ich sie mitteilen kann.
Sonst kriege ich sie nicht zu sehen.
Also wenn ich die zweite Definition nach Prolog übertragen würde,
dann bräuchte ich ein zusätzliches Argument. Und das ist eine Heuristik,
die man immer hat, typischerweise mit Ausnahme der Bullischen Prädikate,
Bullshit-Prädikate haben auf der Steam-Ebene und auf der Prolog-Ebene genau gleich viele Argumente.
Nicht Bullshit-Prädikate, also nicht Bullshit-Funktionen, sorry.
Nicht Bullshit-Funktionen brauchen immer ein zusätzliches Argument,
um den Funktionswert nach draußen
zu transportieren
Also wenn ich das übertragen will in die Prodok-Welt,
brauche ich ein drittes Argument. Sonst kriege ich den Funktionswert,
den ich hier zurückgebe, den kriege ich sonst nicht zu sehen.
So, wir können den Zoo weiter durchgehen,
haben wir noch einen kleinen Augenblick, einen kleinen Augenblick.
Haben wir doch letztes Element einer Liste,
also ich teste es einstellig in diesem Falle,
ich teste, ob ich wieder die leere Liste habe.
In diesem Falle also teste ich nicht auf die leere Liste,
sondern ob die Restliste nach Abtrennen des Kopfes die leere Liste ist,
also tatsächlich teste ich hier,
habe ich die
einer Liste vorliegen
Und wenn ich die einer Liste vorliegen habe, dann ist eben der K,
also das erste Element dieser Liste, der gesuchte Funktionswert.
Andernfalls muss ich das Ganze mit der Restliste rekursiv aufrufen.
Und die Definitionen kennen Sie jetzt zur Genüge,
die haben wir immer und immer wieder verwendet.
Ja, länger einer Liste mit ein bisschen Arrithmetik da drin.
Ich muss also die Liste rekursiv durchgehen,
indem ich immer die Länge der Restliste ermittle und eins da
drauf addiere
Wenn ich denn eine leere Liste vorliegen habe,
dann hat die die Länge null. Ansonsten ist es eins
länger als der Rest, als die Länge der Restliste.
Und das Ganze auf Prolog übertragen ist auch bekannt,
die Lehreliste hat die Länge null.
Ansonsten ist es die Länge der Restlänge plus eins.
Ja, gut, mehr lässt sich dazu auch nicht sagen.
Achso, ja doch, eins, eins kann man hier
noch diskutieren. Endrekursion oder Nicht-Endrekursion? Liegt hier Endrekursion vor
oder ist es nicht Endrekursiv? Erstmal die funktionale Ebene,
ist es Endrekursiv
Nein, es wird nach dem Berechnen dieses rekursiven Aufrufes hier,
wird noch diese Addition durchgeführt.
Das ist nicht entrekursiv ist. Das endrikosiv hier unten?
Auch nicht. Nach der Berechnung dieser Länge wird das noch mal hochaddiert.
Ich kann das hier unten und das haben wir gesehen,
aber endrikosiv machen. Die Definition ist es nicht.
Aber ich kann das hier endrikosiv machen. Das geht nicht
intrikosiv zu machen. Und da können Sie mal überlegen,
wo der Grund legt dafür. Das fürs Wochenende als kleine,
kleines Bonbon. Schönen Dank für die Aufmerksamkeit. Bis nächste
Woche.