WEBVTT

00:06.740 --> 00:07.680
Was machen wir heute?

00:07.780 --> 00:08.820
Heute machen wir drei Sachen.

00:09.060 --> 00:13.760
Einmal den Rest von der letzten Vorlesung fertig, über Interfaces und

00:13.760 --> 00:14.200
Generics.

00:14.820 --> 00:18.460
Dann machen wir eine kleine Vorlesungseinheit über Rekursion.

00:19.080 --> 00:21.880
Und dann haben wir noch ein besonderes Special, weil ja übermorgen

00:21.880 --> 00:22.700
Heiligabend ist.

00:23.360 --> 00:25.500
Auch wenn ich nicht aussehe wie der Weihnachtsmann, habe ich für euch

00:25.500 --> 00:26.120
was mitgebracht.

00:27.580 --> 00:30.440
So, dann steigen wir jetzt wieder ein in die letzte Vorlesung.

00:31.360 --> 00:34.820
Und zwar hatten wir ja generell über Typparameter und so weiter

00:34.820 --> 00:35.760
gesprochen, Generics.

00:36.400 --> 00:39.040
Am Ende gibt es noch mal eine Zusammenfassung, wo ich jetzt gerne

00:39.040 --> 00:42.740
weitermachen würde, ist ein Gebiet Autoboxing.

00:48.460 --> 00:50.880
Probier mal das andere Mikro und hoffe, dass es dann besser ist.

01:13.670 --> 01:16.430
So, dann hoffe ich, dass es jetzt besser zu hören ist.

01:19.750 --> 01:20.550
Ja, Autoboxing.

01:20.630 --> 01:22.430
Diejenigen von Ihnen, die vielleicht schon mal mit C-Sharp

01:22.430 --> 01:24.030
programmiert haben, kennen diesen Begriff.

01:24.410 --> 01:28.210
Boxing, Autoboxing, die Java-Programmierer erst seit kürzerer Zeit.

01:28.290 --> 01:29.370
Das ist nämlich etwas Neueres.

01:29.970 --> 01:30.930
Und zwar, worum geht es da?

01:31.130 --> 01:37.030
Sie erinnern sich, es gibt in Java fest eingebaute Datentypen, die wir

01:37.030 --> 01:37.870
kennengelernt haben.

01:37.870 --> 01:41.630
Integer, short, long, character.

01:42.510 --> 01:47.330
Die sind fest eingebaut und damit eigentlich keine Objekte.

01:47.390 --> 01:48.470
Das hatten wir auch schon kennengelernt.

01:48.570 --> 01:52.630
Es gibt dann immer Klassen, die denselben Namen haben wie die fest

01:52.630 --> 01:57.150
eingebauten Datentypen, aber mit einem großen Buchstabenanfang, wie

01:57.150 --> 01:58.710
das halt auch üblich ist bei Klassennamen.

01:59.850 --> 02:03.450
Jetzt kann man sagen, das ist ja ein bisschen umständlich, dass es das

02:03.450 --> 02:09.030
zweimal gibt, aber der Hintergrund ist halt der, man kann gerade

02:09.030 --> 02:12.510
Ausdrücke in der Regel schneller auswerten, wenn in diesen Ausdrücken

02:12.510 --> 02:16.710
fest eingebaute Datentypen vorkommen, weil man sich ein paar

02:16.710 --> 02:18.610
Speicherzugriffe spart bei der Auswertung.

02:19.410 --> 02:22.870
Und deswegen ist es immer generell eine schlaue Idee, mit den fest

02:22.870 --> 02:26.830
eingebauten Datentypen zu rechnen, wenn man keine Objekte braucht.

02:27.230 --> 02:30.910
Objekt ist sowieso wesentlich umständlicher, weil mit denen kann man

02:30.910 --> 02:32.130
eigentlich nicht direkt rechnen.

02:32.190 --> 02:34.110
Sie können nicht sagen, ein Objekt plus ein anderes Objekt.

02:35.470 --> 02:38.290
Und deswegen fragt man sich, warum wurden denn überhaupt jetzt diese

02:38.290 --> 02:41.810
Objekte eingeführt, die eben eigentlich auch nichts anderes können wie

02:41.810 --> 02:43.030
die eingebauten Datentypen.

02:43.490 --> 02:48.150
Und einer der Hintergründe ist das, was wir vorher kennengelernt

02:48.150 --> 02:50.030
hatten in der letzten Vorlesung, nämlich Generics.

02:50.030 --> 02:54.390
Wenn ich jetzt sage, ich möchte eine Liste von Integer-Werten haben

02:54.390 --> 02:57.410
und ich habe mir meine Liste definiert mit meinem Typ-Parameter, wie

02:57.410 --> 02:59.810
wir das kennengelernt haben, dann erwartet natürlich der Typ

02:59.810 --> 03:05.690
-Parameter, dass bei der Deklaration der Liste für den Typ-Parameter

03:05.690 --> 03:07.330
eine Klasse eingesetzt wird.

03:08.570 --> 03:10.450
Aber kein elementarer Datentyp.

03:10.530 --> 03:12.070
Und wie kann ich jetzt eine Liste von Integer machen?

03:12.150 --> 03:14.450
Die Lösung ist ganz einfach, indem ich die Integer-Klasse, also die

03:14.450 --> 03:16.250
mit dem großen I, nehme.

03:16.890 --> 03:18.310
Haken bei der Sache ist jetzt der.

03:19.910 --> 03:22.050
Wie ich sagte, mit denen kann man aber eigentlich nicht besonders

03:22.050 --> 03:23.110
komfortabel rechnen.

03:24.110 --> 03:26.990
Und genau das soll eben dieses Autoboxing vermeiden.

03:27.210 --> 03:28.470
Also schauen wir uns das nochmal an.

03:28.590 --> 03:36.230
Also hier sehen Sie, wie wir erstmal einige Integer-Objekte uns

03:36.230 --> 03:36.630
besorgen.

03:36.750 --> 03:40.570
Hier New Integer 5, oder dass dieses Objekt dann nachher den Wert 17

03:40.570 --> 03:43.230
drin speichert, oder hier ein Double-Objekt besorgen.

03:44.970 --> 03:50.290
Und wie ich sagte, früher war es nicht möglich, damit zu rechnen.

03:51.210 --> 03:54.210
Und man musste im Prinzip immer hin und her konvertieren.

03:54.290 --> 03:56.710
Man musste sagen, also ich habe jetzt hier, wenn ich so eine einfache

03:56.710 --> 03:59.590
Rechnung durchführen will, wie hier eins aufaddieren.

03:59.930 --> 04:03.330
Dann musste ich tatsächlich von dem Integer-Objekt mir den Wert

04:03.330 --> 04:05.870
besorgen mit einer Int-Value-Abfrage-Funktion.

04:06.310 --> 04:10.990
Da eins draufaddieren und dann mit dem Ergebnis wieder einen neuen

04:10.990 --> 04:14.050
Integer erzeugen, der diesen Wert dann speichert und den man wieder

04:14.050 --> 04:15.110
Integer -Objekt zuweist.

04:15.730 --> 04:17.290
Geht, aber ist natürlich sehr, sehr mühsam.

04:17.630 --> 04:19.910
Und wie Sie sehen natürlich auch, wenn Sie die Anzahl der Aufrufe

04:19.910 --> 04:20.910
anschauen, die Sie hier haben.

04:21.030 --> 04:23.190
Also hier ein New Operator, das ist immer eine teure Angelegenheit,

04:23.610 --> 04:24.950
weil Sie Speicher besorgen müssen dabei.

04:25.430 --> 04:28.110
Und auch hier noch ein Objekt dereferenzieren und noch eine Methode

04:28.110 --> 04:28.490
aufrufen.

04:28.610 --> 04:31.270
Also das ist schon alles sehr rechenzeitintensiv im Vergleich zu, ich

04:31.270 --> 04:33.850
habe einen elementaren Datentyp, wo eben gleich der Wert drin steht.

04:33.850 --> 04:37.010
Es ist aber auch, wie gesagt, ein unangenehmer Code, man muss viel

04:37.010 --> 04:39.290
schreiben, das Ganze ist auch irgendwie schlecht lesbar.

04:40.470 --> 04:44.050
Und eigentlich nur, weil konzeptionell hin und her geswitcht werden

04:44.050 --> 04:46.390
muss, eben habe ich ein Objekt oder einen elementaren Datentyp.

04:47.370 --> 04:51.650
Und deswegen hatte man sich mal irgendwann überlegt, seit Java 5, dass

04:51.650 --> 04:54.810
man das, was eigentlich offensichtlich ist, was der Programmierer

04:54.810 --> 04:58.630
will, nämlich Rechnen mit Objekten, auch erlauben.

04:59.590 --> 05:02.850
Und das Ergebnis heißt dann Boxing oder Auto-Boxing.

05:02.890 --> 05:04.150
Ich hole mal gerade noch meinen Laser-Pointer.

05:13.670 --> 05:15.130
Und zwar sehen Sie das jetzt hier.

05:17.490 --> 05:21.310
Hier haben Sie einen eingebauten Datentyp Integer, eine Variable i von

05:21.310 --> 05:23.350
diesem Typ, setzen den Wert 42 zu.

05:23.730 --> 05:29.070
Und jetzt weisen Sie mit einem normalen Gleichheitszeichen dem Integer

05:29.070 --> 05:32.850
Objekt j dieses i zu.

05:33.750 --> 05:36.910
Wenn Sie das in c machen würden, dann würden Sie dem die

05:36.910 --> 05:40.330
Speicheradresse 42 zuweisen, was in der Regel keine schlaue Idee ist,

05:40.350 --> 05:42.230
weil Sie wissen ja gar nicht, ob der frei ist, der Speicher, mit

05:42.230 --> 05:42.490
Klammern.

05:42.810 --> 05:43.790
Nein, ist er meistens nicht.

05:46.770 --> 05:52.710
Hier in Java heißt es jetzt, dieses i wird automatisch im Prinzip

05:52.710 --> 05:56.930
konvertiert in ein Objekt und das wird dann diesem j zugewiesen.

05:56.930 --> 06:03.790
Und damit wird also dieser Wert von i hier diesem Integer Objekt

06:03.790 --> 06:04.250
zugewiesen.

06:04.450 --> 06:07.730
Das ist das, was Sie hier oben mühsam hinschreiben müssen immer.

06:08.510 --> 06:09.990
Das passiert im Prinzip automatisch.

06:10.410 --> 06:13.670
Das Umgedrehte geht auch, dass man das Unboxing macht, das heißt also

06:13.670 --> 06:17.910
aus dem Objekt als Box was rausholt, nämlich den Wert.

06:17.910 --> 06:22.350
Und dass man bei so einer Zuweisung int k gleich j eben nicht die

06:22.350 --> 06:27.270
Speicheradresse von j, sondern den Wert von j mit int value rausholt.

06:27.410 --> 06:31.410
Und Boxing, Unboxing heißt das halt deswegen, weil man entweder was in

06:31.410 --> 06:35.370
einen Kasten einpackt, in ein Objekt oder wieder auspackt aus dem

06:35.370 --> 06:36.850
Objekt und rausholt den Wert.

06:36.930 --> 06:39.470
Und deswegen halt diese Begriffe Boxing, Unboxing.

06:39.850 --> 06:42.850
Und Autoboxing heißt nur, dass das eben automatisch passiert, dass man

06:42.850 --> 06:44.810
es nicht mehr von Hand hinschreiben muss.

06:46.790 --> 06:49.330
Und der Grund, warum das jetzt hier an der Stelle in der Vorlesung

06:49.330 --> 06:52.570
auftaucht, ist halt, weil es im Zusammenhang mit Generics halt einfach

06:52.570 --> 06:55.130
gut ist zu wissen, dass man auch über elementare Datentypen Generics

06:55.130 --> 06:58.450
machen kann, weil es eben für jeden elementaren Datentyp auch die

06:58.450 --> 07:00.390
entsprechende Klasse gibt.

07:00.910 --> 07:03.430
Und die Objekte davon, wie gesagt, könnt ihr über Boxing, Unboxing

07:03.430 --> 07:04.730
auch relativ leicht ansprechen.

07:07.050 --> 07:11.530
So, jetzt gibt es noch eine Sache in dieser Vorlesung zu fertigmachen

07:11.530 --> 07:15.030
und zwar, wir hatten vorher über generische Klassen gesprochen.

07:15.710 --> 07:20.390
Es gibt auch generische Methoden, braucht man wesentlich häufiger.

07:20.650 --> 07:23.850
Es ist aber so, wir hatten ja schon über statische Methoden auch

