Praxis der Funktionalen Programmierung (20. 10.)
Vorwort
Die Themen, die wir behandeln, gehören grob zu zwei Kategorien:
- exakt zu erfassende Grundlagen (Definitionen)
- zunächst nur intuitiv (durch Benutzung anhand von Beispielen)
zu erfassende Sachverhalte
Natürlich wandert das meiste im Laufe der Zeit von der zweiten
in die erste Abteilung. Es wäre jedoch sehr umständlich,
nur mit dem bereits exakt Definierten zu programmieren.
Da würde es bis Weihnachten dauern,
bis wir überhaupt auch nur ein Bild malen könnten.
Andererseits entspricht dieses Vorgehen auch dem tatsächlichen Programmieren:
man benutzt Bibliotheksfunktionen,
ohne sich um deren Implementierung zu kümmern.
Einordnung
Wir sind im Abschnitt 1, einfache Programme, mit Grafik.
Wir wollen an dessen Ende einige Parkettierungs-Aufgaben lösen können.
Währendher lernen wir die grundsätzliche Struktur
von Haskell-Programmen und -Datentypen kennen.
Definitionen
Die Syntax von Haskell-Programmen
- Module
- bestehen aus einer Folge von Typdeklarationen
und Gleichungen (Definitionen)
- Typdeklaration
- hat die Form "Bezeichner :: Typ".
- Definitionen
- haben die Form "linke Seite = rechte Seite"
- linke Seite
- besteht aus einem Namen, eventuell gefolgt von Mustern
- Muster
- testet Übereinstimmung von Konstruktoren und/oder bindet lokale Bezeichner,
ist entweder ein Bezeichner oder ein Konstruktor,
eventuell gefolgt von Mustern
- rechte Seite
- ist ein Ausdruck
- Ausdrücke
- ein Name (auch Konstruktor), eventuell mit Argumenten (Ausdrücken)
- Konstanten für Zahlen, Zeichen, Zeichenketten
- lokale Bindungen: let lhs1 = rhs1; lhs2 = rhs2; ... in exp
- bedingte Ausdrücke: if exp then exp else exp
Einfache Typen
- Wahrheitswerte: Boolean
- Konstruktoren: False, True
- Zeichen: Char
- Konstruktoren: 'a', 'b', '\n', ...
- Ganze Zahlen: Integer (beliebig lang!)
- Konstruktoren: 12343511561356, 0xF0, ...
- Zeichenketten: String
- Ist gar kein einfacher Typ, sondern String = [ Char ], siehe Listen.
Konstruktoren: Listenkonstruktoren und "foo", "bar\n", ...
Listen
Für einen Typ a bezeichnet [a]
den Typ aller Listen mit Elementen vom Typ a.
Eine Liste ist entweder leer, oder sie ist eine Zelle,
die aus einem Kopf mit Typ a und einem Schwanz mit Typ [a] besteht.
Der Datentyp hat also zwei Konstruktoren, [] und (:).
Diese verwenden wir auch in den Mustern. Beispiel:
länge :: [a] -> Integer
länge [] = 0
länge (x : xs) = 1 + länge xs
(Später programmiert man sowas mit einem fold.)
Statt (1 : (2 : (3 : (4 : [])))) schreiben wir kürzer [1, 2, 3, 4].
Tatsächlich können wir dafür sogar [1 .. 4] schreiben.
Im Allgemeinen können wir auch eine Schrittweite angeben:
Prelude> [1, 3 .. 20]
[1,3,5,7,9,11,13,15,17,19]
Anwendung von Listen
Wir verwenden Listen in dem Programm, das Parkettierungen bestimmt
(Quelltext hier).
Die Idee ist, ein polygonal begrenztes Gebiet
durch die Liste seiner Innenwinkel zu beschreiben.
(Die Streckenlängen erwähnen wir nicht, die sind nämlich alle gleich groß).
Zu implementieren ist dann das Aneinanderlegen zweier solcher Gebiete
(d. h. Testen, ob es überhaupt paßt, und Bestimmen der neuen Randkurve).
List Comprehensions
Für das Durchlaufen und Erzeugen von Listen gibt es eine spezielle Syntax,
die sich an der mathematischen Notation für Mengen orientiert.
Wir erkennen sie an dem senkrechten Strich innerhalb von Listenklammern.
Er trennt den Kopf (einen Ausdruck) vom Rumpf (einer Folge
von Generatoren, Filtern und Bindungen)
- Generator
- ist ein Ausdruck nach einem Rückwärtspfeil.
Er bindet die lokale Variable vor dem Pfeil.
Diese ist ab dort im Rumpf sichtbar,
und auch im Kopf der Comprehension.
Beispiele:
Prelude> [ x * x | x <- [ 1, 2, 3, 4 ] ]
[1,4,9,16]
Prelude> [ x * y | x <- [1,2,3], y <- [2,3,5] ]
[2,3,5,4,6,10,6,9,15]
Wir können uns das als geschachtelte Schleifen vorstellen.
- Filter
- ist ein Ausdruck vom Wert Boolean. Beispiel
Prelude> [ x * x | x <- [0,1,2,3,4,5,6,7], 0 < mod x 3 ]
[1,4,16,25,49]
- Bindung
- hat die Form let Bezeichner = Ausdruck.
Der lokale Bezeichner wird gebunden und ist ab dort
und im Kopf sichtbar.
Beispiel:
Prelude> [ y | x <- [0,1,2,3,4,5,6,7], let y = x * x * x ]
[0,1,8,27,64,125,216,343]
Beispiele
Aufgaben
Intuition
Der Interpreter Hugs
Hugs kann einen Modul-Quelltext laden und dann Ausdrücke auswerten
oder Aktionen ausführen,
die in diesem Modul definiert sind.
Hugs unterscheidet dabei: Ist der Ausdruck vom Typ IO (),
das heißt, eine Aktion, dann wird diese ausgeführt.
Beispiel:
Prelude> putStrLn "hello"
hello
Bei Ausdrücken anderer Typen zeigt Hugs eine ASCII-Repräsentation
des Wertes an. Beispiel:
Prelude> take 10 [1..]
[1,2,3,4,5,6,7,8,9,10]
Beachte: nicht für alle Typen existiert so eine Repräsentation.
Das führt zu solchen Fehlermeldungen:
Prelude> show id
ERROR: Illegal Haskell 98 class constraint in inferred type
*** Expression : show id
*** Type : Show (a -> a) => [Char]
Für nutzerdefinierte Typen muß man die Repräsentation
entweder selbst festlegen oder vom Compiler explizit generieren lassen.
Siehe später bei Typklassen.
Elementare Grafik
(Das Graphics-Paket muß installiert sein.)
Ein Grafik-Programm ist ebenfalls eine Aktion.
Es sieht im einfachsten Fall so aus:
import SOEGraphics
main = runGraphics $ do
let s = 500
w <- openWindow "Grafik" (s, s)
drawInWindow w $ polyline [(0,0), (s,s)]
k <- getKey w
closeWindow w
Beachte: nach dem "do" müssen die Zeilenanfänge genau untereinander stehen.
Was das "do" überhaupt ist, behandeln wir später ausführlich.
Kasten-Grafik
Mit der elementaren Grafik müssen wir ständig in globalen Koordinaten rechnen.
Ich habe eine kleine Bibliothek geschrieben
(Quelltexte hier),
die Zusammensetzungen aus Kästen gestattet,
innerhalb derer wir mit lokalen (oder gar keinen) Koordinaten arbeiten.
Die Implementierung sehen wir uns später an.
Die Benutzung geht so:
import Bild
...
bild $ neben ( über (kreis 1) (kreis 2) ) ( quadrat 3 )
Einige Datentypen "t" implementieren die Methode "form :: t -> Bild",
und es gibt die Abkürzung "zeige x = bild (form x)". Beispiel
import Bild
import Ding
import Form
...
zeige [ [ rot, blau, gelb ], [schwarz, blau] ]
Zu Methoden (und Typklassen und Instanzen) hören wir später mehr.
http://www.informatik.uni-leipzig.de/~joe/
mailto:joe@informatik.uni-leipzig.de