Willkommen zu diesem letzten Teil von der Vorlesungssoftwareentwicklung 2. Sie sehen,
es gibt einen kleinen Nummernsprung, wir haben die Nummer 13 weggelassen, weil 14 zwei Plätze braucht.
Wir haben heute den Teil 1. Wir versetzen nächste Woche fort mit dem Teil 2. Wir beginnen zunächst erstmal damit,
gut, wir haben wieder, wir haben wieder Fragen,
die Sie beantworten können beim nächsten Mal,
kommen da noch ein paar dazu und wir fangen an damit überhaupt erstmal zu überlegen,
wo wozu brauchen wir das funktionale Programmierparadigma,
weil das ist so ein bisschen die Grundvoraussetzung dafür überhaupt zu sagen,
okay, das, was wir haben,
reicht uns nicht aus.
Das, was Sie kennengelernt haben, bis jetzt,
bei uns in SE1 und SE2,
war im Wesentlichen basierend auf dem Imperativen Programmierstil.
Programmierparadigma sollte man vielleicht besser sagen. Ja,
also Basis ist da, dass es Variablen gibt,
dass es Wertzuweisungen gibt und dass es Anweisungen gibt,
die in einer Folge abgearbeitet werden
Und diese Anweisungen bewirken, dass man den Zustand einer Software
verändert und dabei eigentlich die Reihenfolge abarbeitet.
Natürlich, die Reihenfolge ist das, was das Programm ausmacht.
Wir sind dabei mehr oder weniger stillschweigend davon ausgegangen,
dass wir eine sogenannte von Neumann-Architektur haben,
von Neumann-Architektur ist eine Architektur,
wo der Speicher sowohl die Daten als auch das Programm enthält,
sodass man selbst das per Programm verändern könnte
Was wir in dem Fall selten machen, aber es wäre möglich.
Und insgesamt handelt es sich in meinen endlichen Automaten.
Das heißt, wir haben wirklich diskrete Zustände, Zustandsübergänge und
es gibt eine Menge, eine endliche Menge vollen Zuständen.
Und diese Zustandsänderungen, die bewerten durch Operationen bewirkt.
Ob wir dann in das Produkt prozentuale Programmierpartikel oder das Objekt notierte,
haben oder scheidet sich erstmal von dieser Art und Weise Anweisungen abzuarbeiten,
nicht so sehr stark.
Das Problem ist, dass wir
sogenannte Seideneffekte haben
Seideneffekte sind jetzt Dinge, wo das Verhalten abhängig ist von dem,
was ringsherum ist. Das heißt, man kann nicht sagen,
das Programm ist in irgendeiner Weise, wird es immer wieder das Gleiche tun,
sonst hängen von der Umgebung ein bisschen ab.
Lassen Sie uns das kurz sagen, was das ist.
Gemeint ist damit, dass die Umgebung, also es gibt veränderliche Objekte,
es gibt veränderliche Zustände, dass das hier eine Rolle spielt.
Zusätzlich natürlich Benutzereingaben, das ist das, was wir wollen,
das Programm, ein interaktives Programm soll auf dem Benutzer reagieren,
aber auch Sachen, die in den Daten stehen, eine Konsequenz haben.
Das ist durchaus etwas, was wir wollen. Ja,
und das führt natürlich dazu, dass zum Beispiel in dem Objekt orientierten,
in einer Klasse, dass eine Methode etwas Unterschiedliches liefert,
je nachdem, was in der Datenbank steht,
was der Benutzer eingibt oder was für andere Objekte es sind.
Soweit alles ganz gut, aber wenn wir mehrere Prozesse haben,
müssen wir dieses Synchronisieren, damit wir auf Zustände zugreifen.
Und wenn wir Zustandsänderungen haben, während wir etwas arbeiten,
gibt es häufig Effekte, die man schwer hersagen kann oder so etwas.
Und es ist, was auch noch ein Punkt ist,
es ist ein bisschen schwierig, hier Zustandsänderungen im Code zu finden,
immer klar zu wissen, wo denn die Zustandsänderungen passieren.
Solche Seideneffekte spielen besonders eine Rolle, wenn es sich um nebenläufige Programme handelt,
nebenläufig ein anderes Wort dafür, nicht so ganz präzises Wort.
Dafür ist parallel, haben wir zum Beispiel,
wenn wir mit Freds arbeiten, dieses Retz haben die Folge,
ich muss noch mal zurückschauen, ja, die haben die Folge,
Brauchen wir, um mehrere Prozesse zu wohnen,
zu benutzen, um reale Weltprobleme der realen Welt abzubilden,
wo wirklich Dinge parallel funktionieren.
Und sie haben dann ihr Problem,
wenn wir auf gemeinsam, auf einem gemeinsamen Kontext zugreifen.
Solche Kontext können zum Beispiel sein, gemeinsame Variablen,
gemeinsame Ressourcen wie Speicherplatz oder so etwas.
Ja, und da brauchen wir das Red-Sicherheit. Das bedeutet,
dass diese Threads auf gemeinsame Sachen zugreifen können,
diese verändern können und dabei arbeiten können,
ohne sich ja miteinander zu verhaken
Zu verklemmen. Sie kennen vielleicht den Begriff Deadlock. Also
der eine war auf den anderen und der andere war dann
auf den einen und irgendwie geht nichts weiter. Ja,
das hat zur Folge, dass wir, dass wir erreichen,
dass Anwendungen oder Komponenten von verschiedenen Bereichen aus ausgeführt werden können,
ohne dass sie sich gegenseitig behindern.
Das ist das, was wir wollen.
Und dazu braucht man so etwas. Ich möchte Ihnen kurz
einen eines sogenannten Race Condition so etwas darstellen.
Race Condition ist ein Name für einen Effekt,
der eintritt, wenn Dinge parallel durchgeführt werden und die Zeit
spielt eine Rolle.
Race,
der Wettlauf
Wir haben hier mal zwei Freds, die jeweils etwas Unterschiedliches tun.
Und sie greifen auf einen gemeinsamen Kontext zu. In dem Fall
ein gemeinsames Konto. Und wenn wir jetzt mal anschauen,
in dem einen Fred wird ein Abhebelvorgang gemacht und dem anderen wird ein Einzahlvorgang gemacht.
Und jeder von den Freds ist für sich klassisch programmiert.
Er greift auf Variablen zu. Wir haben jetzt hier nicht wirklich
die Programmiersprache, sondern wir haben hier nur das als Pseudocode.
Aber das ist, sollten wir uns vielleicht reichen,
dass wir uns das vorstellen können.
Also, wir schauen mal das eine an.
Hier soll das nehmen,
soll nur darstellen
Zeitachse von oben nach unten. Ja, also wir lassen
uns den Saldo geben und da bekommen wir raus Rundert.
Wir gucken, ob wir das abheben dürfen. Ja,
50 dürften wir abheben, dann heben wir 50 ab und
das Resultat sollte 50 sein.
Soweit völlig okay. In dem Einzelhallenprogramm ist das,
schauen wir uns das ein, ist auch wieder keine Überraschung.
Wir lassen uns den Saldo geben, der Saldo sei 100
im speziellen Fall. Ja, und wir zahlen da 100
ein und dann sollte das Saldo 200 sein.
Keine Überraschung, keine Magie, kein Hexenwerk,
nur Plus und Minus,
Ja, aber wenn die beiden jetzt in dieser Form zeitlich parallel laufen, ja,
dann lasse ich mir hier den Saldo geben, prüfe das.
Und in der Zwischenzeit hat er einzelnes Rett auch angefangen.
Ich hebe hier 50 ab, der hat sich aber die 100 geben lassen,
ist weiterhin der Meinung, er hat 100,
er zahlt 101 und hat dann den Wert
200. Hier ist aber eigentlich der Wert 50, ja,
und dann schreibt er hier in den Speichern die Speicherzelle,
die ein Saldo enthält,
dem Wert
Was ist da falsch gelaufen? Na, eigentlich sollte doch dann 150 dastehen,
weil der halt 50 abgehoben in der Zeit. Ja,
also einfach nur dadurch, dass das zeitlich verschränkt ist und dass
wir hier beides auf eine Variable des Alto des Kontos zugreifen,
kann es hier dazu kommen,
dass ein falsches Ergebnis kam.
Das ist ein Beispiel für so eine Race Condition.
Beides sieht ganz einfach aus.
Sowas zu verhindern ist ein wichtiges Ziel
Was man dazu, dieses Problem ist nicht ganz neu und tatsächlich machen sich die Leute Gedanken,
was sie können und sie benutzen ein Blockiermechanismus, Locking-Mechanismen,
um diesen globalen Zustand zu schützen.
Zum Beispiel schließt man sich gegenseitig aus,
butual exclusion oder mutics, als Kurzwort im Slang,
dass die einen und die anderen nicht gleichzeitig zugreifen können.
Oder es gibt sogenannte Seemar-Afforen, sind mal Foren,
in Österreich heißt das Signal an der Bahn,
hoch und runter geht, Zug, da fahren, Zug,
da nicht, das heißt, sehen wir mal vor,
in Deutschland ist dieses Wort nicht so sehr gebräuchlich, ist ein Anzeiger.
Das ist ein Anzeiger dafür, wer benutzt das gerade?
Ja, und das kann man dazu sagen, okay,
jetzt benutzt der eine das, zum Beispiel zum Einzahlen,
und dann benutzt der andere das zum Auszahlen.
In Java haben wir das Schlüsselwort Synchron heißt, das macht sowas ähnliches.
Ja, und das Problem ist, man muss hier sehr diszipliniert programmieren.
Ja, sonst, sonst würde man die,
wenn man die Simmerforen zum Beispiel nicht beachtet,
würde man einfach genau diesen Effekt der Race Condition haben.
Und Damit das nicht so schlimm ist, gibt es viele Programmierparadigmen,
die eigentlich solche Zustandsänderungen in irgendeiner Vorweise verbieten wollen.
Und wenn wir das aber trotzdem erlauben wollen,
dann wäre das eine Möglichkeit, funktionale Programmierung gut einsetzen zu können.
Ja, und damit sind wir auch bei der Beantwortung der Frage 1. Nämlich,
wo diese Unabhängigkeit brauchen, die unabhängigkeit brauchen wir immer dann,
wenn verschiedene Parallele Dinge auf die Umgebung zugreifen.
Zum Beispiel ist so ein gemeinsamer Zustand ein gemeinsamer Variabler.
Da brauchen wir diese Unabhängigkeit, über die wir jetzt
weitersprechen wollen
Ja, und das ist für mich der Punkt,
wo ich das funktionale Programmierpartik mal einführen möchte.
Ich möchte Ihnen hier in dieser Veranstaltung,
wir möchten Ihnen das funktionale Programm Mehrpartik mal als eine
lohnenswerte Alternative für einen Teil der Aufgaben vorstellen,
nahe bringen und das als Ergänzung zu dem haben,
was wir in Objektorientierung haben.
Sie haben schon, wir sind schon in Landars, mal drauf eingegangen.
Ich glaube, das war Kapitel 8. Wir schauen nochmal,
ich komme nochmal drauf, wo das genau war.
Wir werden zum Schluss wieder dazu kommen.
Aber jetzt erstmal, worum geht es bei funktionaler Programmierung
Es geht darum, die eben genannten Defizite der Abhängigkeit von
der Umgebung auszuschließen und ein anderes Rechenschema zu haben.
Eben Programmierparadigma. Das Basis, und daher hat das Ganze
seinen Namen, sind Funktionen im mathematischen Sinne.
Funktionen, so wie Sinus, Cosinus oder so etwas.
Das Programm ist dann ein Ausdruck, der einen Wert liefert.
Grundlage ist das Zelanda-Kalkül. Das könnten Sie jetzt mal nachschauen
, was das ist. Und wir können hier tatsächlich Funktionen
als das vordergründige verwenden. Wir können Funktionen dazu verwenden,
dass wir Dass sie Variablen bearbeiten, aber wir können diese Funktionen sogar übergeben.
Wir können die Funktionen als Parameter verwenden. Wir können die
Funktion als Rückgabewert haben. Oder wir können sogar Probleme dadurch beschreiben,
dass wir Funktionen ausdrücken. Und das ist zwei wesentliche Prinzipien,
der funktionalen Programmierung sind dabei wichtig, das eine,
die Seiteneffektfreiheit, die wir gerade eben hatten und das andere,
die Unveränderlichkeit.
Ja, und deswegen möchte ich Ihnen
diese Prinzipien hier einführen mit puren Funktionen
So, pure Funktionen, was ist das?
Das ist gleichzeitig die Antwort auf die Frage. Zwei,
weg schauen wir hier hin, was ist eine pure Funktion?
Also pure Funktionen, das sind Funktionen, die immer
das gleiche Ergebnis haben, wenn die gleiche Eingabe vorzukommt.
Das heißt, es gibt keinen Abhängigkeit von irgendeinem globalen Zustand.
Es gibt keine Seiteneffekte, weil das nicht davon abhängt
Voraussetzungen dafür ist, oder Mittel dazu ist, referenzielle Transparenz,
dass Ausdrücke ersetzt werden können, ohne dass sich das Ergebnis verändert.
Das sollen wir gleich mal erklären. Zunächst aber erstmal möchte
ich Ihnen noch verraten, warum wir das Ganze machen.
Der Vorteil ist, dass Tests sehr erleichtert werden.
Testbarkeit war ja der Aufwand für Tests.
Nämlich, ich brauche bei Tests in Kontext nicht einzubeziehen.
Die Umgebung, den globalen Zustand,
weil ich wirklich nur diese
eine Datenverarbeitungsaufgabe habe
Auch die Verständlichkeit ist sehr viel einfacher. Der Aufwand für Verständnis ist geringer,
weil wir eben nicht das ganze Umfeld einbeziehen müssen,
sondern wirken es nur darauf, auf eine Funktion konzentrieren muss.
Also der Name beschreibt, was die tut und damit ist alles gesagt.
Man braucht drumherum, das nicht zu wissen. Ich möchte
Ihnen kurz ein Beispiel bringen, was mir dieser referenzielle Transparenz,
denn gemeint ist mit diesem Begriff,
also wir haben hier so eine Additionsfunktion, alles kein Hexenwerk, ja,
wir addieren zwei Parameter oder wir multiplizieren ein paar Parameter mit der Multiplikationsfunktion.
Ja, und da könnten wir sagen, okay,
wir führen eine Berechnung aus wie hier in Seite.
Zeile elf. Wir addieren zwei zum Produkt von 3 und 4.
Ja, das sollten Sie jetzt mal schnell im Kopf rauskriegen.
Da kommt raus. Nein, nicht zwölf,
sondern 14, weil 3 mal 4 wird zuerst gerechnet.
Das ist die 12 plus 2 ist. 14, ja,
sodass wir das Ergebnis von Zeile 11. Nun könnten wir aber
auch diese Multiplikation 3 und 4 und 12 können wir auch ersetzen,
indem wir diesen Ausdruck verwenden,
addieren 2 und
Da kommt auch 14 raus. Oder wir nehmen nur eine Zuweisung mit der 14.
Auch da kommen 14 raus. Ja, das sind jetzt
Beispiele dafür, dass man am Ende das Gleiche erreicht.
Wir machen das in solchen simplen Beispielen natürlich nicht,
aber wir machen es gern so,
dass wir ein Unterprogramm für irgendetwas nutzen,
was dann ein Teil der Berechnung durchführt.
Ja, und deswegen sind idieren und multiplizieren hier referenziell transparent,
weil wir können sie einfach ersetzen durch etwas anderes in dem Fall.
Das Ergebnis, ja.
Ein Gegenbeispiel möchte ich hier bringen, wir haben eine Klasse
Adira Edda
Und die hat eine interne Variable, ein Feld namens X. Ja,
und wenn wir etwas addieren, addieren wir das zu dem X.
Das ist die Funktion dieses Adiras. Ja,
und wenn wir jetzt zweimal das Gleiche machen,
addiere auch mit dem Wert zwei aufrufen,
nochmal die Adira mit den zwei Wert zwei aufrufen.
In dieser Folge ergibt sich jedes Mal was anderes,
weil natürlich die interne Variable,
das, was der Adira in seinem Feld hier hat,
jedes Mal was anderes ist.
Er fängt mit fünf an,
da kommt sieben nach dem ersten Mal raus,
neun nach dem zweiten Mal und so weiter
Ja, also das ist ein Beispiel, weil wir hier
einen Seiteneffekt haben, einen Effekt auf einen globalen Zustand.
Dieser Zustand ist in der Klasse, im Objekten,
in der Klasse gespeichert hier in dieser Variable X.
Und wir greifen darauf zu, wenn wir die Addition durchführen.
Ja, also das ist hier der Punkt.
Und jetzt möchte ich ein kleines Quiz durchführen mit Ihnen,
um zu sagen, ist denn das eine pure Funktion,
wenn wir die mathematische Zufallszahlenfunktion hier aufrufen
Ja, am besten, Sie stoppen das Video kurz und schauen sich,
überlegen Sie erst mal, was da rauskommen kann oder holen
sich Hilfe oder gucken im Internet nach, wie auch immer.
Und deswegen Kurzpause von zwei Sekunden. Und dann kommt
die Antwort auch von mir schon. Diese Zufallsverzeihenfunktion ist nicht
eine wirkliche Zufallszahlfunktion, wenn sie das wirklich wäre,
dann wäre, da würde das schön stimmen.
Aber die Zufallszahl ist ja hier diese Funktion,
die ist am Ende nachgebildet aus der Didaministrischen Funktionen.
Und das heißt, wir haben doch irgendwie einen Zustand,
wenn mich zu einer bestimmten Tageszeit rechentakt oder wie auch immer,
es hing für ein paar Faktoren ab.
Soweit erst mal zu den puren Funktionen.
Und das war auch die Frage zwei