07:23.850 --> 07:29.610
gesprochen, also Methoden, die zur Klasse gehören, die nicht einzelnen

07:29.610 --> 07:30.950
Objekten zugeordnet sind.

07:31.710 --> 07:35.650
Und letzten Endes kann man auch ganz ohne, dass man eine Klasse

07:35.650 --> 07:40.230
drumherum nochmal neu definiert hat, also zum Beispiel, kann man das

07:40.230 --> 07:43.090
gleich beim Main machen, statische Methoden auch deklarieren.

07:44.310 --> 07:49.130
Und das Ganze sieht dann so aus, dass Sie einen Typparameter zu Anfang

07:49.130 --> 07:52.130
stellen und dann eigentlich mit der Signatur weitermachen, wie Sie es

07:52.130 --> 07:52.330
kennen.

07:52.450 --> 07:55.270
Also Rückgabe, Typ, Name, Parameterliste und bei den Parameterlisten

07:55.270 --> 07:58.610
natürlich auch immer Typ und formaler Parameter, genau wie Sie es

07:58.610 --> 07:58.790
kennen.

07:58.790 --> 08:04.870
Aber Sie können jetzt beim Typ wie auch bei der Parameterliste die

08:04.870 --> 08:08.850
Typvariablen nutzen, also die Typparameter, die Sie hier deklariert

08:08.850 --> 08:09.050
haben.

08:09.150 --> 08:12.610
Wir sehen das hier an einem Beispiel, und zwar RandomSelect.

08:12.810 --> 08:22.070
RandomSelect gibt von zwei Werten einen beliebigen zurück, zufällig.

08:22.790 --> 08:27.250
Und was hier passiert ist, dass man sagt, ich weiß nicht, welchen Typ

08:27.250 --> 08:30.310
die beiden haben, aber die Auswahlprozedur lässt sich eigentlich

08:30.310 --> 08:32.670
unabhängig vom Typ deklarieren.

08:33.130 --> 08:38.830
Und zwar sehen Sie hier, return massRandom größer 5, das ist also mein

08:38.830 --> 08:39.870
Würfeln hier.

08:41.350 --> 08:46.290
Wenn ich davon ausgehe, 50% der Fälle ist der Wert größer als 0,1 bis

08:46.290 --> 08:48.910
1 oder eben 50% ist er da drunter.

08:50.190 --> 08:54.890
Und falls das eben größer 50% ist, gebe ich eben den E1 zurück, sonst

08:54.890 --> 08:56.930
gebe ich eben den formalen Parameter E2 zurück.

08:57.430 --> 08:59.470
Jetzt stellt sich natürlich die Frage, welchen Typ sollen die haben,

08:59.550 --> 09:00.870
und die Antwort ist, es ist eigentlich egal.

09:01.330 --> 09:04.970
Weil der Fragezeichenoperator interessiert das überhaupt nicht,

09:05.030 --> 09:06.590
welcher Typ dahinter steht bei den Ausdrücken.

09:08.370 --> 09:11.670
Und genau dieses, es ist mir doch egal welcher Typ das ist, schreibt

09:11.670 --> 09:14.610
man jetzt an der Stelle hier hin, dass man sagt PublicStatic, das ist

09:14.610 --> 09:17.490
also im Prinzip, um die generische Methode einzuleiten.

09:18.510 --> 09:23.050
Und dann kommt hier der Typ-Parameter t, und t ist dann der Rückgabe

09:23.050 --> 09:27.050
-Typ und genauso auch der Typ für die beiden Parameter-Ausdrücke.

09:28.670 --> 09:32.930
Und damit ist das Ganze mit jedem Typ nutzbar.

09:35.110 --> 09:38.730
Und wenn man sagt, ich möchte jetzt eine Methode haben, die das

09:38.730 --> 09:42.050
Minimum zurückgibt, also nicht zufällig was, dann klappt das natürlich

09:42.050 --> 09:46.370
nicht mehr mit jedem beliebigen Typ, sondern wir werden das machen,

09:46.910 --> 09:50.010
was wir auch letztes Mal besprochen hatten, eine Einschränkung auf den

09:50.010 --> 09:53.510
Parameter -Typen vornehmen, dass wir sagen, nicht mehr beliebige Typen

09:53.510 --> 09:56.530
können angegeben werden bei der Deklaration, sondern Einschränkungen,

09:56.610 --> 10:01.070
und das machen wir hier mit diesem Extents, dass wir sagen, eine

10:01.070 --> 10:05.230
Einschränkung von etwas ist vergleichbar, comparable, Sie erinnern

10:05.230 --> 10:08.810
sich, das hatten wir in der Vorlesung vorher eingeführt, und t muss

10:08.810 --> 10:11.050
jetzt etwas sein, was mindestens mal comparable ist.

10:11.610 --> 10:14.270
Und dann kann ich das Minimum berechnen, indem ich nämlich diese

10:14.270 --> 10:18.150
Compare -to-Funktion aufrufe, und genau die war ja in dem Comparable

10:18.150 --> 10:20.230
-Interface auch tatsächlich deklariert gewesen.

10:24.250 --> 10:31.050
Gut, dann wären wir an der Stelle auch am Ende von dieser Vorlesung

10:31.050 --> 10:33.250
und würden nochmal kurz zusammenfassen, was haben wir jetzt alles

10:33.250 --> 10:35.790
gehört, heute und vor allem natürlich beim letzten Mal.

10:37.430 --> 10:42.310
Wir haben über abstrakte Klassen gesprochen, abstrakte Klassen sind

10:42.310 --> 10:45.950
Oberklassen, die nicht instanzierbar sind, wo man sagt, ich möchte

10:45.950 --> 10:49.530
nicht, dass jemand davon ein Objekt instanziiert.

10:50.070 --> 10:51.910
Warum sind es zwangsläufig immer Oberklassen?

10:52.090 --> 10:55.450
Naja, irgendwann mal muss halt von der abstrakten Klasse geerbt werden

10:55.450 --> 11:00.090
und die abstrakt deklarierten Methoden überschrieben werden, damit man

11:00.090 --> 11:01.210
es nachher instanziieren kann.

11:01.530 --> 11:04.090
Wir haben auch so Spezialfälle gesehen, Sie erinnern sich an das Quiz,

11:04.190 --> 11:07.570
was wir gemacht hatten, dass es tatsächlich auch Oberklassen geben

11:07.570 --> 11:10.250
kann, die abstrakt sind, aber zum Beispiel keine abstrakte Methode

11:10.250 --> 11:12.670
drin haben, alle Methoden schon ausimplementiert sind, man will halt

11:12.670 --> 11:15.750
nur nicht, dass diese Oberklasse instanzierbar ist, sondern es muss

11:15.750 --> 11:16.910
immer noch spezialisiert werden.

11:17.390 --> 11:21.870
Das war zum Beispiel der Fall gewesen, wenn ich Oberklassen habe, die

11:21.870 --> 11:25.210
Dinge der realen Welt beschreiben, die aber auch nicht als Instanzen

11:25.210 --> 11:27.570
vorkommen, sondern immer nur als Zusammenfassung.

11:27.890 --> 11:30.650
Wir hatten das Beispiel Säugetiere, es gibt nicht das Säugetier per

11:30.650 --> 11:36.190
se, aber es gibt natürlich Spezialisierungen vom Säugetier,

11:37.350 --> 11:40.790
Menschenaffen, Hunde, die nämlich dann wieder instanzierbar sind und

11:40.790 --> 11:41.990
von denen es mehrere Instanzen gibt.

11:44.330 --> 11:48.030
Dann haben wir Interfaces kennengelernt.

11:48.530 --> 11:50.690
Interfaces definieren eine Schnittstelle.

11:51.390 --> 11:54.890
Das heißt, die Idee war gewesen, es gibt Entwurfsentscheidungen, die

11:54.890 --> 11:57.490
möchte ich verbergen, in der Regel, wir haben das kennengelernt beim

11:57.490 --> 12:00.410
Beispiel Liste, wie kann die Liste technisch implementiert werden?

12:00.510 --> 12:02.650
Und das trifft natürlich nicht nur auf Listen zu, sondern auf

12:02.650 --> 12:05.730
vielerlei andere Klassen, dass man sagt, wie es eigentlich

12:05.730 --> 12:06.450
implementiert ist.

12:06.510 --> 12:08.530
Da gibt es verschiedene Methoden, Wege, das zu machen.

12:10.410 --> 12:13.490
Die werden aber alle einheitlich angesprochen und dieses einheitliche

12:13.490 --> 12:15.870
Ansprechen definiert man dann eben über das Interface.

12:17.190 --> 12:19.990
Das sieht so ähnlich aus wie eine abstrakte Klasse.

12:20.450 --> 12:24.910
In beiden Fällen hat man Dinge, die selber nicht direkt instanzierbar

12:24.910 --> 12:27.410
sind, sondern es werden Gemeinsamkeiten beschrieben.

12:27.410 --> 12:31.090
Der Unterschied war der, bei abstrakten Klassen sagt man immer, das

12:31.090 --> 12:31.810
ist eine Oberklasse.

12:31.970 --> 12:34.870
Das heißt, es wird davon instanziiert und eine Spezialisierung quasi

12:34.870 --> 12:35.950
abgeleitet.

12:37.250 --> 12:39.830
Bei einem Interface ist es so ähnlich, aber nicht ganz.

12:39.950 --> 12:43.750
Beim Interface ist es so, dass verschiedene andere Klassen sagen

12:43.750 --> 12:47.490
können, ich implementiere diese Schnittstelle, was lediglich heißt,

12:47.830 --> 12:51.170
ich gebe eine Implementierung an für die Methoden, die in der

12:51.170 --> 12:52.270
Schnittstelle deklariert sind.

12:53.030 --> 12:55.750
Das muss nicht notwendigerweise eben eine Spezialisierungsbeziehung

12:55.750 --> 12:56.030
sein.

12:56.030 --> 12:58.250
Und wir haben auch noch den anderen Unterschied kennengelernt.

12:59.870 --> 13:04.330
Klassen können verschiedene, mehrere Schnittstellen gleichzeitig

13:04.330 --> 13:07.690
implementieren, aber es gibt in Java keine Mehrfachvererbung.

13:08.390 --> 13:08.870
Warum das?

13:08.990 --> 13:10.910
Manchmal problematisch ist es mit der Mehrfachvererbung.

13:11.210 --> 13:14.150
Das wissen alle C++-Programmierer und die anderen, die werden es

13:14.150 --> 13:18.490
spätestens in der Programmierparadigmen-Vorlesung hören, dann im

13:18.490 --> 13:19.150
fünften Semester.

13:21.050 --> 13:23.450
Es gibt noch einen weiteren Unterschied zwischen Interfaces und

13:23.450 --> 13:24.250
abstrakten Klassen.

13:24.910 --> 13:27.230
Bei einem Interface ist gar nichts implementiert.

13:27.310 --> 13:29.650
Man kann bei einer Schnittstelle gar keine Implementierung angeben,

13:29.690 --> 13:33.730
man kann wirklich nur eine Signaturliste angeben, Methoden, Signaturen

13:33.730 --> 13:34.090
angeben.

13:34.630 --> 13:36.570
Wohingegen bei einer abstrakten Klasse kann es durchaus sein, dass

13:36.570 --> 13:38.490
einzelne Methoden auch implementiert sind.

13:39.990 --> 13:43.510
Das letzte, was wir kennengelernt hatten, waren Generics gewesen.

13:44.810 --> 13:48.530
Bei Generics ist es so, dass man sagt, ich möchte verallgemeinern, ich

13:48.530 --> 13:52.210
möchte einen konkreten Typ durch eine Typvariable ersetzen.

13:52.210 --> 13:55.010
Das war das in diesen eckigen Klammern gewesen, das T.

13:55.590 --> 13:58.410
Ich könnte auch ein anderes Buchstaben nehmen wie T, wichtig ist nur,

13:58.510 --> 13:59.270
dass es in eckigen Klammern steht.

14:01.150 --> 14:04.110
Und hier ist die Überlegung, dass man sagt, ich habe Programmcode, der

14:04.110 --> 14:08.470
hängt eigentlich gar nicht vom Typ ab, sondern der ist generisch für

14:08.470 --> 14:09.190
verschiedene Typen.

14:09.770 --> 14:12.110
Und das kann man eben über Generics ausdrücken.

14:16.670 --> 14:18.950
So, dann wären wir mit der Vorlesung am Ende.

14:19.070 --> 14:22.610
Wir würden dann jetzt durchstarten zur Rekursionsvorlesung.

14:30.800 --> 14:32.660
Die ist, wie Sie sehen, auch nicht allzu lang.

