-
Notifications
You must be signed in to change notification settings - Fork 24
/
i1selbst.tex
1805 lines (1735 loc) · 60.2 KB
/
i1selbst.tex
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
% Diese Datei ist Teil des Buchs "Schreibe Dein Programm!"
% Das Buch ist lizenziert unter der Creative-Commons-Lizenz
% "Namensnennung - Weitergabe unter gleichen Bedingungen 4.0 International (CC BY-SA 4.0)"
% https://creativecommons.org/licenses/by-sa/4.0/deed.de
\chapter{Programmieren mit Selbstbezügen und Kombinatoren}
\label{cha:selbstbezug}
Bisher haben wir nur Daten vorgestellt, die
eine feste Größe haben und damit im Wortsinn beschränkt sind. Viele
Informationen haben aber eine variable Größe: Die Bücher im Regal werden
immer mehr, Bauwerke bestehen aus variabel vielen Bauteilen. Um
solche Informationen als Daten zu repräsentieren, stellen wir in
diesem Kapitel einen weiteren Aspekt der Datenanalyse vor, den
\textit{Selbstbezug}\index{Selbstbezug}. Hier sind einfache
Beispiele:
%
\begin{itemize}
\item Ein Fluss besteht aus Hauptfluss und Nebenfluss~-- beides wieder
Flüsse.
\item Ein Dateiverzeichnis auf dem Computer kann
Unterverzeichnisse enthalten.
\item Ein großer Bücherstapel besteht aus einem etwas kleineren
Bücherstapel und einem weiteren Buch.
\end{itemize}
%
Bei all diesen Beispielen werden aus kleineren Dingen größere gemacht:
Aus zwei Flüssen, die zusammenfließen, wird ein großer Fluss. Aus
mehreren Dateien und Verzeichnissen wird ein großes Verzeichnis. Aus
einem kleinen Bücherstapel wird ein großer. In der Programmierung
nennen wir das Bauen von großen Dingen aus kleinen Dingern derselben
Sorte \textit{kombinieren}. Die Funktionen, die das erledigen, heißen
\textit{Kombinatoren}\index{Kombinator}: Um die geht es in diesem Kapitel.
\section{Flüsse abbilden}
\mentioncode{selbstbezuege/river.rkt}
\begin{figure}[tbh]
\centering
\begin{tikzpicture}
[
confluence/.style = {shape=rectangle, rounded corners,
draw, align=center},
absent/.style = {},
grow = left,
sibling distance = 6em,
level distance = 10em,
% makes arrows the opposite direction from -latex
edge from parent/.style = {draw,latex-},
every node/.style = {font=\footnotesize}
]
\node [absent] {}
child {
node [confluence] {Epfendorf}
child {
node [confluence] {Tieringen}
edge from parent node [below] {Schlichem} }
child { node [confluence] {Rottweil}
child { node [confluence] {Heimliswald}
edge from parent node [below] {Eschach} }
child { node [confluence] {Dreifaltigkeitsberg}
edge from parent node [below] {Prim} } } };
\end{tikzpicture}
\caption{Der Neckar nahe der Quellen}
\label{fig:neckar}
\end{figure}
\noindent Abbildung~\ref{fig:neckar} ist ein Diagramm, das die
Struktur des Neckar nahe der Quellen beschreibt: Er fließt zunächst
aus den Bächen Eschach und Prim zusammen, und danach fließen immer
weitere Bäche und Flüsse hinzu~-- in der Abbildung noch die
Schlichem.
Die Struktur eines Flusses werden wir durch Daten abbilden, um danach
eine Funktion zu schreiben, die feststellt, ob ein Fluss durch einen
bestimmten Ort fließt. Unser erster Versuch einer Datendefinition
sieht so aus:
%
\begin{lstlisting}
; Ein Fluss kommt entweder aus:
; - einer Quelle
; - einem Hauptfluss und einem Nebenfluss
\end{lstlisting}
%
Man kann zwar schon sehen, dass es sich um eine Fallunterscheidung
handelt, aber wir können die Formulierung schärfen, um klar zu machen,
dass es sich um gemischte Daten handelt:
%
\begin{lstlisting}
; Ein Fluss ist eins der folgenden:
; - ein Bach aus einer Quelle
; - ein Zusammentreffen von einem Hauptfluss und einem Nebenfluss
\end{lstlisting}
%
Daraus können wir direkt eine Signatur-Definition machen:
%
\indexvariable{river}
\begin{lstlisting}
(define river
(signature (mixed creek confluence)))
\end{lstlisting}
%
Die Datendefinitionen für "<Bach"> und "<Zusammentreffen"> fehlen
allerdings noch. In Abbildung~\ref{fig:neckar} steht an den Bächen
jeweils noch der Name. Eine passende Datendefinition sieht so aus:
%
\begin{lstlisting}
; Ein Bach hat folgende Eigenschaften:
; - Ursprungsort
\end{lstlisting}
%
Das klingt ein bisschen komisch, weil der Bach ja nur \emph{eine}
Eigenschaft hat, aber die Datendefinition trotzdem zusammengesetzte
Daten beschreibt. Trotzdem ist das legitim und korrekt~-- niemand
wird sagen wollen, dass der Ursprungsort der Bach \emph{ist}.
Außerdem kannst Du Dir vorstellen, dass ein Bach später noch mehr
Eigenschaften bekommt, die wir in den Daten festhalten
wollen. (Wasserqualität oder Fließgeschwindigkeit zum Beispiel.)
Entsprechend ist es auch sinnvoll, dafür eine Record-Definition zu
schreiben:
%
\indexvariable{creek}
\begin{lstlisting}
(define-record creek
make-creek
creek?
(creek-origin string))
\end{lstlisting}
%
Wenn später noch weitere Eigenschaften hinzukommen, können wir die
Record-Definition erweitern, ohne anderen Code zu beeinträchtigen.
Kommen wir zu den Zusammentreffen. Auch hier ist ein Ort relevant~--
dort, wo sie zusammentreffen. Außerdem ist wichtig, \emph{welche}
Flüsse oder Bäche da zusammentreffen. Die Datendefinition könnte so
aussehen:
%
\begin{lstlisting}
; Ein Zusammentreffen besteht aus:
; - Ort
; - Hauptfluss
; - Nebenfluss
\end{lstlisting}
%
Die Formulierung zeigt eindeutig, dass es sich um zusammengesetzte
Daten handelt. Aber diese Datendefinition weist eine Auffälligkeit
auf, die erst sichtbar wird, wenn wir sie im Zusammenhang der
Gesamt-Datendefinition für Flüsse betrachten:
%
\tikzstyle{every picture}+=[remember picture]
\tikzstyle{fluss} = [shape=rectangle,inner sep=0pt,text depth=0pt]
%
\begin{lstlisting}
; Ein |\tikz\node[fluss](fluss){\textbf{Fluss}};| ist eins der folgenden:
; - |\ldots|
; - ein Zusammentreffen |\ldots|
|\ldots|
; Ein Zusammentreffen besteht aus:
; - Ort
; - Haupt|\tikz\node[fluss](fluss1){\textbf{fluss}};|
; - Neben|\tikz\node[fluss](fluss2){\textbf{fluss}};|
\end{lstlisting}
%
\begin{tikzpicture}[overlay]
\path[->,blue,thick](fluss1) edge [out=0, in=0] (fluss);
\path[->,blue,thick](fluss2) edge [out=0, in=0] (fluss);
\end{tikzpicture}
%
Die Datendefinition für "<Fluss"> benutzt selbst den Begriff
"<Fluss">. Noch offensichtlicher wird das, wenn wir die dazu
passende Record-Definition erstellen:
%
\indexvariable{confluence}
\begin{lstlisting}
(define-record confluence
make-confluence
confluence?
(confluence-location string)
(confluence-main-stem river)
(confluence-tributary river))
\end{lstlisting}
%
In der Definition von \texttt{confluence} wird \texttt{river} benutzt
und umgekehrt. Das ist nichts schlimmes~-- im Gegenteil, diese beiden
\textit{Selbstbezüge}\index{Selbstbezug} erlauben uns, ganz
unterschiedliche Flüsse zu beschreiben, insbesondere solche
unterschiedlicher Größe. Den Abschnitt des Neckar in~\ref{fig:neckar}
können wir so abbilden:
%
\indexvariable{eschach}
\indexvariable{prim}
\indexvariable{neckar-1}
\indexvariable{neckar-1}
\indexvariable{schlichem}
\begin{lstlisting}
(define eschach (make-creek "Heimliswald")) ; Quelle des Neckar
(define prim (make-creek "Dreifaltigkeitsberg")) ; Quelle des Neckar
; erster Zusammenfluss des Neckar:
(define neckar-1 (make-confluence "Rottweil" eschach prim))
; Zufluss des Neckar:
(define schlichem (make-creek "Tieringen"))
; zweiter Zusammenfluss des Neckar:
(define neckar-2 (make-confluence "Epfendorf" neckar-1 schlichem))
\end{lstlisting}
%
An diesen Beispielen sieht man sehr schön, dass
\texttt{make-confluence} zwei Flüsse zu einem kombiniert~-- es handelt
sich um einen \textit{Kombinator}\index{Kombinator}.
Wir hatten angekündigt, eine Funktion zu schreiben, die feststellt, ob
Wasser von einem bestimmten Ort in einen Fluss fließt. Wir machen das
wieder nach Vorschrift, also zuerst die Kurzbeschreibung:
%
\begin{lstlisting}
; Fließt Wasser von einem Ort in Fluss?
\end{lstlisting}
%
In der Kurzbeschreibung sind schon die Substantive "<Fluss"> und
"<Ort"> als Eingaben aufgeführt, die übertragen wir in die Signatur.
Da die Funktion eine Ja-/Nein-Frage beantwortet, liefert sie einen
booleschen Wert und hat ein Fragezeichen hinten am Namen:
%
\begin{lstlisting}
(: flows-from? (string river -> boolean))
\end{lstlisting}
%
Bei den Tests achten wir darauf, dass wir sowohl Bäche als auch Flüsse
testen, und sowohl Fälle, bei denen \lstinline{#t} als auch solche,
bei denen \lstinline{#f} herauskommt:
%
\begin{lstlisting}
(check-expect (flows-from? "Heimliswald" eschach) #t)
(check-expect (flows-from? "Tübingen" eschach) #f)
(check-expect (flows-from? "Heimliswald" neckar-2) #t)
(check-expect (flows-from? "Rottweil" neckar-2) #t)
(check-expect (flows-from? "Berlin" neckar-2) #f)
\end{lstlisting}
%
Das Gerüst ergibt sich wie immer direkt aus der Signatur:
%
\begin{lstlisting}
(define flows-from?
(lambda (location river)
...))
\end{lstlisting}
%
Da es sich bei \lstinline{river} um gemischte Daten handelt, können
wir die Schablone dafür ergänzen. Da \lstinline{river} zwei Fälle hat
(Bäche und Zusammenflüsse), muss die Schablone zwei Zweige haben. Die
Bedingungen sind Aufrufe der jeweiligen Prädikate für
\lstinline{creek} und \lstinline{confluence}:
%
\begin{lstlisting}
(define flows-from?
(lambda (location river)
(cond
((creek? river) ...)
((confluence? river) ...))))
\end{lstlisting}
%
Nun können wir Code für die beiden Zweige ergänzen. Fangen wir mit
den Bächen an. Da es sich bei \lstinline{creek} um zusammengesetzte
Daten handelt (mit nur einem Bestandteil), sollte der Aufruf des
Selektors im Rumpf vorkommen:
%
\begin{lstlisting}
(define flows-from?
(lambda (location river)
(cond
((creek? river)
...
(creek-origin river)
...)
((confluence? river)
...))))
\end{lstlisting}
%
Der Selektor \lstinline{creek-origin} liefert den Ursprungsort. Wenn dieser dem
gesuchten Ort entspricht, so fließt der Fluss durch diesen Ort, sonst
nicht. Im ersten Fall sollte \lstinline{#t} herauskommen, im zweiten
\lstinline{#f}. Das sieht so aus:
%
\begin{lstlisting}
(define flows-from?
(lambda (location river)
(cond
((creek? river)
(if (string=? (creek-origin river) location)
#t
#f))
((confluence? river)
...))))
\end{lstlisting}
%
Dieser Code kann noch etwas vereinfacht werden: Der
\lstinline{if}-Ausdruck sagt ja salopp formuliert:
%
\begin{quote}
Wenn \lstinline{(string=? (creek-origin river) location)} als
Resultat \lstinline{#t} liefert, dann \lstinline{#t}, und wenn es
\lstinline{#f} liefert, dann \lstinline{#f}.
\end{quote}
%
Da reicht es auch, \lstinline{(string=? (creek-origin river) location)} hinzuschreiben.
%
\begin{aufgabeinline}\label{aufgabe:iftruefalse}
Kannst Du diese Vereinfachung auf \lstinline{if}-Ausdrücken
verallgemeinern und als Gleichung schreiben?
\end{aufgabeinline}
%
Als Nächstes können wir uns um den anderen Fall kümmern,
\lstinline{confluence}. Auch dort handelt es sich um
zusammengesetzte Daten, wir können also schon einmal die
Selektoraufrufe hinschreiben:
%
\begin{lstlisting}
(define flows-from?
(lambda (location river)
(cond
((creek? river)
(string=? (creek-origin river) location))
((confluence? river)
...
(confluence-location river)
(confluence-main-stem river)
(confluence-tributary river)
...))))
\end{lstlisting}
%
Soweit die Schablone, wir können uns also Gedanken zur eigentlichen
Aufgabe machen.
Als erstes steht da \lstinline{(confluence-location river)}, der Ort
des Zusammenflusses. Wenn es sich dabei um den gesuchten Ort handelt,
können wir die Frage, ob der Wasser von diesem Ort in den Fluss fließt, bereits
mit "<ja"> beziehungsweise \lstinline{#t} beantworten:
%
\begin{lstlisting}
(define flows-from?
(lambda (location river)
(cond
((creek? river)
(string=? (creek-origin river) location))
((confluence? river)
(if (string=? (confluence-location river) location)
#t
...
(confluence-main-stem river)
(confluence-tributary river)
...)))))
\end{lstlisting}
%
Aber was, wenn der Ort des Zusammenflusses nicht der gesuchte Ort ist?
Dann müssen wir uns an die beiden anderen
Selektor-Aufrufe halten, die uns den Haupt- und den Nebenfluss des
Zusammenflusses liefern. Wenn der eine oder der andere Wasser aus dem
gesuchten Ort enthält, dann könnten wir die Frage unserer Funktion
ebenfalls mit "<ja"> beantworten. Dazu müssten wir also feststellen:
%
\begin{enumerate}
\item Fließt Wasser vom Ort \lstinline{origin} in den Hauptfluss?
\item Fließt Wasser vom Ort \lstinline{origin} in den Nebenfluss?
\end{enumerate}
%
Nun, wir schreiben ja gerade eine Funktion, die feststellt, ob Wasser
von einem bestimmten Ort in einen bestimmten Fluss fließt. Können wir
die benutzen? Wir schreiben das mal hin:
%
\begin{lstlisting}
(define flows-from?
(lambda (location river)
(cond
((creek? river)
(string=? (creek-origin river) location))
((confluence? river)
(if (string=? (confluence-location river) location)
#t
...
(flows-from? location (confluence-main-stem river))
(flows-from? location (confluence-tributary river))
...)))))
\end{lstlisting}
%
Die beiden Aufrufe von \lstinline{flows-from?} entsprechen gerade
den beiden Fragen von oben. Wir können ihre Ergebnisse kombinieren,
um unsere Antwort zu errechnen: Wenn Wasser aus \lstinline{location}
durch den Hauptfluss fließt \emph{oder} der Wasser aus
\lstinline{location} in den Nebenfluss fließt, so fließt auch Wasser
von \lstinline{location} in den "<Gesamtfluss">. So sieht das aus:
%
\indexvariable{flows-from?}
\begin{lstlisting}
(define flows-from?
(lambda (location river)
(cond
((creek? river)
(string=? (creek-origin river) location))
((confluence? river)
(if (string=? (confluence-location river) location)
#t
(or
(flows-from? location (confluence-main-stem river))
(flows-from? location (confluence-tributary river))))))))
\end{lstlisting}
%
Fertig!
In dieser Funktion passiert etwas Besonderes, das es in den
Funktionen davor noch nicht gab: Sie enthält einen Aufruf von sich
selbst, einen sogenannten \textit{rekursiven Aufruf\index{rekursiver
Aufruf}}. Diese rekursiven Aufrufe sind genau an den Stellen, wo
die Datendefinition von \lstinline{river} Selbstbezüge enthält.
Das ist kein Zufall: Nahezu alle Funktionen, die Daten mit
Selbstbezügen akzeptieren, enthalten an den Stellen der Selbstbezüge
rekursive Aufrufe. Daraus machen wir eine Schablone:
%
\begin{konstruktionsanleitung}{Selbstbezüge als Eingabe: Schablone}
\label{ka:selbstbezug-schablone}
Wenn Du eine Funktion schreibst, die Daten akzeptiert, in denen
Selbstbezüge enthalten sind, dann schreibe an die Stellen der
Selbstbezüge jeweils einen rekursiven Aufruf.
\end{konstruktionsanleitung}
%
Ein Nachtrag noch: Der \lstinline{if}-Ausdruck in der
\lstinline{flows-from?}-Funktion passt \emph{fast} auf das Muster
aus Aufgabe~\ref{aufgabe:iftruefalse}. Der einzige Unterschied ist,
dass in der Alternative der Verzweigung nicht \lstinline{#f} steht.
Allgemein wir ein solcher \lstinline{if}-Ausdruck folgendermaßen
ausgewertet:
%
\begin{lstlisting}
(if |$b$| #t |$a$|)
|\evalsto| #t ; falls |$b=$| #t
|\evalsto| |$a$| ; falls |$b=$| #f
\end{lstlisting}
%
Das können wir mit dem Verhalten von \lstinline{or} vergleichen:
%
\begin{lstlisting}
(or |$b$| |$a$|)
|\evalsto| #t ; falls |$b$| |\evalsto| #t
|\evalsto| |$a$| ; falls |$b$| |\evalsto| #f
\end{lstlisting}
%
Wir können den \lstinline{if}-Ausdruck aus \lstinline{flows-from?}
also durch ein \lstinline{or} ersetzen:
%
\begin{lstlisting}
(or (string=? (confluence-location river) location)
(or (flows-from? location (confluence-main-stem river))
(flows-from? location (confluence-tributary river))))
\end{lstlisting}
%
Das können wir sogar noch weiter vereinfachen, weil \lstinline{or}
auch mit drei Operanden funktioniert:
%
\begin{lstlisting}
(or (string=? (confluence-location river) location)
(flows-from? location (confluence-main-stem river))
(flows-from? location (confluence-tributary river)))
\end{lstlisting}
%
Wenn wir das Resultat auf seine Bedeutung hin lesen, steht da folgendes: Wasser fließt aus \lstinline{location} in den Zusammenfluss \lstinline{river}, wenn~\ldots
\begin{itemize}
\item der Zusammenfluss gerade bei \lstinline{location} stattfindet
\centerline{\textbf{oder}}
\item Wasser von \lstinline{location} in den Hauptfluss fließt
\centerline{\textbf{oder}}
\item Wasser von \lstinline{location} in den Nebenfluss fließt.
\end{itemize}
%
So erzählt ist die Funktionsweise von \lstinline{flows-from?}
(hoffentlich) direkt verständlich.
Wir können so aus diesem Beispiel und
Aufgabe~\ref{aufgabe:iftruefalse} zwei Gleichungen entsprechend
Mantra~\ref{mantra:gleichungen} auf Seite~\pageref{mantra:gleichungen}
machen:
%
\begin{lstlisting}
(if |$b$| #t #f) |$=$| |$b$|
(if |$b$| #t |$a$|) |$=$| (or |$b$| |$a$|)
\end{lstlisting}
%
\section{Bilder modellieren}
\label{sec:image-combinators}
Erinnerst Du Dich noch an Abschnitt~\ref{sec:rechnen-mit-bildern} auf
Seite \pageref{sec:rechnen-mit-bildern}? Da haben wir mit Hilfe des
\texttt{image.rkt}-Teachpacks die Funktionen \lstinline{beside} und
\lstinline{above} verwendet, um Bilder zusammenzusetzen:
%
\begin{lstlisting}
(define s1 (square 40 "solid" "red"))
(define c1 (circle 40 "solid" "green"))
(define p1 (star-polygon 20 10 3 "solid" "blue"))
(beside s1 p1)
|\evalsto{} \includegraphics[height=24pt]{elemente/beside.png}|
(above s1 c1)
|\evalsto{} \includegraphics[width=24pt]{elemente/above.png}|
\end{lstlisting}
%
Bei beiden Funktionen war es möglich, die Ergebnisse wieder als Bilder
zu verwenden:
%
\begin{lstlisting}
(above (beside s1 p1) (beside p1 c1))
|\evalsto{} \includegraphics[width=48pt]{elemente/abovebeside.png}|
\end{lstlisting}
%
Für Bilder ist die Signatur \lstinline{image} zuständig; mir ihrer
Hilfe können wir Signaturen für \lstinline{above} und
\lstinline{beside} formulieren:
%
\begin{lstlisting}
(: beside (image image -> image))
(: above (image image -> image))
\end{lstlisting}
%
An den Signaturen lässt sich klar erkennen, dass es sich um
Kombinatoren handelt. Indirekt können wir daraus schließen, dass es
in der Datendefinition von Bildern ebenfalls Selbstbezüge gibt. Die
könnte so aussehen:
%
\begin{lstlisting}
; Ein Bild ist eins der folgenden:
; |\ldots|
; - eine Nebeneinanderstellung von einem Bild und noch einem Bild
; - eine Übereinanderstellung von einem Bild und noch einem Bild
; |\ldots|
\end{lstlisting}
%
Das heißt, bei den Bildern ist das gleiche Konstruktionsprinzip am
Werk wie bei den Flüssen: Aus "<kleinen"> Bildern werden "<größere">,
und daraus gegebenenfalls noch größere undsoweiter.
An den Signaturen von \lstinline{beside} und \lstinline{above} ist
leicht erkennbar, dass es sich um Kombinatoren handelt: Es gehen
\lstinline{image} rein und \lstinline{image} kommt auch wieder
heraus. Um das bei den Flüssen aus dem letzten Abschnitt klar
anzuzeigen, wäre es sinnvoll, für \lstinline{make-confluence}
ebenfalls eine explizite Signatur anzugeben:
%
\begin{lstlisting}
(: make-confluence (string river river -> river))
\end{lstlisting}
%
Kombinatoren verstecken sich in vielen Problemstellungen, Du musst Sie
nur suchen: Dann findest Du erstaunlich oft auch welche. Darum geht
es in Mantra~\ref{mantra:kombinator}:
\mantrakombinator*
\section{Finanzverträge abbilden}
\label{sec:financial-contracts}
\mentioncode{selbstbezuege/contract.rkt}
%
Es ist Zeit für ein etwas größeres Beispiel. Wir bilden
Finanzverträge ab und programmieren dazu eine stark vereinfachte
Version eines Klassikers der Informatik-Forschung, das zu mehreren
Veröffentlichungen und zur Gründung einer erfolgreichen Firma geführt
hat.\footnote{Wer mehr wissen möchte, sollte das Paper von Peyton
Jones und Eber~\cite{FinancialContracts} lesen, das es auch im
Internet kostenlos zum Download gibt.} Selbstbezüge spielen eine wichtige
Rolle.
Wichtig bei diesem Beispiel: Für die Repräsentation von
Finanzverträgen aus diesem Abschnitt haben renommierte
Informatik-Forscher mehrere Monate gebraucht. Wir können sie mit den
bisher präsentierten Programmiermitteln nachbauen, aber Du solltest
nicht von Dir erwarten, dass Du das Beispiel in wenigen Stunden auch
selbst hättest entwickeln können.
In der Finanzbranche werden oft Verträge geschlossen, die zur
strukturierten Zahlung von Geldbeträgen führen. Wer eine Bankerin
fragt, was der einfachste solche Vertrag ist, bekommt oft die Antwort
\textit{Zero-Bond}. Hier ist ein Beispiel für einen Zero-Bond:
%
\begin{center}
Ich bekomme am 31.\ Juni 2030 \EUR{1000}.
\end{center}
%
So ein Vertrag wird immer zwischen zwei Vertragspartnern geschlossen und der
eine ist immer "<ich">. Daher kommt die Formulierung "<Ich
bekomme">. Der Begriff "<Zero-Bond"> setzt sich zusammen aus "<keine
Zinsen"> (Zero) und "<Recht auf spätere Zahlung"> (Bond).
Wir können daraus eine einfache Datendefinition machen:
%
\begin{lstlisting}
; Ein Zero-Bond hat folgende Eigenschaften:
; - Datum
; - Betrag
; - Währung
\end{lstlisting}
%
An der Formulierung "<hat folgende Eigenschaften"> sehen wir sofort,
dass es sich um zusammengesetzte Daten handelt. Daraus folgt direkt
diese Record-Definition:
%
\indexvariable{zero-bond}
\begin{lstlisting}
(define-record zero-bond
make-zero-bond
(zero-bond-date date)
(zero-bond-amount rational)
(zero-bond-currency currency))
\end{lstlisting}
%
Wir müssten noch Definitionen für \texttt{date} und \texttt{currency}
beisteuern, schieben das aber auf die lange Bank. Wir wollen
stattdessen zunächst noch einmal grundsätzlich über die richtige
Datendefinition für solche Verträge nachdenken.
Es gibt ja noch viel mehr solche Verträge: Futures, Forwards, Swaps,
Swaptions~\ldots{} Jeder Vertrag hat mehrere Eigenschaften, das würde
also darauf hinauslaufen, dass wir sehr viele Datendefinitionen für
zusammengesetzte Daten und die zugehörigen Record-Definitionen
schreiben müssten~-- und am Ende eine Datendefinition zu "<Vertrag">,
die auf eine große Signatur für gemischte Daten hinausläuft. Das ist
eine Menge Arbeit, und sie wäre auch nie fertig, weil sich die Banker
ständig neue Verträge ausdenken.
Wer aber genug solche Verträge studiert, bemerkt, dass diese aus
Bausteinen bestehen, die immer wiederkehren. Das suggeriert einen
anderen Ansatz, nämlich diese Bausteine zu identifizieren und dafür
die Datenanalyse durchzuführen. Wir vollziehen stattdessen die
Arbeit der Autoren des Papers~\cite{FinancialContracts} nach. Die haben
(über mehrere Monate) einen Satz bestechend einfacher Bausteine
zusammengestellt, indem sie bekannte Verträge in möglichst kleine und
einfache Bestandteile zerlegten. Jeder dieser Bestandteile ist für
sich genommen ein "<kleiner Vertrag">, es gibt aber auch Kombinatoren,
die aus kleinen Verträgen größere machen, insbesondere auch noch
unbekannte Klassen von Verträgen. Mit diesen Bestandteilen lassen sich
durch den Einsatz von Kombinatoren sehr viele Finanzverträge (auch
noch unbekannte) abbilden.
Da es mehrere Klassen von Bausteinen und Kombinatoren gibt, werden wir
Verträge als gemischte Daten repräsentieren und dafür schrittweise
eine Datendefinition aufbauen:
%
\begin{lstlisting}
; Ein Vertrag ist eins der folgenden:
; - ...
\end{lstlisting}
%
Dazu gehört natürlich eine passende Signatur-Definition, die wir
ebenfalls schrittweise aufbauen werden:
%
\begin{lstlisting}
(define contract
(signature (mixed |\ldots|)))
\end{lstlisting}
%
Um die Bausteine und Kombinatoren zu identifizieren, schauen wir uns
zunächst den Zero-Bond näher an. Auch wenn der einfach aussieht, gibt
es mehrere Aspekte, die auch unabhängig voneinander Sinn ergeben: die
Auszahlung eines bestimmten \textit{Betrags} in einer bestimmten
\textit{Währung} und die Möglichkeit, eine Zahlung \textit{später} zu
veranlassen. Das sind drei separate Ideen, und es ist sinnvoll, diese
getrennt als Daten zu repräsentieren.
\paragraph{Zahlung einer bestimmten Währung}
Fangen wir an mit der Zahlung einer bestimmten Währung. Der
Einfachheit halber nehmen wir an, dass alle Zahlungen in Euro sind.
Um die richtige Daten-Definition zu finden, versuchen wir, die
\emph{einfachste} Euro-Zahlung zu finden, und die besteht aus
\emph{einem} Euro. Die Daten-Definition dazu ist ernüchternd einfach:
%
\begin{lstlisting}
; Ein Euro hat keine Eigenschaften
\end{lstlisting}
%
Wir könnten den Euro einfach als die Zeichenkette \lstinline{"EUR"}
abbilden. Das wäre sinnvoll, wenn der Euro Teil einer Aufzählung ist.
Dies ist allerdings hier nicht der Fall, andere Vertragsbausteine haben
durchaus Eigenschaften. (Siehe dazu auch
Aufgabe~\ref{aufgabe:aufzaehlung-vs-nullstellig} auf
Seite~\pageref{aufgabe:aufzaehlung-vs-nullstellig}.) Wir nehmen die
Datendefinition stattdessen beim Wort "<hat~\ldots{} Eigenschaften">
und bilden den Euro als zusammengesetzte Daten ab. Dann wird daraus
folgende Record-Definition:
%
\indexvariable{one-euro}
\begin{lstlisting}
(define-record one-euro
make-one-euro
one-euro?)
\end{lstlisting}
%
Wir fügen als Nächstes \lstinline{one-euro} zur Daten- und zur
Signatur-Definition von \lstinline{contract} hinzu:
%
\begin{lstlisting}
; Ein Vertrag ist eins der folgenden:
; - ein Euro
; - ...
(define contract
(signature (mixed one-euro |\ldots|)))
\end{lstlisting}
%
\paragraph{Beliebige Beträge}
Als Nächstes bilden wir die Möglichkeit ab, einen Betrag zu wählen.
Eine naheliegende erste Idee wäre, die Idee "<$n$ Euros"> abzubilden,
etwa so:
%
\indexvariable{euros}
\begin{lstlisting}
; Euros haben folgende Eigenschaft:
; - wieviele
(define-record euros
make-euros
euros?
(euros-amount rational))
\end{lstlisting}
%
Wenn wir das hätten, bräuchten wir aber \lstinline{one-euro} nicht mehr.
Außerdem wäre das Resultat dann nicht mehr so einfach wie möglich:
Besser wäre es, wenn wir die Idee "<wieviele"> trennen könnten von
"<Euro">. Dann könnten wir sagen "<eine Vielzahl von einem Euro">. Das
klingt erstmal komisch: %, auch in der Datendefinition:
%%%% HK: "Mehrere haben folgende Eigenschaften klingt mir dann doch zu blöd
%
\begin{lstlisting}
; Eine Vielzahl hat folgende Eigenschaften:
; - wieviele
; - wovon
\end{lstlisting}
%
Aber die richtige Form für zusammengesetzte Daten hat das schon
einmal. Es reicht für eine erste Skizze einer Record-Definition:
%
\begin{lstlisting}
(define-record multiple
make-multiple
multiple?
(multiple-number |\ldots|)
(multiple-of |\ldots|))
\end{lstlisting}
%
Wir müssen aber noch klären, was die Signatur von
\lstinline{multiple-number} respektive \lstinline{multiple-of} ist.
Bei \lstinline{multiple-number} müssen wir uns nicht auf ganze Zahlen
beschränken~-- ein halber Euro geht schließlich auch. Da ist
\lstinline{rational} sinnvoll.
Bei \lstinline{multiple-of} liegt es nahe, \lstinline{one-euro}
hinzuschreiben: Dann könnten wir den Zero-Bond schon hinschreiben.
Allerdings wäre \lstinline{multiple} ziemlich eingeschränkt und nicht
besonders zukunftssicher: Was, wenn andere Währungen dazukommen,
\lstinline{one-gbp} oder so? Vielleicht sind wir versucht, eine
Signatur \lstinline{currency} für Währungen zu definieren und für
\lstinline{multiple-of} zu verwenden. Aber das wäre immer noch zu
eingeschränkt: Man kann ja auch mehrere Aktien (zum Beispiel) im
Rahmen eines Vertrags übergeben~-- oder auch mehrere Zero-Bonds oder
andere Verträge.
Und da ist sie ganz plötzlich, die zentrale Idee: Dass ein Vertrag den
Abschluss \emph{anderer} Ver\-träge verfügen kann. Wir schreiben
deshalb als Signatur von \lstinline{multiple-of} einfach
\lstinline{contract} und schärfen bei der Gelegenheit die Datendefinition:
%
\indexvariable{multiple}
\begin{lstlisting}
; Ein Vielfaches besteht aus:
; - Anzahl
; - Vertrag
(define-record multiple
make-multiple
multiple?
(multiple-number rational)
(multiple-of contract))
\end{lstlisting}
%
Nun gibt es schon zwei Klassen von Verträgen~-- \lstinline{one-euro} und
\lstinline{multiple}. Wir erweitern die Definition von
\lstinline{contract} entsprechend:
%
\begin{lstlisting}
(define contract
(signature (mixed one-euro multiple)))
\end{lstlisting}
%
Mit Hilfe von \lstinline{one-euro} und \lstinline{multiple} können wir
einen Vertrag definieren, der festlegt, dass wir \EUR{$100$} bekommen~--
sofort:
%
\begin{lstlisting}
(define euro100 (make-multiple 100 (make-one-euro))) ; 100 Euros
\end{lstlisting}
%
\begin{aufgabeinline}
Definiere eine Funktion \lstinline{make-euros} mit Kurzbeschreibung,
Signatur und Test wie folgt:
\begin{lstlisting}
; Euro-Betrag auszahlen
(: make-euros (rational -> contract))
(check-expect (make-euros 100) euro100)
\end{lstlisting}
\end{aufgabeinline}
%
Mit Hilfe der Funktion aus der Aufgabe können wir leicht auch
\EUR{200} definieren:
%
\begin{lstlisting}
(define euro200 (make-euros 200)) ; 200 Euros
\end{lstlisting}
\paragraph{Verzögerungen}
Für die Zero-Bonds benötigen wir noch die Möglichkeit,
eine Zahlung zu verzögern.
Dazu benutzen wir den gleichen Trick wie
bei \lstinline{multiple} und definieren einen weiteren Kombinator, der
einen ganzen Vertrag verzögert: Wir schließen also einen Vertrag ab,
später einen Vertrag abzuschließen, und der kann dann die Zahlung
auslösen. Um so einen Vertrag zu beschreiben, benötigen wir das
Datum, an dem der "<spätere"> Vertrag aktiv wird und den späteren
Vertrag selbst:
%
\begin{lstlisting}
; Eine Verzögerung besteht aus:
; - Datum
; - Vertrag, der zu dem Datum gültig wird
\end{lstlisting}
%
Diese Datendefinition beschreibt wieder zusammengesetzte Daten. Die
dazugehörige Record"=Definition sieht so aus:
%
\indexvariable{later}
\begin{lstlisting}
(define-record later
make-later
later?
(later-date date)
(later-contract contract))
\end{lstlisting}
%
Mit \lstinline{later} können wir die Signatur-Definition von
\lstinline{contract} erweitern:
%
\begin{lstlisting}
(define contract
(signature (mixed one-euro multiple later)))
\end{lstlisting}
%
Aber es fehlt noch etwas: Wir haben für \lstinline{later-date} die
Signatur \lstinline{date} verwendet, die noch gar nicht definiert ist.
Ein Datum besteht aus Jahr, Monat und Tag, was direkt zu einer
Record-Definition führt:
%
\indexvariable{date}
\begin{lstlisting}
(define-record date
make-date
date?
(date-year natural)
(date-month natural)
(date-day natural))
\end{lstlisting}
%
\begin{aufgabeinline}
Wir haben für alle Felder von \lstinline{date} die Signatur
\lstinline{natural} verwendet. Diese Signatur ist sehr unpräzise,
zumindest für \lstinline{date-month} und \lstinline{date-day}.
Verbessere die Signaturen von \lstinline{date-month} und
\lstinline{date-day} mit Hilfe von \lstinline{integer-from-to}
aus Abschnitt~\ref{function:integer-from-to} auf
Seite~\pageref{function:integer-from-to}!
\end{aufgabeinline}
%
Bevor es weitergeht, definieren wir noch zwei Beispiele für das Datum:
%
\begin{lstlisting}
(define date1 (make-date 2019 07 26)) ; 26. Juli 2019
(define date2 (make-date 2019 12 24)) ; Weihnachten 2019
\end{lstlisting}
%
Mit Hilfe dieser Definitionen können wir auch endlich zwei Beispiele
für \lstinline{later} angeben:
%
\begin{lstlisting}
(define later1
(make-later date1 euro100)) ; Ich bekomme am 26.7.2019 100|\EUR{}|.
(define later2
(make-later date2 euro200)) ; Ich bekomme Weihnachten 2019 200|\EUR{}|.
\end{lstlisting}
%
Außerdem können wir den Zero-Bond vom Anfang dieses Abschnitts
definieren:
%
\begin{lstlisting}
; Ich bekomme am 31. Juni 2030 1000|\EUR{}|.
(define zero1 (make-later (make-date 2030 06 31)
(make-euros 1000)))
\end{lstlisting}
\begin{figure}[tb]
\centering
\begin{tikzpicture}
\draw [fill=gray!50] (0, 0) rectangle (10, 4);
\draw [fill=gray!75] (0.5, 0.5) rectangle (9.5, 3);
\draw [fill=gray] (1, 1) rectangle (8, 2);
\node [right] at (0.5, 3.5) {\lstinline{later}};
\node at (5, 3.5) {\lstinline{"31.6.20230"}};
\node [right] at (1, 2.5) {\lstinline{multiple}};
\node at (5, 2.5) {\lstinline{1000}};
\node [right] at (1.5, 1.5) {\lstinline{one-euro}};
\end{tikzpicture}
\caption{Zero-Bond als Schichten dargestellt}
\label{fig:zero-bond}
\end{figure}
\noindent Abbildung~\ref{fig:zero-bond} zeigt den Aufbau des
Zero-Bonds \lstinline{zero1} als ineinandergeschachtelte Schichten:
In den \lstinline{later}-Vertrag ist ein \lstinline{multiple}-Vertrag
eingeschachtelt, und dort wiederum der \lstinline{one-euro}-Vertrag.
Aus dieser Sicht ist der \lstinline{multiple}-Vertrag der
"<innere"> Vertrag des \lstinline{later}-Vertrags.
\paragraph{Verträge kombinieren}
Jetzt wo die Zero-Bonds erledigt sind, können wir überlegen, ob es
noch weitere Möglichkeiten geben sollte, Verträge herzustellen. Immer
wenn Du Repräsentationen mit Kombinatoren baust, solltest Du nach
Möglichkeiten suchen, aus zwei Dingen ein größeres Ding zu bauen.
Beispiele für dieses Prinzip haben wir in diesem Kapitel schon mehrere
gesehen:
%
\begin{itemize}
\item Ein Hauptfluss und ein Nebenfluss werden zu einem Fluss
kombiniert.
\item Zwei Bilder werden übereinander, nebeneinander und aufeinander
zu einem Bild kombiniert.
\end{itemize}
%
Auf die Verträge bezogen bedeutet dies, dass wir zwei Verträge
zu einem zusammensetzen, der die Rechte und Pflichten von beiden kombiniert.
So könnte dies gehen:
%
\begin{lstlisting}
; Eine Kombinationsvertrag besteht aus:
; - Vertrag Nr. 1
; - Vertrag Nr. 2
\end{lstlisting}
%
Das sind wieder zusammengesetzte Daten mit zwei Selbstbezügen:
%
\indexvariable{both}
\begin{lstlisting}
(define-record both
make-both
both?
(both-contract-1 contract)
(both-contract-2 contract))
\end{lstlisting}
%
Hier sind zwei Beispiele für \lstinline{both}:
%
\begin{lstlisting}
; Heute 100|\EUR{}|, später nochmal 100|\EUR{}|
(define both1 (make-both euro100 later1))
; Heute 200|\EUR{}|, später nochmal 200|\EUR{}|
(define both2 (make-both both1 later2))
\end{lstlisting}
%
Wir müssen noch die Definition von \lstinline{contract} erweitern:
%
\begin{lstlisting}
(define contract
(signature (mixed one-euro multiple later both)))
\end{lstlisting}
%
\paragraph{Der kleinste Vertrag}
Es gibt noch ein weiteres Prinzip beim Repräsentieren mit
Kombinatoren: Suche immer nach dem \emph{kleinsten} möglichen
Baustein. Wir haben ja schon einen ziemlich kleinen Baustein
definiert: \lstinline{one-euro}. Aber oft ist der kleine Baustein
eine Variation von "<nichts">, und so ist es auch hier: Der kleinste
Vertrag ist einer ohne Rechte oder Pflichten. Ihn sollten wir
unbedingt auch definieren~-- zunächst nur der Vollständigkeit halber,
aber später werden wir ihn tatsächlich noch brauchen.
Solch ein "<Nichts-Vertrag"> hat, wie \lstinline{one-euro} auch, keine
Eigenschaften. Wir repräsentieren ihn aber, genau wie bei
\lstinline{one-euro}, durch einen Record, damit wir ihn mit Hilfe
seiner Signatur in die Definition von \lstinline{contract} einbauen
können:
%
\indexvariable{nothing}