284
Schreibe Dein Programm! Einführung in die Programmierung Herbert Klaeren Michael Sperber 2. Februar 2017

Schreibe Dein Programm! · 2017. 2. 2. · Anhang 1 erläutert die im Buch verwendeten mathematischen Nota-tionen und Termini. Programmiersprache Leider sind die heute in der Industrie

  • Upload
    others

  • View
    0

  • Download
    0

Embed Size (px)

Citation preview

  • ii

    “i1” — 2017/2/2 — 12:52 — page 1 — #1 ii

    ii

    ii

    Schreibe Dein Programm!Einführung in die Programmierung

    Herbert Klaeren Michael Sperber

    2. Februar 2017

  • ii

    “i1” — 2017/2/2 — 12:52 — page 2 — #2 ii

    ii

    ii

    Copyright © 2007-2014 by Herbert Klaeren and Michael SperberDieses Buch ist lizensiert unter der Creative-Commons-Lizenz Na-

    mensnennung 4.0 International (CC BY 4.0), nachzulesen unter http://creativecommons.org/licenses/by/4.0/deed.de.

    Dieses Buch wurde mit LATEX gesetzt, unter Verwendung der suftesi-Klasse vonIvan Valbusa.

    http://creativecommons.org/licenses/by/4.0/deed.dehttp://creativecommons.org/licenses/by/4.0/deed.de

  • ii

    “i1” — 2017/2/2 — 12:52 — page 3 — #3 ii

    ii

    ii

    Inhaltsverzeichnis

    1 Elemente des Programmierens 5

    1.1 Handwerkszeug für das Programmieren 51.2 Bausteine für Programme 61.3 Rechnen ohne Zahlen 61.4 Namen und Definitionen 71.5 Information und Daten 71.6 Domänenwissen 81.7 Kommentare und Formatierung 91.8 Abstraktion 91.9 Kurzbeschreibung und Signatur 111.10 Testfälle 131.11 Unsinnige Daten 151.12 Probleme und Teilprobleme 151.13 Auswertung 15

    2 Fallunterscheidungen und Verzweigungen 21

    2.1 Fallunterscheidungen 212.2 Boolesche Ausdrücke in Scheme 222.3 Programmieren mit Fallunterscheidungen 232.4 Konstruktionsanleitung für Fallunterscheidungen 262.5 Verkürzte Tests 272.6 Binäre Verzweigungen und syntaktischer Zucker 272.7 Signaturdefinitionen 292.8 Unsinnige Daten abfangen 32

    3 Zusammengesetzte Daten 35

    3.1 Computer konfigurieren 353.2 Record-Definitionen 393.3 Schablonen für zusammengesetzte Daten 403.4 Gürteltiere im Computer 41

    4 Gemischte Daten 55

    4.1 Gemischte Daten 554.2 Die Zucker-Ampel 59

  • ii

    “i1” — 2017/2/2 — 12:52 — page 4 — #4 ii

    ii

    ii

    4 Inhaltsverzeichnis

    5 Programmieren mit Listen 69

    5.1 Listen repräsentieren 695.2 Mit Listen programmieren 715.3 Signaturkonstruktoren 755.4 Eingebaute Listen 785.5 Parametrische Polymorphie 785.6 Prozeduren, die Listen produzieren 79

    6 Induktive Beweise und Definitionen 89

    6.1 Aussagen über natürliche Zahlen 896.2 Induktive Beweise führen 916.3 Struktur der natürlichen Zahlen 946.4 Endliche Folgen 956.5 Notation für induktive Definitionen 986.6 Strukturelle Rekursion 996.7 Strukturelle Induktion 101

    7 Prozeduren über natürlichen Zahlen 107

    8 Higher-Order-Programmierung 113

    8.1 Higher-Order-Prozeduren auf Listen 1138.2 Listen zusammenfalten 1188.3 Anonyme Prozeduren 1218.4 Prozedurfabriken 1228.5 Der Schönfinkel-Isomorphismus 123

    9 Zeitabhängige Modelle 135

    9.1 Das Teachpack image2.ss 1359.2 Zwischenergebnisse benennen 1379.3 Modelle und Ansichten 1389.4 Bewegung und Zustand 1389.5 Andere Welten 139

    10 Eigenschaften von Prozeduren 141

    10.1 Eigenschaften von eingebauten Operationen 14110.2 Eigenschaften von Prozeduren auf Listen 14910.3 Eigenschaften von Prozeduren höherer Ordnung 15610.4 Programme beweisen 15710.5 Rekursive Programme beweisen 15810.6 Invarianten 162

    11 Fortgeschrittenes Programmieren mit Rekursion 167

    11.1 Lastwagen optimal beladen 167

    12 Programmieren mit Akkumulatoren 173

    12.1 Zwischenergebnisse mitführen 17312.2 Schablonen für Prozeduren mit Akkumulator 17912.3 Kontext und Endrekursion 18012.4 Das Phänomen der umgedrehten Liste 181

  • ii

    “i1” — 2017/2/2 — 12:52 — page 1 — #5 ii

    ii

    ii

    Inhaltsverzeichnis 1

    13 Bäume 185

    13.1 Binärbäume 18513.2 Suchbäume 18813.3 Eigenschaften der Suchbaum-Operationen 193

    14 Schrittweise Verfeinerung 199

    14.1 Löschen in Suchbäumen 19914.2 Datenverfeinerung 205

    15 Zuweisungen und Zustand 215

    16 Der λ-Kalkül 217

    16.1 Sprache und Reduktionssemantik 21716.2 Normalformen 22216.3 Der λ-Kalkül als Programmiersprache 22316.4 Auswertungsstrategien 22816.5 Die Auswertungsstrategie von Scheme 230

    17 Die SECD-Maschine 233

    17.1 Der angewandte λ-Kalkül 23317.2 Die einfache SECD-Maschine 23417.3 Quote und Symbole 23917.4 Implementierung der SECD-Maschine 24217.5 Die endrekursive SECD-Maschine 25417.6 Der λ-Kalkül mit Zustand 25617.7 Die SECDH-Maschine 25817.8 Implementierung der SECDH-Maschine 259

    1 Mathematische Grundlagen 269

    1.1 Aussagenlogik 2691.2 Mengen 2701.3 Prädikatenlogik 2731.4 Multimengen 2731.5 Relationen und Abbildungen 2741.6 Ordnungen 276

  • ii

    “i1” — 2017/2/2 — 12:52 — page 2 — #6 ii

    ii

    ii

  • ii

    “i1” — 2017/2/2 — 12:52 — page 3 — #7 ii

    ii

    ii

    Vorwort

    TBDSchreibe Dein Programm! ist aus dem Vorgängerbuch Die Macht der

    Abstraktion entstanden, das seinerseits aus dem Vorgängerbuch VomProblem zum Programm entstanden ist. Wir bemerkten nach der Veröffent-lichung vonDie Macht der Abstraktion, daß wir das Buch einerseits einembreiten Publikum einfach zugänglich machen wollten, andererseits kon-tinuierlich Verbesserungen einarbeiten wollten. Beides war mit unseremdamaligen Verleger leider nicht zu machen. Entsprechend haben wiruns entschieden, unsere Arbeit unter neuem Titel fortzuführen undfrei zugänglich zu machen. Es wird hoffentlich die letzte Titeländerungbleiben.

    Wir hatten zwar bereits viel Material aus Die Macht der Abstraktion biszur Unkenntlichkeit revidiert. Die „TBD“-Abschnitte in Schreibe DeinProgramm! kennzeichnen Stellen, deren Notwendigkeit bereits in DieMacht der Abstraktion etabliert werde, die aber noch geschrieben werdenmüssen.

    Lehre mit diesem Buch

    Das hier präsentierte Material entstammt einer Reihe von einführendenVorlesungen zur Informatik für Haupt- und Nebenfachstudenten undfür Geisteswissenschaftler sowie Erfahrungen in zahlreichen Fortbil-dungen. Inhalte und Präsentation wurden dabei unter Beobachtung derStudenten und ihres Lernerfolgs immer wieder verbessert. Der Stoffdieses Buchs entspricht einer einsemestrigen Vorlesung Informatik I mitvier Vorlesungsstunden und zwei Übungsstunden.

    Anhang 1 erläutert die im Buch verwendeten mathematischen Nota-tionen und Termini.

    Programmiersprache

    Leider sind die heute in der Industrie populären Programmiersprachenfür die Lehre nicht geeignet: ihre Abstraktionsmittel sind begrenzt, unddas Erlernen ihrer komplizierten Syntax kostet wertvolle Zeit und Kraft.

    Aus diesem Grund verwendet der vorliegende Text eine Serie vonspeziell für die Lehre entwickelten Programmiersprachen, die auf Racketund Scheme basieren. Diese sind über die DrRacket-Entwicklungsumge-bung Anfängern besonders gut zugänglich.

  • ii

    “i1” — 2017/2/2 — 12:52 — page 4 — #8 ii

    ii

    ii

    4 Vorwort

    Software und Material zum Buch

    Die Programmierbeispiele dieses Buchs bauen auf der Programmier-umgebung DrRacket auf, die speziell für die Programmierausbildungentwickelt wurde. Insbesondere unterstützt DrRacket die Verwendungsogenannter Sprachebenen, Varianten der Sprache, die speziell für dieAusbildung zugeschnitten wurden. Dieses Buch benutzt spezielle Spra-chebenen, die Teil der sogenannten DMdA-Erweiterungen von DrRacketsind.

    DrRacket ist kostenlos im Internet auf der Seite http://www.racket-lang.org/ erhältlich und läuft auf Windows-, Mac- und Unix-/Linux-Rechnern.Die DMdA-Erweiterungen sind von der Homepage zu Schreibe DeinProgramm! erhältlich:

    http://www.deinprogramm.de/

    Dort steht auch eine Installationsanleitung.Auf der Homepage befindet sich weiteres Material zum Buch, insbe-

    sondere Quelltext für alle Programmbeispiele zum Herunterladen.

    Danksagungen

    Wir, die Autoren, haben bei der Erstellung dieses Buchs immens vonder Hilfe anderer profitiert. Robert Giegerich, Ulrich Güntzer, PeterThiemann, Martin Plümicke, Christoph Schmitz und Volker Klaerenmachten viele Verbesserungsvorschläge zum Vorgängerbuch Vom Pro-blem zum Programm.

    Martin Gasbichler hielt einen Teil der Vorlesungen der letzten Infor-matik I, half bei der Entwicklung der DMdA-Erweiterungen und istfür eine große Anzahl von Verbesserungen verantwortlich, die sichin diesem Buch finden. Eric Knauel, Marcus Crestani, Sabine Sperber,Jan-Georg Smaus und Mayte Fleischer brachten viele Verbesserungs-vorschläge ein. Andreas Schilling, Torsten Grust und Michael Hanushielten Vorlesungen auf Basis dieses Buches und brachten ebenfallsviele Verbesserungen ein. Besonderer Dank gebührt den Tutoren undStudenten unserer Vorlesung Informatik I, die eine Fülle wertvoller Kritikund exzellenter Verbesserungsvorschläge lieferten.

    Wir sind außerdem dankbar für die Arbeit unserer Kollegen, diePionierarbeit in der Entwicklung von Konzepten für die Programmier-ausbildung geliefert haben. Eine besondere Stellung nehmen MatthiasFelleisen, Robert Bruce Findler, Matthew Flatt und Shriram Krishnamur-thi und ihr Buch How to Design Programs [Felleisen et al., 2001] ein,das entscheidende didaktische Impulse für dieses Buch gegegen hat.Felleisens Arbeit im Rahmen des PLT-Projekts hat uns stark beeinflußt;das PLT-DrRacket-System ist eine entscheidende Grundlage für dieArbeit mit diesem Buch.

    Herbert KlaerenMichael Sperber

    Tübingen, April 2014

    http://www.racket-lang.org/http://www.racket-lang.org/

  • ii

    “i1” — 2017/2/2 — 12:52 — page 5 — #9 ii

    ii

    ii

    1 Elemente des Programmierens

    TBD

    1.1 Handwerkszeug für das Programmieren

    In diesem Kapitel wird zum ersten Mal die ProgrammierumgebungDrRacket verwendet. (Bezugsquelle und weitere Hinweise dazu stehenim Vorwort.) Zur Verwendung mit diesem Buch müssen in DrRacketdie DMdA-Sprachebenen aktiviert werden. Dies geschieht durch Aus-wahl des Menüpunkts Sprache → Sprache auswählen (bzw. Language→ Choose language in der englischen Fassung), worauf ein Dialog zurAuswahl von sogenannten Sprachebenen erscheint. Dort gibt es in derAbteilung Lehrsprachen eine Überschrift namens DeinProgramm, unter-halb dessen mehrere Einträge erscheinen, die speziell auf die Kapiteldieses Buchs zugeschnitten sind.

    Für den ersten Teil des Buches ist die Ebene Die Macht der Abstraktion- Anfänger zuständig. In Kapitel 5 wird auf Die Macht der Abstraktion(ohne „Anfänger“), und in Kapitel 15 auf Die Macht der Abstraktionmit Zuweisungen umgeschaltet. In Kapitel 17 kommt schließlich DieMacht der Abstraktion - fortgeschritten zum Einsatz.

    DrRacket bietet dem Programmierer ein zweigeteiltes Fenster:

    1. In der oberen Hälfte des Fensters (dem Editor oder Definitionsfenster)steht der Programmtext. Der Editor funktioniert ähnlich wie einreguläres Textverarbeitungsprogramm.

    2. In der unteren Hälfte des Fensters (dem Interaktionsfenster) werdendie Ausgaben des Programms angezeigt. Außerdem kann der Pro-grammierer hier „Fragen“ an das Programm stellen, um einzelneProgrammteile gezielt auszuprobieren.Im Interaktionsfenster berechnet DrRacket die Antworten sofort nachDruck auf die Return-Taste und druckt diese aus. Der Inhalt des In-teraktionsfensters kann allerdings nicht direkt abgespeichert werden.Der Editor ist also für den entstehenden Programmtext gedacht, dasInteraktionsfensters zum schnellen Ausprobieren.

    TBD

    TDB

    Abbildung 1.1. Das Interaktionsfenster von DrRacket

  • ii

    “i1” — 2017/2/2 — 12:52 — page 6 — #10 ii

    ii

    ii

    6 Kapitel 1

    1.2 Bausteine für Programme

    TBD

    1.3 Rechnen ohne Zahlen

    Ausdrücke und Werte gibt es in Computerprogrammen nicht nur inForm von Zahlen. Zum Beispiel gibt es auch Text, wie in Abbildung 1.2beschrieben. (Kästen wie Abbildung 1.2 werden in diesem Buch nochoft dazu dienen, neue Sprachelemente einzuführen.)

    Zeichenketten (auf Englisch Strings) repräsentieren Text. Literale fürZeichenketten haben folgende Form:

    "z1z2 . . . zn"

    Dabei sind die zi beliebige einzelne Zeichen, außer " selbst. Beispiel:

    "Mike was here!"

    Das Anführungszeichen (") kann nicht „ungeschützt“ vorkommen, daes das Ende der Zeichenkette markiert. Es wird als Zeichen innerhalbeiner Zeichenkette durch \" dargestellt:

    "Herbert sagt, Mike wäre \"doof\"!"

    Abbildung 1.2. Zeichenketten

    Mit Text kann DrRacket auch rechnen, und zwar mit der eingebautenProzedur string-append, die zwei Zeichenketten aneinanderhängt:

    (string-append "Herbert" "Mike")

    ↪→ "HerbertMike"(string-append "Mike" " " "ist doof")

    ↪→ "Mike ist doof"Die eingebaute Prozedur string-length liefert die Anzahl der Buchsta-ben in einer Zeichenkette:

    (string-length "Herbert")

    ↪→ 7(string-length "Mike")

    ↪→ 4Die Prozeduren string->number und number->string konvertieren zwi-schen Zahlen und den Zeichenketten, die diese darstellen:

    (string->number "23")

    ↪→ 23(number->string 23)

    ↪→ "23"Programme können auch mit Bildern rechnen. Dazu wird eine Erwei-terung zu DrRacket benötigt, ein sogenanntes Teachpack: Wählen Siedazu im Menü Sprache den Punkt Teachpack hinzufügen und wählenSie image2.ss aus. Danach können Sie zum Beispiel ins Programmschreiben:

  • ii

    “i1” — 2017/2/2 — 12:52 — page 7 — #11 ii

    ii

    ii

    Elemente des Programmierens 7

    (square 40 "solid" "red")

    ↪→(circle 40 "solid" "green")

    ↪→(star-polygon 20 10 3 "solid" "blue")

    ↪→

    Diese Bilder sind Werte wie Zahlen und Zeichenketten auch. Insbeson-dere können Sie mit Definitionen an Namen gebunden werden:

    (define s1 (square 40 "solid" "slateblue"))

    (define c1 (circle 40 "solid" "slateblue"))

    (define p1 (star-polygon 20 10 3 "solid" "cornflowerblue"))

    Mit Bildern kann DrRacket ebenfalls rechnen:

    (beside/align "bottom" s1 c1 p1)

    ↪→

    Bilder und Animationen mit Bildern werden ausführlich in Kapitel 9behandelt.

    1.4 Namen und Definitionen

    TBD

    1.5 Information und Daten

    Eine Definition wie

    (define mehrwertsteuer 19)

    suggeriert, daß die Zahl 19 an dieser Stelle eine Bedeutung „in derrealen Welt“ hat, zum Beispiel in einem Programm, das eine Registrier-kasse steuert oder das bei der Steuererklärung hilft. Die Bedeutungkönnte folgende Aussage sein: „Der Mehrwertsteuersatz beträgt 19%.“Dieser Satz repräsentiert Information, also ein Fakt über die Welt oderzumindest den Ausschnitt der Welt, in dem das Programm arbeiten soll.In Computerprogrammen wird Information in eine vereinfachte Formgebracht, mit der das Programm rechnen kann – in diesem Fall die Zahl19. Diese vereinfachte Form heißt Daten: Daten sind Repräsentationen fürInformation. Beim Programmieren ist eine unserer Hauptaufgaben ent-sprechend, die richtigen Form für die Daten zu wählen, um die für dasProgramm relevanten Informationen darzustellen die Informationendann in Daten zu übersetzen.

    Nicht immer ist offensichtlich, welche Information durch bestimmteDaten repräsentiert werden. Das Datum 23 zum Beispiel könnte eineReihe von Informationen darstellen:

  • ii

    “i1” — 2017/2/2 — 12:52 — page 8 — #12 ii

    ii

    ii

    8 Kapitel 1

    • die Anzahl der Haare von Bruce Willis• die aktuelle Außentemperatur in °C in Tübingen• die Außentemperatur vom 1.7.2000 in °C in Tübingen• die Größe in m2 des Schlafzimmers• die Rückennummer von Michael Jordan

    Damit andere unsere Programme lesen können, werden wir also immerwieder klarstellen müssen, wie Information in Daten zu übersetzen istund umgekehrt.

    Manche Programme können auch Informationen direkt verarbeiten,meist dadurch, daß sie diese erst in Daten übersetzen und dann dieDaten weiterverarbeiten. Der Teil, der diese Übersetzung leistet, heißtBenutzerschnittstelle. Zunächst werden wir uns allerdings primär mit reindatenverarbeitenden Programmen beschäftigen; Benutzerschnittstellenkommen später.

    1.6 Domänenwissen

    Hier ist eine einfache Denksportaufgabe:

    Auf einem Parkplatz stehen PKWs und Motorräder ohne Beiwagen. Zusammenseien es n Fahrzeuge mit insgesamt m Rädern. Bestimmen Sie die Anzahl P derPKWs.

    Die Anzahl P der PKWs plus die Anzahl M der Motorräder mußoffensichtlich die Gesamtzahl n der Fahrzeuge ergeben. Außerdem hatjeder PKW vier Räder und jedes Motorrad zwei Räder. Die Radzahlender PKWs und Motorräder zusammen müssen m ergeben. Es ergibtsich folgendes Gleichungsystem:

    P + M = n4P + 2M = m

    Auflösen der ersten Gleichung nach M ergibt

    M = n− P

    und Einsetzen in die zweite Gleichung führt zu

    4P + 2(n− P) = m4P + 2n− 2P = m

    2P = m− 2n

    P =m− 2n

    2

    Am Ende steht also eine Formel, die wir für konkrete m und n auch ineinen Ausdruck verwandeln können:

    (/ (- 10 (* 2 4)) 2)

    ↪→ 1(/ (- 12 (* 2 3)) 2)

    ↪→ 3

  • ii

    “i1” — 2017/2/2 — 12:52 — page 9 — #13 ii

    ii

    ii

    Elemente des Programmierens 9

    Um zu den Ausdrücken zu kommen, welche die Anzahl der PKWs aus-rechnen, haben wir sogenanntes Domänenwissen benutzt: Die „Domäne“der Aufgabe sind PKWs und Motorräder, und wir wissen, daß PKWsjeweils vier Räder und Motorräder zwei haben. Schließlich haben wirnoch Algebra benutzt, um aus dem Domänenwissen eine Formel zumachen.

    1.7 Kommentare und Formatierung

    Der Ausdruck (/ (- 10 (* 2 4)) 2) ist für unbedarfte Leser schwer zuverstehen. Manchmal hilft ein erläuternder Text beim Verständnis:

    (/ (- 10 (* 2 4)) 2) ; 10 Räder, 4 Fahrzeuge

    ↪→ 1(/ (- 12 (* 2 3)) 2) ; 12 Räder, 3 Fahrzeuge

    ↪→ 3

    Ein Semikolon ; kennzeichnet einen Kommentar. Der Kommentar er-streckt sich vom Semikolon bis zum Ende der Zeile und wird vomScheme-System ignoriert.

    Abbildung 1.3. Kommentare

    Der Text nach dem Semikolon ist ein Kommentar (siehe Abbildung 1.3),der von DrRacket ignoriert wird, aber für menschliche Leser hilfreichist.

    Beim Verständnis kann außerdem die Formatierung des Programmshelfen. Die obigen Ausdrücke können auch folgendermaßen geschrie-ben werden:

    (/ (- 10 (* 2 4))

    2)

    (/ (- 12 (* 2 3))

    2)

    Daß die 2 jetzt jeweils in einer weiteren Zeile steht, läßt die Ausdrückeso ähnlich aussehen wie der Bruch in der Formel. Die Einrückung vorder 2 macht klar, daß die 2 noch in die Klammern vom / gehört.

    Wir als Programmierer müssen uns selbst darum kümmern, an sinn-vollen Stellen eine neue Zeile anzufangen. Um die Einrückung kannsich allerdings DrRacket automatisch kümmern: Die Tab-Taste (linksauf der Tastatur, meist→| o.ä. bedruckt) rückt die Zeile, in der sich derCursor befindet, ein. Außerdem gibt es noch den Menüpunkt Racket→Alles einrücken, der das gesamte Programm einrückt.

    1.8 Abstraktion

    Beim Parkplatzproblem aus Abschnitt 1.6 ist es umständlich, für jedeKombination aus konkreten m und n die Formel neu hinzuschreibenund andere Werte für m und n einzusetzen. Außerdem ist es fehleranfäl-lig. Es muß also besser gehen. Ein erster Schritt in die richtige Richtungist, für m und n auch im Programm Namen zu verwenden:

  • ii

    “i1” — 2017/2/2 — 12:52 — page 10 — #14 ii

    ii

    ii

    10 Kapitel 1

    (define n 4)

    (define m 10)

    (/ (- m (* 2 n)) 2)

    Immerhin ist die ursprüngliche Formel jetzt wieder erkennbar. Leiderkann sie nur einmal verwendet werden. Schreiben wir in dasselbeProgramm die hier darunter:

    (define n 3)

    (define m 12)

    (/ (- m (* 2 n)) 2)

    . . . dann meckert DrRacket:

    m: Für diesen Namen gibt es schon eine Definition

    Für jeden Namen kann es nur eine Definition geben. Wir bräuchtenFormeln, die wir mehrfach verwenden können, bei denen wir sagen kön-nen „ich werde die Formeln mehrmals benutzen und möchte jedesmalandere Werte für n und m einsetzen“. Das geht beim Programmierenmit Abstraktion und sieht konkret so aus:

    (lambda (n m) (/ (- m (* 2 n)) 2))

    Diese Abstraktion besteht aus folgenden Bestandteilen:

    • das Schlüsselwort lambda,• die Namen n und m, die Parameter,• der Ausdruck (/ (- m (* 2 n)) 2), der Rumpf.

    Die Abstraktion hat als Wert eine Prozedur. Wenn der lambda-Ausdruckso in einem Programm steht, druckt beim Auswerten DrRacket auchaus:

    #

    Für sich genommen macht eine Prozedur noch nichts interessantes. Siekann jedoch angewendet werden, was heißt, das konkrete Werte für mund n eingesetzt werden:

    ((lambda (n m) (/ (- m (* 2 n)) 2)) 4 10)

    ↪→ 1((lambda (n m) (/ (- m (* 2 n)) 2)) 3 12)

    ↪→ 3

    Abbildung 1.4 erklärt die beiden Konzepte „Abstraktion“ und „An-wendung“ im allgemeinen.

    Bisher ist jedoch noch nicht viel gewonnen, weil wir den lambda-Ausdruck jedesmall wiederholen mußten. Da er jedoch beide Malegenau gleich ist, können wir ihm mit define einen Namen geben:

    (define parking-lot-cars

    (lambda (n m) (/ (- m (* 2 n)) 2)))

    Am besten verteilen wir das Programm gleich noch auf etwas mehrZeilen und rücken es ein, um es lesbarer zu machen:

  • ii

    “i1” — 2017/2/2 — 12:52 — page 11 — #15 ii

    ii

    ii

    Elemente des Programmierens 11

    Eine Abstraktion hat folgende Form:

    (lambda (p1 . . . pn) e)

    Die pi jeweils Namen, die Parameter und e ist der Rumpf . In e dürfendie pi vorkommen. Der Wert einer Abstraktion ist eine Prozedur, welchefür jeden Parameter eine Eingabe erwartet.Eine Anwendung einer Prozedur hat folgende Form:

    (p a1 ... an)

    p ist ein Ausdruck, der eine Prozedur ergeben muß, die ai sind eben-falls Ausdrücke, die Argumente. Bei der Auswertung einer Anwendungwerden zunächst p und die ai ausgewertet; danach geht es mit derAuswertung des Rumpfes der Prozedur weiter, wobei für die Parameterpi jeweils die Werte der Argumente ai eingesetzt werden.

    Abbildung 1.4. Abstraktion und Anwendung

    (define parking-lot-cars

    (lambda (n m)

    (/ (- m (* 2 n))

    2)))

    Parking-lot-cars können wir jetzt mehrfach verwenden:

    (parking-lot-cars 4 10)

    ↪→ 1(parking-lot-cars 3 12)

    ↪→ 3

    Das sieht doch schon besser aus: Der Name parking-lot-cars ist außer-dem sprechend und erlaubt uns, die eigentliche Formel auch wieder zuvergessen.

    1.9 Kurzbeschreibung und Signatur

    Angenommen, die Prozedurdefinition von parking-lot-cars wird anjemanden weitergegeben, der dieses Buch nicht gelesen hat, aber dieProzedur trotzdem einsetzen soll. Der potentielle Leser kann zwardas Scheme-Programm prinzipiell verstehen, hat aber keinen weiterenHinweis darauf, wofür parking-lot-cars verwendet werden kann.

    Das Problem ist, daß die Definition von parking-lot-cars das End-produkt des Denkprozesses ist, der in Kapitel ?? beschrieben wurde.Der Denkprozeß selbst, der mit der Aufgabenstellung anfängt, ist nichtTeil der Definition. Darum ist es hilfreich, wenn wichtige Aspekte desDenkprozesses als Kommentare bei den Definitionen stehen. Ein stetssinnvoller Kommentar ist eine Kurzbeschreibung der Aufgabenstellung:

    ; aus der Anzahl der Fahrzeuge und Räder die Anzahl der PKWs bestimmen

    Für die Kurzbeschreibung reicht in der Regel eine Zeile: Nehmen Siediese Einschränkung als Gelegenheit, sich knapp, prägnant und präziseauszudrücken.

  • ii

    “i1” — 2017/2/2 — 12:52 — page 12 — #16 ii

    ii

    ii

    12 Kapitel 1

    Als nächstes ist eine besondere Formulierung hilfreich, die sogenann-te Signatur. Wer nur gelesen hat, dass die Prozedur parking-lot-carszwei Argumente n und m hat, könnte ja auf den Gedanken kommen,einen Aufruf der Form

    (parking-lot-cars "zweiundzwanzig" "achtunddreissig")

    zu notieren. Das wird bei der Ausführung eine Fehlermeldung erzeugen,weil die eingebauten Prozeduren /, - und * nur mit Zahlen in Formvon Ziffernfolgen umgehen können, aber nicht mit Zeichenketten, dievielleicht auch Zahlen bezeichnen könnten. In der Tat akzeptieren fastalle Prozeduren nur Argumente einer ganz bestimmten Sorte, in diesemFall Argumente der Sorte „natürliche Zahl“.

    Hier eine Liste der wichtigsten „eingebauten“ Sorten:

    natürliche Zahlen naturalganze Zahlen integerrationale Zahlen rationalreelle Zahlen realZahlen allgemein (inkl. komplexe) numberZeichenketten stringBilder image

    Eine Signatur ist eine Vorstufe für die zu entwickelnde Prozedur undfaßt einige wichtige Informationen zusammen:

    1. den Namen der Prozedur,2. Anzahl und Sorten der Argumente und3. die Sorte des Rückgabewerts der Prozedur.

    Die Prozedur parking-lot-cars akzeptiert zwei natürliche Zahlen undliefert wieder eine natürliche Zahl. Deshalb sieht die Signatur vonparking-lot-cars so aus:

    (: parking-lot-cars (natural natural -> natural))

    Diese Signatur besagt:

    • Parking-lot-cars ist eine Prozedur (das sagt der Pfeil -> zusammenmit den Klammern);

    • parking-lot-cars akzeptiert zwei Argumente (vor dem Pfeil stehenzwei Wörter);

    • die beiden Argumente sind natürliche Zahlen (natural);• die Prozedur liefert wieder eine natürliche Zahl (das ist das natural

    hinter dem Pfeil).

    Die Signatur ähnelt also der mathematischen Notation für Funktionen,die einen bestimmten Typ haben.

    Aus der Signatur ergeben sich, wenn für die beiden Argumentesprechende Namen gefunden worden sind, die ersten beiden Zeilen derfolgenden Definition, das sogenannte Gerüst:

    (define parking-lot-cars

    (lambda (n m)

    ...))

  • ii

    “i1” — 2017/2/2 — 12:52 — page 13 — #17 ii

    ii

    ii

    Elemente des Programmierens 13

    Es bleibt, die passende Formel einzusetzen. Die Definition von parking-lot-carssieht dann vollständig so aus:

    ; aus der Anzahl der Fahrzeuge und Räder die Anzahl der PKWs bestimmen

    (: parking-lot-cars (natural natural -> natural))

    (define parking-lot-cars

    (lambda (n m)

    (/ (- m (* 2 n))

    2)))

    Signaturen können für alle Arten von Werten deklariert werden, nichtnur für Prozeduren. Zum Beispiel so:

    (: pi real)

    Bei parking-lot-cars ist die Signatur noch nicht besonders umfangreichoder kompliziert. Spätere Kapitel werden zeigen, daß sich aus vielenSignaturen ganz automatisch Schablonen ergeben, die dem Programmie-rer einen Großteil der Denkarbeit bei der Entwicklung von Prozedurenabnehmen.

    Aus diesem Grund schreiben wir in diesem Buch die Kurzbeschrei-bung und die Signatur in das Programm, bevor wir die Definitionentwickeln: Die nachträgliche Entwicklung dieser Kommentare ist müh-selig und langweilig. Außerdem sind die Kurzbeschreibung und dieSignatur ein hilfreicher Teil des Problemlösungsprozesses. Schon man-cher Programmierer – Anfänger und Profi – ist an Aufgaben gescheitert,die sich mit Hilfe systematischen Vorgehens anhand der Signatur leichthätten lösen lassen.

    Aus dem fernen Osten stammt der Begriff des „Mantras“ als einemSinnspruch, den es sich lohnt, auswendig zu lernen. Hier das ersteMantra:

    Mantra 1 (Signatur vor Ausführung) Schreiben Sie eine Kurzbeschrei-bung der Aufgabe und eine Signatur ins Programm, bevor Sie dieProzedur selbst programmieren.

    Ab jetzt werden sich die Programmbeispiele in diesem Buch natür-lich an dieses Mantra halten. Kurzbeschreibung, Signatur, Testfälle(beschrieben im nächsten Abschnitt) Gerüst und Schablone sind festeBestandteile einer Konstruktionsanleitung, die systematisch beschreibt,wie eine Aufgabe schrittweise gelöst werden kann. Dieses Buch wirdeine Reihe von Konstruktionsanleitungen vorstellen, die sich stets ander Signatur einer Prozedur orientieren. Alle Mantras sind in Anhang ??und die Konstruktionsanleitungen in Anhang ?? zusammengefaßt.

    1.10 Testfälle

    Vertrauen ist gut – aber Fehler passieren, auch bei sorgfältiger Program-mierung. Angenommen, bei der Programmierung von parking-lot-carswäre folgendes herausgekommen:

    ; aus der Anzahl der Fahrzeuge und Räder die Anzahl der PKWs bestimmen

    (: parking-lot-cars (natural natural -> natural))

    (define parking-lot-cars

  • ii

    “i1” — 2017/2/2 — 12:52 — page 14 — #18 ii

    ii

    ii

    14 Kapitel 1

    (lambda (n m)

    (/ (- m (* 4 n))

    2)))

    Sehen Sie den Fehler auf den ersten Blick? Einfaches Ausprobieren istda vielleicht schneller:

    (parking-lot-cars 1 4)

    ↪→ 0Bei der Entwicklung der Prozedur sollten also Testfälle konstruiertwerden, die an ausgewählten Beispielen überprüfen, ob die geradeprogrammierte Prozedur auch korrekt funktioniert. Testen ist eine un-verzichtbare Tätigkeit des Programmierers.

    Die Testfälle werden am besten vor der Definition der Prozedur aufge-stellt, denn wenn sie erst hinterher geschrieben werden, ist die Gefahrgroß, daß unbewußt das tatsächliche Ergebnis eines Prozeduraufrufsals das gewünschte eingegeben oder besonders kritische Beispiele weg-gelassen werden. (In der industriellen Praxis ist sogar oft üblich, daßjemand anderes als der Autor der Definitionen die Testfälle schreibt.)

    Es ist mühselig, bei der Programmentwicklung ständig Testfälle indie REPL einzutippen und durch einen Vergleich mit den erwartetenErgebnissen herauszubekommen, ob alles in Ordnung ist. In DrRacketgeht es deshalb auch einfacher. Testfälle können zusammen mit denerwarteten Ergebnissen wie folgt spezifiziert werden:

    (check-expect (parking-lot-cars 1 4) 1)

    (check-expect (parking-lot-cars 2 6) 1)

    (check-expect (parking-lot-cars 10 28) 4)

    Beim Druck auf den Start-Knopf überprüft DrRacket, ob die tatsäch-lichen Ergebnisse der Ausdrücke mit den Soll-Werten übereinstimmen.Für fehlgeschlagene Testfälle öffnet sich ein neues Fenster mit Informa-tionen über die Unterschiede zwischen erwarteten und tatsächlichenErgebnissen; ansonsten gibt es eine kurze Meldung, dass die Testfälleerfolgreich waren. Für die obere inkorrekte Version kommt zum Beispielfolgendes heraus:

    3 Tests gelaufen.

    0 Tests bestanden.

    2 Signaturverletzungen.

    Check-Fehler:

    Der tatsächliche Wert 0 ist nicht der erwartete Wert 1.

    in Zeile 4, Spalte 0

    Der tatsächliche Wert -1 ist nicht der erwartete Wert 1.

    in Zeile 5, Spalte 0

    Der tatsächliche Wert -6 ist nicht der erwartete Wert 4.

    in Zeile 6, Spalte 0

    Signaturverletzungen:

    bekam -1 in Zeile 5, Spalte 14 , Signatur in Zeile 2, Spalte 40

    verantwortlich: Prozedur in Zeile 9, Spalte 2

    bekam -6 in Zeile 6, Spalte 14 , Signatur in Zeile 2, Spalte 40

    verantwortlich: Prozedur in Zeile 9, Spalte 2

  • ii

    “i1” — 2017/2/2 — 12:52 — page 15 — #19 ii

    ii

    ii

    Elemente des Programmierens 15

    Eine großzügige Verwendung von Testfällen kann viele Programmier-fehler aufdecken und damit die Programmierung erleichtern und be-schleunigen.

    Mantra 2 (Testfälle) Schreiben Sie für jede Prozedur Testfälle, bevor Siedie Definition schreiben.

    1.11 Unsinnige Daten

    Die Testfälle aus dem vorangegangenen Abschnitt sind alle „sinnvoll“ –die Eingabedaten passen alle zu tatsächlichen Parkplatzsituationen. Wasist aber hiermit?

    (parking-lot-cars 3 9)

    Wie schon in Kapitel ?? (Seite ??) bereits angedeutet, lassen sich die Da-ten 3 und 9 nicht als Information interpretieren: Es gibt keinen Parkplatzmit 3 Fahrzeugen und 9 Rädern – zumindest nicht mit den Einschrän-kungen der Aufgabenstellung auf vollberäderte PKWs und Motorräder.

    Die Prozedur parking-lot-cars stört dies allerdings wenig: Sie liefertmunter die Ausgabe 1.5. Allerdings meldet DrRacket eine Signaturver-letzung, wenn es (parking-lot-cars 3 9) auswertet, da das Ergebniskeine natürliche Zahl ist wie in der Signatur angegeben.

    Das Programm sollte natürlich abseits der Signaturverletzung un-sinnige Daten soweit möglich und praktikabel zurückweisen. Für dieEingabe (parking-lot-cars 3 16) hätte es nämlich keine Signaturverlet-zung gegeben, sondern es wäre eine zunächst unschuldig aussehende5 herausgekommen. Da hätte es zuerst noch der Beobachtung bedurft,dass unmöglich 5 von 3 Fahrzeugen PKWs sein können. Noch feh-len uns die Mittel, solche unsinnigen Eingaben zurückzuweisen; inAbschnitt 2.8 werden wir dies nachholen.

    1.12 Probleme und Teilprobleme

    TBD

    1.13 Auswertung

    Bei der Auswertung eines Programms geht DrRacket nach festen Regelnvor. Schauen wir uns noch einmal das Programm zum Parkplatzpro-blem an:

    (define parking-lot-cars

    (lambda (n m)

    (/ (- m (* 2 n))

    2)))

    (parking-lot-cars 4 10)

    Bei der Auswertung substituiert DrRacket jeweils Namen durch ihre Wer-te. Der erste Schritt ist also, parking-lot-cars durch den dazugehörigenlambda-Ausdruck zu ersetzen:

    (parking-lot-cars 4 10)

    ↪→((lambda (n m) (/ (- m (* 2 n)) 2)) 4 10)

  • ii

    “i1” — 2017/2/2 — 12:52 — page 16 — #20 ii

    ii

    ii

    16 Kapitel 1

    Abbildung 1.5. Stepper in DrRacket

    Jetzt steht dort die Anwendung eines lambda-Ausdrucks auf die zweiZahlen 4 und 10: Diese Argumente werden für die ensprechendenParameter n und m eingesetzt:

    ((lambda (n m) (/ (- m (* 2 n)) 2)) 4 10)

    ↪→(/ (- 10 (* 2 4)) 2)

    Nun werden sukzessive die Teilausdrücke ausgewertet. Das passiertimmer von links nach rechts. Bei einer Anwendung werden erst einmalalle Argumente fertig ausgewertet, bevor es mit der Substitution derParameter und dem Rumpf der Prozezedur weitergeht:

    (/ (- 10 (* 2 4)) 2)

    ↪→(/ (- 10 8) 2)

    ↪→(/ 2 2)

    ↪→1

    Diese Vorgehensweise entspricht der Algebra aus der Mathematik, wowir auch Ausdrücke umformen, indem wir gleiches durch gleichesersetzen.

    Normalerweise zeigt uns DrRacket nur das Endergebnis dieses Pro-zesses an. Es ist aber auch in der Lage, die Schritte einzeln zu visuali-sieren: Dazu müssen Sie auf den Step-Knopf drücken. Es erscheint einneues Fenster, der sogenannte Stepper. Sie können dann vorwärts undrückwärts durch den Substitutionsprozeß navigieren. Abbildung 1.5zeigt das Stepper-Fenster.

    Aufgaben

    Aufgabe 1.1 Betrachten Sie folgende Stromtarife. Beide Tarife bestehenaus einer monatlichen Grundgebühr und einem Teil, der sich nach denverbrauchten Kilowattstunden (kWh) richtet.

    Grundgebühr pro Monat Verbrauchspreis pro kWhTarif „Billig-Strom“ 4,90 Euro 19 CentTarif „Watt für wenig“ 8,20 Euro 16 Cent

  • ii

    “i1” — 2017/2/2 — 12:52 — page 17 — #21 ii

    ii

    ii

    Elemente des Programmierens 17

    1. Schreiben Sie eine Prozedur billig-strom, die den Monatsverbrauchin Kilowattstunden akzeptiert und den im Tarif „Billig-Strom“ zuzahlenden monatlichen Rechnungsbetrag für einen übergebenen Ver-brauch berechnet.

    2. Schreiben Sie eine Prozedur watt-für-wenig, die den Monatsver-brauch in Kilowattstunden akzeptiert und den im Tarif „Watt fürwenig“ zu zahlenden monatlichen Rechnungsbetrag für einen über-gebenen Verbrauch berechnet.

    Halten Sie sich bei jeder Prozedur, die Sie schreiben, an die Konstruk-tionsanleitungen: Schreiben Sie zuerst die Kurzbeschreibung und dieSignatur. Schreiben Sie als nächstes einige Testfälle. Leiten Sie danachdas Gerüst von der Signatur her und vervollständigen Sie den Rumpfder Prozedur.

    Aufgabe 1.2 Vervielfachung von Strings:

    • Schreiben Sie eine Prozedur double-string, die eine Zeichenkettekonsumiert und diese „verdoppelt“, d.h., für eine Eingabe "Sperber"den Rückgabewert "SperberSperber" liefert.

    • Schreiben Sie eine Prozedur quadruple-string, die eine Zeichenkettekonsumiert und „vervierfacht“.

    • Schreiben Sie eine Prozedur octuple-string, die eine Zeichenkettekonsumiert und „verachtfacht“.

    • Schreiben Sie eine Prozedur sixteentuple-string, die eine Zeichen-kette konsumiert und „versechzehnfacht“.

    Aufgabe 1.3 Ein Boot überquert einen Fluss mit Strömung und kommtdurch die Strömung vom geplanten Kurs ab. Dadurch wird die Strecke,die das Boot tatsächlich zurücklegt, länger.

    Flussufer

    Flussufer

    a

    b

    c

    v

    vFluss

    Boot

    Flussufer

    Flussufer

    Geben ist die Breite des Flusses a, die Strömungsgeschwindigkeitdes Flusses vFluss und die Geschwindigkeit des Bootes vBoot. Berech-nen Sie die Länge der Strecke, die das Boot tatsächlich zurücklegt.Programmieren Sie dazu Prozeduren, die folgende Teilprobleme lösen:

  • ii

    “i1” — 2017/2/2 — 12:52 — page 18 — #22 ii

    ii

    ii

    18 Kapitel 1

    1. Schreiben Sie zunächst eine Prozedur speed-ratio, die das Verhältnisder Strömungsgeschwindigkeit des Flusses vFluss zur Geschwindig-keit des Bootes vBoot berechnet.

    2. Schreiben Sie dann eine Prozedur other-shore-offset, die die Längeder Strecke berechnet, die das Boot abgetrieben wird (also den Versatzam anderen Ufer, im Schaubild die Strecke b).

    3. Um c zu berechnen, brauchen Sie den Satz des Pythagoras:

    a2 + b2 = c2

    Schreiben Sie eine Prozedur pythagoras, die c der obigen Gleichungberechnet. Erkennen und abstrahieren Sie weitere Teilprobleme!

    4. Schreiben Sie schließlich eine Prozedur boat-travel-distance, die dietatsächliche Strecke berechnet, die das Boot zurücklegt. Benutzen Siedafür die bisher geschriebenen Prozeduren.

    Aufgabe 1.4 In den USA und in Europa gibt es unterschiedliche Maßefür die Energieeffizienz von Kraftfahrzeugen:

    • In Europa ist das gängige Maß der Verbrauch in Liter pro 100km(l/100km);

    • in den USA ist das gängige Maß die Reichweite in Meilen pro Gallone(mi/gal).

    Schreiben Sie Prozeduren, die zwischen beiden Maßeinheiten umrech-nen. Gehen Sie dazu wie folgt vor:

    Halten Sie sich bei jeder Prozedur, die Sie schreiben, an die Konstruk-tionsanleitungen: Schreiben Sie zuerst die Kurzbeschreibung und dieSignatur. Schreiben Sie als nächstes einige Testfälle. Leiten Sie danachdas Gerüst von der Signatur her und vervollständigen Sie den Rumpfder Prozedur.

    1. Schreiben Sie eine Prozedur liters-per-hundred-kilometers, die eineMenge Benzin in Liter und die Reichweite dieses Benzins in Kilometerakzeptiert und daraus den Verbrauch in Liter pro 100km berechnet.

    2. Schreiben Sie eine Prozedur miles-per-gallon, die eine Entfernungin Meilen und den Benzinverbrauch auf diese Entfernung in Gal-lonen akzeptiert und daraus die Reichweite in Meilen pro Galloneberechnet.

    3. Definieren Sie eine Konstante kilometers-per-mile (eine US-Meileentspricht etwa 1, 61 Kilometer) und schreiben Sie zwei Prozedurenkilometers->miles und miles->kilometers, die jeweils eine Entfer-nung in einer Maßeinheit akzeptieren und die Entfernung in diejeweils andere Maßeinheit umrechnen.

    4. Definieren Sie eine Konstante liters-per-gallon (eine Gallone ent-spricht etwa 3, 79 Liter) und schreiben Sie zwei Prozeduren liters->gallonsund gallons->liters, die jeweils eine Menge in einer Maßeinheitakzeptieren und die Menge in die jeweils andere Maßeinheit umrech-nen.

    5. Schreiben Sie die Prozedur l/100km->mi/gal, die einen Verbrauch inLiter pro 100km akzeptiert und in die Reichweite in Meilen pro Gal-lone umrechnet. Benutzen Sie dafür die Prozeduren, die Sie in den

  • ii

    “i1” — 2017/2/2 — 12:52 — page 19 — #23 ii

    ii

    ii

    Elemente des Programmierens 19

    anderen Teilaufgaben erstellt haben. Sollten Sie auf weitere Teilproble-me stoßen, abstrahieren Sie diese Teilprobleme in eigene Prozeduren.

    6. Schreiben Sie die Prozedur mi/gal->l/100km, die eine Reichweitein Meilen pro Gallone akzeptiert und in den Verbrauch in Literpro 100km umrechnet. Benutzen Sie dafür die Prozeduren, die Siein den anderen Teilaufgaben erstellt haben. Sollten Sie auf weitereTeilprobleme stoßen, abstrahieren Sie diese Teilprobleme in eigeneProzeduren.

    7. Finden Sie heraus, wie hoch der Benzinverbrauch verschiedenerKraftfahrzeuge ist, die Sie täglich im Straßenverkehr in Deutschlandsehen. Vergleichen Sie diesen Verbrauch mit den Reichweitenangabentypischer Kraftfahrzeuge für den US-amerikanischen Markt.

    Aufgabe 1.5 Betrachten Sie das folgende Programm:

    (define x 2) ; --> zwei

    (define y -1) ; --> minuseins

    (define z -3) ; --> minusdrei

    (define f

    (lambda (x z)

    (+ (* x x) z y)))

    (f 4 -2)

    Benennen Sie die Variablen x, y und z, die in den ersten drei Zeilen desProgramms definiert werden, im kompletten Programm um, und zwarx in zwei, y in minuseins und z in minusdrei. Achten Sie bei der Umbe-nennung auf die lexikalische Bindung. Benennen Sie keine Parameterder Prozedur f um.

    Nachdem Sie die Umbenennung durchgeführt haben, welches Er-gebnis liefert der Ausdruck (f 4 -2)? Berechnen Sie das Ergebnis vonHand mit Hilfe des Substitutionsmodells und halten Sie die Zwischen-schritte fest.

    Aufgabe 1.6 Betrachten Sie das folgende Programm:

    (define x 2) ; --> zwei

    (define y 4) ; --> vier

    (define z ; --> f

    (lambda (x y z)

    (+ x (z y))))

    (z y x (lambda (z) (+ x z)))

    Benennen Sie die Variablen x, y und z, die in den ersten drei Zeilen desProgramms definiert werden, im kompletten Programm um. Der neueName der Variable steht als Kommentar im Programm hinter dem Pfeil(-->). Achten Sie bei der Umbenennung auf die lexikalische Bindung.Benennen Sie keine Parameter der Prozedur z um.

    Berechnen Sie, nachdem Sie die Umbenennung durchgeführt haben,von Hand mit dem Substitutionsmodell (z y x (lambda (z) (+ x z))))und halten Sie die Zwischenschritte fest.

  • ii

    “i1” — 2017/2/2 — 12:52 — page 20 — #24 ii

    ii

    ii

    20 Kapitel 1

    Aufgabe 1.7 Betrachten Sie das folgende Programm:

    (define x 1)

    (define y 3)

    (define z 5)

    (define f

    (lambda (x)

    ((lambda (y)

    ((lambda (z)

    (+ z (* x y)))

    (+ x z)))

    (+ x y))))

    (f y)

    Benennen Sie hier alle lokalen Variablen, die innerhalb der Prozedurf gebunden werden, um. Verändern Sie nicht den Namen der Variablenx, y und z aus den ersten drei Zeilen des Programms.

    Nachdem Sie die Umbenennung durchgeführt haben, welches Ergeb-nis liefert der Ausdruck (f y)? Berechnen Sie das Ergebnis von Handmithilfe des Substitutionsmodells und halten Sie die Zwischenschrittefest.Hinweis: In diesen Aufgaben finden Sie keine Kommentare und Signa-turen zu den Prozeduren. Hier können Sie an einem Beispiel sehen,dass es sehr wichtig ist, diese Informationen anderen Programmierernimmer zur Verfügung zu stellen. Denn es kann auch bei kleinen Pro-grammen schwer sein, die Funktionsweise der einzelnen Prozedurenohne Kommentare zu verstehen.

  • ii

    “i1” — 2017/2/2 — 12:52 — page 21 — #25 ii

    ii

    ii

    2 Fallunterscheidungen undVerzweigungen

    Computerprogramme müssen bei manchen Daten, die sie verarbeiten,zwischen verschiedenen Möglichkeiten differenzieren: Ist die Wasser-temperatur warm genug zum Baden? Welche von fünf Tupperschüsselnist für eine bestimmte Menge Kartoffelsalat groß genug? Welches istdie richtige Abzweigung nach Dortmund? Solche Entscheidungen sinddaran festgemacht, daß ein Wert zu einer von mehreren verschiedenenKategorien gehören kann – es handelt sich dann um eine sogenannteFallunterscheidung; mathematische Funktionen und Scheme-Prozedurenoperieren auf Daten mit Fallunterscheidung durch Verzweigungen. Umdiese geht es in diesem Kapitel.

    2.1 Fallunterscheidungen

    Zu den „Flensburg“-Punkten, die es bei Verstößen gegen die Straßen-verkehrsordnung gibt, hat eine Seite im Internet folgendes zu sagen:

    0 bis 3 Punkte Keine Sanktionen4 bis 8 Punkte Bei freiwilliger Teilnahme an Aufbauseminaren: 4 Punkte

    Abzug8 bis 13 Punkte Verwarnung und Hinweis auf freiwilliges Aufbausemi-

    nar9 bis 13 Punkte Bei freiwilliger Teilnahme an Aufbauseminaren: 2 Punk-

    te Abzug14 bis 17 Punkte Teilnahme an Aufbauseminar wird angeordnet14 bis 17 Punkte Bei freiwilliger Teilnahme an verkehrspsychologischer

    Beratung: 2 Punkte AbzugAb 18 Punkte Führerschein wird entzogen

    Wir können zu dieser Aufstellung eine Reihe von Fragestellungen bear-beiten, zum Beispiel welche Sanktionen durch eine bestimmte Punkte-zahl verpflichtend werden oder wieviele Punkte ein Autofahrer nacheiner bestimmten Maßnahme noch auf dem Konto hat. In jedem Fallteilt die Aufstellung mögliche Punktezahlen in bestimmte Kategorienein, je nach Maßnahme. Folgende Maßnahmen gibt es:

    • nichts• Aufbauseminar• verkehrspsychologische Beratung

  • ii

    “i1” — 2017/2/2 — 12:52 — page 22 — #26 ii

    ii

    ii

    22 Kapitel 2

    • Führerscheinentzug

    Dies ist eine Aufzählung.Wir fangen mit der Fragestellung an, welche Zwangsmaßnahme

    für eine bestimmte Punktezahl angeordnet wird. Zwangsmaßnahmengibt es nur drei: keine, Aufbauseminar und Führerscheinentzug, dadie verkehrspsychologische Beratung rein freiwillig ist. Entsprechendzerfällt die Punkteskala in drei Teile: 0− 13, 14− 17 und ab 18. DiePunktezahl gehört also zu einer von drei Kategorien.

    Wenn die Menge, aus der ein Wert kommt, in eine feste Anzahl vonKategorien aufgeteilt wird und bei einem Wert nur die Kategorie zählt,ist diese Menge durch eine Fallunterscheidung definiert. Aufzählungensind damit auch Fallunterscheidungen.

    Eine Funktion, die aus einem Punktestand die Zwangsmaßnahmeermittelt, sieht folgendermaßen aus:

    m(p) def=

    nichts falls p ≤ 13Aufbauseminar falls p ≥ 14, p ≤ 17Führerscheinentzug falls p ≥ 18

    Die Notation mit der großen geschweiften Klammer heißt Verzweigung(engl. conditional); ein Ausdruck wie p ≤ 13, der wahr oder falsch seinkann, heißt Bedingung oder Test.

    2.2 Boolesche Ausdrücke in Scheme

    Tests gibt es auch in Scheme; sie sind dort Ausdrücke. Hier ein Beispiel:

    (

  • ii

    “i1” — 2017/2/2 — 12:52 — page 23 — #27 ii

    ii

    ii

    Fallunterscheidungen und Verzweigungen 23

    #t

    ↪→ #t#f

    ↪→ #f

    Auch mit booleschen Werten kann DrRacket rechnen. Ein Ausdruck derForm

    (and e1 e2 . . . en)

    ergibt immer dann #t, wenn alle ei #t ergeben, sonst #f. Bei zweiOperanden e1 und e2 ergibt (and e1 e2) immer dann #t, wenn e1 und e2#t ergeben:

    (and #t #t)

    ↪→ #t(and #f #t)

    ↪→ #f(and #t #f)

    ↪→ #f(and #f #f)

    ↪→ #f

    Entsprechend gibt es Ausdrücke der Form

    (or e1 e2 . . . en)

    die immer dann #t ergeben, wenn einer der ei #t ergibt, sonst #f. Beizwei Operanden e1 und e2 ergibt (or e1 e2) immer dann #t, wenn e1oder e2 #t ergeben:

    (or #t #t)

    ↪→ #t(or #f #t)

    ↪→ #t(or #t #f)

    ↪→ #t(or #f #f)

    ↪→ #f

    Des weiteren gibt es noch eine eingebaute Prozedur not, die einenbooleschen Wert umdreht, sich also folgendermaßen verhält:

    (not #f)

    ↪→ #t(not #t)

    ↪→ #f

    2.3 Programmieren mit Fallunterscheidungen

    Zurück zu den Punkten in Flensburg: Zunächst schreiben wir eine Pro-zedur, die zu einem gegebenen Punktestand die entsprechende Zwangs-maßnahme ausrechnet. Fast alles, was zur Datenanalyse gehört, habenwir schon am Anfang des Kapitels gemacht: der Punktestand ist einenatürliche Zahl, eine Zwangsmaßnahme ist nichts, ein Aufbauseminaroder der Führerscheinentzug. Diese Informationen müssen wir noch in

  • ii

    “i1” — 2017/2/2 — 12:52 — page 24 — #28 ii

    ii

    ii

    24 Kapitel 2

    Daten umwandeln – dazu benutzen wir einfach die entsprechenden Zei-chenketten "nichts", "Aufbauseminar" und "Führerscheinentzug" undhalten das Ergebnis in einem Kommentar fest:

    ; Eine Zwangsmaßnahme ist:

    ; - "nichts"

    ; - "Aufbauseminar"

    ; - "Führerscheinentzug"

    Die Kurzbeschreibung der Prozedur könnte so aussehen:

    ; Zwangsmaßnahme bei Flensburg-Punktestand errechnen

    Eine passende Signatur ist diese hier:

    (: points-must-do (natural -> (one-of "nichts"

    "Aufbauseminar"

    "Führerscheinentzug")))

    Die Konstruktion one-of bei Signaturen ist neu: In der obigen Signa-tur bedeutet es, daß der Aggregatzustand einer der in der one-of-Signatur angegegebenen Werte ist, also eine der Zeichenketten "nichts","Aufbauseminar" und "Führerscheinentzug".

    Hier sind zwei mögliche Testfälle:

    (check-expect (points-must-do 14) "Aufbauseminar")

    (check-expect (points-must-do 18) "Führerscheinentzug")

    Es folgt das Gerüst der Prozedur:

    (define points-must-do

    (lambda (p)

    ...))

    Auf jeden Fall muß das p irgendwo im Rumpf vorkommen:

    (define points-must-do

    (lambda (p)

    ... p ...))

    Jetzt brauchen wir, wie bei der mathematischen Funktion m aus Ab-schnitt 2.1, eine Verzweigung, nur eben in Scheme. Abbildung 2.1beschreibt das dafür zuständige cond. Es ist also von vorneherein klar,daß eine cond-Form im Rumpf von points-must-do auftauchen muß:

    (define points-must-do

    (lambda (p)

    (cond ... p ...)))

    Bei der Konstruktion der cond-Formen ist entscheidend, wieviele Zwei-ge sie hat. Dabei gibt es eine einfache Faustregel – da die Eingabe vonpoints-must-do – die Punktzahl – in drei Kategorien zerfällt, brauchtdie cond-Form auch drei Zweige:

    (define points-must-do

    (lambda (p)

    (cond

    (... ...)

    (... ...)

    (... ...))))

  • ii

    “i1” — 2017/2/2 — 12:52 — page 25 — #29 ii

    ii

    ii

    Fallunterscheidungen und Verzweigungen 25

    In Scheme werden Verzweigungen mit der Spezialform cond dargestellt.Ein cond-Ausdruck hat die folgende Form:

    (cond

    (t1 a1)(t2 a2). . .(tn−1 an−1)(else an)))

    Dabei sind die ti und die ai ihrerseits Ausdrücke. Der cond-Ausdruckwertet nacheinander alle Tests ti aus; sobald ein Test tk #t ergibt, wirdder cond-Ausdruck durch das entsprechende ak ersetzt. Wenn alle Testsfehlschlagen, wird durch an ersetzt. Die Paarungen (ti ai) heißen Zweigedes cond-Ausdruckes, und der Zweig mit else (auf deutsch „sonst“)heißt else-Zweig. Der else-Zweig kann auch fehlen – dann sollte aberimmer einer der Tests #t ergeben. Wenn doch einmal bei allen ti #fherauskommen sollte, bricht DrRacket das Programm ab und gibt eineFehlermeldung aus.

    Abbildung 2.1. Verzweigung

    Wir benötigen jetzt für jeden cond-Zweig einen Test, der die entspre-chende Kategorie bei den Punkten identifiziert. Dazu müssen wir nurdie entsprechenden Bedingungen aus der mathematischen Fassungnach Scheme übersetzen. Heraus kommt folgendes:

    (define points-must-do

    (lambda (p)

    (cond

    ((= p 14) (= p 18) ...))))

    Der letzte Schritt ist einfach – wir fügen für jeden Zweig die zum Testpassende Maßnahme ein:

    (define points-must-do

    (lambda (p)

    (cond

    ((= p 14) (= p 18) "Führerscheinentzug"))))

    Fertig – könnte man meinen. Wenn Sie das Programm in der REPLlaufen lassen, meldet DrRacket zwei bestandene Tests. Allerdings fälltIhnen vielleicht auf, daß das Programm im Definitionsfenster nach demLauf so aussieht:

    (define points-must-do

    (lambda (p)

    (cond

    ((

  • ii

    “i1” — 2017/2/2 — 12:52 — page 26 — #30 ii

    ii

    ii

    26 Kapitel 2

    ((and (>= p 14) (= p 18) "Führerscheinentzug"))))

    Das "nichts" ist farbig unterlegt, weil DrRacket diesen Ausdruck nochnie ausgewertet hat. Das ist nicht gut, weil es heißt, daß der entspre-chende Zweig durch die Tests noch nicht strapaziert wurde – er istalso möglicherweise fehlerhaft. Anders gesagt: Die Abdeckung des Pro-gramms durch die Tests ist unvollständig. Wenn wir einen Testfall fürden ersten Zweig ergänzen, verschwindet die farbige Unterlegung:

    (check-expect (points-must-do 0) "nichts")

    Trotzdem sind die bestehenden Tests noch suboptimal – wer sagt schließ-lich, daß das Programm zum Beispiel bei 13 Punkten, also genau an derGrenze zwischen dem ersten und zweiten Zweig, das richtige tut. Wirsollten für diese Eckfälle auch Testfälle bereitstellen sowie einen Testfall,der sicherstellt, daß auch bei Punktzahlen oberhalb von 18 immer nochder Führerschein entzogen wird:

    (check-expect (points-must-do 13) "nichts")

    (check-expect (points-must-do 17) "Aufbauseminar")

    (check-expect (points-must-do 100) "Führerscheinentzug")

    Mantra 3 (Abdeckung und Eckfälle) Sorgen Sie für vollständige AbdeckungIhres Programms durch die Testfälle! Testen Sie möglichst alle Eckfälle!

    2.4 Konstruktionsanleitung für Fallunterscheidungen

    Bei der Konstruktion der Prozedur points-must-do haben wir ein be-stimmtes Schema angewendet. Dieses Schema geht zunächst von fol-gender Frage aus:

    Wieviele Kategorien gibt es bei der Fallunterscheidung?

    Ist die Frage beantwortet – durch eine Zahl n – können wir bereitsetwas Code in den Rumpf schreiben, nämlich eine Verzweigung mit nZweigen:

    (define p(lambda (...)

    (cond

    (... ...)

    ... (n Zweige)(... ...))))

    Solch ein „Rumpf mit Lücken“ (die Ellipsen ... stehen für noch zuergänzende Programmteile) ist eine Schablone. Wir, die Autoren, empfeh-len Ihnen, die Schablone bereits hinzuschreiben, wenn Sie die Anzahlder Kategorien bereits kennen, noch bevor Sie weiter über die Pro-blemstellung nachdenken. Das hilft oft, etwaige Denkblockaden zulösen.

    Die Schablone folgt in diesem Fall aus der Struktur der Daten, alsodem Ergebnis der Datenanalyse. Es gibt noch andere Arten von Daten,jede mit ihrer eigenen Schablone. Diese werden wir im Rest des Buchsentwickeln. Für alle folgenden Konstruktionsanleitungen gilt deshalbfolgendes Mantra:

  • ii

    “i1” — 2017/2/2 — 12:52 — page 27 — #31 ii

    ii

    ii

    Fallunterscheidungen und Verzweigungen 27

    Mantra 4 (Schablone) Benutzen Sie ausgehend von einer Datenanalysedie passende Schablone!

    Was die Fallunterscheidung betrifft, können wir die Schablone abernoch weiterentwickeln, indem wir Tests für die einzelnen Fälle derFallunterscheidung ergänzen.

    Die Schablone für Fallunterscheidungen ist noch einmal als Konstruk-tionsanleitung ?? in Anhang ?? zusammengefaßt. (Konstruktionsanlei-tung ?? beschreibt die Konstruktion von Prozeduren im allgemeinen.)

    2.5 Verkürzte Tests

    Natürlich könnten wir die Prozedur auch leicht abkürzen:

    (define points-must-do

    (lambda (p)

    (cond

    ((

  • ii

    “i1” — 2017/2/2 — 12:52 — page 28 — #32 ii

    ii

    ii

    28 Kapitel 2

    FIXME: besseres BeispielDieser Spezialfall mit nur zwei Kategorien, genannt binäre Verzwei-

    gung kommt in der Praxis häufig vor. In Scheme gibt es dafür eineeigene Spezialform, genannt if, die hier kürzer ausfällt als cond:

    (define absolute

    (lambda (x)

    (if (>= x 0)

    x

    (- x))))

    Eine if-Form hat folgende Form:

    (if t k a)

    Dabei ist t der Test und k und a sind die beiden Zweige: die Konsequentek und die Alternative a. Abhängig vom Ausgang des Tests ist der Wertder Verzweigung entweder der Wert der Konsequente oder der Wertder Alternative.

    Tatsächlich ist if die „primitivere“ Form als cond: jede cond-Formkann in eine äquivalente if-Form übersetzt werden, und zwar nachfolgendem Schema:

    (cond (t1 a1) (t2 a2) . . . (tn−1 an−1) (else an))7→ (if t1 a1 (if t2 a2 ... (if tn−1 an−1 an)...))

    Die geschachtelte if-Form auf der rechten Seite der Übersetzung wertet,genau wie die cond-Form, nacheinander alle Tests aus, bis einer #t liefert.Die rechte Seite des cond-Zweigs ist dann gerade die Konsequente desifs. Erst wenn alle Tests fehlschlagen ist die Alternative des letztenif-Ausdrucks dran, nämlich an aus dem else-Zweig.

    Da sich mit Hilfe dieser Übersetzung jede cond-Form durch geschach-telte if-Formen ersetzen läßt, ist cond streng genommen gar nichtnotwendig. Cond ist deswegen eine sogenannte abgeleitete Form. Da condund andere abgeleitete Formen trotzdem praktisch und angenehm zuverwenden sind und damit dem Programmierer die Arbeit versüßen,heißen abgeleitete Formen auch syntaktischer Zucker.

    Um die Funktionsweise von Verzweigungen genau zu beschreiben,dient folgende zusätzliche Regel für das Substitutionsmodell aus Ab-schnitt 1.13:

    binäre Verzweigungen Bei der Auswertung einer Verzweigung wird zu-nächst der Wert des Tests festgestellt. Ist dieser Wert #t, so ist derWert der Verzweigung der Wert der Konsequente. Ist er #f, so istder Wert der Verzweigung der Wert der Alternative. Ist der Wertdes Tests kein boolescher Wert ist, ist das Programm fehlerhaft.

    Auch and und or sind eigentlich syntaktischer Zucker: Es ist immermöglich, einen and-Ausdruck in ifs zu übersetzen. Es gelten folgendeÜbersetzungsregeln:

    (and) 7→ #t(and e1 e2 . . .) 7→ (if e1 (and e2 . . .) #f)

    Ein and-Ausdruck mit mehreren Operanden wird so schrittweise in eineKaskade von if-Ausdrücken übersetzt:

  • ii

    “i1” — 2017/2/2 — 12:52 — page 29 — #33 ii

    ii

    ii

    Fallunterscheidungen und Verzweigungen 29

    (and a b c)

    7→ (if a (and b c) #f)7→ (if a (if b (and c) #f) #f)7→ (if a (if b (if c (and) #f) #f) #f)7→ (if a (if b (if c #t #f) #f) #f)

    Ebenso lassen sich or-Ausdrücke immer in if-Ausdrücke übersetzen,und zwar mit folgender Übersetzung:

    (or) 7→ #f(or e1 e2 . . .) 7→ (if e1 #t (or e2 . . .))

    Beispiel:

    (or a b c)

    7→ (if a #t (or b c))7→ (if a #t (if b #t (or c)))7→ (if a #t (if b #t (if c #t (or))))7→ (if a #t (if b #t (if c #t #f)))

    2.7 Signaturdefinitionen

    Nehmen wir uns zu Übungszwecken noch eine weitere Aufgabe vor:Nehmen wir an, jemand nimmt bei einem bestimmten Punktestand inFlensburg an einer freiwilligen Maßnahme teil – was ist der Punktestandnach der Maßnahme? Die bekannten Größen sind:

    • Punktestand vor der Maßnahme (natürliche Zahl)• freiwillige Maßnahme (siehe Abschnitt 2.1)

    Die unbekannte Größe ist der Punktestand nach der Maßnahme.Die Kurzbeschreibung könnte so lauten:

    ; Punktestand in Flensburg senken

    Die Signatur folgt aus der Datenanalyse:

    (: improve-points (natural (one-of "nichts"

    "Aufbauseminar"

    "verkehrspsychologische Beratung"

    "Führerscheinentzug")

    -> natural))

    Der one-of-Teil der Signatur macht sich da ganz schön breit, zumaler sich weitgehend deckt mit dem entsprechenden Teil der Signaturvon points-must-do auf Seite 24. Entsprechend sollten wir genauso wiebei anderen Werten der Signatur für „Flensburg-Maßnahmen“ einenNamen geben. Das geht mit einer fast ganz normalen Definition:

    (define action

    (signature

    (one-of "nichts"

    "Aufbauseminar"

    "verkehrspsychologische Beratung"

    "Führerscheinentzug")))

  • ii

    “i1” — 2017/2/2 — 12:52 — page 30 — #34 ii

    ii

    ii

    30 Kapitel 2

    Das Wörtchen signature ist aus technischen Gründen nötig.2 Faustre-gel: Signaturen außerhalb von Formen (: ...) müssen immer in ein(signature ...) eingeschachtelt werden..

    Mit dieser Definition gewappnet können wir die Signatur abkürzen:

    (: improve-points (natural action -> natural))

    Entsprechend Mantra 3 versuchen wir, durch mehr Tests als noch beipoints-must-do bessere Abdeckung zu erzielen:

    (check-expect (improve-points 3 "Aufbauseminar") 3)

    (check-expect (improve-points 4 "nichts") 4)

    (check-expect (improve-points 4 "Aufbauseminar") 0)

    (check-expect (improve-points 8 "Aufbauseminar") 4)

    (check-expect (improve-points 9 "Aufbauseminar") 7)

    (check-expect (improve-points 13 "Aufbauseminar") 11)

    (check-expect (improve-points 14 "verkehrspsychologische Beratung") 12)

    (check-expect (improve-points 17 "verkehrspsychologische Beratung") 15)

    (check-expect (improve-points 18 "Aufbauseminar") 18)

    (check-expect (improve-points 18 "verkehrspsychologische Beratung") 18)

    Hier das Gerüst:

    (define improve-points

    (lambda (p a)

    ...))

    Bei der Konstruktion der Schablone müssen wir uns entscheiden, anwelchem Parameter wir uns orientieren, p oder a. Die Entscheidung istwillkürlich – wir entscheiden uns erst einmal für p. (Ausgehend von akommt eine andere aber genauso gute Lösung heraus – das sei Ihnenin Aufgabe ?? als Fingerübung empfohlen.) Bei p gibt es in Bezug aufdiese Aufgabe fünf Kategorien:

    0–3 Punkte Da bringt keine Maßnahme etwas.4–8 Punkte Da bringt ein Aufbauseminar 4 Punkte Abzug.9–13 Punkte Da bringt ein Aufbauseminar 2 Punkte Abzug.14–17 Punkte Da bringt eine verkehrspsychologische Beratung 2 Punkte

    Abzug.über 18 Punkte Auch hier hilft keine Maßnahme.

    Wir brauchen also ein cond mit fünf Zweigen:

    (define improve-points

    (lambda (p a)

    (cond

    (... ...)

    (... ...)

    (... ...)

    (... ...)

    (... ...))))

    2 Es sorgt unter anderem dafür, daß Signaturdefinitionen in beliebiger Reihenfolge ge-schrieben werden und die Links in den Fehlermeldungen von DrRacket auf die richtigeStelle zeigen.

  • ii

    “i1” — 2017/2/2 — 12:52 — page 31 — #35 ii

    ii

    ii

    Fallunterscheidungen und Verzweigungen 31

    Jetzt müssen wir Tests erfinden, die den Kategorien entsprechen:

    (define improve-points

    (lambda (p a)

    (cond

    ((= p 4) (= p 9) (= p 14) (= p 18) ...))))

    Wir fangen mal mit den einfachsten Fällen an – unten und oben in derPunkteskala, wo sich nichts bewegt:

    (define improve-points

    (lambda (p a)

    (cond

    ((= p 4) (= p 9) (= p 14) (= p 18) p))))

    Im zweiten Zweig – zwischen vier und acht Punkten – zählt nur einAufbauseminar, alle anderen Maßnahmen bringen nichts. Darum isthier eine binäre Verzweigung angemessen:

    (define improve-points

    (lambda (p a)

    (cond

    ...

    ((and (>= p 4) (

  • ii

    “i1” — 2017/2/2 — 12:52 — page 32 — #36 ii

    ii

    ii

    32 Kapitel 2

    Fertig! Es gibt trotzdem noch einen Wermutstropfen: Die Abdeckung isttrotz der vielen Tests immer noch nicht vollständig – siehe Aufgabe ??

    2.8 Unsinnige Daten abfangen

    Noch einmal zurück zum Parkplatzproblem, das wir auf Seite ?? pro-grammiert hatten. In Abschnitt 1.11 auf Seite 15 hatten wir bereitsbemerkt, daß die Prozedur parking-lot-cars auch für unsinnige Datenfröhlich ebenso unsinnige Ergebnisse ermittelt.

    Auf Seite ?? wurde bereits eine Bedingung für sinnvolle Daten formu-liert: Wenn n die Anzahl der Fahrzeuge und m die Anzahl der Räderist, dann muß m gerade sein sowie 2n ≤ m ≤ 4n gelten. Wir könnendas in einer binären Verzweigung zum Ausdruck bringen:

    (define parking-lot-cars

    (lambda (vehicle-count wheel-count)

    (if (and (even? wheel-count)

    (

  • ii

    “i1” — 2017/2/2 — 12:52 — page 33 — #37 ii

    ii

    ii

    Fallunterscheidungen und Verzweigungen 33

    Aufgaben

    Aufgabe 2.1 Schreiben Sie eine Prozedur card-type, die den Umsatzeiner Kreditkarte konsumiert und die eine entsprechende Kategorie alsZeichenkette zurückgibt. Verwenden Sie die Konstruktionsanleitung:Schreiben Sie die Kurzbeschreibung auf, führen Sie eine Datenanalysedurch und schreiben Sie die Signatur auf. Erstellen Sie dann das Gerüstund die Testfälle. Vervollständigen Sie danach den Rumpf der Prozedurund vergewissern Sie sich, dass die Tests erfolgreich laufen.

    Umsatz < 15.000 =⇒ Weiß15.000 ≤ Umsatz < 50.000 =⇒ Gold50.000 ≤ Umsatz ≤ 150.000 =⇒ Platin

    150.000 < Umsatz =⇒ Schwarz

    Aufgabe 2.2 1. Schreiben Sie eine Prozedur min-2, die als Argumentezwei Zahlen nimmt und die kleinere der beiden Zahlen zurückgibt.Schreiben Sie außerdem eine Prozedur min-3, die als Argumente dreiZahlen nimmt und die kleinste der drei Zahlen zurückgibt. Verwen-den Sie die Konstruktionsanleitung: Schreiben Sie explizit Kurzbe-schreibung und Signatur auf, erstellen Sie dann das Gerüst und dieTestfälle. Vervollständigen Sie danach den Rumpf der Prozedur undvergewissern Sie sich, dass die Tests erfolgreich laufen.

    2. Schreiben Sie analog eine Funktion max-2 und max-3.

    Aufgabe 2.3 Schreiben Sie die folgenden Prozeduren:

    1. min-of-two, welche die kleinste von zwei gegebenen Zahlen ausgibt2. min-of-three, welche die kleinste von drei gegebenen Zahlen ausgibt3. is-min-of-three?, die überprüft ob die erste von drei gegebenen

    Zahlen das Minimum ist4. valid-value?, die überprüft ob die erste von drei gegebenen Zahlen

    zwischen den beiden anderen liegt; gehen Sie davon aus, dass derAufruf immer (valid-value? value min max) lautet

    5. clamp, die wie folgt definiert ist:

    clamp(x, min, max) =

    x min ≤ x ≤ maxmin x < minmax x > max

    Aufgabe 2.4 Beim Fußball lässt die Rückennummer eines Spielers häu-fig Rückschlüsse auf seine Position zu. Wir machen dabei folgendeAnnahmen:

    • Ein Torwart hat die Rückennummer 1.• Ein Abwehrspieler hat die Rückennummer 2, 3, 4 oder 5.• Ein Mittelfeldspieler hat die Rückennummer 6, 7, 8 oder 10.• Ein Stürmer hat die Rückennummer 9 oder 11.• Ein Ersatzspieler hat eine Rückennummer zwischen 12 und 99.• Alle anderen Rückennummern sind ungültig.

  • ii

    “i1” — 2017/2/2 — 12:52 — page 34 — #38 ii

    ii

    ii

    34 Kapitel 2

    Schreiben Sie nun eine Prozedur mit folgender Signatur:

    (: nummer->position

    (number ->

    (one-of "Torwart" "Abwehr" "Mittelfeld" "Sturm" "Ersatz" "Ungültig")))

    Die Prozedur soll dabei zu einer gegebenen Rückennummer diezugehörige Position berechnen.

    Verwenden Sie beim Schreiben der Prozedur die Konstruktionsanlei-tungen für Prozeduren und für Fallunterscheidungen. Testen Sie dieProzedur nummer->position mit mindestens sechs Testfällen, so dassalle Fälle abgedeckt sind.

    Aufgabe 2.5 Schreiben Sie ein Programm, mit dem Bußgelder automa-tisch bestimmt werden.

    1. Programmieren Sie eine Prozedur zu-langes-parken für die Bewer-tung von zu langem Parken auf einem kostenpflichtigen Parkplatz.Diese bekommt eine Zeitspanne übergeben und gibt das entsprechen-de Verwarngeld zurück.Diese Verwarnungen sind wie folgt festgelegt:

    • Überschreitung der Höchstparkdauer bis 30 Minuten: e5• bis zu einer Stunde: e10• bis zu zwei Stunden: e15• bis zu drei Stunden: e20• länger als drei Stunden: e25

    2. Das Überfahren einer roten Ampel kostet je nach Gefährdungslagemehr, gibt Punkte und Fahrverbote. Schreiben Sie zwei Prozeduren,eine für das Bußgeld rote-ampel-bußgeld, eine für die Punkte in Flens-burg rote-ampel-punkte und eine für das Fahrverbot rote-ampel-fahrverbotwelche ausgibt ob ein Fahrverbot erteilt wird. Übergeben Sie den Pro-zeduren, wie lange die Ampel schon rot war und ob eine Gefährdungoder Sachbeschädigung vorlag.Die Bußgelder sind wie folgend definiert:

    • Bei Rot über die Ampel innerhalb der ersten Sekunde e50 und 3Punkte

    • Bei Rot über die Ampel innerhalb der ersten Sekunde mit Ge-fährdung oder Sachbeschädigung e125, 4 Punkte und 1 MonatFahrverbot

    • Bei Rot über die Ampel nach der ersten Sekunde e125, 4 Punkteund 1 Monat Fahrverbot.

    • Bei Rot über die Ampel nach der ersten Sekunde mit Gefährdungoder Sachbeschädigung e200, 4 Punkte und 1 Monat Fahrverbot.

  • ii

    “i1” — 2017/2/2 — 12:52 — page 35 — #39 ii

    ii

    ii

    3 Zusammengesetzte Daten

    TBDMit anderen Worten: mehrere Dinge werden zu einem zusammenge-

    setzt. Eine andere Betrachtungsweise ist, daß ein einzelnes Ding durchmehrere Eigenschaften charakterisiert ist.

    In Scheme lassen sich solche zusammengesetzte Daten durch Recordsdarstellen. Ein Record ist wie ein Behälter mit mehreren Fächern, indenen die Bestandteile der Daten untergebracht sind.

    3.1 Computer konfigurieren

    Viele Computerhändler erlauben ihren Kunden, bestimmte Kompo-nenten eines neues Computers selbst auszuwählen, zum Beispiel denProzessor, die Festplatte oder die Größe des RAM-Hauptspeichers:

    Eine mögliche Beschreibung dieser Situation ist:Ein Computer besteht aus:

    • Prozessor• RAM• Festplatte

    Natürlich besteht ein Computer auch noch aus anderen Teilen, die aber(zumindest in diesem Beispiel) immer gleich sind. In einer Bestellungmuß der Kunde also nur diese drei Bestandteile angeben. Wir nehmen

  • ii

    “i1” — 2017/2/2 — 12:52 — page 36 — #40 ii

    ii

    ii

    36 Kapitel 3

    an, daß es beim Prozessor nur auf den Namen („Athlon“, „Xeon“,„Cell“, . . . ) ankommt, beim RAM nur auf die Größe in Gigabyte, undauch bei der Festplatte nur auf die Größe in Gigabyte. Eine vereinfachteDarstellung könnte so aussehen:

    Computer:

    Feld KomponenteProzessor "Cell"

    RAM 2Festplatte 250

    Diese Tabelle steht demnach für einen Computer mit Cell-Prozessor, 2Gigabyte RAM und einer 250-Gigabyte-Festplatte.

    Die Begriffe „Feld“ und „Komponente“ sind dabei Termini – das Feldist die Allgemeinbezeichnung für ein Bestandteil, das alle Computerhaben. Die Komponente ist das konkrete Bestandteil eines einzelnenComputers.

    Die Darstellung für solche zusammengesetzte Daten, die aus mehrerenKomponenten (in diesem Fall „Cell“, 2 und 250) bestehen, heißt Record.Alle Records für Computer gehören zu einer gemeinsamen Menge,dem Record-Typ für Computer. (Weiter hinten in diesem Kapitel wirdbeschrieben, wie ein Programm eigene Record-Typen definieren kann.)Der Record-Typ für Computer sieht feste Felder („Prozessor“, „RAM“und „Festplatte“) vor, welche die Komponenten aufnehmen. Für jedesFeld des Record-Typs „Computer“ besitzt also jeder einzelne Computerjeweils eine Komponente, in diesem Fall eine für das Prozessor-, einefür das RAM- und eine für das Festplatten-Feld.

    Der Computerhersteller stellt einen echten Computer her, indemer zunächst den Prozessor, den RAM und die Festplatte fertigstelltund diese dann zum Computer zusammensetzt. Umgekehrt nehmenmanche Bastler aus dem Computer die Einzelteile wieder heraus, zumBeispiel, um sie in einem anderen Computer zu verbauen.

    In der DrRacket-Sprachebene Die Macht der Abstraktion - Anfängersind Computer schon eingebaut. Ein Computer mit Cell-Prozessor,2 Gigabyte RAM und 250 Gigabyte Festplatte wird folgendermaßenhergestellt:

    (make-computer "Cell" 2 250)

    ↪→ #

    Die Prozedur make-computer hat folgende Signatur:

    (: make-computer (string rational rational -> computer))

    Sie macht also aus einer Zeichenkette und zwei Zahlen einen Wertder eingebauten Sorte computer der Computer-Records. Die DrRacket-REPL druckt Record-Werte mit der Schreibweise # aus,damit Sorte und Komponenten sichtbar werden.

    Computer sind Werte wie andere auch und lassen sich zum Beispielan Variablen binden:

    ; Cell, 4 Gbyte RAM, 1000 Gbyte Festplatte

    (define gamer (make-computer "Cell" 4 1000))

    gamer

    ↪→ #

  • ii

    “i1” — 2017/2/2 — 12:52 — page 37 — #41 ii

    ii

    ii

    Zusammengesetzte Daten 37

    ; Xeon, 2 Gbyte RAM, 500 Gbyte Festplatte

    (define workstation (make-computer "Xeon" 2 500))

    workstation

    ↪→ #

    Da die Prozedur make-computer einen Computer „konstruiert“, heißt sieauch Konstruktor. Für das Zerlegen von Computern sind die Prozedurencomputer-processor, computer-ram und computer-hard-drive zuständig:

    (computer-processor gamer)

    ↪→ "Cell"(computer-ram gamer)

    ↪→ 4(computer-hard-drive gamer)

    ↪→ 1000

    Diese drei Prozeduren extrahieren die Bestandteile aus einem Computerund heißen Selektoren. Sie haben folgende Signaturen:

    (: computer-processor (computer -> string))

    (: computer-ram (computer -> rational))

    (: computer-hard-drive (computer -> rational))

    Mit Hilfe des Konstruktors und der Selektoren kann der Programmiererweitergehende Prozeduren definieren. Für den Anfang könnte das eineProzedur sein, die den Gesamtspeicher eines Computers berechnet,also Hauptspeicher und Festplattenspeicher zusammen. Eine solcheProzedur müßte Kurzbeschreibung und Signatur wie folgt haben:

    ; Gesamtspeicher berechnen

    (: total-memory (computer -> rational))

    Hier sind unsere Erwartungen an total-memory, als Testfälle formuliert:

    (check-expect (total-memory workstation) 502)

    (check-expect (total-memory gamer) 1004)

    Das Gerüst müßte folgendermaßen sein:

    (define total-memory

    (lambda (c)

    ...))

    Da in den Gesamtspeicher des Computer sowohl der Hauptspeicher alsauch die Festplatte eingehen, steht schon fest, daß die entsprechendenSelektoraufrufe im Rumpf der Prozedur vorkommen müssen:

    (define total-memory

    (lambda (c)

    ... (computer-ram c) ...

    ... (computer-hard-drive c) ...))

    Das Gesamtspeicher ergibt sich aus Addition der beiden Komponenten:

    (define total-memory

    (lambda (c)

    (+ (computer-ram c)

    (computer-hard-drive c))))

  • ii

    “i1” — 2017/2/2 — 12:52 — page 38 — #42 ii

    ii

    ii

    38 Kapitel 3

    Fertig!Total-memory ist ein Beispiel für eine Prozedur, die einen Record ak-

    zeptiert. Umgekehrt gibt es auch Prozeduren, die Records produzieren.Angenommen, unser Computerhändler bietet neben der Einzelkonfigu-ration von Prozessor, Hauptspeicher und Festplatte einige Standardmo-delle an – sagen wir, ein Billigmodell, ein Modell für Profis (was immerein „Profi“ sein mag) und ein Modell für Computerspieler. Je nach-dem, welches der Modelle der Kunde auswählt, muß die entsprechendeKonfiguration zusammengesetzt werden. Für die Standardkonfigurati-on gibt es drei feste Möglichkeiten, es handelt sich hier also um eineAufzählung.

    Eine Prozedur, die zu einer Standardkonfiguration den passendenComputer fertigt, könnte folgende Kurzbeschreibung und Signaturhaben:

    ; Standard-Computer zusammenstellen

    (: standard-computer ((one-of "cheap" "professional" "game") -> computer))

    Die Testfälle sollten alle drei Standardkonfigurationen abdecken:

    (check-expect (standard-computer "cheap") (make-computer "Sempron" 2 500))

    (check-expect (standard-computer "professional") (make-computer "Xeon" 4 1000))

    (check-expect (standard-computer "game") (make-computer "Quad" 4 750))

    Hier ist das Gerüst:

    (define standard-computer

    (lambda (k)

    ...))

    Da es sich beim Argument um eine Fallunterscheidung – eine Aufzäh-lung mit drei Alternativen – handelt, können wir die dazu passendeSchablone – eine Verzweigung mit drei Zweigen – zum Einsatz bringen:

    (define standard-computer

    (lambda (k)

    (cond

    (... ...)

    (... ...)

    (... ...))))

    Bei den Tests der Zweige müssen wir k mit den Elementen der Aufzäh-lung vergleichen. Da es sich um Zeichenketten handelt, nehmen wirdazu string=?:

    (define standard-computer

    (lambda (k)

    (cond

    ((string=? k "cheap") ...)

    ((string=? k "professional") ...)

    ((string=? k "game") ...))))

    In jedem Zweig müssen wir nun dafür sorgen, daß der entsprechendeComputer hergestellt wird. Für das Herstellen von Computer-Recordsist der Konstruktor make-computer zuständig. Dementsprechend müssenwir in jedem Zweig einen Aufruf an make-computer plazieren:

  • ii

    “i1” — 2017/2/2 — 12:52 — page 39 — #43 ii

    ii

    ii

    Zusammengesetzte Daten 39

    (define standard-computer

    (lambda (k)

    (cond

    ((string=? k "cheap")

    (make-computer ... ... ...))

    ((string=? k "professional")

    (make-computer ... ... ...))

    ((string=? k "game")

    (make-computer ... ... ...)))))

    Jetzt müssen wir nur noch die Argumente für die Aufrufe von make-computerzur Verfügung stellen. Für jeden Aufruf sind das, wie gehabt, der Pro-zessor, die Größe des Hauptspeichers und die Größe der Festplatte.Die entsprechenden Angaben können wir zum Beispiel den Testfällenentnehmen, und es kommt folgendes dabei heraus:

    (define standard-computer

    (lambda (k)

    (cond

    ((string=? k "cheap")

    (make-computer "Sempron" 2 500))

    ((string=? k "professional")

    (make-computer "Xeon" 4 1000))

    ((string=? k "game")

    (make-computer "Quad" 4 750)))))

    Fertig!

    3.2 Record-Definitionen

    Natürlich sind zusammengesetzte Daten in Scheme nicht auf Computer-Konfigurationen beschränkt – der Programmierer kann neue Artenzusammengesetzter Daten selbst definieren. Voraussetzung für dieDefinition einer neuen Art zusammengesetzter Daten ist eine klare Vor-stellung davon, was die Komponenten sind. Dabei hilft eine informelleBeschreibung wie diese hier:

    ; Eine kartesische Koordinate in der Ebene besteht aus X- und Y-Koordinate.

    Eine solche Datendefinition läßt sich direkt in eine Record-Definition inScheme übersetzen. Dies ist eine Form mit dem syntaktischen Schlüssel-wort define-record-procedures. Eine Record-Definition definiert einenneuen Record-Typ und dabei automatisch auch u. a. den Konstruktorund die Selektoren – nur ihre Namen müssen angegeben werden.

    TBD Eine define-record-procedures-Form hat folgende allgemeineGestalt:

    (define-record-procedures tc p(s1 . . . sn))

    Diese Form definiert einen Record-Typ mit n Feldern. Dabei sind t, c, p,s1 . . . sn allesamt Variablen, für die define-record-procedures Definitio-nen anlegt:

  • ii

    “i1” — 2017/2/2 — 12:52 — page 40 — #44 ii

    ii

    ii

    40 Kapitel 3

    • t ist der Name des Record-Typs.• c ist der Name des Konstruktors, den define-record-procedures an-

    legt.• p ist der Name des Prädikats, das define-record-procedures anlegt.• s1, . . . , sn sind die Namen der Selektoren für die Felder des Record-

    Typen.

    Beim Entwurf einer Record-Definition hilft es, mit der Datendefinitionanzufangen, die ausführlich beschreibt, was für Komponenten die Datenhaben. Für Computer sieht diese Datendefinition folgendermaßen aus:

    ; Ein Computer besteht aus:

    ; - Prozessor (string)

    ; - Hauptspeicher-Kapazität in Gbyte (rational)

    ; - Festplatten-Kapazität in Gbyte (rational)

    Hier ist die Datendefinition für kartesische Koordinaten:

    ; Eine kartesische Koordinate besteht aus:

    ; - X-Anteil (real)

    ; - Y-Anteil (real)

    Die Datendefinition muss genau soviele Komponenten aufweisen, wiedie zusammengesetzten Daten Bestandteile haben. Wenn es nicht ab-solut glaskar ist, sollten in Klammern die Signaturen der jeweiligenKomponenten angegeben werden – wie in diesen Beispielen.

    Aus der Datendefinition ergibt sich direkt die Record-Definition. Ins-besondere gehört zu jeder Komponente ein Selektor. Die Namen fürden Konstruktor, das Prädikat und die Selektoren können frei gewähltwerden, sollten aber meist einer einheitlichen Konvention folgen, umanderen das Lesen des Programms zu erleichern. Die gängige Kon-vention ist, daß der Konstruktor mit make- anfängt (make-cartesian),der Name des Prädikats auf ein Fragezeichen endet (cartesian?), unddie Selektoren mit dem Namen des Record-Typs beginnen und auf dieNamen der Felder enden (cartesian-x, cartesian-y).

    TBD

    3.3 Schablonen für zusammengesetzte Daten

    Zusammengesetzte Daten können Sie an Formulierungen wie „einX besteht aus . . . “, „ein X ist charakterisiert durch . . . “ oder „ein Xhat folgende Eigenschaften: . . . “ erkennen. Diese Formulierung bil-det dann – ordentlich aufgeschrieben und ggf. um die Signaturen fürdie Komponenten ergänzt – das Herzstück der Datendefinition. DieseDatendefinition können Sie dann direkt in die dazugehörige Record-Definition übersetzen. Diese muss genauso viele Felder haben wie dieDatendefinition Komponenten beschreibt.

    Diese Methode bildet Konstruktionsanleitung ?? in Anhang ??.

    3.3.1 Zusammengesetzte Daten als Eingabe

    Die Definitionen von total-price folgen dem selben Muster. DiesesMuster ergibt Schablonen für Prozeduren, die Records als Argumenteakzeptieren und läßt sich auch auf andere Record-Typen folgenderma-ßen in eine Konstruktionsanleitung übertragen:

  • ii

    “i1” — 2017/2/2 — 12:52 — page 41 — #45 ii

    ii

    ii

    Zusammengesetzte Daten 41

    1. Stellen Sie fest, von welchen Komponenten des Records das Ergebnisder Prozeduren abhängt.

    2. Für jede dieser Komponenten, schreiben Sie (s c) in die Schablone,wobei s der Selektor der Komponente und c der Name des Record-Parameters ist.

    3. Vervollständigen Sie die Schablone, indem Sie einen Ausdruck kon-struieren, in dem die Selektor-Anwendungen vorkommen.

    Konstruktionsanleitung ?? in Anhang ?? faßt diese Schritte noch einmalzusammen.

    3.3.2 Zusammengesetzte Daten als Ausgabe

    Prozeduren, die zusammengesetzte Daten als Ausgabe haben, müsseneinen entsprechenden Record konstruieren, mithin den Konstruktoraufrufen. Die Schablone wird also folgendermaßen konstruiert:

    Wenn die Prozedur zusammengesetzte Daten als Ausgabe hat, schreiben Sie einenAufruf des passenden Record-Konstruktors in den Rumpf, zunächst mit einerEllipse für jedes Feld des Records.

    Im nächsten Schritt ersetzen Sie dann die Ellipsen durch Ausdrücke,welche die entsprechenden Komponenten berechnen.

    Konstruktionsanleitung ?? in Anhang ?? faßt diese Schritte nocheinmal zusammen.

    3.4 Gürteltiere im Computer

    Nachdem wir aus den Beispielen die Schablonen für zusammengesetzteDaten entwickelt haben, demonstrieren wir diese in diesem Abschnittnoch einmal an einem frischen Beispiel:

    In Texas gibt es viele Gürteltiere, die insbesondere die Highways über-queren und dabei leider oft überfahren werden – am Straßenrand sindentsprechend viele Gürteltiere zu sehen. Außerdem füttern freundlicheAutofahrer gelegentlich die Gürteltiere. Mit diesen beiden Aspekte wol-len wir uns beschäftigen: Was passiert, wenn ein Gürteltier überfahrenwird? Was passiert, wenn ein Gürteltier gefüttert wird? Entsprechendinteressiert uns, ob ein Gürteltier am Leben ist und welches Gewicht eshat. Das können wir direkt in eine Datendefinition übersetzen:

    ; Ein Gürteltier hat folgende Eigenschaften:

    ; - Gewicht (in g)

    ; - lebendig oder tot

    Wiederum handelt es sich sichtlich um zusammengesetzte Daten. Dies-mal trifft die Phrase „besteht aus“ natürlich nicht zu. Stattdessen gehtes um die Eigenschaften, von denen ein Gürteltiert viele hat. Von diesenvielen interessanten Eigenschaften sind aber viele bei allen Gürteltierengleich (Säugetier, zwei Augen, vier Füße etc.) und darum nicht Teilder Datendefinition. Für unsere Aufgaben sind außerdem nur zweiEigenschaften von Belang, weshalb die Datendefinition auch nur dieseauflistet.

    Aus der Datendefinition können wir direkt eine passende Record-Definition machen:

  • ii

    “i1” — 2017/2/2 — 12:52 — page 42 — #46 ii

    ii

    ii

    42 Kapitel 3

    (define-record-procedures dillo

    make-dillo dillo?

    (dillo-weight dillo-alive?))

    („Dillo“ steht kurz für „Armadillo“, englisch für Gürteltier.)Für das Feld alive? könnten wir unterschiedliche Repräsentationen

    wählen: Eine Aufzählung wäre möglich; die Autoren haben sich füreinen booleschen Wert entschieden, der die Frage „Lebt das Gürteltier?“beantwortet. Hier sind die Signaturen für die Record-Prozeduren:

    (: make-dillo (natural boolean -> dillo))

    (: dillo? (any -> boolean))

    (: dillo-weight (dillo -> natural))

    (: dillo-alive? (dillo -> boolean))

    Dabei bedeutet any, dass an dieser Argumentstelle beliebige Datenauftreten dürfen. Riesengürteltiere werden um die 60 kg schwer. Hiersind einige Exemplare:

    (define d1 (make-dillo 55000 #t)) ; 55 kg, lebendig

    (define d2 (make-dillo 58000 #f)) ; 58 kg, tot

    (define d3 (make-dillo 60000 #t)) ; 60 kg, lebendig

    (define d4 (make-dillo 63000 #f)) ; 63 kg, tot

    Fangen wir mit dem unangenehmen Teil an, dem Überfahren, das auseinem lebenden Gürteltier ein totes macht. Hier Kurzbeschreibung undSignatur:

    ; Gürteltier überfahren

    (: run-over-dillo (dillo -> dillo))

    Aus dem Beispiel d1 können wir den ersten Testfall machen:

    (check-expect (run-over-dillo d1) (make-dillo 55000 #f))

    Wir sollten aber auch berücksichtigen, was run-over-dillo mit totenGürteltieren anstellt: Diese bleiben auch nach dem Überfahren tot:

    (check-expect (run-over-dillo d2) d2)

    Hier das Gerüst der Prozedur:

    (define run-over-dillo

    (lambda (d)

    ...))

    Run-over-dillo hat zusammengesetzte Daten sowohl als Eingabe alsauch als Ausgabe. Entsprechend kommen die Schablonen für beideSituationen zum Einsatz. Zunächst die Schablone für zusammengesetzteDaten als Eingabe; wir schreiben die Aufrufe der Selektoren auf:

    (define run-over-dillo

    (lambda (d)

    ... (dillo-weight d) ...

    ... (dillo-alive? d) ...))

    Dazu kommt die Schablone für zusammengesetzte Daten als Ausgabe,also der Aufruf des Konstruktors:

  • ii

    “i1” — 2017/2/2 — 12:52 — page 43 — #47 ii

    ii

    ii

    Zusammengesetzte Daten 43

    (define run-over-dillo

    (lambda (d)

    (make-dillo ... ...)

    ... (dillo-weight d) ...

    ... (dillo-alive? d) ...))

    Wir müssen beim Aufruf des Konstruktors make-dillo angeben, welchesGewicht das frisch überfahrene Gürteltier haben soll und ob es nocham Leben ist. Da das Überfahren das Gewicht nicht ändert, übernimmtder Ausdruck für das Gewicht das Gewicht des Eingabe-Gürteltiers ausder Schablone:

    (define run-over-dillo

    (lambda (d)

    (make-dillo (dillo-weight d) ...)

    ... (dillo-alive? d) ...))

    Das Gürteltier ist nach dem Überfahren auf jeden Fall tot. Da es keineRolle spielt, ob das Gürteltier vorher lebendig war oder nicht, könnenwir den Selektoraufruf (dillo-alive? d) verwerfen:

    (define run-over-dillo

    (lambda (d)

    (make-dillo (dillo-weight d)

    #f)))

    Fertig!Nächste Aufgabe: Gürteltier füttern. Die Standard-Futter-Portion ist

    dabei 500g, und das Gürteltier nimmt durch die Fütterung um dasentsprechende Gewicht zu. Hier Kurzbeschreibung und Signatur:

    ; Gürteltier mit 500g Futter füttern

    (: feed-dillo (dillo -> dillo))

    Hier der erste, naheliegende Testfall:

    (check-expect (feed-dillo d1) (make-dillo 55500 #t))

    Auch bei feed-dillo ist relevant, was es mit toten Gürteltieren macht:Tote Gürteltiere fressen nicht, entsprechend nehmen sie auch nicht zu,wenn man ihnen Futter anbietet:

    (check-expect (feed-dillo d2) d2)

    Hier das Gerüst der Prozedur:

    (define feed-dillo

    (lambda (d)

    ...))

    Feed-dillo hat die gleiche Signatur wie run-over-dillo; entsprechendbenutzen wir die gleiche Schablone:

    (define feed-dillo

    (lambda (d)

    (make-dillo ... ...)

    ... (dillo-weight d) ...

    ... (dillo-alive? d) ...))

  • ii

    “i1” — 2017/2/2 — 12:52 — page 44 — #48 ii

    ii

    ii

    44 Kapitel 3

    Beim zweiten Testfall haben wir gesehen, daß, was feed-dillo betrifft,die Gürteltiere in zwei verschiedene Gruppen fallen: Feed-dillo verhältsich bei lebenden Gürteltieren anders als bei toten: eine Fallunterschei-dung. Entsprechend brauchen wir eine Verzweigung im Rumpf. Dasich der Fall „Gürteltier tot“ dadurch definiert, daß der Fall „Gürteltierlebendig“ nicht eintritt, ist die binäre Verzweigung angemessen:

    (define feed-dillo

    (lambda (d)

    (if (dillo-alive? d)

    ...

    ...)

    (make-dillo ... ...)

    ... (dillo-weight d) ...

    Nun müssen wir noch die beiden Zweige ergänzen. Am einfachstenist die Alternative „Gürteltier tot“, dann nämlich kommt das gleicheGürteltier aus der Prozedur, das hineingegangen ist:

    (define feed-dillo

    (lambda (d)

    (if (d