14:35.640 --> 14:40.540
So, Ihrer Bitte, dass ich immer den Hinweis gebe, wo in diesem

14:40.540 --> 14:43.440
Anfängerlehrbuch über das Programmieren, den Grundkurs Programmierende

14:43.440 --> 14:47.220
Java von Wiesenberger und anderen, wo wir da jetzt sind.

14:47.920 --> 14:51.460
Das hier ist im Kapitel 6, und zwar genau seit Abschnitt 6.2, Rekursiv

14:51.460 --> 14:52.620
definierte Methoden drin.

14:53.140 --> 14:55.160
Das Material, was Sie jetzt bekommen, und da können Sie es natürlich

14:55.160 --> 14:57.180
auch nochmal im Detail nachlesen.

14:59.120 --> 15:02.600
Eine Sache haben Sie wahrscheinlich auch schon bei Kollegen Worsch

15:02.600 --> 15:03.500
gehört.

15:03.620 --> 15:08.200
Einer der wesentlichen Prinzipien der Informatik mit dem Umgang von

15:08.200 --> 15:12.540
Problemen ist, dass man sagt, ich teile diese Probleme auf in

15:12.540 --> 15:15.780
Subprobleme, löse dann die Subprobleme und führe dann das Ergebnis der

15:15.780 --> 15:18.380
Subprobleme zusammen in die Lösung des Gesamtproblems.

15:18.760 --> 15:21.980
Jetzt weiß ich, ich bin jetzt mittlerweile als Informatiker schon so

15:21.980 --> 15:23.600
verbildet, dass ich sage Probleme hier so.

15:23.600 --> 15:26.160
Ich habe das eben trotzdem so in Anführungszeichen gesetzt, weil es

15:26.160 --> 15:29.940
ist klar, viele Probleme, die man so im studentischen Alltag hat, wie

15:29.940 --> 15:32.960
finde ich einen Freund, wie finde ich eine Freundin, wie finde ich

15:32.960 --> 15:35.620
noch einen Sitzplatz beim Vogelbräu oder all so Sachen, das sind

15:35.620 --> 15:37.820
natürlich nicht Dinge, die wir algorithmisch so leicht lösen können.

15:38.340 --> 15:44.280
Mit Problemen meine ich natürlich die Dinge, die wir als Informatiker

15:44.280 --> 15:46.380
mit dem Rechner lösen wollen.

15:47.140 --> 15:49.940
Da ist es aber tatsächlich so, dass man Teile und Herrsche machen

15:49.940 --> 15:50.120
kann.

15:50.120 --> 15:52.520
Sie merken gleich, das klappt zum Beispiel beim Vogelbräu nicht, mit

15:52.520 --> 15:54.160
einem halben Sitzplatz ist mir da nicht gedient.

15:57.960 --> 16:01.280
Diese Technik, die haben Sie wahrscheinlich schon in verschiedenen

16:01.280 --> 16:03.480
Kontexten kennengelernt oder werden Sie auch im Laufe des Studiums

16:03.480 --> 16:05.820
immer wieder kennenlernen, in der Algorithmik ist das eines der

16:05.820 --> 16:08.680
wesentlichen Prinzipien, aber auch bei uns in der Softwaretechnik, die

16:08.680 --> 16:12.060
Überlegung, Klassen zu definieren und methodenweise zu implementieren,

16:12.140 --> 16:14.360
das ist jetzt auch eine Anwendung von diesem Prinzip, in dem man nicht

16:14.360 --> 16:17.160
sagt, ich habe das große Problem, ich muss ein Banktransaktionssystem

16:17.160 --> 16:20.820
schreiben, sondern dass man sagt, was gibt es denn hier für Entitäten

16:20.820 --> 16:24.060
aus der realen Welt, da gibt es verschiedene Rollen von Leuten, die

16:24.060 --> 16:28.780
damit interagieren und dass man nach und nach das abbildet in

16:28.780 --> 16:30.260
kleinere, beherrschbare Teile.

16:32.200 --> 16:38.160
Und dieser Gedanke, den kann man auch in der Algorithmik anwenden und

16:38.160 --> 16:42.140
eine besonders elegante Methode ist, dass man letzten Endes das Ganze

16:42.140 --> 16:46.220
nur über einen Parameter parametrisiert und dann natürlich dieselbe

16:46.220 --> 16:48.560
Funktion mal wieder neu aufruft und das werden wir nachher sehen.

16:48.740 --> 16:54.520
Hier ist erstmal ein Beispiel, wie es zu diesem Teil und Herrscher,

16:54.620 --> 16:57.120
wie das angewandt wird in der Algorithmik, das ist das sogenannte

16:57.120 --> 17:01.380
Rucksackproblem, das ist per se auch ein bekanntes Problem in der

17:01.380 --> 17:03.780
Algorithmik, in dem man sagt, ich habe einen Rucksack, der hat eine

17:03.780 --> 17:07.360
bestimmte Tragenkapazität und ich habe verschiedene Gegenstände, die

17:07.360 --> 17:11.200
ich einpacken will und die Gegenstände haben auch alle einen

17:11.200 --> 17:13.780
verschiedenen Wert und verschiedene Gewichte und jetzt möchte ich

17:13.780 --> 17:16.220
natürlich den Rucksack so packen, dass ich möglichst maximal an die

17:16.220 --> 17:20.360
Gesamtkapazität komme, aber insbesondere auch maximal viel Wert

17:20.360 --> 17:21.360
einpacken kann.

17:21.500 --> 17:24.120
Also der Klassiker, wenn Sie jetzt eine Bank ausrauben, der Tresor ist

17:24.120 --> 17:27.260
auf, Sie haben Goldstücke in verschiedener Größe und Sie können

17:27.260 --> 17:28.680
natürlich nicht alles gleichzeitig raustragen.

17:28.940 --> 17:30.480
So kann man sich das vorstellen.

17:30.620 --> 17:31.520
Das sehen alle in der Informatik.

17:31.960 --> 17:34.440
Algorithmikprobleme haben immer eine Motivation aus der realen Welt.

17:35.920 --> 17:41.580
Und jetzt ist es so, dieses Problem ist bekannt, dass man es eben

17:41.580 --> 17:44.860
nicht mit ganz schnellen Abkürzungen lösen kann.

17:45.020 --> 17:47.680
Das werden Sie auch nachher alles noch in der Algorithmikvorlesung

17:47.680 --> 17:49.220
erklärt bekommen.

17:50.320 --> 17:53.400
Hier nur als Beispiel, wie kann ich das Ganze aufteilen.

17:54.840 --> 17:57.880
Ich wähle also einen Gegenstand, von dem ich potenziell einpacken kann

17:57.880 --> 18:00.880
und jetzt betrachte ich die beiden Teilprobleme.

18:02.340 --> 18:04.600
Einmal der Gegenstand wird eingepackt.

18:05.340 --> 18:08.160
Was heißt das für die Gesamtlösung?

18:08.600 --> 18:14.020
Also wenn Sie jetzt zum Beispiel hier die 12 Kilo einpacken, was heißt

18:14.020 --> 18:15.520
das dann noch für das Gesamtproblem?

18:15.640 --> 18:17.760
Das heißt, dass Sie nur noch 3 Kilo weiter einpacken können.

18:18.760 --> 18:20.260
Und dann können Sie zum Beispiel das und das einpacken.

18:20.300 --> 18:22.920
Jetzt ist die Frage, ist das halt schlauer, als eben andere Dinge

18:22.920 --> 18:26.240
einzupacken, weil das 12 Kilo-Ding bringt Ihnen nur 4 Dollar, wie das

18:26.240 --> 18:29.320
angesagt ist, wogegen das 4 Kilo natürlich 10 Dollar gebracht hätte.

18:29.320 --> 18:32.280
Nur wenn Sie das 4 Kilo eingepackt haben, dann kriegen Sie den

18:32.280 --> 18:33.420
Rucksack nicht mehr voll mit dem.

18:35.040 --> 18:38.460
Und insofern kann man eben beide Probleme überlegen.

18:38.560 --> 18:40.440
Also ich wähle einen Gegenstand aus und dann überlege ich mir, was

18:40.440 --> 18:42.160
heißt das, wenn ich ihn einpacke oder was heißt das, wenn ich ihn

18:42.160 --> 18:42.820
nicht einpacke.

18:43.600 --> 18:48.180
Und dann löse ich im Prinzip für jeden der Fälle das durch, probiere

18:48.180 --> 18:50.760
ich das durch und dann wähle ich immer die bessere Variante.

18:51.000 --> 18:53.540
Das ist so ein ganz kleiner Ansatz, wie man dieses Teil und Herrscher

18:53.540 --> 18:54.200
angehen könnte.

18:55.380 --> 18:58.800
Das Beispiel ist zwar sehr schnell erklärt, weil klar die Situation

18:58.800 --> 19:01.380
kennt jeder von uns, offener Tresor, Geld einladen und so.

19:02.660 --> 19:06.120
Aber es ist natürlich insofern auch nicht so ganz super elegant, muss

19:06.120 --> 19:11.060
ich zugeben, weil wesentlich besser als ausprobieren werden Sie leider

19:11.060 --> 19:11.380
nicht.

19:11.540 --> 19:13.720
Insofern, so viel bringt das Teil und Herrscher auch nicht.

19:14.460 --> 19:15.480
Aber jetzt wieder zur Rekursion.

19:16.440 --> 19:21.720
Also das generelle Prinzip ist, ich möchte eigentlich dasselbe

19:21.720 --> 19:25.720
Berechnungsmuster immer und wieder ausführen, aber mit einer in dem

19:25.720 --> 19:27.240
Sinne immer kleineren Eingabe.

19:27.700 --> 19:30.820
Und zwar bis ich dann an eine Eingabe Parameter gekommen bin, der so

19:30.820 --> 19:33.100
einfach zu behandeln ist, dass ich die Lösung schon beim Programmieren

19:33.100 --> 19:33.960
hinschreiben kann.

19:36.780 --> 19:41.120
Und das funktioniert technisch so, dass man Methoden hat, die sich

19:41.120 --> 19:41.840
selber wieder aufrufen.

19:43.040 --> 19:45.520
Und wenn man das das erste Mal in seinem Leben sieht, dann bekommt man

19:45.520 --> 19:48.500
so einen kleinen Knoten im Hirn und denkt, wie, wenn ich eine Methode

19:48.500 --> 19:51.040
hinschreibe, kann ich dieselbe nochmal aufrufen, das geht?

19:51.380 --> 19:52.540
Und die Antwort ist, ja, das geht.

19:53.040 --> 19:55.040
Nur Sie müssen natürlich schauen, dass das Ganze nochmal irgendwann

19:55.040 --> 19:59.260
aufhört, dieses sich immer wieder selber aufrufen.

19:59.700 --> 20:01.220
Und das werden wir gleich in den Beispielen sehen.

20:01.720 --> 20:04.860
Und zwar erstmal noch ein bisschen Terminologie.

20:05.220 --> 20:11.520
Eine Methode F heißt direktrekursiv, wenn im Rumpf von F Aufrufe von

20:11.520 --> 20:13.040
diesem F eben vorkommen.

20:13.980 --> 20:17.000
Dann gibt es noch den Begriff des indirektrekursiven, das heißt eine

20:17.000 --> 20:21.520
Methode F ist indirektrekursiv, wenn im Rumpf von F eine Methode G

20:21.520 --> 20:25.680
aufgerufen wird, die ihrerseits direkt oder indirekt Aufrufe von F

20:25.680 --> 20:25.880
führt.

20:25.960 --> 20:29.300
Das heißt, Sie können also im Prinzip über mehrere Funktionsaufrufe so

20:29.300 --> 20:31.760
eine Kette bilden, bis Sie dann wieder beim F da sind.

20:31.840 --> 20:33.720
Das gibt es auch, das heißt auch Rekursion ist nur eben

20:33.720 --> 20:34.040
indirektrekursiv.

20:34.960 --> 20:37.120
Das ist insofern ein bisschen ekliger, weil das sehen Sie nicht

20:37.120 --> 20:37.520
sofort.

20:37.960 --> 20:41.040
Wenn Sie F angucken, wissen Sie, Methoden Rumpf von F habe ich vor

20:41.040 --> 20:42.760
mir, und da rufe ich F auf, ist klar, rekursiv.

20:43.080 --> 20:45.900
Wenn Sie natürlich in F gar kein F finden, sondern nur ein G und dann

20:45.900 --> 20:48.760
das G wieder das F aufruft, dann sehen Sie wieder bei G und F, dass

20:48.760 --> 20:49.740
eine Rekursion vorliegt.

