schon die Vorlesung SC1 und 2 gehört haben.
Und die Vorlesung SC3 ist dazu da,
das Thema Programmierung nochmal aus einem ganz anderen Blickwinkel zu beleuchten.
Sie haben, als sich jetzt Diener angemeldet haben, zwei Optionen gesehen.
Sie konnten es einmal wählen aus dem Blickwinkel.
Relationales Programmieren, was der Kollege Menzel macht oder funktionales Programmieren.
Und ich muss sagen, wenn Sie für Funktionales programmieren,
entschieden haben, haben Sie eine ausgezeichnete Wahl getroffen.
Relationales Programmieren wäre auch eine ausgezeichnete Wahl gewesen.
Also es gibt ja keine Möglichkeit, es falsch zu machen.
Wir machen in den Vorlesungen Eigentlich vermitteln wir ein ähnliches Wissen,
nur diskutieren es einen anderen Programmierstehlen. Das Thema dieser Vorlesung hier ist,
wie programmiere ich aus funktionaler Sicht,
aber das Augenmerk liegt jetzt nicht auf der Modellierung der Datenstrukturen,
wie sie es im ersten beiden Semester objektorientierten Entwurf kennengelernt haben,
beim Objektorientierten Entwurf stehen die Objekte im Vordergrund, die Strukturen,
auf denen Algorithmen ablaufen, in der SE3-Vorlesung geht es um die Prozesse, die die Algorithmen durchführen.
Und wie wir die Kontrolle abstrahieren, das ist ähnlich im relationalen Programmierstil.
Wir reden darum, wie wirten. Eine Lösungssuche für bestimmte Probleme
als Algorithmus finden und es so formulieren, dass es möglichst einfach und wiederverwendbar wird.
Und das zweite große Thema wird sein,
also neben dem Abstraktionsvermögen für den Algorithmus selbst,
wird sein, was ist die Bedeutung eines Programms?
Wir programmieren in einer Programmiersprache die Programmiersprache selbst steht auch im Vordergrund,
weil wir zeigen wollen, wie man mit Hilfe von Metaprogrammierung in der Programmiersprache,
die man gerade verwendet, problembezogen, sinnvoll, vernünftig,
auch neue Programmiersprachen entwickeln kann.
Und wie wir die Abstraktionen, die unsere Programmiersprache nicht braucht,
selbst einführen als neue Sprachelemente.
Das werden wir in vielen Beispielen machen. Im Konkurrenzprogramm von Wolfgang Mänzel
wird es am Ende so sein, dass die Programmiersprache es geben.
Reggae, die wir hier verwenden,
dass die in relationalen Programmierspielen mit der Programmierung eingeführt wird.
Und wir machen am Ende hier das Gegenstück,
um zu zeigen, was in den verschiedenen Programmiersprachenwelten möglich ist,
in den verschiedenen Programmierstilen werden wir am Ende des Semesters die Sprache Polock in Reckett realisieren.
Und auch versuchsweise in unserer Welt ein bisschen relationales Programmieren machen.
Das heißt, egal für welche dieser beiden Vorlesungen sie entschieden haben,
sie werden sowohl das Relationale als auch das Funktionale erleben.
Der Fokus ist vor allem darauf, wo sie ihre Übungsaufgaben machen.
Gut, gucken wir mal. Heute möchte ich vor allem
die Grundbegriffe der Programmierung besprechen. Und die funktionale Programmierung,
die Programmiersprachen und dann gehen wir noch mal kurz aufs Organisatorische.
Und dann wollen wir erste Versuche machen mit der Arbeitsumgebung,
damit sie dann auch mit den Übungen loslegen können.
Gut, was ist applikative Programmierung? Das ist
ein Synonym für das Wort funktionale Programmierung. Heute gibt es
vor allem auch ein bisschen drum, um Vokabeln lernen,
dass wir eine gemeinsame Sprache finden über Programmiersprachen zu reden.
Das stammt aus dem schönen Buch von Eberson Pfeffmann und eines
der Klassiker,
nachdem man
funktionales Programmieren lernen kann
Der Sassman hatte geschrieben, First,
we one to isdeplos the idea that the computer language is
not just a way of getting a computer to perform operations.
But rather, it's a novel formal medium for expressing ideas about myself or already.
This programs must violitan for people to read and only incidentably
for machines to execute. And die Programmiersprache Recket,
die wir verwenden, die wird von einem Team,
einem MIT weiterentwickelt, das genau diese Idee verfolgt.
Es geht vor allem darum, wie schreibe ich Programme so,
dass Menschen sie lesen können, ersten Skim oder Recket-Programmen,
die sie sehen werden, werden sie denken, Thema Ziel verfehlt.
Es fordert einiges Gewöhnen dran, aber sie werden schnell sehen,
dass es tatsächlich viel, viel leichter ist,
funktionale Programme zu lesen, als dasselbe Imperativ geschrieben,
wie sie es in Java gemacht hatten.
Die neuen Recket-Systeme haben auch eingebaute Werkzeuge,
sodass man erstmal für Menschen vernünftig aufschreibt,
was man machen will und dann wird der Code,
dieses Scribble-Erweiterung und dann wird daraus automatisch ein Programm zusammengebaut.
Okay,
das zweite.
Wie beliebtet die Essential Material to be addressed by subject at that level is?
Not the syntax of participular programming längwich constructs, not clever
algorithms for computing efficiently. Sorry, unsere Algorithmen und Datenstrukturen,
Leute. Not beeven the mathem magical analysis of algorithms,
sorry Theorie, and the fundations of computings,
but trother the checkneeks used to control the intellectual complexity of larder software systems.
Dieser letzte Punkt hier ist doch eigentlich das,
was ihnen verkauft wurde als Hauptgrund für objektorientiertes Programmieren.
Man kann dasselbe aus der Sicht der funktionalen Programmierung machen,
indem man einen funktionalen Ausdrücken, klar und übersichtlich aufschreibt,
was man erreichen möchte. Und auch Komplexität,
die Intellektuelle, Komplexität, ein solches Programm zu schreiben,
dass man das mithilfe funktionaler Programmierung gut hinkriegen kann.
Das ist vor allem einfach, oder zum Teil überhaupt nur möglich,
in der Welt der Programmiersprachen, die wir hier ansprechen,
funktionale Programmierung, Programmiersprachen aus der Liz-Familie.
Da kommen wir gleich drauf,
was diese Sprachfamilien sein sollen
Underlying our approach to the subject is our conviction,
that computer science is not is science.
Wie bitte? Science ist da gemeint in dem amerikanisch-englischen Sylt
des Sinne des Science Naturwissenschaften meint. Und wir sind in der Min-Fakultät
genau aus dem Grund, weil die Universität geglaubt hat,
wir gehören zu den Naturwissenschaften und sind das wichtige Werkzeug.
Aber trotzdem die Hauptbedeutung der Informatik steht hier,
that it's a kniffe, secniffe,
secniffe sind sess nothing to do
this computers
The Computer Revolution ist eine Revolution in der Wahl,
dass wir denken, und in der Wahl expressieren, was wir denken.
Die Assenz of the change is the emotion, sofort mal
die besten Recalls procedural aphistomology. The study of distructure of
knowledge from a imperitive point of you theken by classical matthem mattiple subjects.
Okay. Da muss ich, glaube ich,
ein bisschen erklären, was hier gesagt werden soll.
Und ich hoffe, ich denke auch ganz treffend ist,
wenn wir Informatik betreiben, Programmiersprachen entwickeln, Programmiersprachen lernen,
Methoden entwickeln, wie mit einem Computer kommunizieren.
Dann können wir dieselben Sprachen nehmen, um erst mal damit
das Problem zu beschreiben, dass es zu lösen gilt.
Wir verwenden es, um die Domäne zu beschreiben,
die Abläufe zu beschreiben. Und das Wichtige ist,
hier im letzten Satz taken bei Classical Massion Matical Subjects.
Wir machen das in einer Art und Weise,
dass wir formal mit diesen Algorithmen umgehen können und beweisen können,
was sie tun, was sie bedeuten.
Und was geschehen soll, wenn dieser Algorithmus ausgeführt wird.
Und das Können wir in vielen Bereichen tun,
wo wir Dinge beschreiben und systematisieren wollen, ohne dass es gleich ein Computer ausführt.
Das ist hier der Computer Revolution, ist eine Revolution in the baby,
think and the way we express what we think.
Unsere Sprache ist doch ein ganz wichtiger Punkt,
der es uns überhaupt erst möglich macht zu denken.
Das ist schwer, über irgendetwas nachzudenken, wofür wir keine Worte haben. Und dadurch,
dass wir Sprachen haben,
die einen Tick formaler sind und die Abstraktionen beschreiben können,
können wir eben über neue Dinge nachdenken und auch das ist Die Essenz im Funktionalen programmieren,
dass wir Wege und Mittel finden,
Dinge zu beschreiben und Probleme zu beschreiben und handhabbar zu machen,
ohne dass es gleich einen Computer ausführen muss.
Und in der Welt der relationalen Programmierung,
die Wolfgang Menzel in seiner Vorlesung vorstellt und auch hier haben wir die wunderbare Situation,
dass die Programmiersprachen eins zu eins aus klaren Konzepten der Logik und Mathematik abgeleitet wurden.
Relationales Programmieren ist die Welt der Relation. Das heißt,
wenn wir sind in der Welt der Relationen sind,
können wir die Mathematik nehmen und alles,
was sie mit Relation anfangen kann,
um zu beweisen,
was das Programm bedeuten soll
Die Semantik ist klar definiert. Genauso über Logik-Kalküls. Und
in der Welt der Funktionalen Programmierung haben wir eben das Ganze mathematische Gebäude der Funktion.
Und das Landerkalkül, um zu zeigen, wie man,
was etwas in der Welt der Funktionen ausgedruckt bedeutet.
Ja, noch mehr Provokationen aus der Einleitung zu dem schönen Buch.
Mathematics Provide, Framework for Dealing press sizely is notions of
what is, computation provide the framework for dealing precise,
liebes notions of how two
Das liegt ein bisschen nahe, dass die Mathematik,
wenn man sie nimmt, per Definition und Satz und Beweis
sehr gut beschreiben kann, statisch, was da ist.
Ja, wir können beweisen, etwas ist wahr, etwas ist falsch.
Wir können etwas herleiten und haben damit nicht automatisch einen Prozess modelliert,
die mit den formalen Methoden der Informatik können wir beschreiben,
wie man etwas macht.
Wir beschreiben einen Weg, wie eine Lösung gefunden werden soll.
Okay, und das ist eben im funktionalen Programmierstil ausgezeichnet,
Und vielleicht dazu jetzt auch noch etwas Organisatorisches. Das Buch von
Ebels Sincessman, aus dem ich eben diese Einführung gewählt habe.
Die richtet sich ein Erstsemester. Es gibt zwei Gruppen von
Lehrbüchern fürs funktionale Programmieren. Die eine Gruppe richtet sich an
fortgeschrittene Masterstudenten. Und die andere richtet sich an Erstsemester.
Und sie sitzen genau zwischen Baum und Borke.
Das heißt, als ich diese Vorlesung zuerst gemacht hatte,
hatten wir sie im ersten Semester
Mit dem hervorragenden Erfolg, dass die Erstsemester genau mit den exzellenten Lehrbüchern,
die für Erstsemester geschrieben waren, lernen konnten.
Als wir sie ins dritte Semester verlegt haben,
als wir gemerkt haben, das ist viel einfacher,
wenn man erst mal die Kollegen für SE1 und 2,
die Schmutzarbeit machen lässt, in ein Bad beizubringen,
dass man ein Programm präzise schreibt und nicht einfach nur so hinschreibt,
was man meint,
sondern so wie es der Rechner versteht und das ist nur so das Programmieren überhaupt erst mal lernt,
die Grundideen, dann kann man sehr viel mehr machen.
Das heißt, im dritten Semester können wir
weit über das hinausgehen,
was in den Erstsemesterbüchern beschrieben wird
Was ich hier gemacht habe, um es für sie benutzbar zu machen,
ist, dass ich ein ziemlich dickes Skript geschrieben habe.
Und in diesem Skript werden die Grundbegriffe, die man,
ob erst so der Drittsemester lernen muss, eingeführt.
Es sind sehr, sehr viele Programmbeispiele drin,
aber die zweite Hälfte von Skript, die ist deshalb so dick,
weil ich viele Fallstudien da reingenommen habe, um zu zeigen,
wie man anspruchsvolle Probleme damit lösen kann.
Das heißt, erschrecken Sie nicht, wenn Sie es runterladen aus Diene.
Das hängt in Stine bei der Vorlesung drin,
als Zipfeil und passt gerade eben so ins Diene rein,
von den Grenzen,
die man
da hochladen darf
Also das Wichtige sind vor allem in der zweiten Hälfte die Fallstudien,
wo wir dann aber nicht mehr jede Zeile Code durchgehen werden,
weil das führt dann etwas ermüdend.
Aber wo sie Beispiele haben und ausprobieren können und sehen können,
wie kann ich damit Dinge, die unglaublich kompliziert aussehen,
ganz leicht machen. Und okay,
also deshalb habe ich aus vielen Büchern Dinge zusammengetragen,
die in diesem Skript vereinigt und habe massiv Programmierarbeit reingesteckt,
um Dinge, die in Büchern,
die für Miranda geschrieben waren oder für Commonlisp geschrieben wurden oder für ersten Anfänge von Reggit,
um das zu übertragen, auszuprobieren.
Ich hoffe, es läuft. Und sie können das dann
hier sehen. Also das Tolle am funktionalen Programmierstil,
den wir hier jetzt kennenlernen werden, das ist erst mathematisch wohlfundiert.
Man definiert eine Funktion, wie man es mathematisch auch machen würde.
Und wendet diese Funktion einfach an, die man funktionale Ausdrücke schachtet.
Fertig ist das Programm. Und wenn man gut in Mathe ist,
kann man sofort beweisen, dass es korrekt ist.
Wenn es denn entscheidbar ist, das Problem.
Ich glaube auch, es ist leicht zu erlernen.
Sie müssen sich nur einen Ruck geben, Augen reiben vielleicht
und sich an dem ungebundenen Anblick der Klammergebirge nicht zu sehr entsetzen.
Aber sie werden merken, man muss nicht viel mehr Syntax lernen,
als dass jede schließende Klammer eine öffnende Klammer eine passende Schließende braucht.
Und man muss nichts deklarieren oder ähnliches, ja,
das ist, muss einfach hinschreiben, was man meint.
Wir haben softwaretechnisch sehr viele wichtige Konzepte,
das werden wir im Laufe des Semesters sehen.
Und wir haben hier eine interpretierte Sprache.
Das ist auch das, was es leicht macht,
wenn man etwas ausprobieren möchte.
Es ist ideal für Rapid Prototyping
Man kann kleine Teile einfach schon mal hinschreiben als Funktion ausprobieren,
muss nicht warten, bis der Rest vom Team alle Pakete fertig hat,
immer seine eigenen Sachen testen kann,
sondern testet die kleinen Bauspeine im Interpritte und wenn man meint,
man hat alles gut hingekriegt, dann kompiliert man das Ganze und hat es dann auch ein bisschen effizienter.
Also die Sprache, die wir verwenden, ist Recket,
ist, die Wurzeln gehen zurück auf kommende List oder überhaupt offenes und dann hieß es zuerst Skim eine Zeit lang,
wurde als Lehrsprache vertreten und inzwischen hat es sich zu Recket entwickelt,
weil so viel dazugekommen ist, dass man sagt,
Die Sprache ist nicht mehr das,
was sie ursprünglich mal war.
Sie verdient einen neuen Namen. Wir haben die funktionale Abstraktion,
das heißt, unser Sprachelement, das sind Funktionen.
Wir haben Symbole und das ist Symbole, sind ein Weg,
um Programme zu schreiben, die Bedeutung herausfinden,
die wirklich Dinge, die mit einer Bedeutung versehen sind, manipulieren.
Wir haben eine dynamische Typisierung. Das heißt,
wir müssen nicht, können, aber wir müssen nicht Typdeklarationen schreiben.
Wenn es völlig klar ist, aus der Funktion,
die wir darauf anwenden, was der Typ ist,
des übergebenen Objektes oder wertes ist, dann wird es genommen und fertig.
Wir haben, wenn man das übertreibt, kannst du dazu führen,
dass man eine Akrobatik ohne Netz und doppelten Boden macht.
Also wenn immer sie meinen, sie müssen Argumente,
die sie an Funktionen übergeben, durch Tube-Deklarationen absichern, dann tun sie es.
Weil wenn die Werte aus seiner Welt kommen,
die sie nicht trauen, wie zum Beispiel der Partner im Projekt,
ja, das ist... Weißt du noch,
ich fang mal auf der C-Bit und immer,
wenn ich da so Programme stehen sie und ein Aussteller sagt,
setz dich doch mal hin und tun sie was.
Und probieren sie es aus, dann gebe ich immer mit Absichtwerte ein,
die außerhalb des vorgesehenen Wertebereichs sind.
Also wie viele Objekte wollen sie kaufen? Ich schreibe rein
minus eins. Rechner stürzt ab, die Aussteller müssen das System neu buten.
Und dann sagen sie ganz entsetzt, oh, ja,
ist ein bisschen peinlich. Aber solche Werte gibt man ja
auch nicht ein, ne? Von wegen. Oder noch schöner,
da war ein Stand, da haben die Leute unzerreißbare Briefumschläge
für sichere Paketversendungen verstanden
Und haben noch gesagt, ja, probieren Sie mal,
können Sie sich zerreißen? Ich gucke mir deinen Vaterverlauf an,
habe nicht Platz zur Vater gerissen, sondern parallel zur Vater.
Ich hatte sofort zwei Hälften der Umschläge in der Hand.
Ja, so darf man auch nicht reißen. Gut,
also Sie wissen, als Drittsemester, wenn es sinnvoll ist,
wenn man Typ-Prüfung, Wertprüfung und sowas macht.
Und wenn die Sprache einem nicht zwingt, es zu tun,
wie Yava es tut, dann tun Sie es eben selbst als sorgfältige Informatiker.
Wichtig ist, mit der Programmierung, wir können die Sprache erweitern.
Das werden wir in der zweiten Semesterhälfte ausprobieren, wenn wir Sprachelemente brauchen,
die nicht vorgesehen sind, schreiben wir sie uns selbst.
Und auf diese Weise kriegen wir eben auch andere Programmiersprachen eingebettet.
Und das andere, was ich genieße, ist,
dass wir keinen so einen taktischen Unterschied zwischen Daten ins Programm haben.
Also wenn sie in Racket sich einen Text zusammenbauen,
der syntaktisch eine Liste ist, dann kann diese Liste ein funktionsfähiges Racket-Programm sein,
was sie on the fly ausführen könnt.
Das heißt, wir haben hier den Spaß,
dass wir zur Laufzeit gut erzeugen,
und automatisches Programmieren machen können und dann nicht mehr alles selber schreiben müssen.
Das geht, weil wir keinen syntaktischen Unterschied zwischen Programmen und Daten
haben. Es ist mit ein Grund,
weshalb wir die Sprache Recket und die Liz-Welt ausgesucht haben und nicht die Welt im Miranda oder im Teskel,
wenn sie die Lehrbücher in der Bibliothek sich anschauen, werden sie feststellen,
Mensch, wir haben da zwei Welten von funktionalen Programmiersprachen.
Die eine hat ihre Wurzeln, Liz und die andere ist,
sie haben mathematisch inspiriert und ist die Welt der streng getypten funktionieren finalen Programmiersprachen.
Miranda, Häskel, wird, all das wird viel verwendet
bei uns von Kollegen in der theoretischen Informatik.
Es sind tolle Sprachen, ja. Als ich mich überlegt habe,
Mensch, welche Sprache nehme ich? Da habe ich gesagt,
ach Gott, mittag, zwei Semestern Informatik.
Und immer im Gängelband von Java mit strengen Typen,
jetzt bitte nicht noch eine Programmiersprache, die ein strenges Typkalkül hat,
das wird auf die Dauer langweilig.
Und vor allem, man hat die Freiheiten nicht.
Also ich möchte Ihnen gern die Chance geben,
dass sie die Stärken einer Sprache mit schwacher Typusierung nutzen.
Weil dann dürfen wir Dinge sagen und denken,
die nicht möglich sind, in der Welt der strenge Typen sprachen.
Wir werden dafür Beispiele sehen, wo es nicht möglich wäre,
genau dasselbe im Recket zu machen, in Miranda zu machen.
Okay. Ja, weil wir jetzt eine neue Sprache,
Programmiersprachenwelt eindringen, müssen wir nochmal bei AW Algorithmus anfangen.
Das haben Sie im ersten Semester natürlich gut gelernt, was ein Algorithmus ist.
Das wird immer definiert in den Erstsemestervorlesungen aus der Sicht der Imperativen Informatik.
Und wir müssen Damit wir weiterhin von Algorithmen sprechen können,
die Definition ein bisschen aufweichen. Sonst geht es schief.
Sie kennen Algorithmus in der Definition oder in Varianten davon,
wie es in den guten alten Lehrbüchern von Bauer und Goals steht,
aus der Zeit eigentlich, als ich selbst Informatik studiert habe.
Ein Algorithmus ist eine präzise, in einer festgelegten Sprache,
abgefasste endliche Beschreibung eines allgemeinen Verfahrens und der Verwendung
ausführbarer,
elementarer Verarbeitungsschritte
Varianten dieser Diffinition habe ich bei Ihnen im Erst-Semester-Skript gesehen.
Das heißt, Algorithmus wird hier dadurch beschrieben,
dass man auf die Sprache Bezug nimmt, in der er beschrieben ist.
Der Algorithmus an sich existiert nicht, sondern wird erst dadurch,
dass man ihn in Worte gefasst hat,
in der Sprache beschrieben hat und dann wird fest,
wird er zu einem Algorithmus.
Und das heißt, wenn ich dasselbe abstrakte Verfahren nehmen würde und
beschreibst einmal einen Vortrag und einmal im Java oder so,
Exakt denselben Ablauf. Ich habe zwei verschiedene Algorithmen,
weil die Sprache selbst zur Definition von Algorithmus gehört.
Das ist etwas, was mich wirklich in die Welt der
Funktionalen Programmierung passt, weil wir die Sprache jederzeit modifizieren können.
Was aber wichtig ist, das sind die ausführbaren, elementaren Verarbeitungsschritte,
die in der Sprache beschrieben werden können oder überhaupt beschrieben werden können.
Was ich nehme, ist die Warmdefinition von Peter Schäfe.
Nicht viel später geschrieben. Peter Schäfe war der erste Kollege,
der hier mutig genug war für die Programmierausbildung,
in Sprache aus der Listefamilie
zu nehmen
Das habe ich mir sein Buch sehr gut angeguckt.
Ein Algorithmus ist ein eindeutig bestimmtes Verfahren und der Verwendung von Grundoperationen über primitiven gegebenen Objekten.
Wir haben hier Grundapparationen statt Ausführerbarer, elementarer Verarbeitungsschritte.
Das legt nahe, dass die Grundoperationen sehr viel komplexer sein
können als elementare Verarbeitungsschritte. Die klassische Definition,
die hat so richtig eine imperative Maschine vor sich hin klickern,
ja, wo Anweisungen auf Anweisungen folgt,
das sind die elementaren Schritte.
Und das macht es zum Algorithmus. Wir geben einfach
einen Satz von Grundoperationen vor und auf die wenden wir
Operationen an
Über gegebene, primitive Objekte aus dem primitiven Objekten können wir größere zusammenbauen.
Wichtig ist, dass ein Algorithmus eindeutig ist. Das heißt,
in jedem Schritt muss klar sein, was zu geschehen hat.
Also der Unterschied zwischen dem Algorithmusbegriff,
ja klassischerweise in der imperativen Programmierung gesehen wird, ist,
dass die Beschreibung mit dazugehört, während die Sprache,
werden in der Sicht der funktionalen Programmierung, nehmen wir an,
es gibt ein Verfahren an sich.
Die Beschreibung ist nicht Teil des Verfahrens. Ja,
und wir können, es ist derselbe Algorithmus,
egal ob wir es im Vortrag ja war oder sonst
wie ausdrücken
Also, der Begriff Algorithmus soll für uns
von der textuellen Beschreibung getrennt sein, als Programm,
von einem Programm dagegen verlangen wir das präzise eindeutig und endlich sein muss.
Warum? Wenn wir es ausführen wollen, das Programm,
dann sollte man in jedem Schritt sicher sein,
was zu tun ist, deshalb präzise.
Es soll eindeutig sein, dass man keine Zweifelsfälle hat.
Und es sollte endlich sein
So, und beim Algorithmus haben wir noch ein paar Vokabeln,
die wir nochmal auffrischen sollten. Sie kennen sie vermutlich inzwischen
alle. Ich wollte sie hier nochmal systematisch zusammenfassen.
Wir sprechen von sequenziellen Algorithmen, wenn alle Schritte streng hintereinander ausgeführt werden.
Oder parallel oder nebenläufig, wenn wir Dinge gleichzeitig machen können.
Zum Beispiel durch mehrere Ausführende. Deterministisch, wenn jeder Schritt vollständig geregelt ist,
aber wenn wir bei bestimmten Stellen präzise gesagt, Regen,
du hast jetzt drei Varianten.
Entscheide dich für eine. Ja, und da sind wir immer noch präzise,
aber wir können an der Stelle uns für eine entscheiden.
Dann heißt er nicht Deterministisch. So, und dein Algorithmus terminiert,
wenn er noch endlich vielen Schritten abbricht,
dass man nicht verwechseln mit ist determiniert, determiniert man,
dass er ein eindeutiges Ergebnis hat.
Das ist wichtig für parallele oder nebenläufige Verfahren oder nicht deterministische Verfahren,
dass egal, egal welchen Weg man gewählt hat,
dasselbe rauskommt.
Dann haben wir ein determiniertes Ergebnis.
Wichtig für uns jetzt ist, wenn wir,
wir legen uns nicht auf eine Programmiersprache fest,
aber wir legen uns auf ein abstraktes Automatenmodell fest,
dass die Grundoperationen bereitstellt.
Wenn wir einen Algorithmus formulieren, ja,
wir stellen uns da einfach eine abstrakte Maschine vor,
von der wir annehmen, sie kann bestimmte Dinge.
Und bezogen auf diese Maschine beschreiben wir den Algorithmus.
Und je nach Art der Maschine, die wir gewählt haben
, haben wir ein anderes Verarbeitungsmodell.
Das Verarbeitungsmodell, das spezifiziert Grundoperationen,
die ein Automat ausführen kann
Ob er addieren kann, subtrahieren oder ob er nur lachen kann oder was weiß ich.
Und wie sich das Automat verhält, wenn er bestimmte Ausgabeeingaben kriegt.
Und zu diesen Verarbeitungsmodellen gehören eben die Grundstile der Programmierung.
Das ist ein Programmierstil, wird mehrdeutig verwendet. Also viele
sagen ja, guter Programmierstil ist, wenn man Kommentare schreibt.
Das ist aber nicht damit gemeint. Ja,
das ist eher jetzt good practice, aber nicht programmierstil.
Der Programmierstil meint, Was für eine Vision habe ich von
dem Automaten, der mein Programm ausführen kann?
Und viele Falschentworfene Programme sind einfach dadurch schlecht,
weil man in seiner eigenen Vision eine ganz andere Vorstellung von
dem Automaten hat, der das Ding ausführen soll.
Also je nachdem, was für eine abstrakte Maschine,
die wir uns denken, was die uns bietet,
haben wir ein unterschiedliches Verarbeitungsmodell.
Was wir in den ersten Semestern gemacht haben,
ist imperative Programmierung und dort haben wir ein Verarbeitungsmodell von
einer Maschine,
die bekommt Anweisungen
Sequenziell. Und diese Anweisungen werden dann ausgeführt. Imperativ ein Befehl,
noch ein Befehl, noch ein Befehl und dann tut sich was.
Objektorientierte Programmierung wird oft synonym gesehen mit imperativer Programmierung.
Ich habe es da auch mal reingeschrieben,
weil das so die klassische Sicht ist.
Wir werden im Funktionalen Programmierstil auch objektorientiert programmieren.
Da ist kein Widerspruch, ja. Ob das ist objektorientiert
, meint eine andere Seite des Ganzen.
Aber es gibt objektorientierte Imperative Sprachen wie eben Java
zum Beispiel
Die bei der funktionalen Programmierung haben wir ein Verarbeitungsmodell,
wo es Werte gibt, ein Universum von Werten.
Schauen wir uns noch an. Und darauf werden Funktionen angewendet.
Und Funktionen können wir aufbauen, indem wir Funktionen kombinieren.
Und verschachtelte funktionale Ausdrücke schreiben. Bei der logischen Programmierung haben
wir ein Verarbeitungsmodell, wo die Maschine logisch schlussfolgern kann.
Und Beziehungen zwischen Objekten auswerten. Wir erzählen der Maschine über jeden Raben,
den wir kennen, ob er schwarz ist oder weiß.
Und wir können dann fragen,
gibt es einen weißen Raben und kriegen dann
die Antwort
Weil in dem Universum, was die Maschine kennt,
in Beziehungen von Werten, findet es uns denen oder nicht.
Bei der Stromorientierten Programmierung, als unsere Maschine, eine,
die Datenquellen hat. Und dann können wir dahinter Filter hängen,
die diese Datenquellen filtert, verarbeitet, transformiert.
Wir haben Kombinatoren, die Ströme zusammenfassen und so weiter.
Wenn wir anschauen, wir werden selbst auch in Recket-Stromorientierte Programmierung einbauen.
Ist schon drin, aber wir schauen uns auch mal an,
wie wir es selbst machen könnten.
Das Wichtige beim Verarbeitungsmodell ist, denken, abstrakt,
was kann die Maschine? Und das ist vollkommen gleichgültig,
ob wir hier eine Kiste stehen haben,
auf der ein Apfel ist oder was anderes.
Die konkreten Hardware ist abstrahiert. Und der Programmierstil,
den wir pflegen, der sollte zum Verarbeitungsmodell passen.
Also wenn wir jetzt eine Maschine haben, die funktionales Programmieren unterstützt,
dann sollten wir nicht ständig imperative Sprachelemente verwenden.
Um es mal gleich einzuflechten, wenn ich in Ihren Übungsaufgaben gesehen werde,
dass Sie im Wesentlichen befehle einen Befehl rein und alles sequenziell ausführen statt Funktionen zu schachteln.
Und das Programm eins zu eins, ein imperatives Java-Programm ist,
das nach Racket sind taktisch transformiert wurde, dann gibt es keine Punkte.
Also wir sollen das Funktionale denken lernen. Und das werden wir sehen,
dass es einfach drin besteht, dass wir funktional kombinieren,
aus Funktionen kombinieren, auf Fährte anwenden, dann haben wir ein Resultat.
Die Programmiersprachen, die wir haben,
wir sind in der Regel jeweils so gewählt mit ihren Sprachelementen,
dass sie einen Programmierstil gut unterstützen und Schlüpf vielleicht ja
haben,
wo man was anderes tun darf
Einfach um sie universell anwendbar zu machen. Das heißt,
Reck getappt, unglaublich viele einladende Löcher, wo sie weiterhin gemütlich Imperativ programmieren könnten.
Was sie aber bitte nicht tun sollten. Und sie werden hoffentlich sehen,
dass für viele, viele praktische Probleme oder Algorithmen,
die man braucht, der Funktionale programmiert, die viel illeganter ist.
Okay, also wir haben Programmiersprachen, sind oft hybrid,
vor allem, wenn sie eben dafür da sind,
dass wir was anderes machen.
Also Programmierstil bezieht sich auf den Modellierungsansatz,
wie stellen wir uns
die Kiste davor
Nicht nur auf Neuem mit Namenswahl kommen wie eine Kommentare und so weiter.
Natürlich sollten sie weiterhin die Best Practice pflegen,
dass sie Variablennamen wählen, die irgendwas mit dem zu tun haben,
was das Programm tun soll.
Ich hatte mein Erstsemester Übungen zu korrigieren, von einer Pferdennährin.
Also die hatte alle Variablen, egal was da abgelegt wurde,
die hießen Schimmelrappen, Apfelschimmel.
Und meine keine Idee, was das Programm tun sollte.
Und natürlich sorgfältiges Testen und so. Ah,
das Ganze nochmal in Bildern
Imperatives Verarbeitungsmodell. Das ist unsere Vorstellung, wir dürfen Befehle geben.
Und dann führt der Rechner es aus. Und was,
hier sehen wir das Verarbeitungsmodell, was der Rechner selbst hat.
Es erwartet, dass man eben Anweisungen gibt,
setze die Variable Kuh auf null, setze die Variable R
auf X und dann teste ein paar Bedingungen und dann mach irgendwas anderes.
Und die klassische Art inspirative Programme zu schreiben,
sind diese Ablaufdiagramme, Flussdiagramme. Später hat man Strukturdiagramme eingeführt,
aber die verbergen nicht wirklich diesen Zustand des Imperaktives Programmieren darin besteht,
dass sich der Zustand des Rechners während der Berechnung ändert.
Unsere Modellierung besteht darin, dass wir die Werte,
die an Variablen gebunden sind, ändern und damit der Zustand der Berechnung sich ändert.
Das ist beim funktionalen Programmieren anders. Wir werden im Funktionalen Programmierstil,
kriegt eine Variable, einen Wert,
wenn sie eingeführt wird und wird nie wieder geändert.
Also im Imperativen Modell haben wir eine Folge von Zustandsänderungen
der Variablen und Objekte. Zustandsänderungen werden durch Anweisungen oder Nachrichten
bewirkt und die Reihenfolge wird durch Kontrollstrukturen ausgeführt
Und Korrektheitsbeweise, die sind in dieser Welt absolut eklig.
Man kann für Imperative Programme die Korrektheit beweisen.
Aber wenn es ein komplexes Programm ist, glaube ich,
den Beweis nie. Also es ist, weißt du,
aus meiner eigenen Zeit, als ich studiert habe,
da wurde gerade die Sprache Pascal eingeführt und in den Schulen
als Unterrichtssprache eingeführt und wurde puck agiert.
Der Pascal Compiler, geschrieben von Peter Ammann,
war der erste Compiler der Welt,
für den die Korrektheit bewiesen war.
Also ich habe Nebencompiler benutzt,
der so viele Fehler hatte
Es ist, aber das, die Korrektheitsbeweise im Imperativen Programmieren
sind einfach deshalb so höllisch, weil sie ständig der Zustand ändert.
Und sie sehen in den Flussdiagrammen, hier kommen Pfeile, die von irgendwo hereinspringen.
Also Spaghetti Code könnte es sein, ja. Und dann zu sagen,
nachdem ich all dies hier gemacht habe,
entweder bin ich von da gekommen oder von da und ich bin jetzt hier,
was passiert, wenn ich jetzt auch noch diese Anweisung ausführe?
Also alles muss über Vor- und Nachbedingungen, Zusätzlicherungen für die Wirkung von Anweisungen geschehen.
Und wenn wir Schleifen im Programm haben, Ist die große Herausforderung,
Schleifen in Varianten zu finden, dass egal wie die Schleife durchlaufen wird,
wie eine Aussage haben, was auf jeden Fall gilt.
Korrektheitsbeweise für Software sind ohnehin etwas kritisch, weil wir schreiben
das Programm und beweisen dann, dass es richtig ist.
Und meistens macht man im Beweis exakt dieselben Denkfehler,
die man gemacht hat, erst mal ins Programm geschrieben hat.
Was man beim Programmieren vergessen hat, vergisst man auch im
Beweis zu hinterfragen. Drittsemester, als ich es,
damals war ich Übungshelferin in der Vorlesung und wir hatten hatten,
die Aufgabe gestellt, gegeben das Programm Bubble Sword,
beweisen Sie, dass es korrekt ist.
Die Musterlösung, die eingereicht wurde, war ein wunderschöner Beweis.
Ja, Schritt für Schritte und weiß nachvollziehen, Zogen hat
ja aber richtig. Hat nur was vollkommen Falsches bewiesen.
Also man versucht eben etwas zu beweisen, was man meint,
was die Essenz des Algorithmus ist und was der Beweis für Bubble Sword bewiesen hatte,
war, die Reihenfolge der Elemente ist hinterher aufsteigend sortiert.
Sagt jeder Mensch, ja will so, klar.
Wenn ich ein Programm habe, das darf dafür sorgt,
dass es hinterher aufsteigend sortiert ist, habe ich doch bewiesen, dass es sortiert.
Ja, wonach keiner gefragt hätte bei dem Beweis, ist,
man hätte auch fragen sollen, habe ich hinterher genauso viele Elemente wie vorher.
Also die veröffentlichte Lösung, die hatte einfach doppelt vorkommende Zahlen ignoriert.
Und die Hälfte der Elemente der Liste fehlte nach dem Sortieren.
Das, was übrig blieb, war sortiert,
aber man hätte fordern müssen, dass die Zahl der Elemente erhalten bleibt.
Wenn man beim Programmieren nicht dran denkt, zu fordern,
was man beim Programmieren vergisst zuvor, dann vergisst man meistens auch beim Beweis.
Deshalb habe ich bei Korrektheitsbeweisen gelegen, wie ich meine Zweifel
Gut, das hatten wir schon gesehen.
Gucken. Nimm doch mal was ganz Simples. Haben Sie
im ersten Schuljahr gelernt? Wie multipliziere ich zwei Zahlen?
Und man kann das stellenweise multiplizieren, ja so machen.
Man schreibt die Zahlen nebeneinander und dann fängt man an, stellenweise durchzugehen.
Die letzte Zahl, multipliziert die Zahl damit und so weiter.
Und addiert dann alles auf, immer wieder erschritten und hat am Ende das Ergebnis.
Peter Schäfer hat das mal akribisch auf Deutsch aufgeschrieben.
Multipliziere die letzte Stelle der zweiten Zahl mit der ersten Zahl
Also wir nehmen die letzte Stelle von zweiten Faktor und multiplizieren es mit der ersten Zahl.
Dann multiplizieren wir das Ergebnis aus eins mit dem Stellenwerte benutzten Stelle,
im Moment verstellen wir der benutzten Stelle. Wir hatten die letzte Stelle genommen,
das heißt, das ist die eine der Stelle,
multiplizieren damit, dann streiche diese, merke,
bei den Stellenwert, der nun in der letzten Stelle,
wir haben jetzt eine neue letzte Stelle, multiplizieren mit der wieder.
Gibt es bereicht, ein Ergebnis aus den Föhrern gegangen,
den Berechnungen, so addiere dies zum Ergebnis des letzten Rechenschrittes,
zahlt das Ergebnis fest.
Andernfalls zahlt das Ergebnis aus Schritt 2 fest,
wenn keine Stellen der zweiten Zahlen ja vorhanden sind,
liegt das Resultat mit dem Ergebnis aus dem Slept ausgeführten Schritt vor.
Andernfalls fahre bei eins fort. Was ihnen hier vor allem
auffallen soll. Eine Anweisung nach der anderen.
Viele Anweisungen und Schritt für Schritt läuft das Ganze ab.
Und immer wieder Entscheidungen habe ich noch eine Stelle oder nicht.
Funktional gedacht würde man, das ist sehr viel einfacher machen.
Ich zeige ihnen anschließend auch gleich mal das Funktionale Ergebnis.
Gucken wir nochmal auf das objektorientierte. Was sie hoffentlich in den ersten Semestern gelernt haben,
als Stärke des objektorientierten sind Dinge,
wie das wir In einer Welt von abstrakten Objekten denken können und beschreiben,
wie wir komplexe Objekte mit Zustand und so weiter manipulieren, verändern.
Es gibt viele Programmiersprachen, diese Essenz des Objektorientierten.
Klar machen, die zweite Stärke des Objektorientierten ist,
dass wir soft wiederverwendbare Software bekommen,
weil wir eben die Objekte selbst als Parameter freilassen können und erweitern können und so weiter.
Also eine klassische objektorientierte Programmiersprache. Wir denken hier in
einer Welt von Bauplätzchen und unser Rechner,
der versteht dann eben eine Welt über das Bewegen von Bausteinen
Robotersprache, die Dinge zusammensetzt hier. Move Block Art,
to Block B, attach Block B und so weiter
. Beim Objektorientierten haben modellieren den Bereich über abstrakte Objekte.
Wir haben gut verständliche Programme, wenn die Leute so programmieren können.
Wir haben eine gute Erweiterbarkeit über Vererbung.
Und wir haben allerdings, wie gehabt, schwierige formale Korrektheitsbeweise,
weil es in der Welt des imperativen Programmierens stattfindet.
Wenn wir die Stärken des Objektorientierten Programmierstils in die funktionale Welt bringen,
dass wir am Ende des Semesters machen werden,
objektorientierte Erweiterungen für funktionale Programmiersprachen anschauen, dann werden Sie sehen,
dass wir in dem Punkt hier weiterkommen werden.
Gucken wir mal aufs funktionale Modell. Da ist unsere Sicht auf den Rechner im Wesentlichen die,
die man mit so einem Taschenrechner in der Hand haben könnte.
Wir haben Universum von Werken und wenden Funktionen drauf an,
aus einem endlichen, bekannten Funktionsvorrat, so viele Tasten wieder reinpassen könnten.
So in der Recket- und Listwelt Gewöhnen Sie sich mal bitte gleich dran,
verwenden wir nicht die Infix-Funktion,
dass die Operatoren zwischen den Operanten stehen, sondern immer Prefix-Motation,
erste Operator und dann kommen all die Argumente.
Und das ist die Welt, wie hier die Maschine kann.
Eine Operation angewendet auf Liste von Argumenten.
Also wir haben funktionale und applikative Verarbeitungsmodelldefinitionen von Funktionen,
Konstruktionen von funktionalen Thermen.
Sehr prägnant,
sehr kurz
Eine formal definierte Semantik. Und die Syntax ist wirklich einfach.
Also wie gesagt, Klammer auf, Operator, die Argumente,
Klammer zu. Vielmehr müssen wir über Syntax nicht reden.
Ein paar sind taktische Schlüsselworte, muss ich Ihnen noch beibringen,
aber im Wesentlichen ist es das.
Okay, jetzt Konkurrenz zu dem Imperativen Multiplizieren.
Die erste Formulierung des Imperativ zu beschreiben,
hat die ganze Folie gefüllt.
Hier kriegen wir es in drei Zahlen hin
Das Produkt zwar ja Zahlen ergibt sich als Summe der Produkte
aus der ersten Zahl und dem mit ihren Stellenwerten multiplizierten Stellen der zweiten Zahl.
Es ist kürzer und prägnanter. Ja, und es wird
kein Ablauf beschrieben. So, es wird nur deklarativ gesagt,
was ist das Produkt zweier Zahlen? Man bezieht sich auf Ausdrücke,
Funktion, Operation, die sind hier in rot hervorgehoben.
Das Produkt wird definiert. Und dann haben wir hier Summe,
Summe muss definiert sein. Wir sehen hier auch schon,
das Ganze ist rekursiv. Ja, das ist ganz,
ganz typisch. Also die erste Hälfte des Semesters werden sie
fast alles rekursiv programmieren
Das ist beim,
ergibt sich automatisch, wenn wir funktional denken und schreiben,
dass man meistens ein Problem dadurch vereinfacht,
indem man die Wald und Konka dafür sorgt,
dass man das Problem auf die Hälfte zurückführt,
indem man sagt, funktional beschreibt,
wie kann ich aus dem Resultat für die Hälfte der Objekte,
das für das augenblickliche Objektchen kriegen.
Und von daher sind ganz typisch funktionale Programme rekursiv,
eben immer einen kleinen Teil erledigen im Ausdruck,
indem man sagt,
wir bilden die Summe aus dem Produkt,
aus der ersten Zahl und den Produkten der anderen Zahl
Und so weiter. Und dann haben wir hier noch Stellen
der zweiten Zahl, also Basisfunktionen, die definiert sein sollen.
Aber es ist kurz prägnant, es ist einfach,
überlegen Sie selbst, ob Sie ab, dass es kürzer ist,
müssen Sie zugeben, ob Sie es besser verständlich finden.
Das hört sich im Laufe der Semesters zeigen. Also was
wir hier haben, ist ein paralleler Algorithmus, ja.
Wir sagen nicht, in welcher Reihenfolge die Produkte aus der
ersten Zahl und den Stellenwerten ermittelt werden.
Das ist offen gelassen. Im Imperativen müssen wir genau sagen,
welcher Reihenfolge wir es haben wollen. Deshalb haben wir diese dumme Schleifwetter gehabt,
die immer wieder zurückgeht, erst eine Stelle,
dann noch eine, hier sagen wir einfach,
wir brauchen es für alle
Und lass uns dem Rechner frei, in welcher Reihenfolge er das macht.
Es ist auch nicht deterministisch. Kann er sich eben entscheiden,
wann er welche Stelle nimmt. Es ist aber terminierend,
es ist determiniert, es ist funktional, es ist regulsiv.
Ja. So, Syntax ist einfach, extrem einfach,
wenig redundant, die wenige Redundanz wird sie ziemlich treffen.
Also ich möchte sie bitten, zu Beginn des Semesters haben wir babyhaft einfache Übungsaufgaben,
dass sie die trotzdem machen, um fit zu werden.
Also die ersten Ausgaben sind dafür da,
dass sie schlichtweg Den Kampf mit dem Recket-System überstehen und in einer Sprache,
die keine Redundanzzeit erst hinkriegen, in vernünftiger Zeit schnell Programme zu schreiben,
weil sie einfach die Syntax verinnerlichen und dann sehen,
dass es klappt.
Also das sollten in der Lage sein, Recket-Programme zu lesen und zu schreiben,
weil während der Klausur am Ende haben wir keinen Rechner mehr.
Sie haben nur Papier und Bleistift. Und dann soll es nicht
zu langsam gehen, weil wenn sie immer alles nachschlagen müssen,
oh Gott, wie war das? Und das noch,
nimm sie die ersten Übungsaufgaben um einigermaßen fit und flott
das hinzukriegen, selbst wenn sie ihnen zu leicht und
zu langweilig vorkommen
Aber das Wichtige ist, dass sie gut und schnell und sicher
einen Ausdruck schreiben können, ohne jedes Mal das Manual zu lesen.
Okay. Es ist extrem einfach wenig redet und dann die wenige Redundanz,
die wird dazu führen, dass sie oft Syntaxfehler machen,
obwohl die Syntax einfach ist.
Und dann vor der Frage stehen, die finde ich den Fehler jetzt schnell.
Also auch das sollten sie üben. Machen sie ruhig bewusst Fehler,
damit sie die Fehlermeldung sehen und wissen, was sie bedeuten.
Diese Mantik von Racket, von der Sprache,
die wir verwenden, wird über das Lamm der Kalkül definiert.
Beginnend mit kommenden Lisp und dann Skin hatten wir
eine Und deinen Racket haben wir eine Familie von Sprachen,
wo ganz klar ist, was jede Anweisung bedeutet.
Und wir haben sie im Lamderkalkül formuliert und können im Lamderkalkül die Bedeutung eines Ausdrucks ausrechnen.
Das werden wir allerdings hier in dieser Programmiersprache nicht machen.
Das überlasse ich den formalen Grundlagen, mit Ihnen das Lamderkalkül durchzuarbeiten.
Die Ostdruckskraft aber ist unglaublich hoch und wir können wirklich
sehr knappe und kurze Programme schreiben und das ist da,
wo der Spaß beginnt.
Syntags leicht zu lernen. So, gucken wir nochmal aufs
Relationale oder logische Verarbeitungsmodell, Wolfgang Münzel in seiner Vorlesung einführt.
Das ist das relationale Modell. Also ich finde die Vision für
die abstrakte Maschine, die dort steht, wirklich schön.
Wir stellen uns ja nur so eine Art mit Knatterton
ein Detektiv vor, der logisch kombinieren kann.
Also ein Programm besteht darin, dass wir das,
was wir über einen bestimmten Fall oder ein Problem müssen, deklarativ hinschreiben.
Das kann ein ziemliches Gemenge sein von Fakten,
von Relationen zwischen Fakten und so weiter.
Und das Suchen in dieser Menge dieser Fakten nach der Lösung
, das nimmt uns der Rechner ab.
Also wir brauchen den Kontrollfluss und den Suchalgorithmus der der die Fakten
kombiniert und die Schlussfolgerungen zieht, das brauchen wir nicht zu programmieren, das kommt automatisch mit.
Wir geben nur die Fakten und ein paar Regeln und dann können wir Fragen stellen.
Wenn wir hier zum Beispiel das alte Reinhard-Mail-Lied,
der Norder ist immer der Gärtner, modellieren wollen, ja,
dann können wir sagen, wenn eine Person X ermordet ist,
ja, dass die Relation Mörder X steht im Zusammenhang,
Gärtner ist der Mörder, ja, wenn eine Person X ermordet wird, dann ist es der Gärtner.
Wir können jetzt eine Anfrage stellen. Wer hat Paul ermordet?
Dann wird in der Fastendatenbasis nachgeguckt und all die Regeln,
die wir haben
Und der Rechner wird uns sagen, wenn jemand fragt,
wer ist der Mörder von Paul?
Die Variable. Dieser Ausdruck ist logisch wahr und erfüllt,
wenn M der Gärtner ist. Und wenn wir dann noch
mal Return geben und fragen, gibt es weitere,
was vielleicht ein Mörderteam, dann erfahren wir nein.
Das heißt, hier in dem Fall in dieser Welt,
die wir modelliert haben, ist kein weiterer Mörder bekannt.
Wir können im relationalen Programmierstil wirklich sehr komplexe Suchprobleme lösen,
indem wir einfach nur die Fakten bekannt geben und den Suchalgorithmus
nach der Lösung,
in all diesen Gewürfe der Rechner
uns abnehmen
In diesem Verarbeitungsmodell. Bringt unglaublich Spaß. Gut,
also wir modellieren explizit Fakten und Beziehungen zwischen Elementen.
Vordefiniert ist alles über Suchen und Schlussfolgern. Also solche Dinge
wie Backtracking und so müssen wir in der Programmiersprache,
die das relationale Programmier unterstützt, überhaupt nicht machen.
Wir haben auch eine einfache Syntac sehr leicht zu lernen.
Wir haben eine formal definierte Semantik. Und genau wie das Funktionale
unglaublich gut für komplexe Programmieraufgaben geeignet. Noch als Kontrast der Stronorientierte
Da werden wir am Ende des Semesters auch mal kurz reingucken,
weil in den verschiedenen Reggae-Dialekten ist das stromorientierte schon drin.
Da stellen wir uns eine Welt vor, in dem es
feste Verarbeitungsstationen gibt. Stellen Sie sich ein Fließband vor,
an jeder Stelle steht eine Instanz,
die aus einem Strom von Werten etwas nehmen kann,
verarbeiten und es in einer neuen Form weitergibt, verarbeiteten Form.
Und Beispiele ist zum Beispiel in jeder Unique-Schale haben wir
so etwas wie ein Grab,
was aus einer langen Liste von Pfeilsfläche mit bestimmten Attributen
raussucht und die können wir dann sortieren mit nur Anzeigen
und so weiter
Ja. Stromorientiert ist für Fließbandarbeitige Verarbeitung super. Aber es
ist, es ist in der Regel kein universelles Verarbeitungsmodell.
Also wir können über Stromorientierte Programmierung nicht jeden intuitiven Algorithmus realisieren.
Meistens verhindert die Programmiersprache das. Also wir könnten da nicht
eine universelle Thuring-Maschine bauen und sagen, okay,
jetzt haben wir unsere Stromorientierte universelle Maschine.
Ja. Okay. Gegenstück zum Verarbeitungsmodell. Das haben Sie
im ersten Semester auch kennengelernt.
Das ist das Benutzermodell
Also die Menschen, die hier den Computer mit seinem Verarbeitungsmodell bauen,
die stellen sich vor, da sitzt eine bestimmte Gruppe von Benutzern,
die das anschließend verwenden wird.
Und die Krise entsteht immer dann,
wenn wechselseitig die Benutzer nichts von dem Verarbeitungsmodell wissen und die,
die das Verarbeitungsmodell implementiert haben, völlig unrealistische Vorstellungen von Benutzer haben.
Programmierstil, was man stets entständigt trifft,
obwohl es nicht offiziell existiert,
das ist das mystische Verarbeitungsmodell
Das mystische Verarbeitungsmodell haben wir, wenn wir einen Computer haben,
vor dem wir sitzen und die ganze Zeit schütteln wir in
den Kopf und sagen, was ist nun schon wieder los?
Weil wir das Bearbeitungsmodell nicht verstanden haben.
Also wenn wir da unerwartete Easter Eggs sehen oder Dinge,
die einfach nicht dazu passen oder jeden Montag können wir einschalten,
verhält er sich anders als am Dienstag davor.
Solche Systeme, die ein mystisches undefiniertes Verarbeitungsmodell haben, die sind einfach riskant.
Also wenn Ich hatte mal im Alter der Bahnhofen-Computer mit mystischem Verarbeitungsmodell gefunden.
Der saß ein Mensch, der verkaufte Computerhoroskope. Und das
passierte sonst was, aber es kamen immer lustige Vorhersagen.
Und da habe ich den Benutzer gefragt,
wie sich ein Hurriskupper erstellen lassen, ob sie dem glauben.
Und ich bin fast vom Glauben abgekommen an der Stelle,
weil sie sagten, ja, natürlich,
dass du in der Zeitung, glaube ich,
die Horoskope nicht, aber da stand ein Computer.
Wenn es der Computer ausgerechnet hat, muss es richtig sein.
Und das ist eben, ja, Was soll man dazu sagen?
Also das lustische Verarbeitungsmodell ist vor allem gekennzeichnet durch unverhersehbare Systemverhalten.
Also seit ich hiess hier reinlaufen habe,
würde ich dem Rechner in dieselbe Gruppe rechnen.
Wir haben überraschende, verborgene Zustände, ja,
unreproduzierbare Fehler, verwirrende, unergonomische Bedienenoberflächen.
Unverständliche konfuse Handbücher. Also wenn Sie vor der Wahl stehen,
sich ein System zu kaufen, dann bitte keins mit mystischen Verarbeitungsmodell,
ja.
Also da gibt es
nur einen Zweck damit
Und standhaft entschieden, Nein sagen, so etwas verwende ich nicht für Projekte,
wo irgendjemand hinterher von abhängt und irgendetwas Wichtiges davon abhängt.
Also, wenn Sie selbst neue Systeme auf den Nackt bringen,
ein neues Verarbeitungsmodell entwerfen. Ja, beschreiben Sie es,
dokumentieren Sie es? Machen Sie es so beherrschbar,
dass der Benutzerin in der Lage sind, zu verstehen,
was das tun soll. Und dann haben wir es.
Und genau diese Mystik bezieht sich auf diesen Kommentar von,
ho, das wäre einer der Entwickler der Programmiersprache,
Allgäu 68, also lang, lang ist es her.
Und richtete sich damals gegen eine neue Programmiersprache,
die vom Department of Defense geschaffen wurde. Das heißt,
das Department of Defense hat ein großes Team, ein Wissenschaftlerinnen und Wissenschaftlern eingesetzt,
um eine neue Programmiersprache zu schreiben, nämlich die Programmiersprache EDA.
Und Hor hatte damals Die Sprache eda mit allem,
was sie tut, in Programme,
die drin geschrieben werden, in diese Kategorie mystisch eingeordnet.
Er hat es mit anderen Worten gesagt. Seine Worte waren
eine unzuverlässige Programmiersprache, aus der unzuverlässige Programme hervorgehen,
bedeutet eine sehr viel größere Gefahr für unsere Gesellschaft,
als unsichere Automobilmobile, giftige Festizide und reakte Unfälle in Kernkraftwerken.
Ich glaube, er hat damals der Sprache Eda ziemlich Unrecht getan.
Wir haben sie bei uns im Labor ein paar Jahre ausprobiert,
ich unfreiwillig, aber es war damals,
gab das meiste Geld für die Informatik in genau in den Bereichen,
wo die Sprache verwendet wurde.
Also wenn da so ein großer, teurer Geldgeber dahinter steht,
dann wird eine Sprache gut gepusht. Und mein Doktorvater wollte
der Erste sein in Hamburg, der eh da verwendet.
Ich wäre lieber bei Pascal geblieben, aber wie auch immer.
Diese Sprache hatte so viele neue Elemente,
wo eben wissenschaftlich noch nicht genau fundiert war,
wie sicher sie sind, wie gut sie funktionieren.
Es hat sich gezeigt, dass es gehen kann und funktioniert und dass eda-Programme besser waren.
Nur musste die Sprache verwendet werden von Informatikern und Informatikern und nicht von geschulten Frisierlingen,
die noch einen Programmierkurs belegt hatten und dann loslegen sollten.
Wenn Sie sich vorstellen, eine Sprache,
die neue Programmierkonzepte einführt, die ein Informatikstudium erfordern,
dann darf man eben keine Programmierer ransetzen,
die nicht Informatik gelernt haben, sondern nur kodieren.
Und das war genau dieser schwierige Spagat zwischen Benutzermodell und eben Programmierstil, Verarbeitungsmodell.
Die Sprache wurde konzipiert für Informatikerinnen und Informatiker als Anwender
und wurde dann in der Praxis in den Firmen eingesetzt in
dem Programmiererinnen und Programmierer dran saßen und nicht Informatiker.
Und die hatten einfach nicht gelernt, was generische Funktionen sind,
was Klassen sind. Die haben in Eda Fortrand programmiert.
Und die Programme haben damit natürlich nichts hingekriegt, was man in einer Sprache,
die jetzt das Klassenmodell einführt und ähnliches, generische Klassen.
Dazu muss man Informatik gelernt haben. Und deshalb sitzen sie
jetzt nämlich auch hier, dass sie spätestens im dritten Semester
neue Programmiersprachen und neue Konzepte und Verarbeitungsmüdelle kennenlernen.
Das sind nicht
ihr Leben lang
Dran gebunden sind, so zu denken, wie man in Java denkt.
Das macht Informatiker und Informatikerinnen im Gegensatz eben zu Programmierern aus.
Und bringt mich auch zu der Frage, die wir diesem
Semester nicht beantworten werden, falls da keine Antwort gibt.
Was ist eigentlich der beste Programmierstil? Sie werden für jede
Art von Programmierstil Fanclubs finden, die ganz klar sagen,
wow, also für mich ist objektorientiertes Programmierer wie in Java mit das Beste.
Bleibt mir mit anderen Sachen vom Leibe.
Andere sagen, ich kriege die Krise
Wenn ich in einer empirativen Welt denken muss, ich kann
am besten meine Probleme in Funktionalen beschreiben oder im Relationalen.
Wenn Sie als Informatikerin oder Informatiker später in die Welt hinausgehen,
dann sollten Sie als erstes das Problem draufhin untersuchen,
in welchem Verarbeitungsmodell kann ich dieses Problem am angemessensten modellieren?
Entscheiden Sie sich für den Programmierstil, der es möglich macht,
das zu machen. Wenn Sie ein Robotikmodell haben,
bei dem es um das Werkeln mit Gegenständen geschieht,
klar, da nimmt man einen Programmierstil,
Objektorientiert ist und ihnen die Objekte modellieren lässt,
kann funktional sein, kann stromorientiert sein, je nachdem,
wo ihr Roboter hinterher sitzen wird.
Und bei der Programmierung ist es eben jetzt für sie die allerwichtigste Aufgabe.
Das ist zunächst das Anwendungsproblem analysieren. Grundoperationen, Abstriktionen festlegen,
für sich aufschreiben, Mensch, was ist zu tun?
Und dann gucken sie auch die Verarbeitungsmodelle.
Brauche ich einen Number-Cruncher? Ja, habe ich hier,
ist die Essenz des Problems im Wesentlichen Stelle in Umerik.
Muss ich bestimmte Dinge modellieren oder geht es darum,
dass sich komplexe Zusammenhänge zwischen Objekten
feststellen muss
Und wenn Sie bei der dritten Aufgabe sind,
dann würde ich spätestens zum Funktionalen oder relationalen Programmierstil greifen und
nicht die ersten beiden nehmen.
Ja, und wenn Sie feststellen, oh je,
Mensch, das ist aber ein so spannendes Problem.
Und keins der Programmierstile, die ich habe,
kann das ausdrücken, was ich eigentlich tun muss.
Ja, dann, also es gibt keine Programmiersprache, die es kann.
Es gibt keinen Programmierstil, der es richtig kann. Ja,
wunderbar, dann schreiben Sie Ihre Doktorarbeit über einen neuen Programmierstil.
Und wenn eben keine der zur Verfügung stehenden Programmiersprachen diese Modellierung bietet,
Dann erwägen sie sich, das selbst zu schaffen.
Also das, was wir in diesem Semester üben werden.
Sie führen ihr eigenes Problem adäquates Verarbeitungsmodell ein, zu sagen,
das muss meine Maschine, können denn das Problem zu lösen?
Dann definieren sie eine passende Programmiersprache dazu. Implementieren eine virtuelle Maschine,
die dieses Verarbeitungsmodell implementiert, schreibende Veröffentlichung. Und sie werden sehen,
sechs Monate später oder so wird jeder Lispdialekt,
diese ihre Programmiersprache inkooperiert haben,
wenn sich herausstellt,
dann braucht er sie wirklich
Okay. Guck mal immer in der Welt der Programmiersprachen,
doch auf ein paar andere Parameter. Das Wichtigste ist auch
die Ausdruckskraft. Wir sagen, die Programmiersprache ist berechnungsuniversell.
Wenn in ihr jeder intuitive Algorithmus formuliert werden kann. Das heißt,
praktisch gesagt oder aus der Sicht der theoretischen Informatik,
wenn sie in dieser Programmiersprache nur universelle Touring-Maschine schreiben können,
dann haben sie eine berechnungsuniverselle Programmiersprache,
denn für universelle Touring-Maschinen, glauben wir per Definition,
dass die jeden intuitiven Algorithmus ausführen könnten.
So, General-Purpose-Programmiersprachen sind in der Regel Berechnungsuniversell, ne?
Also sowas für Liz-Prolog, ja, war C. Ja,
da können wir wirklich jeden Algorithmus drin formulieren,
mehr oder weniger treffend oder geeignet für das, was wir beschreiben wollen.
Spezialsprachen, Datenbankabfragesprachen, Modellierungssprachen in der Regel nicht berechnungsuniversell, ne?
Okay. Gucken wir uns mal die Familien an.
Ich habe sie schon erwähnt. Für die funktionale Programmierung,
die wir hier haben, da haben wir einmal die Liz-Familie, die ich wirklich liebe.
Und dazu gehören eben Reckets geben, kommen Lizbia.
Ganz typisch dafür sind, dass sie hybrid sind. Wir können jederzeit,
wenn wir wollen und es für nach einer Wissenserforschung für notwendig erachten,
auch in diesen Sprachen imperativ programmieren.
Sollte man nicht einfach so tun, sondern wirklich nur,
indem man sich überlegt, brauche ich diesen Bruch.
Symbole, dynamische Typisierung. Dann haben wir die streng getypten referenziell transparenten Sprachen,
da haben wir die Klassiker, Miranda, Häske,
also ich habe eine Zeit lang auch immer Miranda genommen fürs
funktionale Programmieren und habe dann immer gemerkt,
und mir bringt das einfach keinen Spaß.
Die Sprache ist toll. Das, was gut geht,
bringt Spaß, ja, da freut man sich immer.
Also einfach mal so mit 1 Punkt, Punkt,
die Menge aller natürlichen Zahlen hinschreiben oder Filter drüber schicken und so
, ist elegant, ja, bringt Spaß, ist super.
Aber wenn man nur versucht für irgendeine gegebene,
verschachtelte Liste mit Unterlisten, eine rekursive Funktion zu schreiben,
die die Tiefe dieser Liste erstellmittelt,
dann sagt ein Miranda knallhart,
geht nicht
Wir haben einen Typkonflikt. Die Elemente der Funktion haben,
je nach Schachtelungstiefe, nämlich einen unterschiedlichen Typ.
Und man kann in derselben rekursiven Funktion nicht über,
nicht eine zehnfach geschachtelte, eine neunfach geschachtelte übergeben.
Da ist diese, die Syntax der Sprache verbietet das als Typfehler.
Und damit war die Hälfte von den Beispielen,
die ich unterrichten wollte, nicht möglich.
Weil wir keine Rekursionen machen konnten. Mit einer freien Typisierung
. Das können wir in der Liz-Welt richtig gut auskosten.
Und da ist es möglich. Es gibt einen guten Grund,
warum Meskel und Miranda streng getypt sind,
die haben ein eingegebautes Typ Kalkül und können damit viele,
viele Sicherheitsprüfungen automatisch machen, was auch sehr,
sehr gut ist und für viele Anwendungen ist das prima.
Es macht aber eben die Metaprogrammierung schwierig und deshalb sind wir
, bleiben wir hier bei der Listenfamilie, ja.
Wir werden die Stärke der Symbolverarbeitung kennenlernen. Also sie brauchen für
den Rest des Semesters nicht mehr drüber nachdenken über Stringtokenizer und sowas,
also Datentyps dringen können wir im Wesentlichen vergessen,
wir machen alles mit Listen und mit Symbolen und das ist
einfach und schnell und mächtig,
Gut, Liz ist eine der ältesten Programmiersprachen, die noch benutzt werden.
Und ist so lebendig wie eh und je, das ist echt scholl.
Ich habe das Programmieren von Liz bei John McCasty gelernt,
der ist der Erfinder dieser Programmiersprache. Und der hat die
erste Implementation gestartet, als Computer noch keine grafischen Bedien-Oberflächen hatten.
Und man im Wesentlichen eine Massembler-Code programmiert hat. 1958,
er hat einfach das Lamderkalkül genommen mit dem Lamderkalkül,
konnte man ja das berechnen mit Hilfefunktionen modellieren.
Kann man immer noch. Genau dafür ist es da. Und er hat gesagt,
Mensch, ehe ich jetzt mühsam, Kobol oder Fortrenn,
was es damals gab, aufs Landerkalkül abbilde,
nehme ich doch lieber gleich das Landerkalkül als Programmiersprache.
Ja, dann mache ich keine Fehler bei der Abbildung.
Genial. Und das hat er eben gemacht,
die Ausdruck, die mathematische Schreibweise,
wie man im Landerkalkülfunktionen beschreibt und anwendet, gesagt hast,
aus meiner Programmiersprache, die nennen wir das.
Und Liz ist aber inzwischen seit den ersten Anfängen hat es
sich wirklich entwickelt und es hat eine riesige Familie von
weiteren Programmiersprachen
Zur Folge gehabt. Und wir verbänden hier Recket,
der ursprüngliche Name Lysp, heißt offiziell laut Nic Casys Lift Processing.
Manche sagen, es heißt was anderes.
Steht für Lots of Sülly in Cities Parencieses. Also wir haben
ziemlich viele Klammern. Ansonsten ist es da. Okay,
der erste Lyft Dialekt, der mir wirklich Spaß gebracht hat,
das war ein sie kommendes.
Die Lysp Dialekte davor, die hatten eine ziemliche Krankheit.
Die haben sich in Schorbritt,
hatten die Programme andere Ergebnisse,
wenn man
sie kompiliert hat
Und an sie kommendes ist per Definition das,
was man im Interpret der getestet hat, dasselbe,
was rauskommt, man dasselbe Programm kompiliert und das dann ausführt.
Und es hat schöne Erweiterungen wie Clim zum Beispiel und so weiter.
Skim war dann die erste Variante von Displi,
speziell für die Ausbildung geschaffen wurde,
zum Unterricht in den Schulen oder für Erstsemester.
Und darauf haben wir jetzt das jüngste Kind,
das ist eben unser Recket. Funktionale Abstraktion, Symbole,
dynamische Typisierung, vorgezogene Auswertung. Was das ist,
kriegen wir vier Doppelstunden später.
Mit der Programmierung
Teil der Sprache, dass man neue Sprachelemente einführt und
die Semantik und denen eine andere Semantik gibt.
Ja, kein syntaktischer Unterschied. Okay. Auch noch mal
ein Zitat von Norweg, was es wirklich deutlich macht.
Warum? Das so lebendig ist und immer wieder,
mussten wir gucken, wie viel Zeit haben wir eigentlich noch.
Immer wieder so propagiert wird. If you going to long in language,
it might, as well be want miss a growing,
litricia, rather than a dettan.
Also Liz ist ein Überlebenskünstler durch die eingebaute programmieren wurde,
sobald sich hier mal was Neues ausgedacht hat,
wurden andere gesagt haben, at to budget, das kannst du nicht.
Hat sich jemand hingesetzt und hat sofort in die Sprache eingebaut,
ne? Und es ist einfach gängige Praxis,
neue Programmiersprachen zu definieren und die sind dann lisp und fertig.
Und das, weil es so ein Chamäleon ist, ja,
da haben was, ich gehe hier mal ganz schneller durch.
Ja, das ist Flexibility, a lousers to a dept
a programming style changed, but more importantly,
lisp can it dept to your particular programming problem.
In other languages, you fit,
your problem to the language, this lisp,
you extense the language to fit your problem
Und das ist es, was sie lernen sollten.
Das Chamäleon. Aber man kommt nur auf die Idee,
das zu machen, wenn man mehr als ein Verarbeitungsmodell kennt.
Also wenn sie programmieren gelernt haben, mit einer Sprache und
lieber ein Tellerrandgenre schauen und die anderen Sprachen mal ausprobieren,
dann haben sie da ihren Hammer, wie fortdran, Zeit lebens.
Und egal, was sie modellieren müssen,
sie nehmen den Hammer und machen aus ihrem Problem einen Nagel,
damit sie es lösen können mit ihrem Hammer.
Es ist ziemlich dämlich. Und wenn man keinen Hammer hat,
Ja, dann nimm sie eben alles,
was sie haben und verwenden ist, wie ein Hammer,
auch wenn es dafür nicht gemeint war.
Das war eben genau das Problem der zitierten Forttrennprogrammierer,
die mit einmal in Adapt programmieren sollten,
haben nie generische Funktionen und innliches gesehen und haben das verwendet,
wie ein Fortdrang.
Ja, und die Programme waren im Leben fortrang, fertig.
Also, ein Vorurteil gegen unsere schöne Liftwelt ist das List-Programm ineffizient werden.
Kommt eigentlich daher, dass wenn man bestimmte Sachen,
die für nunmehre gedacht waren, effiziente Vortragmodule gibt,
den Lift nachprogrammiert und weil es funktional so elegant ist,
schreibt man eine Rekursion über Matrizen nennen.
Und das macht das noch interpretiert. Das ist natürlich langsam.
Da gewinnt Fort dran. Wenn Sie aber dieselben Sachen machen,
indem Sie das nicht interpretiert machen, sondern das Programm anschließend kompilieren lassen.
Und die passenden Sprachelemente wählen, Typdeklarationen anführen, das geht alles.
Dann sind meistens die entsprechenden Listprogramme sogar schneller als der
ursprüngliche Fortrandcode
Deshalb List-Programme in der Regel, wenn es nicht jetzt am
Missbrauch von mit dem mathematischen Funktionen geht, langsam sind,
ist, dass man in List sich an Probleme herantraut,
die man gar nicht erst versuchen würde in anderen Programmiersprachen.
Es ist ja eine universelle Programmiersprache,
die speziell auch ausgelegt ist für Probleme der künstlichen Intelligenz.
Und in dieser Welt gibt es einfach Probleme,
die nicht gut zu lösen sind, wenn wir Programmiersprachen haben,
die Rekursionen und rekursive Datenstrukturen nicht gut unterstützen und die einem nicht die Schlussfolgerungsmöglichkeiten bieten oder die Möglichkeiten,
dass man eben tatsächlich tatsächlich Inferenzmaschinen baut Schlussfolgerungen baut und all diese Dinge.
Und die Dinge sind so kompliziert. Ich habe Menschen gesehen,
die im Expertensystem in Assembler geschrieben, nur damit schneller wird.
Das ist eigentlich der absolute Wahnsinn. Weil damit kommt man
so schwer verständliche Programme, weil man ein unadäquates Verarbeitungsmodell genommen hat,
dass das Ganze nicht funktionieren kann.
Also Lustprogramme sind in der Regel gut,
schnell und lassen sich auch für Dinge,
die kommerziell gebraucht werden,
sehr gut einsetzen
Okay. Ja, jetzt haben wir durch die Kameraprobleme am Anfang
ein bisschen die Möglichkeit verloren, jetzt noch das Organisatorische zu besprechen.
Aber das meiste, davon werde ich das nächste Mal machen.
Ich wollte noch mal kurz sagen, die Materialien finden sie zur Vorlesung,
finden sie Skript und die Folien direkt in Stine bei der Vorlesung verlinkt.
Die Übungsaufgaben, die finden sie und auch in Stine unter
Materialien zu allen Einheiten, wenn sie auf die Übungen gehen.
Das ist nicht bei jeder Übungsgruppe einzeln, sondern es gibt ja einen Eintrag,
wo man es für alle Teilnehmer ran hängt,
da durch die Übungsaufgaben reingetan.
Sie finden da jetzt als erstes zwei Aufgabenblätter.
Eins ist die Präsenzübung. Das wird nächste Woche während der
Übungszeit besprochen. Drücken Sie sich es ruhig schon aus,
damit Sie es haben und schauen Sie auch schon mal drauf.
Wir werden in der ersten Übungsstunde das Racket-System kennenlernen,
sehen, wie definiere ich dort Funktionen und all sowas,
wenn Sie einen Laptop haben, bringen Sie ihn mit.
Und außerdem werden Sie schon das Übungsblatt,
was dann in der Woche drauf abgegeben werden soll,
Das können sie auch schon mitnehmen, weil es vorbesprochen werden soll,
damit sie wissen, was zu tun ist.
Für die Übungen, also wir haben jetzt
diese Präsenzübung und ausgeteilt, ist das erste Aufgabenblatt.
Sie werden sehen, geplant, sind 13 Aufgabenblätter, ja.
Und hier sieht man, wie viele Punkte sie bekommen können.
Es ist ja jeweils eine Woche Zeit,
abgegeben werden sollen die Lösungen am Mond,
eine Woche später am folgenden Montag,
bis mittags 12 Uhr sollten wir in der Mailbox
von ihrem Übungshelfer oder der Übungshelferin sein.
Geben Sie sie per E-Mail ab
Sie können, je später das Semester wird,
desto mehr Punkte gibt es, um den Schein zu kriegen,
sollten sie die Hälfte der Punkte erreicht haben.
Das ist egal, ob sie alles in der ersten oder zweiten Hälfte machen.
Aber die Zahlen, sie sollten die Hälfte der Punkte erreicht haben.
Geben Sie die Aufgaben immer als Dreier-Team ab.
Bilden Sie in Ihrer Übungsgruppe Teams von drei Leuten
und bearbeiten es gemeinsam und geben dann nur eine Lösung ab.
Jeder sollte aber einen Teil der Lösung, eine Aufgabe selbst vorstellen.
So zweimal. Jeder sollte zweimal vorgetragen haben und dann haben
die es geschafft. Okay, damit. Dann bis zum
nächsten Mal