So, einen schönen guten Tag, meine Damen,
guten Tag, meine Herren.
Wir machen hier Softwareentwicklung. Sie sind im dritten Teil eines Zykluses,
der für Softwareentwicklung, mein Name ist Wolfgang Metzl.
Ich bin hier für die Logikprogrammierung zuständig und fange damit an,
dass ich einen Überblick gebe über die verschiedenen Grundbausteine des SE,
der Softwareentwicklungszykluses.
Zwei davon haben Sie ja mit Sicherheit schon kennengelernt.
Das ist die SE1-Sessé. Zwei, wo es sich
hauptsächlich um objektorientierte Programmierung, allerdings mit der Einschränkung auf
zustandsorientierte Programmierung geht
In SE3 gibt es einen parallele Veranstaltung zur funktionalen Programmierung von
Frau Dreschler. Gleich am Anfang die Frage,
wer von Ihnen behört gerade die Veranstaltung bei Frau Dreschler?
Na, das ist wie im letzten Jahr auch.
Wer hat Sie schon gehört? Das ist aber,
das waren jetzt mehr als 100 Prozent. Gut,
aber ich kann im Wesentlichen davon ausgehen, dass die meisten
von Ihnen mit funktionaler Programmierung schon in Berührung gekommen sind.
Hier geht es jetzt also um relationale Programmierung oder auch Logikprogrammierung
genannt. Es gibt dann immer noch ein Praktikum,
ich habe das Praktikum Logikprogrammierung in Klammern gesetzt,
in den letzten Jahren nicht mehr die Kapazität hatten, es anzubieten.
Vielleicht bessert sich die Lage jetzt wieder etwas,
weil wir einen zweiten Professor in der Sprachverarbeitung bekommen haben und das könnte mir etwas Luft verschaffen.
Gut, es geht hier also darum, grundlegende Konzepte,
Methoden und Werkzeuge für die Softwareentwicklung in einem alternativen Paradigma kennenzulernen,
während also SE1, SE2 sich mit dem Mainstream der Programmierung befasst,
der objektorientierten Programmierung,
geht es hier um eine alternative Sichtweise und da möchte ich gleich an den Anfang vollkommen klarstellen,
ich bin hier nicht zu missionieren
Erwarte nicht, dass irgendjemand von ihnen zum Professionellen Logikprogrammierer sich entwickelt. Die Rolle,
die diese Veranstaltung spielt, ist, das,
was universitäre Lehre generell machen sollte, nämlich die Perspektive Weiten,
den Blick weiten auf die Dinge, die es auch noch gibt,
damit man mal einen Eindruck davon bekommt,
was es abseits des Mainstreams auch noch für Möglichkeiten gibt,
dass man die Chancen, die sich diese Technik,
die diese Technik bietet, wenigstens kennt,
damit man sie im Ernstfalle einsetzen kann.
Die Informatik ist voller überraschender Entwicklungen,
teilweise auch voller Umwege und Neuauflagen alter Ideen und man ist nie sicher,
ob nicht morgen irgendeine dieser alten Ideen wieder hochpoppt und sich plötzlich zu einem Renner entwickelt.
Also mit der funktionalen Programmierung haben wir das zurzeit,
naja, es flaut auch schon wieder ein bisschen ab,
dass durch die Hochgradie Parallelisierung mit nicht Dutzend,
sondern tausenden oder zehntausenden von Prozessoren parallel nebeneffektfreie Programmierung plötzlich wieder ein ganz,
ganz hohes Gut wird.
Und zustandsorientierte Programmierung ist per se nicht nebeneffektfrei. Funktionale Programmierung,
es ist zu einem hohen Maße, Logikprogrammierung ist es fast vollständig.
Wenn wir nicht irgendwelche Ausnahmen dazu machen. Also in diesem
Sinne teilt die Logikprogrammierung mit der funktionalen Programmierung die Fähigkeiten,
Nebeneffektfreie Programme zu erzeugen, die sich dann für eine hochgradio Parallelisierung eignen.
Und im Zusammenhang mit dem Maperduce Paradigma ist also
die funktionale Programmierung jetzt plötzlich wieder en vogue,
weil ich damit eben riesengroße parallele Verarbeitungsprozesse betreiben kann
Die Logikprogrammierung, ups, ist wie kaum ein anderes Paradigma in
der Lage zwischen Methoden und Werkzeugen auszuwählen oder das zu üben.
Ich habe ein Verarbeitungsproblem gegeben, welche Werkzeuge sind eigentlich angemessen,
um dieses Verarbeitungsproblem zu lösen. Weil nämlich sehr,
sehr mächtige Werkzeuge als Basis der Programmiersprache Freihaus zur Verfügung stehen.
Dinge, die man in anderen Programmiersprachen immer erst extra programmieren
muss, die kriegt man hier freihaus.
Das sind im Wesentlichen ein bidirektionales Pattern-Matching,
was ist in der Form in keiner anderen Programmiersprache gibt.
Und das ist, dass die nicht deterministische Suche
ein Basiskonstrukt der Programmiersprache ist
Oder eigentlich besser des Verarbeitungsmodells ist. Auch das gibt es
in keiner anderen Programmiersprache oder in einem, sagen wir es so,
in keinem anderen Programm, hier Paradigma.
Denn es gibt natürlich andere Logikprogrammierungssprachen außer Prolook beispielsweise.
Ja, natürlich, wenn wir über Softwareentwicklung reden,
dann reden wir nicht nur über Programmierung, sondern müssen wir es auch machen.
Es ist der einzige Weg, Programmierung zu erlernen ist,
es tatsächlich praktisch zu betreiben.
Und deshalb werden die Übungen im Wesentlichen programmiertechnische Aufgaben behalten,
behandeln. Es gibt auch ein paar Sachen,
wo es um Konzepte und Begriffe geht,
aber dass der Kern der Aktivitäten ist tatsächlich
praktisches Programmieren
Gut, ja, ein wesentliches Merkmal der Logikprogrammierung ist auch,
dass sie es eine sehr,
sehr große Nähe zu den formalen Konstrukten der Informatik hat.
Also Dinge, die man sonst typischerweise in der Theorie wiederfindet,
die hat man hier, wenn nicht in der Programmiersprache selbst,
dann doch aber sehr dicht dabei.
Also man kann relativ schnell relativ komplexe und mächtige Werkzeuge realisieren.
Und insofern eignet sie sich wunderschön um die Brücke zwischen
der Theorie und der praktischen Informatik zu schlagen.
Da muss ich gleich am Anfang ein Geständnis machen.
Ich habe den Zugang zur Theorie auch erst über die
Logikprogrammierung erreicht
Ja, weil man dort plötzlich mit Dingen hantieren und experimentieren kann, die sonst trockene Papier- und Bleistiftaktivitäten sind.
Das macht den Reiz der ganzen Sache für mich zumindest aus. Gut,
wir haben einerseits die Vorlesung, wo es darum geht,
Konzepte vorzustellen, Begriffe zu klären, Zusammenhänge herzustellen,
Beispiele zu diskutieren, ergänzt durch das Selbststudium,
wo es eben so darum geht, dass sie anhand der Materialien,
die zur Verfügung stehen, sich selbst Dinge vertiefen oder weiterentwickeln,
dass sie tatsächlich praktische Programmierübungen machen, im Gegensatz zu SE1,
SE2, wird es hier keine Präsenzübungen am Rechner geben,
sondern Klassenraumübungen,
wo das, was sie zu Hause vorbereitet haben,
diskutiert und vorgestellt wird.
Lerngruppenarbeit ist gewünscht und befördert. In den Übungen selber
gibt es also einerseits Hausaufgaben,
andererseits Präsenzübungen und im Wesentlichen dient das also der gegenseitigen Verständnis
über Lösungen und Lösungsalternativen
Ja, das ist einer der Gründe, weshalb ich mich
seit Jahren standhaft weigere Musterlösungen, zu meinen Übungsaufgaben zu veröffentlichen.
Ich habe sie natürlich. Die Übungsgruppenleiter kriegen sie auch nicht. Aber was ich möchte ist,
dass in den Übungen Lösungen gegeneinander gehalten werden,
dass es nicht heißt, das ist die richtige Lösung,
sondern es gibt Lösungen, A, es gibt Lösungen B,
es gibt Lösungen C, es gibt Lösung D.
Jeder hat Vorteile und Nacht der Teile. Und diese Vor-
und Nachteile gegeneinander abzuwägen und gegenüberzustellen, das ist Aufgabe der Übung.
Also die idealen Übungen die ich bisher mitbekommen habe,
sind genau so abgelaufen, dass wir drei studentische Lösungen hatten.
Und dann habe ich gesagt, und jetzt hole ich auch
noch die professorale Lösung aus dem Kasten.
Und dann halten wir die Firma gegeneinander und gucken, welche davon sind besser.
Und nicht immer, ist natürlich immer die Frage,
was heißt besser, in welchem Sinne besser.
Nicht immer ist die Lösung des Professors die beste davon gewesen.
Es gibt durchaus, und das ist charakteristisch für die Programmierung immer,
es gibt viele Wege, die nach oben führen.
Die richtige Lösung gibt es nicht. Es gibt immer verschiedene
Lösungsalternativen und zwischen dem muss man abwägen können.
Auch das soll in den Übungen gepflegt werden
Gut, und dann etwas, was man üblicherweise vergisst,
ist die Rückkopplung. Ich bin sehr froh,
wenn sie mich auf Mängel und Schwachstellen dieser Lehrveranstaltung hinweisen.
Je eher, desto besser, desto eher haben wir eine Chance, sie abzustellen.
Wenn sie warten bis zum Evaluationsbogen, dann ist es zu spät.
Danach kann man nichts mehr ändern. Dann ist das Semester rum und
die Sache ist gelaufen. Also wenn sie irgendwo Bauchschmerzen haben,
wenn sie irgendwo sich unwohl fühlen, sagen sie es.
Dann können wir versuchen, das abzuschaffen.
Ich kann es natürlich nicht versprechen, aber zumindest versuchen kann
man es
Wir haben neun Übungsgruppen, und da sind wir beim allerersten Problem,
die reichen nicht aus, um sie alle unterzubringen.
Ich habe hier einfach mal, das sind nicht die ganz aktuellen,
die sind jetzt schon zwei oder drei Tage alt,
die Zahl der Anmeldungen aufgetragen.
Die Zahlen sind relativ wenig aussagekräftig,
weil man ja sich für Alternativgruppen anmelden kann.
Also die übliche Kapazität von 15 Plätzen deutlich übersteigen.
Aber es gibt ein ungefähres Bild davon,
wo das größte Gedränge ist. Also insbesondere diese beiden Mittwochfrühübungen scheinen
nicht ganz so stark nachgefragt zu sein,
wie alle anderen Übungen
Das ist erstmal ein grobes Bild. Deshalb jetzt die Frage,
wer von Ihnen ist noch nicht mit einem Übungsgruppenplatz versorgt?
Das sind weniger, als ich erwarte. Ihr könnt aber
die Hand richtig hochheben, ich brauche eine Zahl.
Gut, das sind elf oder zwölf Leute, habe ich gezählt.
Können wir mal einen ungefähren Durchgang machen,
für welche der Übungsgruppen Sie sich interessieren würden?
Also wir fangen von vorne an. Dienstag früh um acht
für Frühaufsteher. Okay, das sollte machbar sein.
Mittwoch früh um acht
Das sollte auch machbar sein. Mittwoch um zehn. Ja
, okay. Also das Prozedere ist jetzt das Folgende.
Wir müssen versuchen, irgendwie eine einigermaßen Gleichverteilung hinzubekommen.
Und im Wesentlichen obliegt es den Übungsgruppenbetreuern zu sagen,
ob es noch möglich ist, weitere Interessenten aufzunehmen oder nicht.
Wenn es in einzelnen Gruppen überlaufen ist,
dann muss der Übungsgruppenleiter auch die Notbremse ziehen können und sagen können,
nee, tut mir leid,
hier ist voll
Nun ist, wie die Erfahrung lehrt, der Übungsgruppenbesuch auch eine etwas dynamische Angelegenheit.
Typischerweise sind am Anfang mehr Interessenten da als am Ende.
Das heißt, das ändert sich auch über der Zeit.
Möglicherweise könnte es sein,
dass sie erstmal in den sauren Apfel beißen müssen,
eine Übungsgruppe zu nehmen, die nicht ganz so ihren Wünschen entspricht,
um dann später noch wechseln zu können.
Wenn sich die Nachfrage nach den Übungen möglicherweise geändert hat.
Okay, wir waren also hier mit vier Leuten
in der Mittwoch 10 Uhr,
Mittwoch 12 Uhr
Oh, das überrascht mich. Da hätte ich zwei Leute,
nur das, da hätte ich großen Anrang erwartet.
Ja, okay. Mittwoch 14 Uhr und Donnerstag 8 Uhr, Donnerstag 10 Uhr.
Aber das sieht ja ziemlich erfreulich aus. Also, meine Bitte,
gehen Sie in die Übungsgruppe, die Sie sich wünschen,
dass der einzelne Engpass, den ich jetzt sehe,
es war diese mittwochs 10 Uhr Gruppe. Gehen Sie dahin,
sprechen Sie mit dem Übungsgruppenleiter und das sollte bei dem,
also wenn da nicht irgendwo jetzt eine verborgene Anzahl von Studierenden auftaucht,
sollte das eigentlich gut machbar sein.
Dann, wenn das sich so umsetzen lässt, dann haben wir eigentlich kein Problem.
Jedenfalls kein ernsthaftes. Okay, die erste Übung nächste Woche,
das scheint ja auch in Stine drin oder das habe ich da eingetragen,
die Übungsaufaufgaben finden Sie immer auf einer Wiki-Seite unseres Arbeitsbereiches.
Und zwar, wenn Sie über den Arbeitsbereich Notz,
was für natürlich sprachliche Systeme steht,
links im Seitenmenü gucken und da Lehrveranstaltungen, Logikprogrammierung,
da finden Sie die Seite für die Veranstaltung Dort gibt es die,
ein kommentiertes, eine kommentierte Foliensammlung.
Wer bisher vergeblich versucht hat, auf die zuzugreifen,
seit einer halben Stunde ist sie dort verfügbar.
Da finden sich die Übungsaufgaben und dort werden wir dann auch gute,
gelungene Lösungen veröffentlichen. Das Schema oder das Modell,
was sich hier praktizieren möchte, ist, ja,
ich hatte schon angekündigt,
nicht die Musterlösung des Professors werden veröffentlicht,
sondern gelungene studentische Lösungen werden veröffentlicht
Da sind sie natürlich gefragt, ohne ihre Einverständnis kann da
nichts veröffentlicht werden. Da sind sie gefragt und gebeten,
dass sie das Einverständnis auch erteilen,
dass wir das dort tatsächlich publizieren können,
möglicherweise auch in alternativen Varianten, wenn wir mehrere deutlich unterschiedliche,
aber gelungene Lösungen zur Verfügung haben.
Ja, die Bearbeitung der Aufgaben überwiegend am Rechner,
aber unbetreut im Gegensatz zur SE1, SE2.
Es gibt zwei Möglichkeiten, sie können die Installationen im Rechenzentrum nutzen.
Das ist Variante Nummer eins oder Variante Nummer zwei ist,
sie installieren sich selbst ein Produktsystem.
Wir verwenden hier das Prologsystem aus Amsterdam von einem
interessanterweise geisteswissenschaftlichen Institut entwickelt und seit Jahren gepflegt und erweitert.
Das ist Open Source, nein, sorry, doch,
es ist auch Open Source, ja,
aber es ist auf jeden Fall frei verfügbar.
Den Link zum Download finden Sie auf der Seite der Lehrveranstaltung
Das gibt es also für Mac, für Windows und für Linux.
Da können Sie sich selbst eine Installation ziehen.
Ich möchte Sie dringlichst auffordern, dies möglichst bald zu machen.
Denn die Verfügbarkeit des Verarbeitungssystems ist essentiell für die Bearbeitung der Übungsaufgaben. Ja,
die Lösung, die Sie zu Hause erarbeiten,
sollen dann hier in der Übungsgruppe dann diskutiert werden,
damit einerseits der Übungsgruppen Betreuer sich darauf vorbereiten kann,
auf diese Diskussion und andererseits um die berühmten Punkte zu verteilen,
geben Sie bitte Ihre Lösungen per Mail beim Übungsgruppenleiter ab.
Und zwar so, dass es Dateien sind,
die man direkt in den Rechner laden kann.
Das mag bei dem ersten oder zweiten Zettel noch nicht so relevant sein,
aber spätestens ab dem dritten Übungsblatt ist es relevant.
Dass man tatsächlich eine Datei bekommt,
die man laden kann und die man ausführen kann,
damit man sofort testen kann, ob das,
was sie da gemacht haben, funktioniert oder woran es möglicherweise liegt,
dass es nicht funktioniert.
Sehr vorteilhaftes ist auch, wenn sie auf der Einreichung immer markieren,
was für welche der Lösungen sie tatsächlich oder für welche Aufgaben sie ihre Lösung vortragen wollen.
Das hilft bei der Planung des Übungsgruppenbetriebs etwas. Gut,
ich hatte schon angedeutet, Lerngruppenarbeit ist gewünscht und möglich,
aber nach oben hin limitiert.
Mehr als drei Studierende in einer Gruppe macht keinen Sinn,
dann wird der Grad des Trittbettfranz einfach zu groß,
damit alle aktiv mitarbeiten können,
gibt es eine sinnvolle Obergrenze und die
haben wir auf drei festgelegt
Ja, Übungsschein ist natürlich ein alter Begriff, aber die
erfolgreiche Teilnahme an der Übung wird durch drei Kriterien definiert.
Das erste ist eine Universitätsweite Regelung, regelmäßig aktive Teilnahme an
85 Prozent der Lehrveranstaltung, zählt da dazu.
Das ist, wie gesagt, nichts Veranstaltungsspezifisches,
das ist eine Universitätsregelung. Sie müssen von den möglichen Punktzahlen
mindestens 50 Prozent erreicht haben
Gemeinerweise variiere ich die Punktzahl pro Aufgabenblatt. Weil die ersten Aufgabenblätter
naturgemäß erstmal den Charakter haben, dass man in Tritt kommt.
Und die Aufgaben eher auf der Skala hin zum Trivialen trainieren.
Sie werden das sehen, wenn sie das Aufgabenblatt in der
Hand haben. Deshalb gibt es für die ersten Aufgabenblätter,
hups, weniger Punkte als für die späteren.
Das steigt also bis zum fünften Aufgabenblatt Linie A
an und bleibt dann konstant bei 30 Punkten pro Blatt.
Sodass also von vornherein klar ist,
ab diesem Grad hier ungefähr sind wir auf dem Schwierigkeitsniveau,
was dann auch für die Prüfung relevant ist.
Dann hat sich das Ganze mehr oder weniger eingespielt
Gut, dadurch ergibt sich, oh nein,
da müssen wir in diesem Jahr, das müssen wir,
das müssen wir noch modifizieren.
Das ist in diesem Jahr nicht aktuell, weil wir,
weil wir Ausfälle haben. Der DS Akademikus ist einer dieser
Ausfälle oder ist es der Ausfall? Ja, wir müssen,
wir brauchen eine Sonderregelung für eine DS Akademikus,
der fällt nämlich auf einen Mittwoch,
wenn ich richtig informiert bin und die meisten unserer Übungsgruppen sind an dem Mittwoch.
Da wäre ich aber zum gegebenen Zeitpunkt noch sagen,
wie wir damit umgehen werden.
Drittes Kriterium ist dann die aktive Teilnahme,
die im Wesentlichen dadurch passiert,
dass sie ihre Lösungsvarianten auch vor der Gruppe vortragen und
zur Diskussion stellen
Das ist ja ein wesentlicher Punkt unseres gesamten Übungsbetriebes,
dass hier sogenannte Soft-Skills auch trainiert werden sollen.
Und das ist im Wesentlichen der Punkt.
Es ist eben anders als in der Physik,
die so etwas nicht macht, wo die Übungsgruppenteilnahme vollkommen freigestellt ist.
Wir legen großen Wert auf diese Diskussion in der Übungsgruppe,
weil wir das als eine essentielle Fähigkeit eines Informatikers ererachten,
dass er in der Lage ist,
seine Lösungen verständlich zu präsentieren und zur Diskussion
zu stellen
Gut, zu dem Prologsystem, ich hatte es schon angekündigt,
das, was wir im Wesentlichen für die,
oder was, was ich empfehlen würde für die Übung.
Man kann jedes beliebige andere nehmen. Ist das sogenannte SWI und das
steht eben für Sozialwettenschappligen Innstituten oder irgend so etwas auf Niederländisch.
Und das kann man sich runterladen. Es hat ein integriertes Objektsystem,
das werden wir mal uns kurz angucken im Zusammenhang mit der Weihnachtsaufgabe,
wo es eher um Spaß als um Inhalt geht
und ist für die Zwecke der Lehrveranstaltung hier völlig ausreichend.
Alles,
was wir brauchen,
ist dort verfügbar
Wer ist etwas anspruchsvoller und professioneller haben will? Für den haben wir
eine Lizenz eines des Sixus-Prologs, was ein semiprofessionelles System ist.
Und dafür gibt es Studentenlizenzen. Also wer sich dafür interessiert,
wendet sich bitte an Michael König im Rechenzentrum,
der kann den Downloadkey mitteilen, dass man sich das System auch unterladen kann.
Gut, da gibt es dann zahlreiche Erweiterungen des eigentlichen Kernparadigmas durch Korotin,
durch Constraint Handling und so weiter. Das sind dann,
ist für Leute, die tief das Interesse in diesem Bereich haben.
Literatur gehe ich jetzt nicht darauf ein. Es gibt jede
Menge Literatur zur Logikprogrammierung. Die meisten davon sind etwas älter,
was nicht heißt, dass sie schlechter sind.
Im Gegensatz zu anderen Programmierparadigmen, also in der imperativen Welt
, jagt ja eine Modewelle die andere.
Das ist ja eigentlich charakteristisch für die Informatik. Wenn es gestern Java war,
muss es heute Ruby sein oder was auch immer,
oder fancy ist Skala zum Beispiel,
da kann man sich von der Masse abheben.
Verglichen mit diesem sehr, sehr schnell aufeinander folgenden Wellen von
Systemen und Aufwandprogrammiersprachen, ist die Logikprogrammierung erstaunlich stabil.
Ich bin jetzt nicht ganz sicher,
wann das Edinburgh Prologue aufgetaucht ist, aber es muss irgendwann
Ende der 70er oder Anfang der 80er Jahre des letzten Jahrhunderts gewesen sein.
Und seitdem ist das Ding in der Syntax- und in der Semantik stabil geblieben.
Da hat sich nichts mehr dran geändert im Kern.
Natürlich gibt es Erweiterungen, aber der Kern ist stabil geblieben.
Da hatte ich überhaupt nichts mehr dran geändert.
Davor gab es ein oder zwei Vorläufer, die hatten semantische
Unterschiede Oder aber so ein taktisch waren sie anders notiert.
Aber seitdem, der Adam Burrow Prolog Variante ist Produktkonstant stabil geblieben.
Und hat bis heute mit ganz, ganz kleinen Ausnahmen,
also eine Ausnahme müssen wir in der Vorlesung tatsächlich behandeln,
dass das gepunktete Park eingepunktetes Paar mehr ist.
Das ist eine Verbeugung vor der objektorientierten Programmierung,
weil man eben auch eine Notation wie in Java haben wollte,
wo ich die Methoden mit Punktsyntax angeben kann.
Dadurch ist der Punkt jetzt plötzlich anders verwendet worden,
als er es früher war.
Aber der Punkt ist dort sowieso nur noch eine Reminiszenz an
die frühen Liz-Varianten, aus denen das alles mal entstanden ist
Benutzt hat ihn eh keiner. Und deshalb ist der Verlust
relativ gut zu verschmerzen. Aber das sind so feine Nuancen,
die eigentlich der Normalnutzer überhaupt nicht mitkriegt.
Ansonsten kann man immer noch die alten Programme immer noch
da reinladen und sie werden weitgehend funktionieren.
Mit Ausnahme solcher eher technischen Dinge, dass sich
der Datentyp Zeichenkette irgendwann mal geändert hat.
Gegenüber einer logikorientierten Sichtweise früher zu einer mehr technisch orientierten Sichtweise
Gut, also was ich immer noch sehr empfehlen kann,
ist dieses Buch von Clocksin und Manage.
Das hat eben auch schon fünf Ausgaben hinter sich gebracht und
ist eine sehr, sehr gute Einführung in die Denkwelt.
Ich werde mich an keines dieser Bücher hier halten,
weil ich der Meinung bin,
dass eine gute Wissenschaft immer vom Einfachen zum komplizierten fortschreitet.
Dummerweise ist die Informatik keine gute Wissenschaft. Sie fängt immer
mit dem komplizierten an und endet dann irgendwann mal beim Einfachen.
Zum Beispiel, dass sie als allererstes die Leute mit
einer Programmiersprache konfrontiert,
die Turing mächtig ist
Warum eigentlich? Warum zuallererst alle Probleme,
die die Turing-Mächtigkeit mit sich bringt, präsentieren?
Warum nicht mit einfachen Modellen anfangen? Die Informatik hat die einfachen Modelle.
Sie nutzt sie nicht. Ein einfaches Programmiermodell wäre der endliche Automat.
Damit kann man einen einfachen Einstieg in die Informatik machen.
Die Physik macht das. Die Physik fängt mit der Mechanik an,
geht dann über die anderen klassischen Gebiete der Physik,
über die Quantenmechanik bis hin zur relativistischen Quantenmechanik.
Vom Einfachen zum Komplizierten, nicht mit dem Komplizierten
gleich Anfang
Die Informatik macht es, warum? Ich weiß es nicht genau umgekehrt.
Ich werde in dieser Vorlesung hier einen strikten Durchlauf vom Einfachen
zum Komplizierten machen. Wir fangen mit einem der einfachsten Modelle
an, was die Informatik zu bieten hat.
Das ist die relationale Datenbank. Und wir werden in zwei Schritten
ganz zwei Schritte, zusätzliche Mechanismen zur relationalen Datenbank hinzufügen.
Und dann haben wir eine voll ausgebaute Programmiersprache. Dieses Herangehen hat
eine Gefahr. Das wiegt sie lange Zeit in der Sicherheit.
Auch das ist ja alles ganz einfach. Das haben wir
ja alles schon mal irgendwo in GDB zum Beispiel gehört.
Der Schwierigkeitsgrad kommt dann durch die Erweiterung. Und spätestens,
das ist ein Erfahrungswert, ich möchte dich drohen,
spätestens wenn die Rekursion ins Spiel kommt,
Haben viele keinen Boden mehr unter den Füßen.
Und deshalb kann ich nur appellieren,
nehmen sie die trivialen Dinge am Anfang ernst.
Das ist sozusagen das immer wiederkehrende Training von Abläufen,
von Begriffen, von Konzepten und so weiter.
Wir werden sie brauchen, wenn es komplizierter wird.
Und es wird komplizierter. Keines dieser Bücher folgt diesem Schema
. Deshalb kann ich es nur empfehlen als Begleitlektüre,
aber es ist kein Ersatz für die Veranstaltung her
So, das ist dann der Durchlauf, den wir machen.
Ich werde also heute noch ein paar Vorbemerkungen zu Programmierparadigmen
im Vergleich machen, funktional, imperativ, logikorientiert.
Und dann kommt der Durchlauf, den ich eben angekündigt habe.
Wir fangen an mit den relationalen Datenbanken. Wir erweitern es
, um den Begriff der Deduktion. Wir werden hier bei
der Welt der Relationalen Datenbanken ist alles noch sauber.
Diese Mantik ist sauber. Das, was unser Programm logisch fordert,
ist immer noch das, was die Maschine tatsächlich ausführt.
Spätestens beim Einführen der Deduktiven Datenbanken fallen die beiden Sachen auseinander
Das ist in jeder Programmiersprache so. Aber ich habe hier eben
einen harten Kern, der noch eine saubere Semantik hat.
Das Ganze wird erst schwierig und problematisch in dem Augenblick,
wo ich die Erweiterung hinzunehme.
Also erster Schritt ist der von der relationalen zur deduktiven Datenbank.
Und zweiter Schritt ist, dass ich zusätzlich noch rekursive Datenstrukturen einführe.
Und nachdem ich das gemacht habe, habe ich eine vollständige
Turing-mächtige Programmiersprache. Alles andere sind nette Ergänzungen. Insbesondere,
dass wir für Listen und zwar verkettete Listen eine extra Syntax einführen,
damit, weil es einfach bequem ist,
damit zu arbeiten
Wir brauchen dann noch eine ganze Reihe von extra und metalogischen Prädikaten,
also so etwas wie das Interface zum Betriebssystem, drucken,
lesen und begleichen.
Das möchte man ja alles machen mit Programmen. Und wir brauchen noch etwas,
was sich Prädikate höherer Ordnung nennt, in Analogie zu dem,
was man in der funktionalen Programmierung als Funktion höherer Ordnung kennen.
Also Dinge, mit denen ich aggregieren kann und dergleichen mehr.
Wir machen dann einen Exkurs in die funktionale Programmierung,
insbesondere werden wir dort im Prolog ein Interpreter für Steam schreiben,
damit sie mal sehen,
wie man Meta-Programmierung im Logikparadigma betreiben kann,
sodass im Endeffekt
ein vollständiger Interpreter für eine funktionale Programmiersprache
dabei herauskommt
Und zwar werden wir es mit all den, sorry,
mit all den Besonderheiten,
die sie vielleicht schon kennen aus der funktionalen Programmierung,
mit Closures und so weiter tatsächlich umsetzen.
Wir machen dann wieder einen Sprung zurück in den Logikprogrammierungsteil.
Dieser Einschub ist deshalb hier etwas vorgezogen, weil ich die
funktionale Programmierung auch noch in einer Übung behandeln möchte.
Damit sie nicht in die Klausur gehen, ohne dass das
vorher jemals in der Übung gemacht worden ist.
Das hier hinten ist sozusagen die Creme der Logikprogrammierung.
Das werde ich in der Prüfung nicht ansprechen.
Das ist aber tatsächlich der Punkt,
wo man zeigen kann, dass Logikprogrammierung deutlich mehr kann als
andere Paradigmen
Das ist der Punkt, wo es mir heute noch immer so
ein bisschen kalt den Rücken runterläuft und wo ich mich frage,
wo ist eigentlich die Grenze zwischen Informatik und Magie?
Da steht man ein bisschen davor und sagt sich, wow,
was passiert da eigentlich? Gut, das ist der Ablauf,
den ich hier plane und dann können wir auch schon einsteigen.
Also es geht um Programmierparadigmen und hier ist mal kurz gegenübergestellt,
ein und dasselbe Programm.
Wir kommen gleich in die Details, für das Testen,
ob ein Element in einer Liste enthalten ist, ja oder nein.
Als emperatives Programm als funktionales Programm als Logikprogramm.
Details kommen gleich noch
Also wir haben so etwas wie Berechnungsuniverselle, Verarbeitungsmodelle.
Das sind eben die drei, die ich eben erwähnt habe.
Wir haben so etwas wie eingeschränkte Verarbeitungsmodelle, Eingeschränktheit deshalb,
weil sie eben nicht Turing mächtig sind.
Natürlich haben wir auch in relationalen Datenbanken immer eine Turing mächtige Programmiersprache mit dazu.
Aber das ist nicht die Datenbank. Das ist eine Programmiersprache
, die auf der Datenbank arbeitet. Das relationale Modell,
also dieses Entity Relationship Modell mit Selektion, Projektion und so weiter.
Dieser Kern der relationalen Datenbank ist nicht Turing mächtig.
Damit kann ich nicht universelle Berechnungen durchführen
Wir wissen ja, dass alles, was man berechnen kann,
kann man mit einer Touring-Maschine berechnen, aber nicht mit einer relationalen Datenbank.
Oder ein Tabellenkalkulationsprogramm ist zum Beispiel nicht Touring-mächtig.
Ganz einfach deshalb, weil die Größe der Tabelle beschränkt ist.
Und deshalb kann ich nicht beliebig tiefe Iteration oder Rekussion durchführen.
Es geht auch, aber es ist immer mit dem Kopieren
von Zellen verbunden und deshalb stellt es ein Problem dar.
Also imperative Verarbeitung heißt im Wesentlichen, es werden Anweisungsfolgen abgearbeitet,
die verändern den internen Zustand eines Automaten und veranlassen Ausgabeoperationen oder Einlesoperationen.
Also einfaches Beispiel ist eben, ich habe diese Mengenfunktionen,
in diesem Falle habe ich hier noch die Längenangabe gemacht und der Kern ist dieser Zyklus hier,
also über die gesamte Länge der Liste, vergleiche,
ob das jeweilige Listenelement das gesuchte Element ist und falls ja,
dann verlasst die ganze Funktion mit dem Wert True.
Andernfalls gib ein False zurück. Das Wesentliche ist hier also,
dass, oder machen wir erstmal den Kontrast zur funktionalen Programmierung,
dass es ein Scream-Programm, was im Wesentlichen das Gleiche tut,
mit dem großen Unterschied, dass jetzt dieses Konditional hier selbst einen Wert hat.
Das ist der Unterschied zur imperativen Programmierung. Hier,
diese IF-Anweisung hat keinen Wert. Die wird einfach abgearbeitet,
aber sie hat, ich kann von außen nicht abfragen,
ob sie war oder falsch ist oder so etwas.
Alles, was sie tut, ist ein Nebeneffekt hervorzurufen.
Eine Aktion auszulösen, die steht hier hinten. Hier wird
eine Aktion ausgelöst, während hier ein Wert ermittelt wird.
Es wird der Wert dieses Ausdruckes ermittelt.
Das ist der wesentliche Unterschied zwischen Imperativ und funktionaler Programmierung
Und der Wert dieses Konditionals, der bestimmt sich jetzt
eben aufgrund dieser Bedingungen, die hier angegeben sind.
Also wenn die Liste leer ist, dann ist der Wert False.
Wenn denn das gesuchte Element gleich dem ersten Element der Liste ist,
dann ist der Wert True und andernfalls rufen wir das Ganze rekursiv auf,
um die Restliste abzuarbeiten.
In der Logikprogrammierung zieht es wieder noch ein bisschen anders aus.
Wir haben jetzt hier ein logisches Deduktionssystem und wir wollen prüfen,
ob eine gegebene Wertekombination zu einer Relation gehört oder nicht.
Es gibt also eine Relation, die zwischen einer Liste,
das ist der Hint,
der zweite Argument und einem Element
definiert ist
Und für Paare von Elementen und Listen ist entweder diese Membership-Relation
wahr oder falsch. Also entweder das Element ist drin in
der Liste oder es ist nicht drin in der Liste.
Und wir wollen nur diesen Zusammenhang beschreiben. Wir wollen nicht beschreiben,
wie das eine aus dem anderen berechnet werden kann.
Wir sagen hier nur, wenn das erste Element der Liste
das gesuchte Element ist, ist alles in Ordnung.
Andernfalls muss das gesuchte Element in der Restliste enthalten sein.
Und das prüfen wir, indem wir das Ganze rekursiv aufrufen.
Wir beschreiben nur den Zusammenhang zwischen erstem Argument und zweitem Argument.
Dadurch, wenn das Erster Argument als erstes Element dieser Liste auftaucht,
dann sind wir zufrieden. Wenn das erst das gesuchte Element
in der Restliste irgendwo enthalten ist, ist es auch zufrieden.
Wenn nichts davon gilt, dann ist es eben nicht wahr.
Dann gehört dieses Paar nicht zu der Relation.
Das ist die Idee von relationaler Programmierung.
Und wir kommen gleich zu den Konsequenzen, die das hat.
Erst nochmal ein paar Erweiterungen. Es gibt also andere Berechnungsuniverselle
Verarbeitungsmodelle, das Objektorientierte wird oftmals als solches bezeichnet.
In meinen Augen ist es das nicht wirklich,
weil Objektorientierung mehr oder weniger ein Schema ist,
wie ich komplexe Softwareprogramme organisieren kann.
Die eigentliche Berechnung wird in dem
zugrunde liegenden Paradigma gemacht
Das ist entweder imperativ oder funktional. Die ersten objektorientierten Sprachen waren
reine funktionale Sprachen. Später sind es imperative Sprachen geworden.
Warum die Logikprogrammierung hier nicht auftaucht, das ist etwas,
worüber sie mal nachdenken können. Vielleicht werden sie am Ende der Veranstaltung
die Antwort darauf haben, warum das nicht gut zusammenpasst.
Also auch die, ich hatte es ja vorhin angekündigt,
für das SWI-Prolog gibt es ein Objektsystem, was dazugehört,
aber im Wesentlichen steht das Objektsystem neben dem Logikprogrammierungssystem.
Ich kann die wechselseitig aufrufen. Ich kann also von Prolog
aus sagen, ja hier, Gib mir diese,
oder wende diese Methode auf jenes Objekt an.
Das kann ich machen. Ich kann auf Form Objektsystemen aus Polock-Programme aktivieren.
Aber es ist nicht vollständig integriert. Und dafür gibt es
einen guten Grund. Ein anderes Modell ist etwa das konstraintbasierte Modell. Das hat große Ähnlichkeiten mit der Logikprogrammierung,
aber während die Logikprogrammierung alle Alternativen,
vaiablen Belegungen aufzählen muss und dadurch möglicherweise sehr,
sehr ineffizient ist, ist das konstraintbasierte Modell erheblich cleverer,
weil es durch Anwendung von Konstraints durch die sogenannte Beschränkungserfüllung testet,
welche Werte ich von vornherein rausstreichen kann,
welche ich gar nicht erst überprüfen muss,
weil sie sowieso zu keiner sinnvollen Lösung führen
Das wird beispielsweise in Grundlagen der Wissensverarbeitung behandelt.
Gut, weitere eingeschränkte Verarbeitungsmodelle. Ich hatte schon
den endlichen Automaten erwähnt, reguläre Grammatiken, reguläre Ausdrücke.
Das steht ja alles für das gleiche formale Konzept.
Das sind ja alles nur syntaktisch andere Notationen für die gleiche Modellklasse.
Tabellenkalkulationsprogramme hatte ich auch. Markabsprachen, die teilweise auch eine
operationale Semantik haben, dass sie damit rechnen kann.
Aber sie sind eben nicht berechnungsuniversell. Die relationalen Datenbanken
hatte ich auch schon erwähnt
Gut, wir kommen jetzt zu einem Begriff, der etwas problematisch ist,
weil nämlich jedes Verarbeitungsmodell in einer gewissen Weise eine Abstraktion darstellt.
Wir haben eine Maschine, eine Hardware, die hat einen bestimmten Befehlssatz,
mit dem ich Prozesse auf der Maschine abarbeiten kann.
Keine der Programmiersprachen benutzt direkt diesen Befehlssatz, sondern es sind heutzutage immer,
es gab mal die Zeit, als man das gemacht hat,
aber heutzutage sind da immer Interpreter oder Compiler dazwischen geschaltet.
Und diese kommen Piler und Interpreter, dienen oftmals dem Zweck,
die Komplexität dessen, was auf der Hardware-Ebene verfügbar ist, vom Nutzer abzuschirmen.
Wenn wir uns mit all den technischen Details auf der Hardware-Ebene
herumschlagen müssten, würden wir verrückt werden.
Dann würden die einfachsten Programme unsere Fähigkeiten übersteigen. Deshalb legen wir
Abstraktionsschichten oben drüber, sodass wir viele der Details da unten gar nicht sehen.
Wenn wir also jetzt irgendein komplexeres Werkzeug aufrufen aus unserer Programmiersprache raus,
nehmen wir mal an,
eine relationale Datenbank und dort ein Select und so weiter hinschreiben,
wenn wir das zu Fuß auf der Ebene der Hardware über all die Schichten virtueller Speicher,
realer Speicher und so weiter, tatsächlich durchhalten müssten,
dann würden die einfachsten Anfragen unsere Fähigkeiten übersteigen.
Es wird erst dadurch möglich, dass ich abstrahiere,
dass ich da ein Maschinenmodell einführe, was mit der Hardware
da drunter nur noch sehr bedingt etwas zu tun hat.
Was mir aber das Leben erheblich einfacher macht.
Also ich habe elementare Operationen des Prozessors,
bilde ich ab auf komplexe Operationen einer abstrakten Maschine.
Oder ich habe einen Speicher, möglicherweise auch einen virtuellen
Speicher und ich bilde ihn ab auf ein abstraktes Speichermodell.
Sowas wie ein Stack beispielsweise. Ein Stack ist ein abstraktes Speichermodell,
was oftmals gar keine direkte Entsprechung auf der Hardware-Ebene hat.
Und das wird, diese Abstraktion wird oftmals auch als Befreiung der Programmierung von Neumann-Stil behandelt.
Und die funktionale Programmierung und die Logikprogrammierung sind bei dieser Befreiung
von Neumann-Stil die radikalsten Ansätze, die wir haben.
Also wir haben bei den drei zugrunde liegenden Paradigmen ganz unterschiedliche, gerade von Abstraktionen.
Und ich gehe jetzt hier nicht in die Details,
es sollte nur deutlich sein, dass die Abstraktion,
die die Logikprogrammierung vornimmt, Radikalste ist.
Wenn ich in der funktionalen Programmierung an vielen Stellen noch die Hardware durchschimmern sehe,
bei der Imperativen, ich sehe teilweise direkt noch zur Verfügung habe,
ist das logikbasierte Modell von der Hardware vollkommen abgehoben.
Wir haben praktisch keinerlei Möglichkeiten mehr auf den realen Speicher zurückzugreifen,
sondern nur auf das abstrakte Speichermodell.
Denn das abstrakte Speichermodell ist eine relationale Datenbank. Das,
womit wir hantieren, ist in der Programmiersprache ist eine relationale Datenbank.
Und die hat mit dem physischen Speicher da unten nur noch sehr,
sehr wenig Gemeinsamkeiten. Und das betrifft jetzt alles,
was Sie hier auch sehen, und das ist eine
wesentliche Botschaft hier
Die Verarbeitung, dass sie von der Verarbeitungsrichtung abstrahieren kann,
das ist ein Alleinstellungsmerkmal der Logikprogrammierung.
Das geht in keinem der anderen Paradigma.
In der funktionalen Programmierung habe ich immer die Beschränkung,
ich muss von den Argumentwerten zum Funktionswert berechnen.
Ich kann niemals umgekehrt den Funktionswert vorgeben und sagen,
welche Argumente muss ich einfügen, damit der Funktionswert rauskommt.
Das funktioniert nicht in der funktionalen Programmierung. In der Logikprämierung
ist das genau das Ziel. Das, was wir erreichen wollen,
ist das gleiche Verhalten wie bei den relationalen Datenbanken.
Bei einer relationalen Datenbanktabelle ist nicht spezifiziert und das war der Grund,
weshalb relationale Datenbanken überhaupt erfunden wurden.
Da ist nie spezifiziert, was ist Input,
was ist Output
Ich kann jedem, also jedes Attribut als Input verwenden,
ich kann jedes Attribut als Output verwenden.
Und ich kann in beliebigen Kombinationen von Input auf Output Argumente schließen.
Und diese Grundhaltung der relationalen Datenbanken, die wollen wir
jetzt auf die allgemeine Programmierung, auf eine Doing-Mächte-Programmiersprache übertragen.
Das ist das Ziel hier dabei. Gut,
nun ist der Begriff der Abstraktion, ja ein bisschen belastet.
Wenn man ihn versucht zu definieren, dann kommt irgend so etwas heraus,
dass Abstraktion immer das Fahrvorheben bestimmter Merkmale von irgendeinem Gegenstandsbereich ist,
die jemand als relevant betrachtet und Gegenzug dazu, andere,
die als irrelevant betrachtet werden, vernachlässigt werden.
Damit ist schon mal wesentlich gesagt,
was relevant und was irrelevant ist, ist erstens subjektiv,
verschiedene Personen werden zu unterschiedlichen Einschätzungen dazu kommen und zweitens aufgabenspezifisch.
Was für die eine Aufgabe relevant ist,
muss für die andere Aufgabe noch lange nicht relevant sein.
Diese Abhängigkeiten bekommen wir sofort mit hinein. Deshalb ist Abstraktion
etwas Problematisches. Wir haben von vornherein eine Quelle für Missverständnis,
wenn unterschiedliche Personen unterschiedliche Dinge als wichtig erachten,
dann führt das möglicherweise zur Kommunikationsproblemen
Auf der anderen Seite haben wir kaum ein anderes Mittel,
mit dem wir die Komplexität nicht nur in der Informatik,
sondern in der Welt reduzieren können auf ein handhabbares Maß.
Nur durch Abstraktion kommen wir überhaupt dazu, dass wir über unsere Realität,
über unsere Welt, die uns umgibt, reden können.
Ansonsten könnten wir es nicht. Ja,
wenn ich diesen Raum beschreiben sollte,
wenn ich nicht das abstrakte Konzept Hörsaal hätte,
wie würden Sie das machen, was das hier ist?
Wie wollten Sie beschreiben, was ist das?
Was sind das hinten für komische Steine mit Löchern drin?
All solche Dinge müsste man beschreiben. Wir erachten die Steine
mit den Löchern da hinten als unwesentlich für das Konzept Hörsaal.
Wir sagen, es ist ein Hörsaal und jeder weiß,
dass es irgendetwas an der Hochschule,
was dazu dient,
größere Gruppen mit einer Vorlesung
zu versorgen
Das steckt in der Abstraktion des Begriffes Hörsaal alles drin.
Und wir brauchen es, um mit der Komplexität,
mit der wir umgeben sind, umgehen zu können.
Was ist völlig anders, wenn wir da rausgucken,
noch hängen sie da oben. Wie wollen wir über diese Blätter da
oben reden, wenn wir nicht den Begriff Laub hätten?
Damit können wir sagen, jetzt ist das Laub aber unten
und meinen damit alle Blätter sind runtergefallen.
Wenn wir das Konzept Laub nicht hätten, müssten wir anfangen aufzuzählen.
Blatt 1 ist unten, Blatt 2 ist unten, Blatt 3 ist unten.
Das ist für uns in Fleisch und Blut übergegangen.
Wir können nicht anders. Deshalb ist es sehr eigentümlich,
dass man sehr, sehr oft mit der Einstellung konfrontiert wird.
Das ist mir zu abstrakt, das kann ich nicht verstehen.
Falsch. Was nicht hinreichend abstrakt ist, kann ich nicht verstehen.
Ich muss es abstrahieren, sonst ist Verständnis per se
nicht möglich. Abstraktion ist das elementarste Grundlage dafür,
dass wir überhaupt etwas verstehen können über die Welt.
Und deshalb ist die unverzichtbar. Es ist eine zentrale Kulturleistung
des Menschen. Ich komme gleich noch auf Beispiele.
Sie ist aber, das hatte ich schon diskutiert,
Aufgaben bezogen und subjektiv und bietet sich daher als Missverständnis an.
Als Quelle für Missverständnisse an. Also, wie reduziert Abstraktion
Komplexität? Das soll hier ungefähr die Umrisse von Hamburg sein.
Das sind ungefähr die Umrisse von Berlin.
Die Größenverhältnisse stimmen leider nicht
Was ist die Entfernung zwischen Hamburg und Berlin?
Wie geben Sie das an? Wie kommen Sie darauf,
dass das 250 sind? Ja, ja,
gut, aber was messen Sie da eigentlich?
Sie können, dass die bittere Wahrheit ist,
eine Entfernung zwischen zwei Städten existiert nicht.
Das ist die bittere Wahrheit. Sie können unendlich viele Entfernungen
zwischen diesen beiden Städten angeben.
Überabzählbar unendlich viele.
Sie müssen
die Stadt abstrahieren
Dieses berühmte Beispiel, wir stellen uns vor, die Q sei eine Kugel.
Genau das müssen wir hier anwenden. Was der Physiker auch macht,
damit er Aussagen über die mechanischen Eigenschaften einer Kuh machen kann,
muss er sie abstrahieren zu einer Kugel.
Und das ist ein physikalisches Modell, mit dem er umgehen kann.
Wenn wir über die Entfernung zwischen zwei Städten reden,
müssen wir sie abstrahieren zur Punkten.
Nur dann können wir überhaupt über eine Entfernung reden.
Entfernungen sind nur zwischen Punkten definiert und nicht zwischen Flächen.
Wir machen das intuitiv. Wir müssen uns darüber bewusst werden,
dass wir es machen. Und in dem Augenblick,
wo wir auf den Rechner gehen, müssen wir uns
sehr besonderer Weise darüber bewusst machen oder müssen wir uns bewusst machen,
welche Vereinfachung wir eigentlich vornehmen.
Und das ist eben eine solche Vereinbarung, die wir vornehmen,
um die Komplexität der Welt auf ein handhabbares Maß zu reduzieren.
Wir wollen eben, um auszurechnen, wie viel Benzin wir
wohl brauchen werden oder wie lange die Reise dauern wird,
wollen wir eine Entfernungsangabe haben.
Aber um sie machen zu können, müssen wir das Problem
erstmal vereinfachen. Oder mit anderen Worten abstrahieren. Wir müssen
die Tatsache weg abstrahieren, dass Städte Flächen sind.
Für andere Aufgabenstellungen ist eine solche Abstraktion totaler Nonsens.
Wenn ich Bebauungspläne aufstellen will, dann nutzt es mir nichts,
eine Stadt auf einen Punkt zusammenschnorren zu lassen.
Dann kann ich keinen Bebauungsplan mehr aufstellen
Abstraktion ist auch eine Kulturleistung. Das kann man,
glaube ich, am besten am Handel sich veranschaulichen.
Wenn früher eben wahre gegen Ware gehandelt wurde,
da musste man eben mit seiner Ziege auf den Marktplatz,
um die dort gegen fünf Hühner tauschen zu können,
ist dann später wahre gegen Material wert getauscht worden.
Das war der Zeit, als die Münzen noch in Gold
gegossen worden sind und man mit der Münzwaage ermitteln konnte,
was ist der Materialwert der Goldmünzen,
die da gerade auf dem Tisch liegen und erscheint einem der Materialwert der Goldmünzen,
angemessen für das Tauschobjekt,
was ich
da erwerben will
Später ist der Materialwert der Münzen
ersetzt worden durch einen Nominalwert, also unsere heutigen Münzen.
Da hat der Nominalwert nichts mehr mit dem Wert des Metalls
zu tun, sondern das ist eine ideelle Größe.
Und der letzte Schritt ist, dass wir heutzutage
gar keine Münzen mehr austauschen, sondern nur noch Bits.
Wir schieben nur noch Zahlen über ein Draht hin und her,
von echtem Geld, in diesem Sinne ist gar nicht mehr die Rede.
Es ist sogar schon davon die Rede,
dass das Bargeld vollkommen abgeschafft werden soll.
Dann ist der letzte materielle Rest des Tauschgeschäftes weg.
Dann werden nur noch Informationen hin und her geschickt
Das ist Abstraktion über die Geschichte,
wie man immer mehr von dem Materialwert abstrahiert hat,
Schritt für Schritt für Schritt, bis gar nichts mehr davon übrig bleibt.
Und der Verrechnungswert einer Überweisung hat eben auch nichts mit der Energie zu tun,
die ich brauche, um die Bits über die Leistung zu schicken.
Als Quelle von Missverständnissen passiert ganz, ganz oft den VM von Maseinheiten,
wenn ich also irgendwie nur eine Temperaturangrabe von 25 Grad habe,
dann weiß ich eben nicht, welches System ist das?
Sind das gerade Fahrenheit? Sind das gerade Celsius? Vielleicht kann ich
es aus dem Kontext erschließen, aber nicht immer sich nicht immer sicher.
Messe ich Entfernung in Meilen oder in Kilometern, wenn ja,
wenn Meilen dann, welche Art von Meilen sind,
das Seemeilen, Landmeilen oder sonst was.
Wenn ich ein Verkehrsschild habe, auf dem einfach nur eine große 30 steht,
dann brauche ich auch den Kontextinformationen dazu,
welche Art von Maßeinheit steckt da dahinter.
Und ach so, ja, welche Meilen, das hat
man schon diskutiert. Die Frage ist tatsächlich äußerst relevant,
immer wieder das meist zitierte Beispiel dafür,
an dieser Frage ist mal eine Marssonde zugrunde gegangen,
wo man genau diese unterschiedlichen Sichten der Entwickler auf das Problem gesehen hat.
Die einen haben die Entfernung zum Mars in Yards ermittelt und
die andere haben die Entfernung zum Mars in Metern ermittelt.
Dummerweise ist das beides nicht miteinander kompatibel gewesen,
die Sonne ist auf der einen Seite des Marses verschwunden und
auf der anderen Seite nie wieder aufgetaucht.
Wahrscheinlich ist sie irgendwann mal runtergekracht in dem Glauben,
sie ist noch weiter oben.
Ganz einfach, eine Abstraktion, warum nämlich?
In welchem Programm schleppt irgendjemand Maßeinheiten mit? Hand aufs Herz
hat irgendjemand schon mal ein Stück Software geschrieben,
wo die Maßeinheiten in der Software drinstehen
Ich glaube nicht. Aber genau das müsste man machen,
um das zu vermeiden. Dann kann man
Typ sicher programmieren und dann kann man sicher sein,
dass diese Angabe jetzt hier eben nicht in Meilen erfolgt,
sondern in Kilometern erfolgt.
Macht aber keiner. Aus Bequemlichkeit macht es keiner. Die
Mittel dazu haben wir. Das ist genau die Stelle,
wo die Missverständnisse auftreiten. Wir reduzieren eine physikalische Maßeinheit auf einen numerischen Wert,
ohne dazu zu schreiben, wie dieser numerische Wert zu interpretieren ist.
Und dann ist klar, dass Person A das anders verstehen
kann als Person B. Mit solchen Missverständnissen müssen wir dann leben.
Und manchmal haben sie eben
fatale Folgen
Also in der Softwareentwicklung haben wir verschiedene Arten von Abstraktionen.
In vielen Fällen ist es Abstraktion vom Einzelfall,
dass wir generelle Zusammenhänge aufschreiben wollen,
weil wir nicht alle Einzelfälle aufzählen können, zum Beispiel.
Wir wollen von irrelevanten Aspekten abstrahieren. Das ist meistens dann
ein Anwendungsbezug. Zum Beispiel das Weglassen der Maßeinheiten kann ein
solcher Anwendungsbezug sein. Ich schreibe ja mein Programm für den
kontinattaleureuropäischen Kontext und nicht für die britischen Inseln.
Was aber, wenn das Programm dann doch mal da eingesetzt wird.
Das wissen Sie nicht zum Entwicklungszeitpunkt
Zumindest müsste kommuniziert werden. Halt, halt, halt,
halt. Das ist ein Programm, was unter diesen Umständen
nicht eingesetzt werden kann. Das ist übrigens ein Kontextualisierungsproblem,
was zum Abschluss einer iranischen Verkehrsmaschine geführt hat.
Weil die Besatzung auf dem amerikanischen Raketenkreuzer nicht einschätzen konnte,
die Lage bezüglich des Wellenganges oder irgendein Problem ist das gewesen.
Ich kriege die Details jetzt nicht mehr zusammen.
Aber solche Konsequenzen kann das haben
Und das habe ich auch schon angesprochen,
die Abstraktion von der Hardware, um Komplexität zu reduzieren.
Hier ist das erste Beispiel für die Generalisierung.
Also ich möchte weg vom Einzelfall hin zur Beschreibung komplexer Zusammenhänge.
Das sieht trivial aus, aber das ist die Essenz der Informatik.
Anstatt eine solche Tabelle aufzuschreiben, ich habe einen Inputwert,
ich habe einen Output-Wert, 1 wird auf 1 abgebildet,
2 auf 4, 3 auf 9,
schreibe ich eben ein Programm, was mir die Quadratwurzel,
sorry, das Quadrat einer Zahl berechnen kann.
Und dann kann ich damit unendlich viele Zahlen verhandeln,
hier habe ich immer eine endliche Menge, aufzählen muss.
Im Analogie dazu benutze ich hier auch den Begriff der relationalen Abstraktion. Wenn ich also weiß,
dass die eine Amsel ein Vogel ist, ein Star,
ein Vogel ist, eine Maiseinvogel ist und eine Amsel fliegen kann,
ein Star fliegen kann und ein Maides fliegen kann,
dann kann ich auch eine solche Regel schreiben und sagen,
was immer ein Vogel ist, kann auch fliegen.
Natürlich gibt es Ausnahmen zu dieser Regel,
aber wenn ich ausnahmenfreie Welten haben will,
muss ich in die Mathematik gehen wie hier,
in dem Augenblick, wo ich in der realen Welt bin,
habe ich immer Ausnahmen,
aber ich kann natürlich ähnliche Dinge auch aus der mathematischen Welt
auf relationale Art und Weise beschreiben
Vergröberung ist ein wesentliches Mittel. Ich lasse
Sonder- und Ausnahmefälle weg, betrachte also nur die Regelfälle,
wie wir das eben bei den Vögeln gemacht haben.
Ich lasse die Maßeinheiten weg, haben wir schon diskutiert.
Ich lasse Ausgabeinformationen weg. Ich teile also dem Nutzer nicht
alle Dinge mit, wie eine Berechnung zustande gekommen ist,
beispielsweise. Und dann ist die Ausgabe eine isolierte Zahl.
Und diese isolierte Zahl ist wieder der Interpretation ausgeliefert.
Das kann jeder auf seine individuelle Art interpretieren. Ich lasse
zum Beispiel Kommentare weg. Auch das ist eine Vergröberung,
dass ich eben an mein Programm nicht daran schreibe,
was ich mir eigentlich gedacht habe,
als ich das Programm geschrieben habe
Und das mache ich meistens in Abhängigkeit von einem intuitiven Gefühl,
was ist wichtig für meine Aufgabe, was ist unwichtig.
Nur leider Gottes, diese Bewertung ist immer subjektiv.
Das ist ihre Sicht auf die Dinge. Nicht die Sicht
der anderen Person. Und deshalb ist die Kunst der Informatik
immer die Kunst, sich in die andere Person zu versetzen.
Und das ist der Grund, weshalb wir kommunikativer Kompetenz,
ein so hohes Maß zu ordnen,
damit man das mal am konkreten Fall gesehen hat, wo verschieden Sichten aufeinanderprallen.
Und ein anderer, das Das gleiche Stück Code,
völlig anders interpretieren kann als man selbst.
Dass es hier ungefähr dargestellt, wir haben also immer die Situation,
dass wir irgendein Anwendungsproblem haben und für dieses Anwendungsproblem irgendeine Berechnung durchführen wollen.
Und dazu brauchen wir ein Programm. Das Programm ist
von anderen Leuten geschrieben worden. Wir haben also Leute,
die das Programm geschrieben haben, wir haben Leute,
die die Informationen in das Programm eingeben und wir haben Leute,
die die Informationen aus dem Programm entnehmen.
Und wenn die nicht auf allen Ebenen, über Einstimmung bezüglich
ihrer Abstraktionen getroffen haben, dann bricht das Ganze zusammen.
Und dann liest man in der Zeitung,
die EDV hat versagt
Nein, hat sie nicht. Die EDV hat richtig funktioniert.
Was versagt hat, sind die Menschen, die hier,
hier und hier sitzen. Die konnten nicht miteinander reden.
Das ist das Problem. Irgendwo ist Informationen verloren gegangen auf diesem Weg.
Dass irgendjemand entweder der hier unten oder der hier oben der Meinung war,
diese Information ist doch gar nicht relevant für das Problem.
Ist sie manchmal eben doch. Und das kann man eben jetzt wieder noch unterscheiden. Einerseits,
ich muss erst mal immer einen ersten Abstraktionsschritt machen und einen zweiten Abstraktionsschritt,
also der erste Abstraktionsschritt heißt,
aus dem Anwendungsproblem überhaupt erst mal die relevante Information entnehmen.
Und der zweite Abstraktionsschritt ist, diese relevante Information so
zu kodieren, sie korrekt ins System eingegeben wird.
Das sind also immer noch mal zwei Schritte,
die wir auf allen Ebenen haben. Aber jedenfalls wichtig ist,
dass diese Abstraktionen, die dort verwendet werden, miteinander harmonieren.
Das muss sichergestellt werden. Verbergen ist auch eine Abstraktionsleistung,
vielleicht bei der eine Informatik massiv Gebrauch gemacht wird.
Wenn ich im Speicher eine solche Sequenz von Nullen und Einsen habe,
dann sagt mir diese Sequenz von Nullen und Einsen eben nicht,
wie das zu interpretieren ist.
Ob das eine ganze Zahl ist, also eine Intagerzahl ist,
ob das ein Maschinenbefehl ist, ob das eine Speicheradresse ist,
ob das ein Buchstabenpaar ist.
Ich sehe es dem Ding nicht an
Da steckt im Code nicht drin. Und genau das ist
der Grund, der dann zum Segmentation Falt führt.
Ich interpretiere eben plötzlich etwas als Speicheradresse, was gar keine Speicheradresse ist.
Und dann crasht das Programm. Und das passiert oft genug
. Und da macht sich jetzt plötzlich ein hochgradige Abstraktion von
der Hardware bezahlt. In dem Augenblick,
wo das Programm gar nicht mehr weiß, ob es sich,
oder wo es überhaupt keinen Zugriff mehr auf Speicheradressen hat,
bin ich vor solchen Schäden geschützt.
Wir werden hier ein Programmierparadigma kennenlernen,
was hochgradig von Point an Gebrauch macht.
Sie kennen alle das Problem Nullpointer Acception und so weiter.
Wer hat die Fehlernachricht
noch nicht gesehen
Eine Fehlernachricht, die hier nicht auftreten kann. Wir haben
eine Programmiersprache, die massivst von Point dann Gebrauch macht,
ohne dass sie im Verlauf dieser Lehrveranstaltung ein einziges Mal
eine Chance haben auf den Point dazuzugreifen.
Es geht natürlich, wenn man will,
kann man auf der Maschine alles machen.
Aber zum Kern der Logikprogrammierung gehört es nicht,
dass da mit Point dann gearbeitet wird.
Es wird auf einer abstrakten Maschinenebene und dass da Point da
unten drunter liegen, das sehen sie nicht.
Sie kriegen sie nie zu Gesicht. Sie können sie
auch nicht ändern. Und deshalb haben sie keine Chance,
die Speicheradressen falsch einzugeben. Das ist das Privileg des Systems
. Und da wacht das System peinlichst darüber,
dass dieses Privileg nicht gebrochen wird
Das lässt sie an die Stellen nicht ran. Und damit ist
eine ganze Klasse von Fehlern einfach unmöglich in diesem Paralletbar.
Das ist einer der großen Vorteile. Ja, Abstraktion durch Verbergen.
Ich möchte also viele Details der Hardware nicht nach oben poppen lassen,
sondern ich möchte sie unter einer Decke halten,
sodass der Programmierer damit überhaupt nicht konfrontiert ist.
Also eines, der aus der Historie der Informatik
ein prominentes Beispiel ist, das GoTo.
Das GoTo geht auf einen Befehl zurück,
der heute in jedem Assembler, also in jedem Maschinenbefehlkatalog
drinsteht.
Das ist die bedingte Sprunganweisung
Ja, die ist in frühen Programmiersprachen wie Fortran und
so weiter einfach Ende der Veranstaltung.
Okay. In frühen Programmiersprachen wie Fortran ist die einfach
von der Maschinenebene auf die Programmiersprachenebene gehoben worden.
Um 1980 hat Etzgar Deikstra entdeckt, dass es gar nicht
nötig ist, diese Sprungbefehle zu benutzen.
Ich kann immer ein äquivalentes Programm angeben, was vollständig auf
diese Befehle verzichtet. Das ist ein Durchbruch in der Informatik
gewesen,
nannte sich damals
strukturierte Programmierung
Auf diese Art und Weise kann ich solche Sprungbefehle vermeiden und
damit verschwindet eine ganze Klasse von Programmierfehlern.
Denn dieses, das steht's da? Ja,
Go-2-Concided Harmful, das ist der Titel eines Papers gewesen,
was er damals publiziert hat. Wenn ich nämlich ein Go-Topes verwende,
springe ich in einen Teil eines Programms, dessen Kontext ich nicht kenne.
Ich springe aus einer Situation in eine andere Situation und weiß überhaupt nicht,
ob die Kontextbedingungen miteinander verträglich sind. Und das hat so richtig schweren Programmierfehlern geführt,
dass Leute eben gesagt haben, ach ja, klar,
da muss ich einfach nur noch mal dahin springen und dann macht es das,
arbeitet das noch mal ab, ohne zu bedenken,
dass da jetzt andere Voraussetzungen vorliegen und dass das dann in Einzelfällen zu gravierenden Programmen hier Fehlern fühlen kann.
Und deshalb ist das gerade zu einem Befreiungsschlag gewesen,
dass er da extra entdeckt hat, ja,
das brauche ich eigentlich gar nicht.
Die ganze Fehlerquelle kann ich eliminieren, ohne dass ich einen Verlust alleine.
Oder eben das andere ist Wertemanipulation als Fehlerquelle,
Wertemanipulation als die essentielle Operation des Imperativen Paradigmas.
Darauf basiert genau das imperative Paradigma, dass ich Speicherzellen habe,
deren Jetzt ist aber genug.
Dass ich Speicherzellen habe, deren Wert ich verändere.
Und dann ist das Berechnungsergebnis vom Zustand der Maschine abhängig.
Und diese Zustandsabhängigkeit ist ein Problemfall. Weil ich jetzt
die Bedeutung des Programmes hängt von nicht sichtbaren Eigenschaften ab.
Und das bezeichnet man dann als Seiteneffekt. Und es gibt
verschiedene Möglichkeiten mit solchen Seiteneffekten umzugehen. Die Objektorientierung versucht es einzudämmen,
indem sie die Speicherinformation kapselt in Objekten,
wo ich nur über definierte Schnittstellen überhaupt noch Zugriff habe dazu.
Das heißt, ich kann nicht beliebig die Werte verändern,
sondern nur über ganz wohldefinierte Schnittstellen
Und damit habe ich lokale Gültigkeitsbereiche und das Problem ist entschärft,
aber nicht beseitigt. Die funktionale Programmierung versucht, die Wertemanipulationsmöglichkeiten zurückzudrängen.
Wer immer bei Frau Dreschler funktionale Programmierung genossen hat,
wird sich an Set Bang erinnern.
Dass Bang da hinten ist nur eine Warnsignal,
verwende dies möglichst nicht. Oder wenn du es verwendest,
musst du dir sicher sein, was du tust.
Das ist mit Zurückdrängen von Wertemanipulationsmöglichkeiten genannt. Ich kann es
immer noch tun, aber ich werde gewarnt,
dass es eine riskante Angelegenheit ist,
die ich gerade mache
Was Scream immer noch hat, sind nicht lokale Gültigkeitsbereiche.
Und das ist eine weitere Fehlerquelle. Alle beide sind in
der Logikprogrammierung beseitigt. Es gibt sie nicht mehr.
Auch die Wertemanipulation ist ein Privileg des Verarbeitungsmodells.
Sie als Programmierer kommen da nicht ran. Jedenfalls nicht,
wenn sie die normalen Mitteln verwenden. Da müssen sie tatsächlich
hinter die Kulissen gehen. Dann geht das. Und es
werden extrem lokale Gültigkeitsbereiche eingeführt. Also das ganze Verschattungsproblem,
was wir in der funktionalen Programmierung haben,
eine variable verschattet die andere und so weiter.
Es existiert hier nicht. Es geht gar nicht
Weil die Gültigkeitsbereiche sehr eingeschränkt sind,
einer der wichtigsten Sätze dieser Veranstaltung wird lauten,
der Gültigkeitsbereich einer Variablen ist die Klausel.
Und die Klausel ist üblicherweise ein sehr, sehr kurzer Programmabschnitt.
Gut, also Abstraktion ist ein wesentlicher Bestandteil der Softwareentwicklung.
Wir kommen nicht aus ohne Abstraktion. Wir müssen es machen.
Wir können nur auf die Art und Weise an überschaubare und kompakte
Programmlösungen oder Problemlösungen. Ansonsten wird das Ganze unübersichtlich und unüberschaubar
. Es kann aber leicht zur Fehlerquelle werden.
Das heißt, wir müssen bewusst mit den Abstraktionen umgehen.
Und das heißt, im Wesentlichen Sorgfalt bei Entwicklung und Dokumentation.
Bei der Entwicklung müssen wir uns selber Rechenschaft darüber ablegen,
was haben wir jetzt alles nicht beachtet und was könnte ein anderer in das,
was wir getan haben, hinein interpretieren.
Und wir müssen das, was wir uns gedacht haben,
tatsächlich dokumentieren und zwar gründlichst. Und einer der wichtigsten Elemente
ist der Programmkommentar. Der geht nämlich an die anderen Entwickler,
die mit dem Programm mal weiterarbeiten sollen.
Und damit haben wir einen Paradox, ein echtes Paradox der Programmierung.
Wir müssen dokumentieren, woran wir nicht gedacht haben
Das macht Programmierung so schwierig. Wir müssen aufschreiben,
all die Dinge, die wir übersehen haben,
an die wir bewusst oder unbewusst nicht gedacht haben, die müssen wir dokumentieren.
Und das ist ein ernsthaftes Problem. Das ist ein Paradox,
da gibt es auch keinen Ausweg daraus. Da gibt es
nur eins Selbstdisziplin und immer wieder sich selber in Frage stellen.
Und diskutieren mit anderen, kommunizieren,
das ist die einzige Möglichkeit, die wir haben,
dass unser Softwareprodukt anderen zur Diskussion stellen,
damit sie sich dazu äußern,
ob sie das auch als Lösung akzeptieren würden oder ob
welche Mängel sie da drin entdecken
Und auf die Art und Weise sind dann eben solche Softwareentwicklungsmethoden,
wie kommen Codewalk oder so entstanden,
wo in der Arbeitsgruppe jeder seinen Code vorzustellen hat,
damit die anderen das nachvollziehen können, ob das plausibel ist,
was da gemacht worden ist oder nicht.
Das sind so elementare Formen, das Qualitätsmanagement in der Softwareentwicklung
oder per Programming ist genau das Gleiche.
Zwei Leute sitzen auf der Maschine und der,
der nicht die Tastatur hat, muss dem anderen eben erklären,
was er sich gerade dabei gedacht hat.
Und der hat zu überprüfen, ob dies stimmig ist oder
ob man da was übersehen hat oder ob da Inkonsistenzen
drin sind
Die relationale Abstraktion ist insofern radikal, das hatte ich
vorhin schon angedeutet, dass sie auch vom Algorithmusbegriff abstrahiert.
Und das ist nun, haut fast den Boden aus,
denn in einer klassischen Informatikvorlesung steht am allererstens am Anfang immer der Algorithmusbegriff.
Was ist ein Algorithmus? Da können Sie jedes Lehrbuch zur Informatik hernehmen,
da steht auf den ersten 20 Seiten irgendwo mal,
was ist ein Algorithmus?
Es ist das Ziel der Logikprogrammierung,
den Begriff des Algorithmuses nicht zu beseitigen,
sondern auch den Algorithmus in eine Ecke zu drängen,
wo sie
keinen Zugriff mehr drauf haben
Auch hier gilt, ja, wir werden in dieser Veranstaltung
auch uns angucken, wie wir das machen können,
um dann doch die Algorithmen wieder in den Griff zu bekommen.
Aber standardmäßig erstmal ist es nicht. Und der Trick wird,
ich erkläre gleich, wie es gemacht wird.
Klassischerweise ist also ein Algorithmus immer beschrieben,
als ein dynamischer Ablauf, möglicherweise parametrisierbar. Der hat Eingabedaten,
der produziert Ausgabedaten und dann gibt es irgendeinen prozeduralen Ablauf,
der die Eingabedaten in die Ausgabedaten überführt.
Das ist die klassische Sicht auf den Algorithmus.
Was Logikprogramme machen, ist eben nicht mehr,
sie haben keine Eingabedaten, sie haben keine Ausgabedaten,
alles kann Eingabe sein,
alles kann Ausgabe sein
Und sie beschreiben nur den generellen Zusammenhang zwischen diesen Datenstrukturen,
die dort auftreten. Und deshalb lassen sie prinzipiell beliebige Verarbeitungsrichtungen zu.
Und deshalb wird der Algorithmusbegriff hier erst mal in Frage gestellt,
weil ich eben die Grundelemente eines Algorithmuses gar nicht mehr habe.
Ich greife ins Leere. Die Frage ist, wo bleibt der Algorithmus dann?
Aber er kann ja nicht weg sein. Irgendwo muss er
ja abgeblieben. Aber wir gucken uns erst mal das Beispiel an.
In der funktionalen Programmierung habe ich immer eine Richtungsabhängigkeit drin.
Also wenn ich diese Definition der Member-Funktion habe,
muss ich diese beiden Vagablen Aufruf der Funktion gebunden haben.
Wenn eine dieser beiden keinen Wert hat, scheitert die ganze Abarbeitung.
Dann wird irgendwann mal eine Fehlernachricht kommen, dass eben Liste
keinen Wert hat oder das Element keinen Wert hat.
Das ist das, was passiert wird. Das heißt,
ich habe nur eine einzige mögliche Verwendungsweise dieses Programms und das sagt,
gib mir ein Element, gib mir eine Liste und ich werde dir ein Wahrheitswert zurückliefern.
Das ist die Signatur dieses Programms. Und ich kann also aufrufen,
ist A-Element der leeren Liste und ich kriege als Wert ein Forz zurück.
Ich kann aufrufen, ist B ein Element der Liste ABC und
ich kriege ein True als Funktionswert zurück und ich kann testen,
ob D ein Element der Liste ABC ist und ich kriege ein Fords zurück.
Jetzt der Blick in die Logikprogrammierung, das ist das äquivalente Programm,
wie das, was das genau, was das alles bedeutet hier,
das kriegen wir wahrscheinlich noch vor Weihnachten irgendwann mal.
Erste Variante ist wie die funktionale Programmierung. Gib mir ein Element,
gib mir eine Liste und ich liefere dir einen Wahrheitswert.
Also ich rufe auf, ist A, dass ein Element
der leeren Liste und ich kriege ein False zurück vom System.
Heißt, diese Aussage ist nicht wahr. Und oder ich
liefer Ich gebe ihm ein B und eine Liste A,
B, C und ich kriege ein Systemreaktion ein True zurück.
Nicht als Wert, das ist nicht der Wert dieses Ausdrucks,
sondern es ist der Wahrheit, ja, es ist die Systemreaktion,
ja, also das ist immer der Input,
den ich hier habe, hinter dem Promptzeichen.
Und das ist der Response des Systems. Gut,
und wenn ich eben teste, ob D in der Liste A,
B, C enthalten ist, kriege ich wieder ein False zurück.
Das ist genau das Gleiche, was wir auch in der
funktionalen Programmierung hatten. Und jetzt kommt der Mehrwert.
Ich kann also auch eine zweite Aufrufvariante angeben,
die sagt, gib mir eine Liste und ich verrate dir
alle Elemente,
die sie enthält
Also, ich rufe beispielsweise auf,
welche Elemente enthält die leere Liste und ich kriege ein Falls heraus,
was heißt, es gibt keine Elemente in der leeren Liste.
Oder ich rufe auf, gib mir alle Elemente,
die in der Liste ABC enthalten sind und ich kriege als Alternativen die Informationen,
eh, kann entweder A sein oder B sein oder C sein.
Wenn ich das machen will, brauche ich einen Mechanismus und mit alternativen Lösungen umzugehen.
Und das ist auch ein Alleinstellungsmerkmal der Logikprogrammierung.
Lösungen sind nicht eindeutig oder müssen nicht eindeutig sein.
Ein Funktionswert ist per mathematischer Definition
immer eindeutig gegeben
Ein Berechnungsergebnis hier kann Alternativen zulassen. Also für einen Prozeduraufruf
kriege ich hier drei alternative Berechnungsergebnisse. Und das ist in
meinen Augen ein großer Vorteil. Sie werden vielleicht sagen,
ja, das macht die Sache unnötig, kompliziert.
Es zwingt den Entwickler in Alternativen zu denken.
Und das ist etwas, was wir bitter nötig haben,
weil wir gemäß unserer psychischen Ausstattung dazu fast verdonnert sind,
immer eindeutige Lösungen zu suchen.
Und in diesem Sinne, sorry, das tut mir leid,
aber hat klassische Programmierung und auch die funktionale Programmierung
in meinen Augen etwas von einem Kindergartenverhalten
Die Welt wird auf eine Art und Weise simplifiziert,
wie sie nicht ist. Während die Logikprogrammierung die Vielfalt
der Welt und die Alternativen Interpretationsmöglichkeiten tatsächlich explizit sichtbar macht.
Insofern halte ich das für ein ganz entscheidenden Vorteil,
wenn man durch das Paradigma gezwungen wird,
sich über diese Alternativen immer Rechenschaft abzulegen.
Dritte Aufrufvariante, gib mir ein Element und ich werde dir sagen,
welche Listen, in welchen Listen sie enthalten ist.
Ich sage also, gib mir alle Listen,
die ein A enthalten und ich kriege als erste Antwort,
es ist die Liste, wo das A als erstes Element drin auftaucht.
Danach gibt es noch eine Restlist,
aber was da drinsteht,
ist uninteressant
Es ist zu beachten, dass allein dieses Ergebnis jetzt hierfür unendlich viele Ergebnisse steht.
Das können beliebig viele Listen sein, da die Länge dieser Liste nicht beschränkt ist.
Die Restliste hier kann beliebig lang sein und damit habe ich
unendlich viele Ergebnisse, die durch dieses eine Pattern beschrieben werden.
Es gibt aber trotzdem noch Alternativen und wenn ich danach frage,
kriege ich die zweite Lösung, Variante, das ist,
wo immer das A an zweiter Position auftaucht.
Und auch hier ist es so, dass beliebig viele Restlisten
danach folgen können, auch das steht wieder für unendlich viele Lösungen.
Das heißt, hier kriege ich jetzt unendlich viele Pattern,
von denen jedes Einzelne für unendlich viele Lösungen steht.
Und damit haben wir ein drittes Alleinstellungsmerkmal der Logikprogrammierung.
Wir können über unendlich große Mengen von Lösungen reden.
Ohne Schwierigkeit, wie wir hier sehen. Es gibt auch noch die vierte Aufrufvariante,
ich gebe gar nichts vor und ich möchte alle Kombinationen von Elementen und den Listen,
in denen sie enthalten sind, haben,
da müssen sie einfach die Ergebnisse von dem hier,
mit dem hier kombinieren und dann kriegen sie ungefähr einen Eindruck,
was dann rauskommt.
Das können sie ausprobieren mit dem System.
Ich kenne keine sinnvolle Verwendungsweise dafür. Für die anderen
drei Verwendungsweisen kenne ich sinnvolle Einsatzmöglichkeiten hierfür
eher nicht
Wesentlich ist eben nur, dass die Logikprogrammierung diese Richtungsunabhängigkeit hat.
Im Programm selber ist nicht festgelegt, was ist Input, was ist Output.
Es kann sowohl das der Input sein, als auch das.
Es kann sowohl das der Output sein, als auch das.
Alle Kombinationen sind möglich. Im Normalfall. Wir werden Einschränkungen sehen,
wo wir Einschränkungen machen müssen. Und die passieren immer dann,
wenn wir aus guten Gründen auf die Maschine,
auf die Hardware tatsächlich zurückgreifen wollen, nämlich auf den Numerik-Prozessor da drin.
Der arbeitet funktional. Und dann können wir nicht
von unserer Programmiersprache erwarten, dass sie das Relationale durchsetzen kann
In dem Augenblick, wo wir auf die Hardware zurückgreifen,
direkt, dann landen wir auch in einem funktionalen Schema
und dann sind nicht mehr alle Aufrufvarianten tatsächlich benutzbar.
Aber auch das kommt später. Das müssen wir uns
heute nicht angucken. Gut, die Essenz ist also,
unser Logikprogramm für Member macht nicht einen konkreten Algorithmus,
sondern es ist ein Algorithmenschema. Es sagt nur allgemein,
was sind die Zusammenhänge zwischen den verschiedenen Argumenten,
die ich habe und was dann davon Input und was Output ist,
wird nicht im Programm festgelegt,
sondern erst beim Aufruf des Programms
Beim Aufruf des Programms entscheidet es sich,
was sind die Input-Informationen, was sind die Output-Informationen.
Und deshalb ist Logikprogrammierung die radikalste Form von deklarativer Programmierung, die ich kenne.
Ich schreibe nicht auf, was getan werden soll,
ich schreibe auf, was gilt in der Welt.
Und das hat Kai von Look,
das ist ein Kollege von der Fachhochschule, mal in die schönen,
prägnannten Worte gefasst, sagen, wie es ist.
Nicht sagen, was zu tun ist, sondern sagen,
wie es ist. Und die Maschine soll bitte schön selber ableiten,
was zu tun ist. Das ist Aufgabe der Maschine,
Aufgabe des Programmierers ist es nur zu sagen,
was geht zu beachten.
Und deshalb sind im allgemeinen Fall Logikprogrammierung Programme
immer Richtungsunabhängig
Das ist auch ein Klassiker der Logikprogrammierung. Der alte Algorithmusbegriff,
der teilt sich jetzt auf in zwei Komponenten und das
geht auf Bob Kowalski ein, der Pioniere zurück.
Der gesagt hat, wir haben zwei Single zu unterscheiden.
Das eine ist die Logik, die Programmlogik.
Die steckt tatsächlich im Programm nach wie vor drin.
Und das zweite ist eine Kontrollstruktur, eine Steuerstruktur, ein Ablauf.
Und dieser Ablauf ist in der abstrakten Maschine verdrahtet.
Der ist fest verdrahtet in der abstrakten Maschine. Und den
kann ich Nicht ändern, ich kann nur Programme schreiben,
die trotz dieses fest verdrahteten Ablaufes ein anderes Verhalten simulieren.
Das gucken wir uns auch noch an, wie das funktioniert.
Und deshalb laufen also viele dieser Kontrollprobleme dieser Steuerstruktur-Probleme,
die es in allen Programmiersprachen gibt, hier ins Leere,
weil das eben auch ein Privileg der abstrakten Maschine ist.
Da kommen wir im Normalfall auch nicht ohne weiteres ran.
Alles, worum wir uns kümmern müssen,
ist dieser rein deklarative Zusammenhang zwischen verschiedenen Datenstrukturen.
Also in unserem Beispiel eben ein Element und eine Liste
von Elementen
Und die Verarbeitungsrichtung, die wird eben erst beim Aufruf festgelegt und
noch nicht selber im Programm spezifiziert. Gut, wir können
hybride Modelle aufbauen, indem wir verschiedene Verarbeitungsmodelle miteinander kombinieren.
Also zum Beispiel ist in jeder emperativen Sprache gibt es Elemente der funktionalen Programmierung,
nur eben nicht in der Schönheit,
wie in der funktionalen Programmierung per se, wo Funktionenbürger erster Klasse sind,
das sind sie in C oder Java eben nicht.
Aber natürlich kann die Funktion definieren und anwenden.
Das kann ich auf jeden Fall machen. Oder die Kombination zwischen
imperativer Programmierung und objektorientierter Programmierung, in schwacher Form in C Plus,
in starkere Form in Java oder Python beispielsweise.
Funktionale und objektorientierte Programmierung hat Frau Dreschler garantiert,
auch in ihrer Veranstaltung behandelt. Eines der ersten,
nein, das erste war endlich Smalltalk,
aber in Common Lisp ist es erstmalig im großen Stil benutzt worden und so weiter.
Also solche Kombinationen sind jederzeit möglich. Ja, was folgt aus
all dem? Wir haben es offensichtlich mit verschiedenen Verarbeitungsmodellen zu tun,
die unterschiedliche Charakteristiken haben, die unterschiedlich stark abstrahieren,
die unterschiedliche Basiskonstrukte haben,
mit denen man
irgendeine Programmsemantik beschreiben kann
Aus diesem Verarbeitungsmodell resultiert ein bestimmter Programmierstil.
Also welche Programme lassen sich mit dem Modell gut aus,
welche Probleme lassen sich gut umsetzen?
Welche lassen sich weniger gut umsetzen? Das wird dort festgelegt.
Klassischerweise findet man ganz oft, wenn man muss nur die Anzeigen gucken,
da wird eben ein PHP-Entwickler gesucht oder ein Ruby-Entwickler gesucht oder ein Java-Entwickler gesucht.
Was am Anfang steht, ist immer, ich brauche jemanden,
der eine bestimmte Programmiersprache beherrscht.
Meine Sicht auf die Welt sieht anders aus.
Meine Sicht auf die Welt sieht aus,
ich brauche jemanden,
der ein bestimmtes Problem lösen kann
Der die Fähigkeiten hat, das Problem zu verstehen und in eine adäquate Lösung umzusetzen.
Alles hier hinten ergibt sich später. Ich muss als allererstes
eigentlich gucken, welches Problem möchte ich lösen?
Welche Charakteristika hat dieses Problem? Dann muss ich ein Verarbeitungsmodell auswählen,
was geeignet ist oder was gut zu diesem Problem,
die ich dort habe, passt.
Und aus dem Verarbeitungsmodell resultiert ein bestimmter Programmierstil und da
eben der Programmierstil nicht nur auf das Einverarbeitungsmodell eingeschränkt ist,
sondern ich in verschiedenen Programmierverarbeitungsmodellen den gleichen Programmierstil pflegen kann,
folgt zum Schluss,
dass die Entscheidung über die Programmiersprache eigentlich das Allerletzte ist
Leider Gottes ist so die Welt nicht so,
weil im Wesentlichen Legacy, Legacy Programme existieren,
die weiter gepflegt werden müssen, die weiter eingebunden werden müssen.
Deshalb gibt es wegen der Altlasten eben immer Einschränkungen aus
der Umgebung heraus, da sich keiner so richtig zumutet,
einen radikalen Schnitt vorzunehmen und einen Neuansatz zu wagen.
Und andererseits zu viele unterschiedliche Programmierparadigmen in einem und demselben System
auch ein Risiko darstellen,
weil es dann immer Anpassungsprobleme an irgendeiner Stelle gibt
Aber eigentlich sollte die Denkbauungsweise so geprägt sein,
dass man erst mal über das Verarbeitungsproblem nachdenkt,
ehe man über die Programmiersprache nachdenkt.
Die klassische Sichtweise ist immer erst, suche ich mir einen Hammer
raus und dann gehe ich mit dem Hammer auf mein Problem lösen.
Ob der Hammer überhaupt geeignet ist, das Problem zu lösen
, das fragt keiner. Es muss umgekehrt sein,
ich muss erst das Problem angucken, dann fragen,
ob ich nicht vielleicht lieber eine Zange brauche,
um das Problem zu lösen.
Hier mal ein Versuch eines Überblicks. Ich habe also
diese drei Säulen-Imperative funktionale und logikbasierte,
das kann ich sowohl auf der Ebene des Stils als
auch auf der Ebene des Verarbeitungsmodells
Und ich kann eben im imperativen Modell auch beschränkt funktional deshalb eine unterbrochene Linie.
Beschränkt funktional arbeiten. Und im funktionalen Modell kann ich natürlich
auch imperativ arbeiten. Das ist genau das Set Bang.
In dem Augenblick, wo ich das Set Bang verwende,
verlasse ich das funktionale Paradigma und handle imperativ.
Genau das Gleiche geht in der Logik basierten Programmierung auch.
In dem Augenblick, wo ich ein Assert mache,
bin ich im imperativen Paradigma.
Interessanterweise gibt es diese umgekehrten Pfeile hier nicht.
Es ist sehr, sehr schwer im immperativen Programmprogramm.
Paradigma logikbasiert zu programmieren. Dann müsste ich nämlich
die Inferenzmaschine erst mal schreiben
Und wenn ich die habe, kann ich sie auch dann benutzen.
Und das Gleiche gilt auch für das funktionale Paradigma.
Ja, also es ist nicht ohne weiteres möglich,
logikbasierte Programmierung in diesen anderen Paradigmen umzusetzen,
in der umgekehrten Richtung geht es praktisch immer.
Gut, die Grundoperationen, die ich eben habe,
sind im Imperativen, ich arbeite Folgen von Anweisungen ab,
im funktionalen Ich-Werte-Funktionen aus und im logikbasierten Ich wilde logische Schlüsse nach.
Welche Art die sind, das gucken wir uns noch an.
Die abstrakte Maschine da unten drunter ist dann entweder eine Turing-Maschine im Fall des Imperativen Modells,
der Lamda-Kalkül im Falle des Funktionalen Modells oder der Prädikatenkalkül erster Stufe im Falle der Logikprogrammierung.
Wir wissen, dass alle drei Äquivalent sind, Berechnungsäquivalent sind.
Alles, was sich im Lamda-Kalkül machen kann, kann ich
mit der Turing-Maschine machen und so weiter und so fort.
Also es gibt auf der Ebene keinerlei bevorzugten Mechanismus.
Das ist jeweils immer machbar. Und das mathematische Grundkonzept ist eben
hier immer die Ersetzungsanweisung. X ist X plus 1. Wir werden sehen,
dass eine Anweisung dieser Art im Logikparadigma einen Widerspruch in sich selbst darstellt,
weil es nämlich absolut unlogisch ist, was da steht.
Hier wird ein dynamischer Ablauf beschrieben und nicht einen Zusammenhang zwischen Werten
In funktionalen Paradigma ist es eben die Berechnung eines Funktionswertes auf
der Basis eines Funktionsausdruckes und in der relationalen Welt ist es das Überprüfen,
ob ein Tubel-Bestandteil einer Relation ist oder nein.
Oder wie diese Tubel zu konstruieren, sodass sie Bestandteil der
Relation sind, das ist auch eine Sichtweise darauf.
Und all diese Paradigmen muss sich irgendwie per Compiler oder Interpreter
auf die Hardware-Architektur tatsächlich abbilden. Ja,
das können wir zusammenfassen, das ist sozusagen die Werbebotschaft,
wir haben mit der Logikpramierung ein sehr hohes Abstraktionsniveau.
Wir haben mächtige Werkzeuge der Informatik als Basiskonstrukte zur Verfügung,
relationale Datenbanken, habe ich schon erwähnt.
Nicht derterministische Suche und Mustervergleich. Das sind die wesentlichen dabei.
Wir haben unmittelbare Konzepte zu zentralen Begriffen der Mathematik und der
theoretischen Informatik, also so etwas wie Relation, Funktion.
Das finden Sie auf dem allerersten Übungszettel, Unifikation,
müssen wir erklären, was das ist, Substitution,
müssen wir auch klären, was das ist und so weiter.
Aber das sind alles Begriffe, die Sie in der theoretischen Informatik auch begeben.
Und wir benutzen das Ganze oder was weiß ich,
Termstrukturen bilden dann einen Verband Weiß noch jemand, was ein Verband ist?
Kriegen wir noch. Was heißt Unterspezifikation eines Terms?
Was ist ein Transucer? Was ist eine Grammatik?
Was sind Eigenschaften von Relationen wie Symmetrie, Transitivität?
Was heißt der Begriff, eine Ableitung, Gott dergleichen mehr?
All das werden wir hier wieder finden. Und wie Sie
vielleicht schon bemerkt haben, wenn ich verschiedene Programme einander gegenübergestellt habe,
das Dichteste ist eigentlich immer das Logikprogramm gewesen.
Es sind unglaublich kompakte Problemlösungen, die man da produzieren kann,
weil sie eben eine sehr hohes Abstraktilionsniveau haben und relativ dichte
Beschreibungen an den Problemstellungen dran ermöglichen.
Ich muss nicht erst komplizierte Datenstrukturen dazwischen legen, um diesen Zusammenhang herzustellen.
Und auf die Art und Weise kann man funktionsfähige Prototypen,
also Funktionsprototypen, nicht Mock-ups, Funktionsprototypen relativ schnell herstellen,
auch wenn sie den Entwurf einer neuen Programmiersprache etwa beinhalten.
Deshalb also der Versuch, ein Scream-Interpreter in ein Produkt zu schreiben,
um zu zeigen, wie aufwendig ist denn sowas überhaupt.
Okay, wir haben eine berechnungsuniverselle Programmiersprache. Klar,
sonst wäre es keine, indem wir ganz,
ganz wenige, aber dafür sehr mächtige
Basiskonzepte miteinander verbinden
Das ist eben der Begriff der Ulifikation, und zwar von rekursiven Datenstrukturen,
was das genau heißt, gucken wir uns noch an.
Das ist der Begriff der Thermersetzung und Vayablen-Substitution,
den brauchen wir um Regeln anwenden zu können.
Und das ist die Lichtdeterministische Suche, die immer dann aktiv wird,
wenn es verschiedene Alternativen gibt, die analysiert werden müssen.
Und als letztes, Syntax und Semantik der Sprache sind nicht Gott gegeben.
Das mag überraschen. Also stellen Sie sich beispielsweise vor,
Sie wollen die Sonntags von Java verändern.
Da wird sich der Compiler aber böse beschweren darüber.
Das ist ein ganz, ganz einfacher Angelegenheit in Prolog.
Wir können jederzeit gucken wir uns dann auch ziemlich bald an.
Die sonntags der Sprache, unseren individuellen Vorlieben und Bedürfnissen anpassen.
Da gibt es einen weiten Spielraum dazu. Der Witz ist aber
auch, dass wir die Semantik der Sprache verändern können.
Und dann wird es spannend. Dann ziehen wir uns nämlich,
oder dann sägen wir den Ast ab, auf dem wir sitzen.
Und das ist die Metaprogrammierung, genau das.
Dorthin möchte ich kommen, dass ich ihn zeigen kann,
wie man jetzt mehr oder weniger regide oder radikal
auch die Semantik der Sprache verändern kann.
Und so beispielsweise das Abarbeitungsmodell von Prolog auf den Kopf stellen kann.
Wenn also diese nichteteministische Suche ist im Standardfall
eine Tiefensuche
Wenn ich eine Breitensuche haben will,
muss ich natürlich die Suche in die eigene Hand nehmen,
aber ich kann es machen. Ich brauche nur einen ganz,
ganz einfachen Metainterpreter zu schreiben, der genau das bewirkt,
dass eben die Alternativen jetzt nicht mehr in einem Stack gespeichert werden,
sondern in einer Warteschlange gespeichert werden.
Und schwupp, schon habe ich statt Tiefensuche Breitensuche.
Das ist auch mit relativ wenig Aufwand machbar. Und auf
die Art und Weise kann man experimentelles Prototyping für neue Verarbeitungsmodelle betreiben,
weil man solche fundamentalen Eigenschaften der Sprache mit relativ geringem Aufwand verändern kann.
Also das ist eine der, auch einer der Punkte,
wo man ja mit Ehrfurcht davor steht und sagt, Nett.
Nett, dass man sowas machen kann. Okay,
dann der nächste Punkt ist dann der Einstieg in die Welt der Relationalen Datenbanken,
aber das müssen wir nicht mehr heute machen.
Ich schenke Ihnen drei Minuten. Ich hoffe,
Sie schreiben es mir gut.
Bis nächsten Freitag