20:49.920 --> 20:52.260
Trotzdem haben Sie nämlich eine indirekte Rekursion.

20:52.780 --> 20:56.180
Und da gibt es noch einen Sonderfall, das sogenannte endständige

20:56.180 --> 20:57.620
Rekursive.

20:57.780 --> 21:01.480
Und zwar eine Methode F ist endständig rekursiv, wenn im Rumpf nach

21:01.480 --> 21:05.120
dem rekursiven Aufruf von F kein weiterer Code mehr steht.

21:06.580 --> 21:07.860
Warum ist das so wichtig?

21:08.880 --> 21:10.900
Das wird Ihnen im Verlauf des Informatischen vielleicht nochmal

21:10.900 --> 21:11.380
klarer.

21:11.380 --> 21:16.300
Wenn das der Fall ist, können Sie schematisch die Rekursion auflösen

21:16.300 --> 21:16.960
in eine Schleife.

21:17.280 --> 21:19.080
Und das machen viele Compiler auch automatisch.

21:19.600 --> 21:22.240
Und das heißt, wenn Sie es immer schaffen, wenn Sie rekursiv

21:22.240 --> 21:25.200
programmieren, weil Sie das besonders elegant finden oder eine schöne

21:25.200 --> 21:28.080
Lösung dabei rauskommt, die Sie schön hinschreiben, die gut lesbar und

21:28.080 --> 21:31.320
gut wartbar ist, versuchen Sie es, wenn es geht, das endständig

21:31.320 --> 21:33.040
rekursiv hinzuschreiben nach der Compiler, mehr

21:33.040 --> 21:35.540
Optimierungsmöglichkeiten, performancemäßig da was rauszuholen.

21:36.220 --> 21:40.180
So, was passiert bei dem rekursiven Aufruf, eben von diesem F, wenn

21:40.180 --> 21:41.260
das wieder neu aufgerufen wird?

21:41.680 --> 21:46.680
Es wird eine, wenn man so will, neue Instanz dieser jeweiligen Methode

21:46.680 --> 21:47.200
gestartet.

21:47.380 --> 21:52.560
Und mit Instanz ist gemeint, es werden im Prinzip alle Daten, die die

21:52.560 --> 21:56.340
Methode zum Bearbeiten braucht, auch tatsächlich im Speicher nochmal

21:56.340 --> 21:57.120
neu angelegt.

21:58.740 --> 22:01.360
Das Ihnen fällt sofort auf von Ihrem bisherigen Wissen.

22:01.440 --> 22:04.000
Das betrifft zum Beispiel alle lokalen Variablen, die in der Methode

22:04.000 --> 22:04.720
deklariert waren.

22:05.320 --> 22:08.640
Diese lokalen Variablen müssen natürlich nochmal neu angelegt werden,

22:08.640 --> 22:10.600
weil Sie haben das F ja auch nochmal neu aufgerufen.

22:11.520 --> 22:16.500
Und auch alle Parameter, das heißt, die lokalen Variablen von einem

22:16.500 --> 22:20.480
Aufruf von F, der geschafft wird mit einem anderen Aufruf von F, da

22:20.480 --> 22:23.020
werden die lokalen Variablen nicht überschrieben, sondern es gibt im

22:23.020 --> 22:25.480
Prinzip einen neuen Satz von lokalen Variablen, einen neuen Satz von

22:25.480 --> 22:26.020
Parametern.

22:26.880 --> 22:30.060
Was Sie noch nicht so genau kennengelernt haben, was ich immer nur so

22:30.060 --> 22:33.440
auf der Tonspur erwähnt habe, es ist natürlich klar, wenn Sie eine

22:33.440 --> 22:37.460
Methode F aufrufen, muss natürlich zur Laufzeit das Programm sich

22:37.460 --> 22:40.840
merken, von welcher Stelle wurde denn das F aufgerufen, um nach dem

22:40.840 --> 22:43.300
Abarbeiten von F wieder dahin auch rückspringen zu können.

22:43.380 --> 22:44.800
Das ist die sogenannte Rücksprungadresse.

22:45.300 --> 22:47.620
Auch die muss man sich natürlich jedes Mal neu merken, wenn ich das F

22:47.620 --> 22:48.600
immer wieder neu aufrufe.

22:53.160 --> 22:55.320
So, und jetzt wird es mal Zeit für ein Beispiel.

22:55.820 --> 22:57.200
Wir machen ein ganz einfaches Beispiel.

22:58.020 --> 22:58.700
Fakultätsfunktion N.

22:59.200 --> 23:03.620
N-Fakultät, also dieses N-Ausrufezeichen, das kennen Sie schon im

23:03.620 --> 23:05.320
Prinzip seit der Grundschule.

23:05.320 --> 23:06.540
Naja, oder kurz danach.

23:09.260 --> 23:13.080
Ist im Prinzip so definiert, schon die mathematische Definition ist

23:13.080 --> 23:14.440
sehr elegant, wenn man sie rekursiv hinschreibt.

23:14.840 --> 23:16.840
0-Fakultät ist definierterweise 1.

23:18.040 --> 23:23.380
Und wenn ich N-Fakultät habe, dann sage ich halt, das ist dasselbe wie

23:23.380 --> 23:27.340
N mal, dann N-1-Fakultät, für das N größer gleich 1.

23:28.080 --> 23:30.880
Das ist nicht definiert erstmal hier für negative Zahlen, brauchen wir

23:30.880 --> 23:33.640
auch nicht, das ist auch erstmal hier definiert, nur für natürliche

23:33.640 --> 23:33.960
Zahlen.

23:35.440 --> 23:37.120
Verallgemeinerung brauchen wir jetzt erstmal nicht besprechen.

23:37.620 --> 23:38.760
So, und wie lässt sich das hinschreiben?

23:38.880 --> 23:42.000
Relativ elegant in unserem Java-Code, wo eigentlich genau diese

23:42.000 --> 23:46.020
mathematische Definition eigentlich nur in Java gecodet wird.

23:46.700 --> 23:50.620
Sie sagen, ich habe mal eine Fakultätsfunktion, ich habe den Parameter

23:50.620 --> 23:56.340
N, und wenn das N größer 0 ist, also dann wird dieser Fall hingehen,

23:56.400 --> 24:00.620
dann schreibt man hin, return N mal Fakultät von N-1, das ist exakt

24:00.620 --> 24:01.480
das, was hier steht.

24:03.500 --> 24:09.660
Oder das else hier, das ist also dann der Fall, wenn N gleich 0 ist,

24:10.080 --> 24:11.820
dann wird das Ergebnis 1 zurückgegeben.

24:12.480 --> 24:15.240
Und das ist insofern ein sehr einfaches, aber ein sehr treffendes

24:15.240 --> 24:18.700
Beispiel für Rekrussion.

24:19.560 --> 24:19.740
Warum?

24:20.520 --> 24:24.860
Naja, weil hier an der Stelle dieser Parameter N immer verringert

24:24.860 --> 24:27.760
wird, Sie sehen, hier Fakultät wird aufgerufen, aber mit einem um 1

24:27.760 --> 24:34.100
verringerten Parameter, und für 0 ist die Lösung so einfach und klar,

24:34.360 --> 24:37.500
dass Sie sie schon beim Programmieren hinschreiben können, nämlich 1.

24:38.400 --> 24:40.180
Das war nicht so schwer, weil es ist genau so definiert.

24:42.600 --> 24:45.500
So, eine Sache muss man sich jetzt überlegen, wenn man so munter

24:45.500 --> 24:46.300
rekursiv was aufruft.

24:47.240 --> 24:51.460
Erstens mal, wie ich sagte, bei jedem rekursiven Aufruf hier von der

24:51.460 --> 24:55.760
Fakultätsfunktion werden alle Parameter, alle lokalen Variablen,

24:56.100 --> 24:59.400
Rücksprungadressen, nochmal neu Speicher für gebraucht.

24:59.560 --> 25:03.560
Das kostet immer Zeit, diesen Speicher anzulegen, zu verwalten, und es

25:03.560 --> 25:04.240
kostet auch Speicher.

25:04.520 --> 25:08.460
Das heißt, wenn Sie jetzt von sehr hohen Zahlen die Fakultät auf die

25:08.460 --> 25:11.820
Art und Weise ausrechnen, haben Sie natürlich jedes Mal, wenn Sie die

25:11.820 --> 25:14.060
Fakultät von einer Million ausrechnen, wollen Sie ein Millionen Mal

25:15.180 --> 25:16.520
immer neu den Speicher belegen.

25:16.620 --> 25:18.980
Dass das nicht besonders speichereffizient ist, ist Ihnen klar.

25:19.960 --> 25:22.220
Deswegen ist es auch nicht gerade super schnell, rekursiv zu

25:22.220 --> 25:24.500
programmieren, aber es ist manchmal sehr elegant, hinzuschreiben.

25:26.580 --> 25:29.440
Das andere, was man sich natürlich überlegen muss, ist noch schlimmer.

25:30.580 --> 25:33.420
Stellen Sie sich mal vor, Sie würden hier nicht n-1, sondern sowas wie

25:33.420 --> 25:34.560
n -1 hinschreiben.

25:34.660 --> 25:37.240
Der Compiler würde sich nicht beschweren, aber es ist klar, das

25:37.240 --> 25:38.360
Programm würde nicht konvergieren.

25:38.560 --> 25:42.180
Es würde divergieren, weil Sie diesen Abbruchfall, wo Sie einmal nicht

25:42.180 --> 25:45.860
in die Rekursion reingehen, nämlich hier, das Rekursionsende, die

25:45.860 --> 25:47.640
Abbruchbedingung, nie erreichen werden.

25:48.580 --> 25:51.000
Das heißt, Sie müssen sich natürlich jedes Mal überlegen, wenn Sie

25:51.000 --> 25:54.240
einen rekursiven Aufruf machen, habe ich meine Parameter auch so

25:54.240 --> 25:57.880
manipuliert, dass ich mir als Programmierer sicher bin, dass ich

25:57.880 --> 26:00.200
tatsächlich auch mal irgendwann die Bedingungen erreiche, wo die

26:00.200 --> 26:04.160
Rekursion abbricht, nämlich dieser Teil in Turn 1, wo nicht mehr was

26:04.160 --> 26:05.100
rekursiv aufgerufen wird.

26:05.480 --> 26:08.440
Das ist hier eigentlich relativ schnell sichtbar der Fall.

26:08.740 --> 26:14.280
Sie starten mit einer positiven Zahl n und Sie verringern im Prinzip

26:14.280 --> 26:15.740
jedes Mal die Zahl um 1.

26:16.200 --> 26:18.520
Und dann ist klar, dann werden Sie irgendwann auch mal auf n gleich 0

26:18.920 --> 26:19.280
stoßen.

26:20.580 --> 26:21.960
Das ist nicht zu vermeiden.

26:22.340 --> 26:25.880
Sie sehen aber auch, wenn ich diese Funktion mit einer negativen n

26:25.880 --> 26:31.100
aufrufen würde, dann käme ich halt eine 1 raus.

26:31.320 --> 26:31.820
Glück gehabt.

26:33.060 --> 26:35.660
Aber ansonsten ist klar, wenn ich eine negative Zahl immer noch als 1

26:35.660 --> 26:38.240
abziehen würde, das würde nicht auf die 0 draufkommen.

26:40.360 --> 26:42.540
Das heißt also, jedes Mal, wenn Sie eine Rekursion hinschreiben, das

26:42.540 --> 26:43.940
ist im Prinzip genauso wie bei einer Schleife.

26:44.100 --> 26:46.760
Bei einer Schleife müssen Sie sich auch überlegen, wird die

26:46.760 --> 26:47.360
terminieren?

26:47.460 --> 26:49.600
Wird die Abbruchbedingung von der Schleife jemals erreicht?

26:49.740 --> 26:50.960
Und dasselbe gilt im Prinzip auch hier.

26:51.920 --> 26:56.180
Wird dieser Rekursionsende auch tatsächlich jemals erreicht?

26:56.300 --> 26:57.420
Das müssen Sie sich natürlich auch fragen.

26:57.960 --> 27:01.620
Schauen wir uns mal an, was bei der Auswertung von dieser

27:01.620 --> 27:03.780
Fakultätsfunktion passiert.

27:07.020 --> 27:09.340
Wir rufen jetzt mal die Fakultät von 4 auf.

27:10.060 --> 27:11.960
4, weil wir nicht so beliebig viel Platz hier haben.

27:12.740 --> 27:22.380
Also, was passiert, also 4 wird jetzt aufgerufen und wir hören erstmal

27:22.380 --> 27:24.100
4 größer 0, das ist nicht der Fall.

