-
Notifications
You must be signed in to change notification settings - Fork 24
/
i1list.tex
1831 lines (1684 loc) · 65 KB
/
i1list.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 Listen}
\label{cha:rek}
\label{cha:list}
Im vorigen Kapitel haben wir bereits Daten mit Selbstbezug
kennengelernt, die Informationen variabler Größe repräsentieren
können: Flüsse, Bilder und Finanzverträge. Die Repräsentationen dafür
waren recht speziell das jeweilige Einsatzgebiet gekoppelt. In diesem
Kapitel lernen wir eine besonders praktische Datendefinition mit
Selbstbezug kennen, die in nahezu allen Einsatzgebieten nützlich ist:
\textit{Listen}.\index{Liste}
Hier sind einige Listen aus dem täglichen Leben:
%
\begin{center}
\begin{tabular}{l@{\qquad}l@{\qquad}l@{\qquad}l@{\qquad}l@{\qquad}l}
Brot & Herbert & 1 & Axl & Dauerwelle & Pumps \\
Butter & Mike & 2 & Slash & Dreadlocks \\
Käse & & 3 & Duff & Irokese \\
& & 4 & Dizzy & Vokuhila \\
&& 5 & Izzy \\
&& 6 & Melissa \\
&&& Brain\\
&&& Bumblefoot
\end{tabular}
\end{center}
%
Keine dieser Listen ist auf ihre jeweilige Länge festgelegt: Zur Liste
mit "<Brot"> könnten beispielsweise noch "<Gurken"> hinzukommen, zur
Liste mit Axl, Slash undsoweiter könnte zum Beispiel noch Steven
dazukommen.
\section{Listen repräsentieren}
\mentioncode{listen/list0.rkt}
%
In der Einführung haben wir Listen mit unterschiedlichen Sorten von
Elementen gesehen. Für den Anfang konzentrieren wir uns zunächst
einmal auf Listen von Zahlen, um die Dinge einfach zu halten. Dann
schreiben wir eine Funktion, um die Zahlen aus so einer Liste zu
addieren. Es gibt verschiedene Möglichkeiten, Listen zu
repräsentieren. Die Repräsentation, die wir in diesem Kapitel
einführen, hat als besonders einfach und universell verwendbar
herausgestellt.
Diese Repräsentation ist nach dem gleichen Prinzip entstanden die zum
Finanzverträge in Abschnitt~\ref{sec:financial-contracts} auf
Seite~\pageref{sec:financial-contracts}: Wir suchen zunächst nach der
einfachsten Liste, die wir uns vorstellen können. Man könnte auf die
Idee kommen, dass dies eine Liste mit nur einem Element ist, aber noch
einfacher ist eine mit gar keinen Elementen~-- die \textit{leere
Liste}.\index{leere Liste}\index{Liste!leer} Natürlich werden
wir auch nichtleere Listen brauchen, darum ist der erste Entwurf
einer Datendefinition wie folgt:
%
\begin{lstlisting}
; Eine Liste aus Zahlen ist eins der folgenden:
; - eine leere Liste
; - eine nichtleere Liste
\end{lstlisting}
%
Die leere Liste hat keine Bestandteile, wir werden sie
aber später von den nichtleeren Listen unterscheiden müssen. Wir
benutzen deshalb eine leere Record-Definition:
%
\indexvariable{empty-list}
\begin{lstlisting}
; leere Liste
(define-record empty-list
make-empty-list
empty?)
\end{lstlisting}
%
Beim Namen für das Prädikat haben wir etwas gemogelt. Von Amts wegen
müsste es \lstinline{empty-list?} heißen. Da es aber noch öfter
vorkommen wird, kürzen wir es zu \lstinline{empty?} ab.
Wir definieren außerdem eine Abkürzung für die leere Liste.
Auch diese kommt noch öfter vor, und wir müssen sie so nicht jedesmal
neu komstruieren:
%
\begin{lstlisting}
(define empty (make-empty-list))
\end{lstlisting}
%
Nun sind die nichtleeren Listen dran. Nach dem Kombinatorprinzip aus dem
vorigen Kapitel sollten wir eine nichtleere Liste konstruieren, indem
wir eine bestehende Liste erweitern:
%
\begin{lstlisting}
; Eine nichtleere Liste besteht aus:
; - dem ersten Element
; - einer Liste aus Zahlen mit den restlichen Elementen
\end{lstlisting}
%
Diese spezielle Definition für nichtleere Listen heißt
\textit{Cons-Liste}.\index{Cons-Liste} Das zweite Feld enthält
eine "<Liste aus Zahlen">~-- da ist der Selbstbezug.
Wir ändern die Datendefinition für Listen entsprechend ab:
%
\begin{lstlisting}
; Eine Liste aus Zahlen ist eins der folgenden:
; - eine leere Liste
; - eine Cons-Liste
; Eine Cons-Liste besteht aus:
; - dem ersten Element
; - einer Liste aus Zahlen mit den restlichen Elementen
\end{lstlisting}
%
Die Datendefinition für Cons wandeln wir in eine Record-Definition um.
Dafür nehmen wir an, dass die Signatur für Listen von Zahlen
\lstinline{list-of-numbers} heißt~-- die werden wir im Nachgang
definieren:
%
\indexvariable{cons-list}\indexvariable{first}\indexvariable{rest}
\begin{lstlisting}
(define-record cons-list
cons
cons?
(first number)
(rest list-of-numbers))
\end{lstlisting}
%
Ähnlich wie bei \lstinline{empty-list} haben wir die Funktionen der
Record-Definition kürzer genannt als sonst, weil sie noch oft
vorkommen werden:
sonst:\indexvariable{cons-list}\indexvariable{cons}\indexvariable{cons?}\indexvariable{first}\indexvariable{rest}\label{def:cons}
Der Konstruktor heißt statt
\lstinline{make-cons-list} hier \lstinline{cons}, das Prädikat nur \lstinline{cons?} statt
\lstinline{cons-list?} und bei den Selektoren fehlt jeweils das
\lstinline{cons-} vorn.
Es fehlt noch die Definition der Signatur \lstinline{list-of-numbers}
für "<Liste von Zahlen">, die aus leeren Listen und Cons-Listen
gemischt wird:
%
\indexvariable{list-of-numbers}
\begin{lstlisting}
(define list-of-numbers
(signature
(mixed empty-list
cons-list)))
\end{lstlisting}
%
Hier sind einige Beispiele für Listen:
%
\begin{lstlisting}
; leere Liste
(define list0 empty)
; einelementige Liste mit der Zahl 42
(define list1 (cons 42 empty))
; Liste mit den Zahlen 1 2 3
(define list3 (cons 1 (cons 2 (cons 3 empty))))
; Liste mit den Zahlen e und pi
(define list2 (cons 2.7183 (cons 3.14159 empty)))
; Liste mit den Zahlen 2 3 5 7
(define list4 (cons 2 (cons 3 (cons 5 (cons 7 empty)))))
\end{lstlisting}
%
\begin{figure}[tb]
\centering
\begin{tikzpicture}
\draw (0, 0) rectangle (9.5, 4); \node [below] at (1, 4)
{\lstinline{cons-list}}; \node [right] at (1, 3) {\lstinline{2}};
\draw (2, 0.5) rectangle (9, 3.5); \node [below] at (3, 3.5)
{\lstinline{cons-list}}; \node [right] at (3, 2.5)
{\lstinline{3}}; \draw (4, 1) rectangle (8.5, 3); \node [below] at
(5, 3) {\lstinline{cons-list}}; \node [right] at (5, 2)
{\lstinline{5}}; \draw (6, 1.5) rectangle (8, 2.5); \node [right]
at (6, 2) {\lstinline{empty-list}};
\end{tikzpicture}
\caption{Struktur der Liste mit den Elementen 2 3 5}
\label{fig:list-structure}
\end{figure}
%
\noindent An den Beispielen kann man sehen, dass es einen Unterschied
zwischen der intuitiven Struktur von Listen und ihrer Repräsentation
gibt. Intuitiv besteht eine Liste aus einer Aneinanderreihung ihrer
Elemente. Die Repräsentation ist aber geschachtelt, wie
Abbildung~\ref{fig:list-structure} zeigt. Sie erlaubt uns
insbesondere, aus kleineren Listen größere zu bauen und trotzdem die
kleineren Listen wiederzuverwenden:
%
\begin{lstlisting}
; Liste mit den Zahlen 1 2 3 5 7
(define list5 (cons 1 list4))
\end{lstlisting}
%
Dieses Beispiel zeigt außerdem, dass der Konstruktor \lstinline{cons} an eine
existierende Liste \emph{vorn} ein Element anhängt.
\label{sec:list-sum}
Nun sind wir bereit, eine Funktion über Listen zu schreiben, welche
die Zahlen einer Liste aufaddiert. Kurzbeschreibung und Signatur
sehen so aus:
%
\begin{lstlisting}
; Summe der Elemente einer Liste von Zahlen berechnen
(: list-sum (list-of-numbers -> number))
\end{lstlisting}
%
Als Nächstes brauchen wir Testfälle, die wir mit Hilfe der
Beispiel-Listen von oben (und gegebenenfalls einem Taschenrechner)
konstruieren:
%
\begin{lstlisting}
(check-expect (list-sum list2) 5.85989)
(check-expect (list-sum list3) 6)
(check-expect (list-sum list4) 17)
\end{lstlisting}
%
\begin{aufgabeinline}
Schreibe auch Testfälle für \lstinline{list-sum} auf Basis der
Beispiel-Listen \lstinline{list0} und \lstinline{list1}! Falls Du
Dir nicht sicher bist, rate!
\end{aufgabeinline}
%
Als Nächstes kommt wie immer das Gerüst:
%
\begin{lstlisting}
(define list-sum
(lambda (list)
|\ldots|))
\end{lstlisting}
%
Die Signatur sagt, dass \lstinline{list-sum} eine Liste als Eingabe
akzeptiert~-- gemischte Daten. Die Schablone dazu sieht so aus:
%
\begin{lstlisting}
(define list-sum
(lambda (list)
(cond
((empty? list) |\ldots|)
((cons? list) |\ldots|))))
\end{lstlisting}
%
Beim \lstinline{cons-list}-Fall handelt es sich um zusammengesetzte
Daten, wir können also entsprechend der Schablone dafür
Selektor-Aufrufe hinschreiben. Bei \lstinline{rest} sitzt außerdem
ein Selbstbezug, wir schreiben also auch einen rekursiven Aufruf in
die Schablone:
%
\begin{lstlisting}
(define list-sum
(lambda (list)
(cond
((empty? list) |\ldots|)
((cons? list)
|\ldots|
(first list)
(list-sum (rest list))
|\ldots|))))
\end{lstlisting}
%
Nun sind alle möglichen Konstruktionsanleitungen angewendet. Wir
vervollständigen die Schablone, zuerst den Fall für
Cons-Listen. Dort steht in der Schablone \lstinline{(first list)}
und \lstinline{(list-sum (rest list))}. Zum Verständnis
hilft, die beiden Ausdrucke ins Deutsche zu übersetzen:
%
\begin{itemize}
\item \lstinline{(first list)} ist das \emph{erste Element der Liste} und
\item \lstinline{(list-sum (rest list))} ist die \emph{Summe der restlichen
Elemente}.
\end{itemize}
%
Gefragt
ist aber die Summe \emph{aller Elemente}, dazu müssen wir die beiden
noch addieren:
%
\begin{lstlisting}
(+ (first list) (list-sum (rest list)))
\end{lstlisting}
%
Es bleibt der Fall der leeren Liste. Intuitiv hat die gar keine
Summe, aber irgendetwas müssen wir da hinschreiben. Wir schreiben
vorläufig $0$ ins Programm, werden diese Wahl aber noch diskutieren:
%
\indexvariable{list-sum}
\begin{lstlisting}
(define list-sum
(lambda (list)
(cond
((empty? list) 0)
((cons? list)
(+ (first list) (list-sum (rest list)))))))
\end{lstlisting}
%
Damit funktionieren die Testfälle und die Funktion ist fertig!
\begin{aufgabeinline}
Verfolge die Auswertung der Testfälle im Stepper!
\end{aufgabeinline}
\begin{aufgabeinline}
Ändere den Zweig für die leere Liste und schreibe etwas anderes als
0 hin. Funktionieren die Testfälle dann noch?
\end{aufgabeinline}
%
Tatsächlich ist $0$ die einzig richtige Zahl, die im
\lstinline{empty}-Fall stehen darf. Das mag intuitiv einleuchten: die
leere Liste ist schließlich "<nichts"> und 0 ist auch irgendwie
"<nichts">. Ein weiteres Beispiel wird aber gleich zeigen, dass diese
Argumentation zu einfach ist.
Wir schreiben als Nächstes eine Funktion, die alle Zahlen einer Liste
\emph{multipliziert}. Hier sind Kurzbeschreibung, Signatur, Tests
und Gerüst:
%
\begin{lstlisting}
; Produkt der Elemente einer Liste von Zahlen berechnen
(: list-product (list-of-numbers -> number))
(check-expect (list-product list1) 42)
(check-expect (list-product list3) 6)
(check-expect (list-product list4) 210)
(define list-product
(lambda (list)
|\ldots|))
\end{lstlisting}
%
Die Schablone entsteht genau wie bei \lstinline{list-sum} aus den
Konstruktionsanleitungen für gemischte Daten, zusammengesetzte Daten
und Selbstbezüge:
%
\begin{lstlisting}
(define list-product
(lambda (list)
(cond
((empty? list) |\ldots|)
((cons? list)
|\ldots|
(first list)
|\ldots|
(list-product (rest list))))))
\end{lstlisting}
%
Wir kümmern uns wieder zuerst um den
\lstinline{cons}-Fall. In der Schablone stehen \lstinline{(first list)}, das erste
Element und \lstinline{(list-product (rest list))}, das Produkt der
restlichen Elemente. Wir müssen nur noch beide miteinander
multiplizieren, um das Produkt aller Elemente zu berechnen:
%
\begin{lstlisting}
(* (first list) (list-product (rest list)))
\end{lstlisting}
%
Bleibt wieder der \lstinline{empty}-Fall~-- wieder die leere Liste.
Die Versuchung ist groß, wieder "<nichts"> hinzuschreiben, also 0.
Dann allerdings schlagen \emph{alle} Testfälle fehl:
\begin{alltt}
Der tatsächliche Wert 0 ist nicht der erwartete Wert 42.
Der tatsächliche Wert 0 ist nicht der erwartete Wert 6.
Der tatsächliche Wert 0 ist nicht der erwartete Wert 210.
\end{alltt}
%
Warum kommt immer $0$ heraus? Das können wir anhand der Auswertung
von
\lstinline{(list-product list3)}
sehen. Die sieht so aus~-- wir haben
einige Zwischenschritte ausgelassen:
%
\begin{lstlisting}
(list-product list3)
|\evalsto| (list-product (cons 1 (cons 2 (cons 3 empty))))
|\evalsto| (cond ((empty? (cons 1 |\ldots|)) |\ldots|) ((cons? (cons 1 |\ldots|)) |\ldots|))
|\evalsto| (cond ((empty? (cons 1 |\ldots|)) |\ldots|) ((cons? (cons 1 |\ldots|)) |\ldots|))
|\evalsto| (* (first (cons 1 |\ldots|)) (list-product (rest (cons 1 |\ldots|))))
|\evalsto| (* 1 (list-product (rest (cons 1 |\ldots|))))
|\evalsto| (* 1 (list-product (cons 2 (cons 3 empty))))
|\evalsto| (* 1 (* 2 (list-product (cons 3 empty))))
|\evalsto| (* 1 (* 2 (* 3 (list-product empty))))
|\evalsto| (* 1 (* 2 (* 3 (cond ((empty? empty) 0) |\ldots|))))
|\evalsto| (* 1 (* 2 (* 3 (cond (#t 0) |\ldots|))))
|\evalsto| (* 1 (* 2 (* 3 0)))
\end{lstlisting}
%
Die Auswertung ist auf so einem guten Weg~-- aber um Schluss macht
\lstinline{list-product} alles kaputt und multipliziert mit $0$. Und so
ist es mit \emph{jeder} Eingabe. Wir müssten also
statt mit $0$ mit einer Zahl multiplizieren, die das Ergebnis nicht
kaputtmacht sondern genauso lässt, wie es ist. Im Fall der
Multiplikation ist das die $1$, nicht die $0$. Die richtige Funktion
sieht damit so aus:
%
\indexvariable{list-product}
\begin{lstlisting}
(define list-product
(lambda (list)
(cond
((empty? list) 1)
((cons? list)
(* (first list) (list-product (rest list)))))))
\end{lstlisting}
%
Rückblickend können wir damit auch erklären, warum bei
\lstinline{list-sum} im \lstinline{empty}-Fall $0$ stehen muss: Die $0$
macht beim Addieren das Ergebnis nicht kaputt. Darum spricht man auch
davon, dass $0$ das \textit{neutrale Element}\index{neutrales Element}
bezüglich der Addition ist, $1$ das neutrale Element bezüglich der
Multiplikation.\label{page:neutrales-element}
\section{Listen mit anderen Elementen}
\mentioncode{listen/list-of.rkt}
%
Das Prinzip "<Liste"> ist nicht auf Listen von Zahlen beschränkt:
Genauso gut kann man sich Listen aus Zeichenketten, den Tieren aus
Kapitel~\ref{sec:animal} oder Bildern vorstellen. Um das zu
ermöglichen, können wir die Datendefinition
anpassen. Wir entfernen alle Erwähnungen von
Zahlen und sprechen ausschließlich von "<Elementen">:
%
\begin{lstlisting}
; Eine Liste ist eins der folgenden:
; - eine leere Liste
; - eine Cons-Liste
; Eine Cons-Liste besteht aus:
; - dem ersten Element
; - einer Liste mit den restlichen Elementen
\end{lstlisting}
%
Tatsächlich können wir Listen bereits bauen, deren Elemente keine
Zahlen sind:
%
\indexvariable{string-list}
\begin{lstlisting}
(define string-list (cons "Mike" (cons "Sperber" empty)))
\end{lstlisting}
%
\begin{aufgabeinline}
Probiere die Definition von \lstinline{string-list} aus!
\end{aufgabeinline}
%
Diese Definition kann DrRacket zwar ausführen, aber es gibt jede
Menge Signaturverletzungen.
Die verletzten Signaturen sind allesamt \lstinline{number}, am
prominentesten in der Record-Definition von \lstinline{cons-list}.
Wenn wir dort auch Zeichenketten verwenden wollen, ohne Listen aus
Zahlen zu verbieten, müssen wir über
\lstinline{number} und \lstinline{string} abstrahieren. Bei der
Record-Definition geht das, indem wir aus \lstinline{cons-list} stattdessen
\lstinline{(cons-list-of element)} machen.
Das wirkt wie ein
\lstinline{lambda} mit \lstinline{element} als Parameter. Wir können
dann \lstinline{element} innerhalb
der Record-Definition benutzen:\label{function:cons-list-of}
%
\indexvariable{cons-list-of}
\begin{lstlisting}
(define-record (cons-list-of element)
cons
cons?
(first element)
(rest |\ldots|))
\end{lstlisting}
%
Diese Record-Definition definiert \lstinline{cons-list-of} als Funktion,
die eine Signatur akzeptiert (die Signatur der Elemente) und eine
Signatur zurückliefert, nämlich die der Cons-Listen mit der gegebenen
Signatur für die Elemente. Die Signatur können wir auch hinschreiben:
%
\begin{lstlisting}
(: cons-list-of (signature -> signature))
\end{lstlisting}
%
Da \lstinline{cons-list-of} eine Signatur konstruiert, nennen wir diese
Funktion auch \textit{Signatur-Konstruktor}.\index{Signatur-Konstruktor}
Das \lstinline{element} ist ein \textit{Signatur-Parameter}\index{Signatur-Parameter}.
Wir müssen also immer da, wo wir vorher \lstinline{cons-list} stand,
\lstinline{(cons-list-of ...)} schreiben und eine Signatur für die
Elemente angeben, also zum Beispiel \lstinline{(cons-list-of number)}
oder \lstinline{(cons-list-of string)}.
Die Signatur von \lstinline{rest} war vorher
\lstinline{list-of-numbers}, die müssen wir also auch ändern. Wie
genau, ergibt sich erst im Zusammenhang mit der abstrahierten
Definition von \lstinline{list-of-numbers}. Da es sich hier um eine
ganz normale Definition mit \lstinline{define} handelt, können wir ein
\lstinline{lambda} benutzen, um über den Signatur der Elemente zu
abstrahieren:
%
\indexvariable{list-of}
\begin{lstlisting}
(define list-of
(lambda (element)
(signature
(mixed empty-list
(cons-list-of element)))))
\end{lstlisting}
%
Wir können mit dieser Definition nun statt \lstinline{list-of-numbers} also
\lstinline{(list-of number}) schreiben. Damit können wir auch die
Record-Definition von \lstinline{cons-list-of} vervollständigen, indem
wir beim \lstinline{rest}-Feld als Signatur \lstinline{(list-of element)} schreiben, wo vorher
\lstinline{list-of-numbers} stand:
%
\begin{lstlisting}
(define-record (cons-list-of element)
cons
cons?
(first element)
(rest (list-of element)))
\end{lstlisting}
%
Das erledigt die Signaturverletzungen.
\begin{feature}{\texttt{define-record} mit Signatur-Parametern}{scheme:define-record-parameters}
Eine \lstinline{define-record}"=Form\indexvariable{define-record}
mit Signatur-Parametern hat folgende allgemeine Gestalt:\label{def:define-record-parameters}
%
\begin{alltt}
(define-record (\(t\) \(\mathit{sp}\sb{1}\) \(\ldots\) \(\mathit{sp}\sb{k}\))
\(c\)
\(p\)
(\(\mathit{sel}\sb{1}\) \(\mathit{sig}\sb{1}\))
\(\ldots\)
(\(\mathit{sel}\sb{n}\) \(\mathit{sig}\sb{n}\)))
\end{alltt}
%
%
\begin{itemize}
\item Die Signatur-Parameter $\mathit{sp}\sb{1} \ldots \mathit{sp}\sb{k}$
können in den Signaturen
\(\mathit{sig}\sb{1}\ldots\mathit{sig}\sb{n}\) verwendet werden.
\item $t$ ist der Name des Signatur-Konstruktors mit der Signatur:
%
\begin{alltt}
(: \(t\) (signature \(\ldots\) \textrm{(\(k\)-mal)} -> signature))
\end{alltt}
\item $c$, $p$ und $\mathit{sel}_1, \ldots, \mathit{sel}_n$ sind
die Namen von Konstruktor, Prädikat und Selektoren.
\end{itemize}
%
\end{feature}
Abbildung~\ref{scheme:define-record-parameters} fasst die
neue Form von \lstinline{define-record} mit
Signatur-Parametern zusammen.
Bei dieser neuen Form von \lstinline{define-record} ist
nicht mehr offensichtlich, wie die Signaturen des Konstruktors und der
beiden Selektoren aussehen sollen, wenn wir sie ausschreiben. Wir
brauchen dafür ein neues Konstrukt, um auszudrücken, dass potenziell
bei jedem Aufruf von \lstinline{cons}, \lstinline{first} und
\lstinline{rest} eine andere Elementsignatur verwendet wird. Das
sieht so aus:
%
\begin{lstlisting}
(: cons (%element (list-of %element) -> (cons-list-of %element)))
(: first ((cons-list-of %element) -> %element))
(: rest ((cons-list-of %element) -> (list-of %element)))
\end{lstlisting}
%
Das \verb|%| kennzeichnet
\textit{Signaturvariablen}\index{Signaturvariable}. Die
Signaturvariable \lstinline{%element} steht für eine beliebige
Signatur. Die Signatur-Deklaration von \lstinline{cons} bedeutet
außerdem, dass die Signatur von neuem Element (das erste \lstinline{%element})
und die Signatur der Listenelemente (das \lstinline{%element} in
\lstinline{(list %element)} übereinstimmen sollten. (DrRacket
überprüft dies aber nicht.)
Vielleicht ist Dir der Gedanke gekommen, dass statt
\lstinline{%element} in den obigen Signaturen auch \lstinline{any}
stehen könnte. Rein technisch gesehen ist das korrekt. Allerdings
wären die resultierenden Signaturen weniger präzise. Nimm einmal
an, dort stünde zum Beispiel das hier:
%
\begin{lstlisting}
(: cons (any (list-of any) -> (cons-list-of any)))
\end{lstlisting}
%
Diese Signatur sagt "<\lstinline{cons} akzeptiert irgendeinen Wert und
irgendeine Liste und produziert wieder irgendeine Liste">. Zum
Vergleich sagt die Signatur
%
\begin{lstlisting}
(: cons (%element (list-of %element) -> (cons-list-of %element)))
\end{lstlisting}
%
aus, dass das neue Element, das \lstinline{cons} akzeptiert, die
gleiche Signatur erfüllen muss wie die schon vorhandenen Elemente der
Liste und die Elemente der Resultatliste: Alle drei Vorkommen von \lstinline{%element} müssen in einer
konkreten Anwendung von \lstinline{cons} die gleiche Signatur sein.
Bevor das Programm wieder
funktioniert, müssen wir noch ein kleines Problem
beheben:
\lstinline{List-of-numbers} ist nicht mehr
definiert, steht aber noch in den Signatur"=Deklarationen von
\lstinline{list-sum} und \lstinline{list-product}. Wir können
entweder \lstinline{list-of-numbers} durch \lstinline{(list-of number)}
ersetzen oder eine Signatur-Definition
dazuschreiben:
%
\indexvariable{list-of-numbers}
\begin{lstlisting}
(define list-of-numbers (signature (list-of number)))
\end{lstlisting}
%
Diese Definition muss vor \lstinline{list-sum}
und \lstinline{list-product} stehen, damit sie dort bekannt ist.
\section{Konstruktionsanleitung}
Bei der Konstruktion von Funktionen, die Listen akzeptieren, haben wir
drei Schablonen kombiniert: gemischte und
zusammengesetzte Daten sowie Selbstbezüge. Wir
werden noch viele Funktionen mit Listen als Eingabe schreiben.
Deshalb lohnt es sich, solchen Funktionen eine eigene
Konstruktionsanleitung zu widmen:
\begin{konstruktionsanleitung}{Listen als Eingabe: Schablone}
\label{ka:listen-eingabe-schablone}
Die Schablone für eine Funktion, die eine Liste akzeptiert, sieht
folgendermaßen aus:
%
\begin{lstlisting}
(: |\(f\)| (... (list-of |\(\mathit{elem}\)|) ... -> ...))
(define |\(f\)|
(lambda (|\ldots| |\(\mathit{list}\)| |\ldots|)
(cond
((empty? |\(\mathit{list}\)|) |\ldots|)
((cons? |\(\mathit{list}\)|)
|\ldots|
(first |\(\mathit{list}\)|)
|\ldots|
(|\(f\)| |\ldots| (rest |\(\mathit{list}\)|) |\ldots|)
|\ldots|
))))
\end{lstlisting}
\end{konstruktionsanleitung}
\section{Eingebaute Listen}
Da Listen in diesem Buch noch oft vorkommen und es umständlich ist,
jedesmal die Definitionen für \lstinline{cons} und \lstinline{list-of} an
den Anfang von Programmen zu setzen, macht die nächste Sprachebene das
Programmieren mit Listen einfacher.\index{Sprachebene}
\smallskip
\noindent\textbf{Schalte deshalb in die nächste Sprachebene hoch:}
\smallskip
\noindent Fange eine neue Datei an. Wähle danach wieder das Menü
\texttt{Sprache} aus, darunter den Menüpunkt \texttt{Sprache auswählen} und dann
\texttt{Schreibe Dein Programm!} (\emph{ohne} \texttt{Anfänger}).
Dann drücke noch einmal \texttt{Start}.
\begin{feature}{\texttt{list}}{scheme:list}
Die eingebaute Funktion \lstinline{list}\indexvariable{list} erlaubt es, Listen aus ihren Elementen
ohne Verwendung von \lstinline{cons} zu erzeugen. Sie
akzeptiert eine beliebige Anzahl von Argumenten, macht daraus eine
Liste und gibt diese zurück:
%
\begin{lstlisting}
(list 1 2 3)
|\evalsto| #<list 1 2 3>
(list "Axl" "Slash" "Izzy")
|\evalsto| #<list "Axl" "Slash" "Izzy">
\end{lstlisting}
\end{feature}
Ab sofort sind \lstinline{cons}, \lstinline{cons?}, \lstinline{first} und
\lstinline{rest} vordefiniert genau wie der Signaturkonstruktor
\lstinline{list-of}. Es gibt außerdem eine praktische neue Funktion
\lstinline{list}, die in Abbildung~\ref{scheme:list} beschrieben ist.
In der neuen Sprachebene werden nichtleere Listen übersichtlicher ausgedruckt
als bisher, nämlich als
\lstinline{#<list ...>}, wobei die Listenelemente zwischen den spitzen
Klammern aufgereiht sind:
%
\begin{lstlisting}
(list "Axl" "Slash" "Izzy")
|\evalsto| #<list "Axl" "Slash" "Izzy">
\end{lstlisting}
%
\section{Parametrische Polymorphie}
\label{sec:parametric-polymorphism}
\label{sec:more-lists}
\mentioncode{listen/polymorphism.rkt}
%
Keine Angst vor der hochtrabenden Überschrift: Wir schreiben in diesem
Abschnitt eine recht einfache Funktion, welche die Länge einer Liste
ermittelt.\indexvariable{list-length} Hier ist die
Kurzbeschreibung:
%
\begin{lstlisting}
; Länge einer Liste berechnen
\end{lstlisting}
%
Der Anfang einer Signatur-Deklaration sieht so aus:
%
\begin{lstlisting}
(: list-length ((list-of |\ldots|) -> natural))
\end{lstlisting}
%
Was können wir anstelle der Ellipse hinschreiben? Der Funktion sollte
egal sein, auf welche Signatur die Elemente der Liste passen. Wir
können dies zum Ausdruck bringen, indem wir eine Signaturvariable
verwenden, genau wie bei den Signaturen von \lstinline{cons},
\lstinline{first} und \lstinline{rest}. Das sieht so aus:\label{page:list-length}
%
\begin{lstlisting}
(: list-length ((list-of %element) -> natural))
\end{lstlisting}
%
Die Verwendung der Signaturvariable \lstinline{%element} bedeutet,
genau wie oben, dass die Signatur der Elemente der Liste bei jedem
Aufruf von \lstinline{list-length} anders sein kann.
Solche Funktionen, die Argumente akzeptieren, deren Signaturen
Signaturvariablen enthalten, heißen \textit{polymorph} oder auch
\textit{parametrisch polymorph} (weil die Signaturvariable eine Art
Parameter abgibt), und das dazugehörige Konzept heißt
\textit{parametrische
Polymorphie}\index{Polymorphie}\index{parametrische Polymorphie}:
ein großes Wort, das hier für eine kleine Sache steht. Mehr und interessantere
Beispiele für parametrische Polymorphie wird es in
Kapitel~\ref{cha:higher-order} geben.
Weiter mit \texttt{list-length}~-- hier ist das Gerüst:
%
\begin{lstlisting}
(define list-length
(lambda (lis)
|\ldots|))
\end{lstlisting}
%
Die Schablone ist wie gehabt:
%
\begin{lstlisting}
(define list-length
(lambda (lis)
(cond
((empty? lis) |\ldots|)
((cons? lis)
|\ldots| (first lis) |\ldots|
|\ldots| (list-length (rest lis)) |\ldots|))))
\end{lstlisting}
%
Es ist \texttt{list-length} egal, was der Wert von \texttt{(first
lis)} ist. Die Länge der Liste ist unabhängig davon, was für Werte
sich darin befinden~-- entscheidend ist nur, wieviele es sind. (Dieser
Umstand ist gerade verantwortlich für die parametrische Polymorphie.)
Deshalb können wir den Selektoraufruf \texttt{(first lis)} aus der Schablone streichen und
diese dann zum vollständigen Rumpf ergänzen:
%
\indexvariable{list-length}
\begin{lstlisting}
(define list-length
(lambda (lis)
(cond
((empty? lis) 0)
((cons? lis)
(+ 1
(list-length (rest lis)))))))
\end{lstlisting}
%
Die Funktion~\lstinline{list-length} ist unter dem
Namen~\lstinline{length}\indexvariable{length} fest
eingebaut.\label{func:length}
\section{Funktionen, die Listen produzieren}
\mentioncode{listen/dillo-list.rkt}
%
In den vorherigen Abschnitten haben wir ausschließlich Funktionen
programmiert, die Listen \emph{akzeptieren}. In diesem Abschnitt
schreiben wir Funktionen, die Listen \emph{produzieren}. Das geht mit
Techniken, die wir bereits vorgestellt haben. Wir machen die Sache
interessanter, indem wir in einem ersten Beispiel Listen von
zusammengesetzten Daten betrachten und in einem zweiten Beispiel zwei
Listen verarbeiten.
\subsection{Gürteltiere überfahren}
Auf Seite~\pageref{page:run-over-dillo} haben wir die Funktion
\texttt{run-over-dillo} geschrieben, die für das Überfahren von
Gürteltieren zuständig ist. In diesem Abschnitt schreiben wir die
Funktion, die das gleich massenweise erledigt, beispielsweise für alle
Gürteltiere auf einem Highway.
Dazu übernehmen wir Daten- und Record-Definition von Gürteltieren aus
Abschnitt~\ref{page:run-over-dillo} sowie die Funktiondefinition von
\lstinline{run-over-dillo}. Du kannst auch einfach den Gürteltier-Code
erweitern~-- dann musst Du allerdings die Sprachebene auf
\texttt{Schreibe Dein Programm!} hochschalten, damit die Listen zur
Verfügung stehen.
Gürteltiere können wir in Listen stecken, ebenso wie Zahlen,
Zeichenketten oder boolesche Werte. Hier ist ein Beispiel:
%
\indexvariable{highway75}
\begin{lstlisting}
; Gürteltiere auf Highway 75
(define highway75 (list dillo1 dillo2 dillo3 dillo4))
\end{lstlisting}
(\lstinline{Dillo1}, \lstinline{dillo2}, \lstinline{dillo3} und \lstinline{dillo4} sind die
Beispielgürteltiere aus Abschnitt~\ref{page:run-over-dillo}.)
Diese Liste hat die Signatur \lstinline{(list-of dillo)}. Wenn wir eine
Funktion schreiben wollen, die alle Gürteltiere aus einer Liste
überfährt, müsste diese also folgende Kurzbeschreibung und Signatur
haben:
%
\begin{lstlisting}
; Gürteltiere überfahren
(: run-over-dillos ((list-of dillo) -> (list-of dillo)))
\end{lstlisting}
%
Als Testfall kann obige Beispielliste herhalten:
%
\begin{lstlisting}
(check-expect (run-over-dillos highway75)
(list (make-dillo 55000 #f)
dillo2
(make-dillo 60000 #f)
dillo4))
\end{lstlisting}
%
Zur Erinnerung: \lstinline{dillo2} und \lstinline{dillo4} sind bereits tot,
dementsprechend sind sie überfahren wie zuvor.
Hier ist das Gerüst:
%
\begin{lstlisting}
(define run-over-dillos
(lambda (dillos)
|\ldots|))
\end{lstlisting}
%
Die Funktion akzeptiert eine Liste als Eingabe, wir können also, wie
schon so oft, die entsprechende Schablone zum Einsatz bringen:
%
\begin{lstlisting}
(define run-over-dillos
(lambda (dillos)
(cond
((empty? dillos) |\ldots|)
((cons? dillos)
|\ldots| (first dillos) |\ldots|
|\ldots| (run-over-dillos (rest dillos)) |\ldots|))))
\end{lstlisting}
%
Im ersten Zweig ist die Sache klar: Geht eine leere Liste rein, kommt
auch eine leere Liste raus. Im zweiten Zweig können wir uns erst
einmal um das erste Gürteltier kümmern. Wir haben ja bereits eine
Funktion, die ein einzelnes Gürteltier überfährt; diese können wir auf
das erste Element der Liste anwenden:
%
\begin{lstlisting}
(define run-over-dillos
(lambda (dillos)
(cond
((empty? dillos) empty)
((cons? dillos)
|\ldots| (run-over-dillo (first dillos)) |\ldots|
|\ldots| (run-over-dillos (rest dillos)) |\ldots|))))
\end{lstlisting}
%
Lesen wir noch einmal die beiden Ausdrücke, die im zweiten Zweig
stehen:
%
\begin{itemize}
\item \lstinline{(run-over-dillo (first dillos))} ist das erste Gürteltier
der Liste, überfahren.
\item \lstinline{(run-over-dillos (rest dillos))} ist eine Liste der
restlichen Gürteltiere, auch überfahren.
\end{itemize}
%
Gefragt ist eine Liste \emph{aller} Gürteltiere, überfahren:
Wir müssen also nur die Resultate der beiden Ausdrucke mit
\lstinline{cons} kombinieren:
%
\indexvariable{run-over-dillos}
\begin{lstlisting}
(define run-over-dillos
(lambda (dillos)
(cond
((empty? dillos) empty)
((cons? dillos)
(cons (run-over-dillo (first dillos))
(run-over-dillos (rest dillos)))))))
\end{lstlisting}
%
Fertig!
Dieses Beispiel zeigt, dass wir für Funktionen, die Listen produzieren,
keine neue Technik brauchen: Wenn eine Funktion eine leere Liste
produzieren soll, benutzen wir an der entsprechenden Stelle
\lstinline{empty}, und bei nichtleeren Listen benutzen wir
\lstinline{cons}, bringen also die Schablone für Funktionen zum
Einsatz, die zusammengesetzte Daten produzieren.
\subsection{Gürteltiere extrahieren}
Wir nehmen uns noch eine weitere Übung vor, bei der Listen produziert
werden: Aus einer Liste von Gürteltieren wollen wir alle (noch)
lebenden herausholen, vielleicht, um sie in Sicherheit zu bringen.
So sehen Kurzbeschreibung, Signatur, ein Testfall und Gerüst aus:
%
\begin{lstlisting}
; Lebendige Gürteltiere aufsammeln
(: live-dillos ((list-of dillo) -> (list-of dillo)))
(check-expect (live-dillos highway75)
(list dillo1 dillo3))
(define live-dillos
(lambda (dillos)
|\ldots|))
\end{lstlisting}
%
Wir fügen als Nächstes die Standard-Schablone für Funktionen ein, die
Listen akzeptieren:
%
\begin{lstlisting}
(define live-dillos
(lambda (dillos)
(cond
((empty? dillos) |\ldots|)
((cons? dillos)
|\ldots| (first dillos) |\ldots|
|\ldots| (live-dillos (rest dillos)) |\ldots|))))
\end{lstlisting}
%
Der erste
Fall betrifft die leere Liste~-- aus einer leeren Liste von
Gürteltiere können wir keine lebendigen Gürteltiere herausholen,
da kommt also \lstinline{empty} hin.
Im zweiten Fall ist es etwas aufwendiger. Wir machen uns wieder klar,
was die beiden Bestandteile bedeuten, die in der Schablone stehen:
%
\begin{itemize}
\item \lstinline{(first dillo)} ist das erste Gürteltier in der Liste.
\item \lstinline{(live-dillos (rest dillos))} sind die lebendigen
unter den restlichen Gürteltieren.
\end{itemize}
%
Da die Gesamtaufgabe ist, die lebendigen Gürteltiere
zu extrahieren, sollten wir beim ersten Gürteltier feststellen, ob
es (noch) lebt. Da steht dann folgender Zwischenstand:
%
\begin{lstlisting}
(define live-dillos
(lambda (dillos)
(cond
((empty? dillos) empty)
((cons? dillos)
|\ldots| (dillo-alive? (first dillos)) |\ldots|
|\ldots| (first dillos) |\ldots|
|\ldots| (live-dillos (rest dillos)) |\ldots|))))
\end{lstlisting}
%
Der Aufruf \lstinline{(dillo-alive? (first dillos))} bietet sich
für eine binäre Verzweigung an:
%
\begin{lstlisting}
(define live-dillos
(lambda (dillos)
(cond
((empty? dillos) empty)
((cons? dillos)
(if (dillo-alive? (first dillos))
|\ldots|
|\ldots|)
|\ldots| (first dillos) |\ldots|
|\ldots| (live-dillos (rest dillos)) |\ldots|))))
\end{lstlisting}
%
Wir müssen uns jetzt überlegen, was wir in die beiden Zweige des
\lstinline{if} schreiben. Im ersten Zweig~-- der Konsequente~-- ist
das Gürteltier \lstinline{(first dillos)} (noch) am Leben, im
zweiten~-- der Alternative~-- ist es tot.
In der Alternative (Gürteltier tot) ist \lstinline{(live-dillos (rest dillos))} schon
die richtige Antwort. \lstinline{(first dillos)} fällt unter den Tisch:
%
\begin{lstlisting}