27:24.160 --> 27:25.420
Wir sind also nicht beim Rekursionsende.

27:25.540 --> 27:29.200
Also machen wir 4 mal Fakultät 4 minus 1.

27:30.060 --> 27:30.940
Wird ausgeführt.

27:33.240 --> 27:35.760
Das else eben nicht, weil hier ist größer 0.

27:36.600 --> 27:39.160
So, da habe ich also 4 mal Fakultät von 3 hier stehen.

27:39.160 --> 27:41.420
Die 4 minus 1 werte ich so 3 aus.

27:43.020 --> 27:43.880
Was passiert dann?

27:43.980 --> 27:47.360
Ich gehe wieder rein in die Fakultätsfunktion, in den Rumpf.

27:47.800 --> 27:49.440
Habe jetzt die Abfrage 3 größer 0.

27:50.760 --> 27:53.360
Wenn ja, wird 3 mal Fakultät minus 1 aufgerufen, sonst 1.

27:53.700 --> 27:56.060
1 wird aber natürlich nicht aufgerufen, sondern es wird der Teil

27:56.060 --> 27:59.200
ausgewertet, weil eben 3 größer 0 ist.

27:59.740 --> 28:03.960
Also habe ich hier 4 mal 3 mal Fakultät von 2 stehen, das kommt hier

28:03.960 --> 28:04.200
her.

28:05.080 --> 28:06.600
Jetzt wird das ganze Spielchen wiederholt.

28:06.600 --> 28:08.620
2 größer 0, wird erst abgefragt.

28:09.520 --> 28:11.840
Dann 2 mal Fakultät minus 1, sonst 1.

28:11.980 --> 28:13.740
Wird natürlich auch wieder der Teil ausgewertet.

28:13.960 --> 28:18.060
Also habe ich jetzt, wenn ich das hier einsetze dafür, 4 mal 3 mal 2

28:18.060 --> 28:19.320
mal Fakultät von 1.

28:20.540 --> 28:21.340
So, dasselbe nochmal.

28:22.600 --> 28:24.220
1 größer 0, ja ist der Fall.

28:24.560 --> 28:26.280
Also wird dieser Teil wieder ausgewertet.

28:26.360 --> 28:28.060
1 mal Fakultät 1 minus 1.

28:28.860 --> 28:31.600
So, und jetzt habe ich hier stehen, 4 mal 3 mal 2 mal 1 mal Fakultät

28:31.600 --> 28:32.040
von 0.

28:32.580 --> 28:33.440
Jetzt komme ich wieder.

28:33.440 --> 28:34.880
0 größer 0.

28:37.840 --> 28:39.940
So, das ist jetzt endlich mal false.

28:40.360 --> 28:44.740
Also gehe ich in die 1 rein, gebe die 1 zurück und dann wird der

28:44.740 --> 28:46.420
gesamte Ausdruck ausgewertet zurückgegeben.

28:49.720 --> 28:52.860
Sie sehen also schon an diesem kleinen Beispiel, wie aufwendig das

28:52.860 --> 28:53.480
ganze wird.

28:54.100 --> 28:57.200
Und das, was den schlimmsten Aufwand verursacht, nämlich jedes Mal der

28:57.200 --> 28:59.400
neue Aufruf von der Funktion, das haben wir hier gar nicht

28:59.400 --> 28:59.960
dargestellt.

29:00.760 --> 29:02.960
Aber Sie sehen, im Prinzip funktioniert das.

29:02.960 --> 29:05.240
Und das Schöne ist halt, wie gesagt, das ist sehr nah an der

29:05.240 --> 29:08.200
mathematischen Definition, was natürlich auch ein gut verständlicher

29:08.200 --> 29:08.660
Code ist.

29:10.060 --> 29:13.980
Jetzt gibt es Stellen, wo diese Rekursion tatsächlich auch mal

29:13.980 --> 29:17.980
nützlich ist, und zwar die Binomialfunktion.

29:18.540 --> 29:21.380
N über K, das kennen Sie wahrscheinlich schon aus der Kombinatorik von

29:21.380 --> 29:25.380
der Schule, wenn nicht, wird es Ihnen auch bei Herrn Worsch vorkommen

29:25.380 --> 29:26.960
und auch in den Mathematik-Vorlesungen.

29:30.200 --> 29:37.200
Für positive oder nicht negative Ns und Ks kann man die

29:37.200 --> 29:44.680
Binomialfunktion definieren als N über K und zwar mit N-Fakultät

29:44.680 --> 29:48.300
geteilt durch N-K-Fakultät mal K-Fakultät.

29:48.700 --> 29:50.240
Warum das so ist, will ich hier nicht erklären.

29:50.540 --> 29:52.080
Das wird Ihnen an einer anderen Stelle erläutert.

29:52.620 --> 29:54.540
Nehmen Sie die Formel erstmal so hin, dass es so definiert.

29:55.120 --> 29:56.940
Und jetzt könnte ich das natürlich im Prinzip codieren.

29:57.040 --> 29:59.340
Ich habe jetzt eben die Fakultätsfunktion vorgestellt, jetzt kann ich

29:59.340 --> 30:03.500
das natürlich lässig hier diesen Bruch hinschreiben in Java-Code.

30:04.860 --> 30:10.560
Der Nachteil ist, N-Fakultät kann natürlich sehr groß werden, auch

30:10.560 --> 30:13.280
wenn N über K vielleicht gar nicht so groß ist, weil sich letzten

30:13.280 --> 30:19.560
Endes durch das Teilen hier und den großen Nenner dieser hohe Wert von

30:19.560 --> 30:21.940
dem Zähler eigentlich relativ schnell wieder wegkürzt.

30:22.620 --> 30:24.480
Nichtsdestotrotz, wenn ich es natürlich erstmal hinschreibe, sage ich,

30:24.540 --> 30:26.380
ich möchte jetzt erstmal N-Fakultät ausrechnen, ist natürlich klar,

30:26.380 --> 30:29.700
Sie kommen sehr schnell in einen Bereich rein, wo Sie beim Integer

30:29.700 --> 30:31.200
oder auch beim Loggen einen Überlauf haben.

30:32.520 --> 30:35.800
Und deswegen ist es relativ schlau, gar nicht die Fakultät explizit

30:35.800 --> 30:40.240
auszuwerten, sondern eine rekursive Formel zu nutzen, und zwar gilt

30:40.240 --> 30:45.720
auch, kann man beweisen, aber keine Sorge, machen wir hier nicht, N

30:45.720 --> 30:53.480
über K ist gleich dasselbe wie N-1 über K-1 plus N-1 über K.

30:54.580 --> 30:57.320
Und weil genau diese rekursive Formel gilt, habe ich natürlich jetzt

30:57.320 --> 31:01.100
Glück, jetzt kann ich nämlich das hier nutzen für einen rekursiven

31:01.100 --> 31:09.040
Aufruf, weil N über K berechne ich in Hilfe von N über K und die

31:09.040 --> 31:11.320
Abbruchbedingungen habe ich hier auch schon gleich stehen, N über 0

31:11.320 --> 31:12.900
ist nämlich 1 und N über N auch.

31:13.860 --> 31:16.640
Und auf die Art und Weise kann ich das hinschreiben, ohne die Fakultät

31:16.640 --> 31:19.720
explizit auszurechnen und die Gefahr von diesem Überlauf damit

31:19.720 --> 31:20.140
vermeiden.

31:20.140 --> 31:27.220
So, das ist also jetzt erstmal die mathematische Definition und das

31:27.220 --> 31:28.640
ist der Java-Code dazu.

31:29.700 --> 31:35.820
Lassen Sie sich hier nicht von diesem ersten If irritieren, das soll

31:35.820 --> 31:39.980
nur ausschließen, dass man mit einem negativen K, wo die wir es

31:39.980 --> 31:42.820
nämlich gar nicht definiert hatten, oder mit einem K größer N das

31:42.820 --> 31:44.800
Ganze aufrufen, das dann soll an der Stelle 0 kommen.

31:44.880 --> 31:48.860
Der interessante Teil, der diese mathematische Definition reflektiert,

31:49.080 --> 31:50.500
der steckt nämlich jetzt exakt hier drin.

31:52.480 --> 31:57.740
Also das ist unsere Formel Binom, die also dieses N über K berechnet

31:57.740 --> 32:01.140
und als Parameter habe ich hier N und K es ergibt auch wieder einen

32:01.140 --> 32:08.080
Integerwert und jetzt nutze ich eigentlich genau diese Definition aus,

32:08.760 --> 32:12.380
falls K gleich 0 ist oder K gleich N, genau das habe ich hier in Java

32:12.380 --> 32:15.240
hingeschrieben, gebe ich den Wert 1 zurück, genau wie es hier

32:15.240 --> 32:19.960
definiert wurde, ansonsten gehe ich in die Rekursion und rechne hier

32:19.960 --> 32:27.400
dann von N-1 Komma K-1 das ist dieser Ausdruck und addiere da drauf,

32:27.500 --> 32:31.140
hier ist die Addition, N-1 K, also das was da steht.

32:32.840 --> 32:34.780
Warum konvergiert das Ganze?

32:34.840 --> 32:37.380
Das ist natürlich jetzt hier nicht mehr ganz so schnell zu sehen, wie

32:37.380 --> 32:40.200
das eben bei der Fakultät war, aber auch hier wieder das Argument,

32:40.700 --> 32:44.660
letzten Endes zähle ich diese Werte immer weiter runter, ich erhöhe

32:44.660 --> 32:50.720
die nicht und ich zähle auch das N an der Stelle runter, auch wenn das

32:50.720 --> 32:53.700
K hier gleich bleibt und das heißt, ich komme entweder mal auf diesen

32:53.700 --> 32:57.640
Fall oder auf diesen Fall und breche eben mit der 1 ab und gehe nicht

32:57.640 --> 32:58.200
in die Rekursion.

32:58.300 --> 33:00.480
Wie gesagt, hier ein bisschen schwerer zu sehen, funktioniert aber

33:00.480 --> 33:00.860
auch so.

33:02.080 --> 33:04.880
So, und jetzt kann man sich das nochmal grafisch anschauen, was

33:04.880 --> 33:10.500
passiert jetzt hier beim Aufruf von Binom 3, 2, also das heißt so viel

33:10.500 --> 33:18.980
wie 3 über 2 und ich habe jetzt hier quasi die Funktion, den Code euch

33:18.980 --> 33:22.440
nochmal hingeschrieben und hier, wo diese Striche stehen, da werden

33:22.440 --> 33:24.180
gleich die aktuellen Parameter eingesetzt.

33:24.260 --> 33:26.220
Die formalen Parameter sind natürlich N und K.

33:27.320 --> 33:31.880
Beim ersten Aufruf 3, 2, setze ich das hier ein, das sind die

33:31.880 --> 33:36.880
aktuellen Parameter, habe jetzt hier genau diese if-Abfrage, die ist

33:36.880 --> 33:40.320
in beiden Fällen false, das ist false, das ist false, oder also auch,

33:40.500 --> 33:46.640
also gehe ich hier rein und rufe jetzt Binom 3-1, 2-1 plus Binom von 3

33:46.640 --> 33:47.560
-1, 2 auf.

33:49.540 --> 33:52.680
Das werte ich schonmal gleich aus, hier die Ausdrücke und gehe damit

33:52.680 --> 33:53.600
in die Rekursion rein.

33:54.220 --> 33:59.400
Jetzt habe ich also hier Binom 2, 1, mache wieder diese Abfrage, die

33:59.400 --> 34:03.120
ist wieder in beiden Fällen falsch, also gehe ich wieder hier auch in

34:03.120 --> 34:07.320
die Rekursion rein, in den else-Teil, werte die Ausdrücke auch gleich

34:07.320 --> 34:11.940
aus, rufe jetzt Binom 1, 0 auf und dann danach Binom 1, 1.

34:12.440 --> 34:16.900
So, 1, 0, das ist jetzt hier interessant, jetzt bin ich hier bei 0

34:16.900 --> 34:19.780
gleich gleich 0, das ist wahr und damit ist auch dieser gesamte

34:19.780 --> 34:20.320
Ausdruck wahr.

34:20.920 --> 34:23.660
Ich gehe nicht mehr in die Rekursion rein, sondern gehe hier in die

34:24.580 --> 34:30.160
Retoniere die 1, gebe die zurück, und auf der anderen Seite, also der

34:30.160 --> 34:32.620
Ausdruck ist schon mal ersetzt, und jetzt gehe ich hier noch in den

34:32.620 --> 34:39.940
zweiten Teil, Rekursiv rein, 1, 1, hier sehen Sie, ist jetzt der

34:40.520 --> 34:45.260
zweite Teil von der IF-Abfrage, von dem ODA-Ausdruck wahr, das heißt,

34:45.380 --> 34:48.100
ich gebe auch hier eine 1 zurück, das heißt, das ersetzt sich mit

34:48.100 --> 34:51.400
einer 1, ich bin nicht weiter in die Rekursion reingegangen, gebe also

34:51.400 --> 34:55.060
jetzt hier 2 zurück, so das ist also den ersten Teil, habe ich jetzt

34:55.060 --> 34:57.720
schon mal ersetzt, mit einer 2, und jetzt gehe ich natürlich noch in

34:57.720 --> 35:00.560
den zweiten Rekursiven Ausdruck hier rein, den muss ich natürlich nach

35:00.560 --> 35:04.160
wie vor auswerten, den habe ich ja bisher noch nicht gemacht, und

35:05.720 --> 35:10.520
jetzt gehe ich hier rein, wieder gleiches Schema, 2 gleich 0 ist

35:10.520 --> 35:16.040
falsch, aber 2 ist wahr, also gebe ich hier wieder die 1 zurück, und

35:16.040 --> 35:18.960
gebe dann als insgesamtes Ergebnis eben 3 zurück.

35:20.040 --> 35:24.880
So, und jetzt haben Sie gesehen, letzten Endes, wenn ich hier mehr als

35:24.880 --> 35:29.420
eine Rekursion drin habe, in dem Ausdruck, dann verzweigt das jetzt so

35:29.420 --> 35:31.860
baumartig, und der Baum wird dann nachher im Prinzip in der

35:31.860 --> 35:33.320
Tiefensuche durchgelaufen.

35:37.380 --> 35:40.960
Jetzt könnte man sagen, ein bisschen optimieren kann man es ja schon,

35:41.780 --> 35:45.660
weil es kann ja sein, dass im Laufe dieser ganzen Binomauswertungen

35:46.580 --> 35:51.980
ich vielleicht Binomausdrücke habe, die ich schon mal ausgewertet habe

35:51.980 --> 35:54.600
vorher, und das Ergebnis möchte ich ja eigentlich nicht nochmal neu

35:54.600 --> 35:57.300
kompliziert berechnen, vor allem, wenn ich dann Ewigkeiten in der

35:57.300 --> 35:58.500
Rekursion noch absteigen muss.

36:00.320 --> 36:02.920
Und das ist eine Variante, die wir hier sehen, das ist ein bisschen

36:02.920 --> 36:04.520
schlauer, nämlich mit einem Cache.

36:04.940 --> 36:10.620
Und zwar habe ich hier erstmal eine Funktion binom, n über k, also

36:10.620 --> 36:15.560
wieder die zwei Parameter, und definiere hier einen Cache,

36:16.300 --> 36:17.740
zweidimensionales Array,

36:23.210 --> 36:27.970
wo ich also n plus 1 Werte habe und k plus 1 Werte.

36:29.090 --> 36:32.230
Das plus 1 kommt dadurch, dass ich auch die 0 speichern will, also 0

36:32.230 --> 36:33.710
bis n sind halt eben n plus 1 Werte.

36:35.070 --> 36:40.150
Und dann rufe ich hier, das sieht aus wie rekursiv, ist aber nicht, zu

36:40.150 --> 36:44.850
einer Routine binom auf, aber das ist nicht dieselbe binom-Routine,

36:45.210 --> 36:47.810
das ist nämlich überladen, das Wort binom, hier sehen Sie, die hat

36:47.810 --> 36:50.430
nämlich drei Parameter, nämlich n, k und diesen Cache, den ich hier

36:50.430 --> 36:50.990
angelegt habe.

36:51.950 --> 36:54.590
Das heißt, das ist also erstmal hier noch kein rekursiver Aufruf, auch

36:54.590 --> 36:55.630
wenn hier derselbe Name steht.

36:56.150 --> 36:59.150
Aber hier unten ist jetzt wieder die Rekursion drin und zwar, was

36:59.150 --> 36:59.610
mache ich hier?

36:59.670 --> 37:03.890
Ich habe hier die Routine binom mit drei Parametern, n und k und eben

37:03.890 --> 37:06.150
meinem zweidimensionalen Array als Cache.

37:07.710 --> 37:10.690
Und der Cache ist, wie der Name Cache eben immer andeutet, ein

37:10.690 --> 37:13.370
Zwischenspeicher, in dem Ergebnisse gehalten werden.

37:16.430 --> 37:20.510
Hier schaue ich nach, ist im Prinzip das, was ich hier als n und k

37:20.510 --> 37:23.790
mitgegeben habe, wenn ich das n und k als Indizes im Cache nehme,

37:24.250 --> 37:25.050
gleich 0.

37:25.210 --> 37:30.570
Wenn das der Fall ist, dann heißt das, an der Stelle ist im Cache noch

37:30.570 --> 37:31.410
gar kein Wert gelegt.

37:31.530 --> 37:33.430
Das ist ein Trick, den Sie häufiger sehen werden.

37:35.090 --> 37:40.510
Sie verwenden die Parameter quasi als Indizes für eine Datenstruktur,

37:40.690 --> 37:43.090
für einen Cache in dem Falle, um zu schauen, wurde das schon mal

37:43.090 --> 37:43.750
ausgewertet.

37:44.210 --> 37:46.850
Und wenn jetzt hier 0 ist, heißt das, es wurde noch nicht ausgewertet.

37:46.910 --> 37:51.190
Dann muss ich tatsächlich das machen, was ich schon vorher mir genau

37:51.190 --> 37:52.270
als Algorithmus auch hatte.

37:54.730 --> 37:57.890
Wir schauen jetzt nach, bin ich im Bereich des Abbruchs der Rekursion,

37:58.410 --> 38:00.570
dann mache ich in der Stelle jetzt erstmal keinen Return, sondern

38:00.570 --> 38:04.110
erstmal nur einen Eintrag in den Cache.

38:04.810 --> 38:07.790
Oder ansonsten mache ich auch einen Eintrag in den Cache, gehe aber

38:07.790 --> 38:10.010
natürlich in die Rekursion auch nochmal tatsächlich rein.

38:14.310 --> 38:17.430
das Spielchen mache ich jetzt im Prinzip genauso wie eben von der

38:17.430 --> 38:19.390
Ausführung getragen, aber wie gesagt, die Ergebnisse immer brav in

38:19.390 --> 38:22.210
meinen Cache ein und wenn ich ganz am Ende bin mit der Rekursion, also

38:22.210 --> 38:27.110
alles wieder aufgestiegen bin, alles zu Ende ist, dann gebe ich den

38:27.110 --> 38:31.850
Cache an der Stelle N K mit diesen Indizes wieder zurück und habe dann

38:31.850 --> 38:35.690
letzten Endes den Wert, der eigentlich angefragt war, zurückgegeben

38:35.690 --> 38:37.390
und halt auch noch nebenbei den Cache gefüllt.

38:37.530 --> 38:40.250
Also ich könnte wahrscheinlich noch jede Menge andere Einträge mit

38:40.250 --> 38:41.890
zurückgeben, aber das war ja nicht gefragt gewesen.

38:42.630 --> 38:44.710
Das war ja im Prinzip nur der Wert für N über K gefragt.

38:45.010 --> 38:49.990
Trotzdem habe ich mal auf diese Art des Cachings gespart, den selben

38:49.990 --> 38:52.530
Binomenausdruck mehrfach auszuwerten mit den selben Parametern.

38:52.650 --> 38:55.210
Sondern ich werte den nur einmal aus und dann gebe ich das im Prinzip

38:55.210 --> 38:55.930
im Cache zurück.

38:57.510 --> 39:00.810
Wenn nämlich hier ein Wert ungleich Null steht, ein positiver Wert,

39:01.230 --> 39:03.550
dann heißt es, dieser Wert ist schon belegt worden und dann gebe ich

39:03.550 --> 39:04.650
einfach auch diesen Wert zurück.

39:07.370 --> 39:12.770
Das Ganze baut natürlich davon auf, dass wenn ich hier mit New Int ein

39:12.770 --> 39:16.110
Integer anlege, dass es defaultmäßig mit Null gefüllt wird.

39:16.250 --> 39:18.130
Wenn Sie in einer Programmiersprache arbeiten, wo Sie das natürlich

39:18.130 --> 39:20.650
nicht erwarten können, müssen Sie natürlich erstmal noch dieses

39:20.650 --> 39:23.070
Integer -Array mit Null füllen, weil Null heißt ja letzten Endes

39:23.070 --> 39:23.910
ungültiger Eintrag.

39:23.990 --> 39:25.050
Hier wurde doch nichts berechnet.

39:29.340 --> 39:34.780
Gut, dann wären wir an der Stelle am Ende von unserer

39:36.200 --> 39:36.600
Rekursionsvorlesung.

39:36.660 --> 39:38.260
Aber springen Sie doch nicht auf, es kommt gleich noch was.

39:39.300 --> 39:41.680
Und zwar fassen wir das zusammen.

39:42.400 --> 39:46.380
Die Idee von Rekursion ist, dass ich ein Problem löse durch die

39:46.380 --> 39:48.600
Berechnung einer kleineren Instanz des Problems.

39:48.680 --> 39:51.300
Und diese kleinere Instanz wird ausgedrückt in der Regel durch einen

39:51.300 --> 39:52.160
kleineren Parameter.

39:54.820 --> 39:58.680
Und das Ganze mache ich halt so weit, bis das Problem so klein ist,

39:58.860 --> 40:01.400
dass ich die Instanz schon beim Programmieren lösen kann, sprich also

40:01.400 --> 40:02.800
hart reinkoden kann meinen Code.

40:05.100 --> 40:07.380
Ich hatte Ihnen gesagt, man muss darüber nachdenken, dass das Ganze

40:07.380 --> 40:08.000
auch terminiert.

40:08.080 --> 40:10.140
Sie können natürlich, genauso wie bei einer Schleife, lässig einen

40:10.140 --> 40:11.520
Code hinschreiben, der nicht terminiert.

40:11.600 --> 40:11.980
Auch klar.

40:13.960 --> 40:18.420
Und der Speicherverbrauch ist lineal zu der Zahl der rekursiven

40:18.420 --> 40:19.460
Aufrufe, die Sie haben.

40:20.340 --> 40:23.380
Weil nämlich jedes Mal beim Aufruf eben für lokale Variablen,

40:23.500 --> 40:27.320
Parameter, Rücksprungadressen einfach immer ein Speicherplatz belegt

40:27.320 --> 40:27.540
wird.

40:31.420 --> 40:31.640
Gut.

40:31.920 --> 40:36.280
Dann komme ich schnell, bevor alle weglaufen, zum dritten Teil der

40:36.280 --> 40:37.020
heutigen Vorlesung.

40:40.580 --> 40:42.280
Nämlich zur sogenannten Weihnachtsvorlesung.

40:47.790 --> 40:50.410
So, das ist also die Vorlesung, wenn man so will, 10 Beta.

40:51.750 --> 40:53.970
Beta ist meistens immer beim Programmieren was, was noch nicht so

40:53.970 --> 40:54.450
stabil ist.

40:55.310 --> 40:57.110
Das ist die Programmiersprache Intercal.

40:57.270 --> 40:59.910
Mal ganz schnell, wer hat schon Programmiererfahrungen in Intercal

40:59.910 --> 41:00.330
gesammelt?

41:01.410 --> 41:02.430
Keiner, das ist gut.

41:03.470 --> 41:06.230
Es hätte mich auch zugegebenermaßen etwas irritiert und ich würde mir

41:06.230 --> 41:09.150
Gedanken machen, über das deutsche Bildungssystem, wenn Sie es schon

41:09.150 --> 41:09.810
gesammelt hätten.

41:11.230 --> 41:15.710
Und zwar, wie gesagt, es gibt das Weihnachtsgeschenk Ihrer

41:15.710 --> 41:19.970
Programmiervorlesung und drin ist in der Box nämlich die Intercal

41:19.970 --> 41:20.950
-Programmiersprache.

41:22.470 --> 41:25.030
Programmieren lernt ganz leicht gemacht.

41:25.370 --> 41:29.890
Also erstmal, Intercal ist nicht von mir, ich finde es aber, weil ich

41:29.890 --> 41:31.210
Informatiker bin, ziemlich lustig.

41:33.350 --> 41:36.070
Intercal ist also intuitiver Weise ein Akronym für die

41:36.070 --> 41:39.130
Programmiersprache mit dem Namen Compiler Language with no

41:39.130 --> 41:40.150
Pronouncible Acronym.

41:40.330 --> 41:43.850
And therefore for obvious reasons called Intercal, so steht es im

41:43.850 --> 41:44.670
Tutorial.

41:46.150 --> 41:48.410
Es ist eine vollkommen einfache Programmiersprache.

41:48.490 --> 41:50.810
Man merkt das auch in der Zeit, die man brauchte, um sie zu

41:50.810 --> 41:51.310
definieren.

41:51.450 --> 41:52.790
Und zwar war das am 26.

41:53.230 --> 41:57.330
Mai 72 morgens in Princeton.

41:58.310 --> 42:00.950
Und tatsächlich, also diese beiden Herren hatten das damals definiert,

42:01.030 --> 42:03.350
weil sie wahrscheinlich irgendwie auch nichts Besseres zu tun hatten.

42:04.650 --> 42:07.890
Und die Motivation war, eine Programmiersprache zu definieren, die

42:08.290 --> 42:11.530
überhaupt keine Ähnlichkeit hat mit Sprachen, die es schon gibt.

42:11.650 --> 42:13.950
Jetzt können Sie sagen, das war aber damals 72 einfach.

42:14.530 --> 42:15.210
Stimmten aber nicht.

42:15.390 --> 42:17.370
Damals gab es schon jede Menge Programmiersprachen.

42:18.010 --> 42:21.510
Fortran, Basic, COBOL, C steckte in seinen Kinderschuhen, Vorgänger

42:21.510 --> 42:23.550
von C, BCPL, B war auch schon da.

42:23.890 --> 42:26.230
Also es ist nicht so ganz einfach gewesen, auch zu der Zeit.

42:27.310 --> 42:32.010
Und ein besonderes Feature war, dass man einige Sachen in der

42:32.010 --> 42:34.850
Programmiersprache, einige Features erstmal nicht dokumentiert hat.

42:38.100 --> 42:41.440
Ein ganz einfaches Programm, damit Sie sehen, also es ist wirklich

42:41.440 --> 42:42.100
super einfach.

42:44.820 --> 42:46.700
Das ist ein Hello-World-Programm.

42:46.840 --> 42:49.940
Wir hatten in Java, muss ich zugeben, noch nie ein Hello-World

42:49.940 --> 42:51.180
-Programm gehabt.

42:51.280 --> 42:54.780
Aber ein Hello-World-Programm soll eigentlich dem Anfänger zeigen, was

42:54.780 --> 42:58.540
sind die elementaren Konstrukte, die man hinschreiben muss, um den

42:58.540 --> 43:00.100
String Hello-World auszugeben.

43:00.540 --> 43:02.840
Hier sehen Sie, das Ganze ist ganz einfach.

43:03.200 --> 43:05.460
Es gibt nicht viele Sprachkonstrukte, wie Sie sehen.

43:06.960 --> 43:10.900
Und letzten Endes, was man also ist, man schreibt an gewissen Stellen

43:10.900 --> 43:12.020
tatsächlich gewisse Werte.

43:12.180 --> 43:15.940
Und wenn man das ablaufen lassen würde, steht Hello-World raus.

43:17.060 --> 43:19.860
Ich möchte so ein paar Highlights jetzt hier, die Sie vielleicht beim

43:19.860 --> 43:21.700
ersten Drauflesen etwas übersehen haben.

43:23.060 --> 43:23.500
Please.

43:25.320 --> 43:28.900
Es geht um Höflichkeit, guten Stil und Manieren beim Programmieren.

43:30.080 --> 43:32.580
Ich habe Ihnen ja immer vorher gesagt, Sie schreiben das Programm für

43:32.580 --> 43:33.300
die nächsten Menschen.

43:33.640 --> 43:34.660
Sie sehen, bei Intercalus ist es anders.

43:34.740 --> 43:36.820
Sie schreiben das Programm offenbar für den Compiler.

43:36.820 --> 43:39.060
Und der will natürlich freundlich gebeten werden.

43:40.220 --> 43:42.180
Deswegen schreibt man hier nur wieder mal Please hin.

43:43.860 --> 43:47.140
Please ist ein Identifier, also ein eingebautes Wort dieser

43:47.140 --> 43:49.800
Programmiersprache und man macht es im Wesentlichen aus ästhetischen

43:49.800 --> 43:50.100
Gründen.

43:50.960 --> 43:54.320
Please kann man mit Do verwenden, muss es aber nicht.

43:57.200 --> 44:02.160
Negieren kann man mit Do und dann ein Not oder auch N't, mit Apostroph

44:02.160 --> 44:02.440
machen.

44:03.380 --> 44:06.880
Was aber auch dem Compiler wichtig ist, ist, dass man weder schleimt

44:06.880 --> 44:09.360
noch zu unhöflich ist.

44:10.420 --> 44:15.940
Und deswegen, wenn man zu häufig ein Please verwendet, kommt eine

44:16.780 --> 44:20.120
Meldung zurück, eine Unterstützung, das ist aber eine Fehlermeldung

44:20.120 --> 44:21.640
vom Compiler, dass man zu schleimig ist.

44:23.640 --> 44:27.360
Aber wenn man es zu selten verwendet, dann gibt es ein Program to Root

44:27.360 --> 44:30.620
Error, also dass das Programm zu ruppig wäre.

44:32.060 --> 44:35.400
Und diese Unterstützungen werden durch Fehlermeldungen ergänzt.

44:36.200 --> 44:41.260
Es gibt, ohne Witz, mittlerweile für Emacs, wer von Ihnen kennt Emacs?

44:42.080 --> 44:43.760
Ein paar Unix-Soide gibt es noch.

44:43.960 --> 44:47.560
Emacs ist also einer der klassischen Editoren im Unix-System.

44:48.080 --> 44:51.400
Man macht den Grabenkrieg zwischen den VI-Usern und den Emacs-Usern.

44:52.180 --> 44:53.760
VI ist ein anderer Editor bei Unix.

44:54.440 --> 44:57.320
Bei Emacs gibt es mittlerweile einen Modus, der ein Intercal-Programm

44:57.320 --> 45:01.340
genau die richtige Zahl von Please automatisch einstreut, damit man

45:01.340 --> 45:03.000
eben beide Fehlermeldungen vermeidet.

45:03.920 --> 45:09.060
Dass man also nicht zu viel hat oder eben ein Program to Root Error

45:09.060 --> 45:09.440
bekommt.

45:09.840 --> 45:12.140
Und da muss man sich zum Glück dank Emacs nicht mehr darum kümmern.

45:12.240 --> 45:14.460
Da gibt es einen Emacs-Modus, der das erledigt.

45:15.860 --> 45:17.520
Datentypen gibt es natürlich auch.

45:17.940 --> 45:19.360
Es gibt 16-Bit Integer.

45:20.140 --> 45:21.400
Da schreibt man einen Punkt davor.

45:22.140 --> 45:25.680
Und es gibt 32-Bit Integer, die mit einem Doppelpunkt, logisch, weil

45:25.680 --> 45:28.000
zweimal so viel, dargestellt werden.

45:28.820 --> 45:32.680
Also insofern sollte man auch Punkt 123 nicht mit Doppelpunkt 123

45:32.680 --> 45:33.740
verwechseln.

45:34.420 --> 45:38.380
Das ist ähnlich sinnig wie in Java mit 2,5 und 2,5f, damit man das

45:38.380 --> 45:39.000
auch nicht verwechselt.

45:39.060 --> 45:39.800
Aber hier ist es genauso.

45:40.260 --> 45:41.720
Wenn Sie das vergleichen mit Java.

45:43.160 --> 45:45.440
Boolean char, byte short, int, long, float, double.

45:45.680 --> 45:46.580
Alles viel zu kompliziert.

45:46.740 --> 45:48.300
In Intercal hat man nur zwei.

45:48.420 --> 45:50.620
Sie sehen also, das ist eine wirklich viel, viel einfache

45:50.620 --> 45:51.520
Programmiersprache.

45:54.900 --> 45:59.820
Konstanten sind immer 16-Bit lang und zwar entsprechend logischerweise

45:59.820 --> 46:02.660
von 0 bis eben zu der maximalen 16-Bit-Zahl.

46:02.780 --> 46:04.300
65.536.

46:04.580 --> 46:07.620
Und man kann ein Hash da vorstellen, damit man eben sagt, das ist eine

46:07.620 --> 46:08.780
Konstante, ein Hash-Wert.

46:08.980 --> 46:10.960
Haben Sie eben in dem Hello World Programm auch gesehen.

46:11.860 --> 46:13.040
Wenn man das vergleicht mit Java.

46:14.280 --> 46:15.720
Public, Static, Final.

46:16.200 --> 46:18.440
Sie sehen wieder, Intercal viel, viel einfacher.

46:20.440 --> 46:22.100
Arrays gibt es natürlich auch in Intercal.

46:23.840 --> 46:31.160
Und zwar, wenn Sie ein Hochkomma nehmen für ein 16-Bit Wertarray und

46:31.160 --> 46:34.580
mit einem Semikolon für ein 32-Bit-Value-Array.

46:35.280 --> 46:38.460
Die Verwendung des Semikolons in allen Programmiersprachen und nachher

46:38.460 --> 46:39.400
war immer komplett anders.

46:39.640 --> 46:41.160
Hier ist es halt eine Einleitung für ein Array.

46:42.400 --> 46:48.060
Dann folgt eine Zahl, also als Index, aus diesem Integer-Bereich von 1

46:48.060 --> 46:53.180
bis eben maximal 16-Bit-Integer und danach kommt ein Sub, um zu sagen,

46:53.320 --> 46:56.000
dass das, was vorher war, jetzt ein Index war.

46:57.040 --> 46:59.220
Dann kann man ein Leerzeichen machen, das ist aber optional.

47:00.720 --> 47:08.420
Und dann folgt ein Subscript, ein Ausdruck und das kann also jetzt zu

47:08.420 --> 47:10.900
mehrdeutigen Ausdrücken führen.

47:12.740 --> 47:13.300
Ohne Quatsch.

47:13.400 --> 47:18.520
Immer dann, wenn White Space ist, so wie Leerzeichen oder Zeilen oder

47:18.520 --> 47:20.760
so, eine Semantik in der Programmiersprache ist aber ganz übel.

47:21.300 --> 47:23.980
Und wenn man sie bringen kann oder nicht, ist es auch mal relativ

47:23.980 --> 47:24.320
übel.

47:24.900 --> 47:26.460
In VOR ist es nämlich auch so ähnlich.

47:26.580 --> 47:29.420
Sie können hinter das VOR bei FORTRAN auch sofort weitermachen mit

47:29.420 --> 47:30.260
einer Variablename.

47:30.420 --> 47:33.560
Also wenn der Compiler passt VORI, dann weiß er nicht, ist VORI jetzt

47:33.560 --> 47:36.040
eine Variablename oder heißt das VOR, Leerzeichen I.

47:36.260 --> 47:37.280
Also keine gute Idee.

47:37.840 --> 47:40.520
Und noch was anderes aus der Anekdotenkiste realer

47:40.520 --> 47:41.220
Programmiersprachen.

47:41.620 --> 47:45.380
Wer von Ihnen MAKE kennt, also das Programm, um Compiler anzustoßen

47:45.380 --> 47:47.860
und ein bisschen größeres Projekt durchzukomplieren, das wurde von

47:47.860 --> 47:48.780
einem Physiker entwickelt.

47:48.980 --> 47:51.340
Und den hätte man eigentlich lieber in der Physik belassen sollen,

47:51.400 --> 47:52.420
anstatt dass er Informatik macht.

47:52.720 --> 47:55.900
Der kam nämlich auch auf die Idee, dass White Spaces nämlich

47:55.900 --> 47:58.520
Leerzeichen tatsächlich eine Bedeutung haben.

47:58.600 --> 48:01.120
Und es ist, wie Informatiker sagen das schon immer, keine schlaue

48:01.120 --> 48:04.480
Idee, wenn Dinge eine Bedeutung haben, die man nicht sieht.

48:05.660 --> 48:08.600
Hat er aber so gemacht, wer schon mal mit MAKE programmiert hat, weiß,

48:08.660 --> 48:12.160
das ist die Hölle, wenn der Editor sechs Leerzeichen mal schnell durch

48:12.160 --> 48:15.620
einen Tab ersetzt und dann auf einmal für den MAKE Compiler oder MAKE

48:15.620 --> 48:18.560
Interpreter darf eben nicht mehr eine gewisse Zahl von Leerzeichen

48:18.560 --> 48:20.300
stehen, die eine gewisse semantische Bedeutung haben.

48:20.360 --> 48:21.720
Weil da steht ja jetzt Tab und Leerzeichen.

48:22.280 --> 48:24.040
Also all so ein Schwachsinn, keine gute Idee.

48:24.160 --> 48:25.240
Und man riecht es auch schon hier.

48:26.020 --> 48:27.480
Die Verwirrung ist gesorgt.

48:28.020 --> 48:30.000
Folgende Ausdrücke sind nämlich äquivalent.

48:30.980 --> 48:37.320
Der da, Apostroph, Punkt, 1 Tilde, 2 Apostroph und Ausrufezeichen, 1

48:37.320 --> 48:39.380
Tilde, 2 Apostroph.

48:39.540 --> 48:40.420
Wer hätte das gedacht?

48:41.720 --> 48:44.020
Aus Platzgründen habe ich den Vergleich zu Java einfach mal

48:44.020 --> 48:46.640
weggelassen, weil da wissen Sie ja, wie man indiziert.

48:47.720 --> 48:48.120
Gotos.

48:48.600 --> 48:50.960
Jetzt haben wir in Java das Goto noch nicht kennengelernt.

48:51.320 --> 48:54.160
Mal kurz Handtuch, wer kennt Goto als Operator noch nicht?

48:55.140 --> 48:56.980
Also ein paar noch mal ganz schnell erklärt.

48:57.080 --> 49:01.720
Also Goto ist vor Ewigkeit in den 60er Jahren mal eingeführt worden,

49:01.780 --> 49:04.560
um zu sagen, wenn ich beim Programmablauf an eine Stelle komme, wo

49:04.560 --> 49:08.980
Goto steht, dann springt der Programmablauf an die Marke, die beim

49:08.980 --> 49:09.600
Goto steht.

49:09.740 --> 49:12.340
Also ich könnte zum Beispiel sagen, irgendwo eine Marke definieren,

49:13.040 --> 49:15.260
Schleife und dann komme ich runter an Goto und dann springe ich wieder

49:15.260 --> 49:17.880
zu Schleife und dann gehe ich wieder runter bis zum Goto und springe

49:17.880 --> 49:18.480
wieder zu Schleife.

49:19.080 --> 49:20.760
Und damit kann man zum Beispiel Schleifen realisieren.

49:22.240 --> 49:24.860
Und Goto heißt also wesentlich nur, gehe zu dem Punkt, der danach

49:24.860 --> 49:27.280
angegeben ist und dann springt eben der Kontrollschluss dahin.

49:28.620 --> 49:29.700
Jetzt ist es so,

49:33.480 --> 49:37.260
Goto vereinfacht zwar manchmal das Schreiben von Code, aber sicherlich

49:37.260 --> 49:39.620
nicht das Lesen und das Verstehen von Code.

49:39.740 --> 49:42.100
Also wenn man sich hier sowas anschaut, ich habe eine Schleife, die

49:42.100 --> 49:46.060
tatsächlich hier so funktioniert, wenn irgendwas ist, ist es hier Goto

49:46.060 --> 49:49.740
Error, dann muss ich also dahin springen und wenn hier, das ist die

49:49.740 --> 49:51.960
Abbruchbedingung, dann mache ich Loop Exit, gehe ich dahin und wenn

49:51.960 --> 49:54.380
ich alles das nicht gemacht habe, dann gehe ich, mache ich wieder Goto

49:54.380 --> 49:55.300
Loop und gehe wieder hier hin.

49:55.640 --> 49:58.320
Also das wäre beispielsweise so eine Schleife ohne Weil oder auf

49:58.320 --> 49:59.740
Vorgehen geschrieben, sondern nur mit Goto.

50:00.600 --> 50:03.440
Das hat man mal eingeführt und dachte noch, das wäre gut, weil man

50:03.440 --> 50:07.060
halt Schleifen braucht, bis aber dann einer der sehr berühmten

50:07.060 --> 50:11.580
Informatiker, Edgar Dijkstra aus den Niederlanden, schon vor den 70er

50:11.580 --> 50:14.620
Jahren gesagt hat, in einem Artikel, das war eigentlich ein Brief,

50:14.820 --> 50:18.280
eher eine Zeitschrift, Goto considered harmful und er hat vorgemacht,

50:18.480 --> 50:21.420
was eigentlich alles für eine schlechte Lesbarkeit führt und dass man

50:21.420 --> 50:24.860
es eigentlich nicht braucht, weil man mit den Konstrukten, die sie

50:24.860 --> 50:31.420
gelernt haben, der Programmierung, weil vor der sogenannten

50:31.420 --> 50:33.780
strukturierten Programmierung das Goto eigentlich gar nicht braucht.

50:33.900 --> 50:37.020
Es gibt ein paar Fälle, wo das Leben einfacher ist mit Gotos, wo man

50:37.020 --> 50:41.600
sich ein oder zwei Bedingungsauswertungen spart, aber im Großen und

50:41.600 --> 50:44.280
Ganzen, wie gesagt, braucht man eigentlich kein Goto und deswegen habe

50:44.280 --> 50:45.920
ich es Ihnen auch noch gar nicht beigebracht, aber hier für die

50:45.920 --> 50:47.540
InterKAL -Vorlesung brauchen Sie das nämlich.

50:48.520 --> 50:50.680
Aber wie gesagt, denken Sie dran, also wie gesagt, Goto considered

50:50.680 --> 50:52.380
harmful, das war die Meinung von Dijkstra.

50:52.880 --> 50:55.580
Wenn Sie alleine diesen Satz googlen, Goto considered harmful, kommen

50:55.580 --> 50:58.560
Sie sofort auch auf eine Online-Version des Artikels oder des Briefes.

50:58.900 --> 51:00.720
Immer noch heutzutage sehr lesenswert.

51:01.340 --> 51:06.520
Aber was hat man jetzt in InterKAL daraus gemacht?

51:07.840 --> 51:09.320
Und zwar hat man einen Next

51:12.420 --> 51:16.660
Konstrukt eingeführt, also es gibt DoLabel oder natürlich auch

51:17.980 --> 51:23.560
PleaseDoLabel Next und dann werden diese Sachen auf einem Stack

51:23.560 --> 51:28.360
gespeichert und dieser Stack, also die Anzahl von Aufrufen, die Sie

51:28.360 --> 51:30.760
damit machen können, der hat die Tiefe von 79.

51:31.640 --> 51:35.580
Wenn Sie die 79 überspringen zur Laufzeit, gibt es eine Meldung vom

51:35.580 --> 51:38.120
Laufzeitsystem Program has disappeared in the Black Lagoon.

51:39.420 --> 51:41.500
Sie sollten also als Programmierer selber schon darauf achten, dass

51:41.500 --> 51:44.940
Sie das nicht übertreiben mit Sprüngen zu Labels.

51:46.220 --> 51:50.300
Damit Sie mit dem Stack ein bisschen haushalten können, gibt es das

51:50.300 --> 51:51.520
Forget -Keyword.

51:52.520 --> 51:54.640
Forget heißt einfach vergessen Sie die Rücksprungadresse.

51:56.740 --> 52:02.380
Es gibt den Hinweis, man sollte eigentlich nach jedem Next auch immer

52:02.380 --> 52:04.820
mal so ein Forget machen, damit die Rücksprungadresse auch wirklich

52:04.820 --> 52:06.160
weg ist aus dem Stack.

52:06.720 --> 52:08.200
Das macht er nämlich nicht automatisch.

52:09.360 --> 52:13.600
Wenn Sie jetzt zu viele Forgets machen nacheinander, dann wird der

52:13.600 --> 52:15.100
Stack irgendwann mal einfach komplett entleert.

52:17.800 --> 52:21.000
Die eigentliche Lösung für das GoTo-Problem, also dass ein schlecht

52:21.000 --> 52:21.960
wartbarer Code usw.

52:22.040 --> 52:24.960
rauskommt, kommt aber jetzt, und das halte ich persönlich immer noch

52:24.960 --> 52:28.760
für das Highlight von Interkall, nämlich die Come-From-Anweisung.

52:30.200 --> 52:33.200
Come-From ist also die Inverse zum GoTo.

52:33.580 --> 52:36.200
Also wenn man bei einem GoTo sagt, wenn Sie an diese Stelle kommen,

52:36.320 --> 52:38.440
springen Sie bitte dahin, dann kann man noch bei einem Lesen der

52:38.440 --> 52:39.480
Stelle ahnen, was passiert.

52:40.020 --> 52:41.120
Come-From ist viel besser.

52:41.600 --> 52:44.300
Sie schreiben irgendwo in Ihrem Code Come-From so und so hin.

52:44.400 --> 52:45.540
Wieder eine Marke oder eine Zeile.

52:46.580 --> 52:49.760
Und wenn ich dann an dieser Zeile bin, wo gar nichts steht, springe

52:49.760 --> 52:51.820
ich aber weg, weil nämlich irgendwo stand ja Come-From.

52:54.500 --> 52:59.100
Sie ahnen also, das ist ein wunderbarer, nicht-lokaler Eingriff in die

52:59.100 --> 53:00.100
Semantik von Programmen.

53:00.220 --> 53:00.920
Ganz, ganz herrlich.

53:03.240 --> 53:05.960
Und Sie können natürlich auch, um das Ganze noch zu beöffentlichen,

53:06.020 --> 53:08.000
Please do Come-From schreiben.

53:09.060 --> 53:12.500
Und auch hier wieder merkt man, diese extrem nützliche Sache wird in

53:12.500 --> 53:13.900
Java einfach gar nicht angeboten.

53:17.420 --> 53:20.640
Dann gibt es natürlich auch noch einen Hinweis, dass das Programm

53:20.640 --> 53:21.600
beendet werden soll.

53:22.900 --> 53:23.260
Give up.

53:24.480 --> 53:25.520
Gib es einfach auf.

53:30.340 --> 53:32.740
Das Ganze ist natürlich, wie gesagt, gar nicht von mir.

53:32.840 --> 53:35.480
Sie können das nachlesen.

53:36.220 --> 53:40.660
Es gibt viele Versionen vom InterCal Sprachstandard mittlerweile im

53:40.660 --> 53:40.880
Netz.

53:41.000 --> 53:43.820
Hier ist eines mit einer ganz kurzen URL mal angegeben.

53:44.560 --> 53:49.440
Es gibt nämlich auch viele spannende weitere Sachen, die wir jetzt gar

53:49.440 --> 53:51.520
nicht besprochen haben, die auch noch drin sind.

53:52.020 --> 53:55.440
Und an der Stelle möchte ich auch schon zur Zusammenfassung kommen.

53:56.000 --> 53:58.080
Was sind also die Vorteile von InterCal?

53:59.480 --> 54:01.100
Es ist absichtlich sehr einfach.

54:01.260 --> 54:03.020
Sie merken das schon bei der Datentyp-Deklaration.

54:03.860 --> 54:06.320
Sie entspricht natürlich vollkommen der natürlichen Intuition von

54:06.320 --> 54:06.980
Programmierern.

54:07.900 --> 54:10.240
Hat auch überhaupt nicht diesen ganzen neumodischen Schnickschnack wie

54:10.240 --> 54:13.660
Typsystem, Objektorientierung, Vererbung, Polymorphie, alles das, was

54:13.660 --> 54:15.840
Ihnen gerade noch Sorgen in der letzten Vorlesung bereitet hat.

54:16.260 --> 54:18.480
Und damit ist es perfekt geeignet für die Anfänger.

54:19.640 --> 54:24.800
Aus dem Sprachstandard wörtlich übernommen, zitiert von mir, Since it

54:24.800 --> 54:28.360
is an exceedingly easy language to learn, one might expect it would be

54:28.360 --> 54:30.700
a good language for initiating novice programmers.

54:31.880 --> 54:36.840
Perhaps surprising, then, is the fact that it would be more likely to

54:36.840 --> 54:39.940
initiate a novice into a search for another line of work.

54:41.580 --> 54:46.160
As it turns out, Intercal is more useful, which is saying much, as a

54:46.160 --> 54:47.800
challenge to professional programmers.

54:48.880 --> 54:53.020
Ja, und an dieser Stelle, das war, wie gesagt, die kleine

54:53.020 --> 54:53.920
Weihnachtsvorlesung.

54:55.040 --> 54:58.880
Wünsche ich Ihnen ein frohes und gesegnetes Weihnachtsfest und einen

54:58.880 --> 55:00.600
guten Rutsch ins neue Jahr.

55:01.220 --> 55:03.760
Und dann geht es aber tatsächlich in der nächsten Vorlesung doch zum

55:03.760 --> 55:04.600
Glück weiter mit Java.

55:05.060 --> 55:05.720
Vielen Dank.

