Programmieren in Haskell Syntax und Semantik von Haskell Programmieren in Haskell 1 Was wir heute (und nächstes mal) machen • Datentypdefinitionen • Wertdefinitionen, Variablenbindungen • Musterbindungen • Funktionsbindungen • Bewachte Gleichungen • Lokale Definitionen mit where und let • Gültigkeitsbereiche • Fallunterscheidungen • Syntaktischer Zucker / Kernsprache Programmieren in Haskell 2 Datentypen, Datentypdefinitionen data Instrument = | | | Oboe HonkyTonkPiano Cello VoiceAahs data Musik Note Ton Dauer Pause Dauer Musik :*: Musik Musik :+: Musik Instr Instrument Musik Tempo GanzeZahl Musik Programmieren in Haskell = | | | | | 3 Wahrheitswerte data Bool = False | True Programmieren in Haskell -- vordefiniert 4 Ganze Zahlen data Ganz = Zero | Succ Ganz Zero, Succ Zero, Succ (Succ Zero), . . . Eingebaute Datentypen: Int und Integer Programmieren in Haskell 5 Tupeltypen data Pair a b = Pair a b data Triple a b c = Triple a b c Eingebaute Datentypen: (a,b), (a,b,c) Programmieren in Haskell 6 Listen data List a = Empty | Front a (List a) Eingebauter Datentyp: [a] Programmieren in Haskell 7 Allgemeine Form data T a1 . . . am Programmieren in Haskell = C1 t11 . . . t1n1 | ... | Cr tr1 . . . trnr 8 Allgemeine Form data T a1 . . . am = C1 t11 . . . t1n1 | ... | Cr tr1 . . . trnr T : Typkonstruktor; Ci : (Daten-)Konstruktoren; ai : Typvariablen; tij : Typen oder Typvariablen Programmieren in Haskell 8 Selektorfunktionen auf Tupeln fst :: (a,b) -> a fst (a,b) = a -- vordefiniert snd :: (a,b) -> b snd (a,b) = b -- vordefiniert Programmieren in Haskell 9 Selektorfunktionen auf Listen head :: [a] -> a head (x:xs) = x -- vordefiniert tail :: [a] -> [a] tail (x:xs) = xs -- vordefiniert Programmieren in Haskell 10 Selektorfunktionen auf Musik ton :: Musik -> Int ton (Note ton dauer) = ton dauer :: Musik -> Rational dauer (Note ton dauer) = dauer dauer (Pause dauer) = dauer Programmieren in Haskell 11 Listen mit Field Labels data List a = Empty | Front { head :: a, tail :: List a } Dies definiert Selektorfunktionen head, tail. Programmieren in Haskell 12 Musik mit Field Labels data Musik = | | | | | Note Pause (:*:) (:+:) Instr Tempo { { { { { { ton :: Int, dauer :: Rational } dauer :: Rational } links :: Musik, rechts :: Musik oben :: Musik, unten :: Musik instrument :: Instrument, musik tempo :: Int, musik :: Musik } } } :: Musik } deriving Show Dies definiert Selektorfunktionen ton, dauer, links, rechts, oben, unten, instrument, tempo. Programmieren in Haskell 13 Typsynonyme type GanzeZahl type Bruch = Int = Rational type Ton type Dauer = GanzeZahl = Bruch Programmieren in Haskell 14 Nutzen und Gefahren von Typsynonymen type OrdList a = [a] sort :: [a] -> OrdList a Programmieren in Haskell 15 Nutzen und Gefahren von Typsynonymen type OrdList a = [a] sort :: [a] -> OrdList a sort xs = xs Typ ok? Programmieren in Haskell 15 Nutzen und Gefahren von Typsynonymen type OrdList a = [a] sort :: [a] -> OrdList a sort xs = xs Typ ok? Ja Programmieren in Haskell 15 Nutzen und Gefahren von Typsynonymen type OrdList a = [a] sort :: [a] -> OrdList a sort xs = xs Typ ok? Ja Ergebnis geordnete Liste? Programmieren in Haskell 15 Nutzen und Gefahren von Typsynonymen type OrdList a = [a] sort :: [a] -> OrdList a sort xs = xs Typ ok? Ja Ergebnis geordnete Liste? Nein (jedenfalls nicht im allgemeinen) Programmieren in Haskell 15 Newtype-Deklaration newtype Natural = MakeNatural Integer deriving (Show,Eq) toNatural :: Integer -> Natural toNatural x | x < 0 = error "Can’t create negative naturals!" | otherwise = MakeNatural x fromNatural :: Natural -> Integer fromNatural (MakeNatural i) = i Programmieren in Haskell 16 instance Num Natural where fromInteger = toNatural x + y = toNatural (fromNatural x + fromNatural y) x - y = let r = fromNatural x - fromNatural y in if r < 0 then error "Unnatural subtraction" else toNatural r x * y = toNatural (fromNatural x * fromNatural y) Programmieren in Haskell 17 Wertdefinitionen, Variablenbindungen theFinalAnswer :: Integer theFinalAnswer = 42 aShortList :: [Integer] aShortList = [1,2,3] helloWorld :: String helloWorld = "Hello World" monotonie :: Musik monotonie = c’ (1/1) :*: monotonie Programmieren in Haskell 18 Musterbindungen myList = Front 1 (Front 2 (Front 3 Empty)) Front x (Front y zs) = myList (x:y:zs) = [1,2,3] Allgemein: p = e, mit p Muster, e Ausdruck Programmieren in Haskell 19 bruderJakob = Tempo andante (einsatz (0/1) (einsatz (2/1) (einsatz (4/1) (einsatz (6/1) (Instr VoiceAahs endlos :+: (transponiere 12 endlos)) :+: endlos) :+: endlos ))) Tempo t (Instr i (m1 :+: m2 :+: m3:+: m4)) = bruderJakob Programmieren in Haskell 20 Funktionsbindungen length :: [a] -> Int length [] = 0 length (a:as) = 1 + length as Programmieren in Haskell -- vordefiniert 21 Allgemeiner Fall f p11 . . . p1n = e1 ... = ... f pk1 . . . pkn = ek pij : Muster, ei : Ausdrücke Programmieren in Haskell 22 Bewachte Gleichungen member :: (Ord a) => a -> OrdList a -> Bool member a [] = False member a (b:bs) = a >= b && (a == b || member a bs) member a [] = False member a (b:bs) = if a < b then False else if a == b then True else member a bs member a [] member a (b:bs) | a < b | a == b | a > b Programmieren in Haskell = False = False = True = member a bs 23 Lokale Definitionen mit where sumSquares :: Int -> Int -> Int sumSquares x y = sqX + sqY where sqX = x * x sqY = y * y Programmieren in Haskell 24 Lokale Definitionen mit let sumSquares :: Int -> Int -> Int sumSquares x y = let sqX = x * x sqY = y * y in sqX + sqY Programmieren in Haskell 25 Gültigkeits- oder Sichtbarkeitsbereiche f :: Int -> Int f x = f (x+x) where x = 1 f x = x Programmieren in Haskell -- Negativ-Beispiel 26 Gültigkeits- oder Sichtbarkeitsbereiche f :: Int -> Int f x = f (x+x) where x = 1 f x = x -- Negativ-Beispiel f1 :: Int -> Int f1 x1 = f2 (x2+x2) where x2 = 1 f2 x3 = x3 Programmieren in Haskell 26 Abseitsregel • Das erste Zeichen der Definition unmittelbar nach dem where bestimmt die Einrücktiefe des neu eröffneten Definitionsblocks. • Zeilen, die weiter als bis zu dieser Position eingerückt sind, gelten als Fortsetzungen einer begonnenen lokalen Definition. • Zeilen auf gleicher Einrücktiefe beginnen eine neue Definition im lokalen Block. • Die erste geringer eingerückte Zeile beendet den lokalen Block. Programmieren in Haskell 27 Fallunterscheidungen length :: [a] -> Int length [] = 0 length (a:as) = 1 + length as -- vordefiniert length’ :: [a] -> Int length’ as = case as of [] -> 0 a:as’ -> 1 + length’ as’ last’ :: [a] -> a last’ as = case reverse as of a:as’ -> a Programmieren in Haskell 28 Allgemeine Form von Fallunterscheidungen case e of { p1 m1 ; . . . ;pn mn } Die pi sind beliebige Muster und die mi nehmen wahlweise eine der beiden folgenden Formen an: -> e where { d1 ; . . . ;dp } oder | g1 -> e1 ... | gq -> eq where { d1 ; . . . ;dp } Programmieren in Haskell 29 Funktionsausdrücke Ist n + 1 ein Wert oder eine Funktion? Als Funktion: f n = n + 1 \n -> n + 1 Allgemeine Form eines Funktionsausdrucks: \p1 . . . pn -> e . \p1 p2 -> e als Abkürzung für \p1 -> \p2 -> e Programmieren in Haskell 30 Gestaffelte Funktionen add :: Integer -> Integer -> Integer add m n = m + n add’ :: Integer -> (Integer -> Integer) add’ = \m -> \n -> m + n Programmieren in Haskell 31 Gestaffelte Funktionen add :: Integer -> Integer -> Integer add m n = m + n add’ :: Integer -> (Integer -> Integer) add’ = \m -> \n -> m + n Programmieren in Haskell 31 note :: Int -> Int -> Dauer -> Musik note oct h d = Note (12 * oct + h) d note Eine Note in einer beliebigen Oktave und von beliebiger Höhe und Dauer. note 2 Eine Note in der dritten Oktave und von beliebiger Höhe und Dauer. note 2 ef Die Note f in der dritten Oktave und von beliebiger Dauer. note 2 ef (1/4) ergibt Note 29 (1/4). Programmieren in Haskell 32 Syntaktischer Zucker / Kernsprache • Mustergleichungen / case-Ausdrücke length [] = 0 length (x:xs) = 1 + length xs length’ xs = case xs of [] -> 0 (x:xs) -> 1 + length’ xs • Funktionsdefinitionen / Funktions-Ausdrücke add m n = m + n add’ = \m -> \n -> m + n Programmieren in Haskell 33 • where-Klauseln / let-Ausdrücke double n = add0 (dup n) where dup a = (a,a) double n = let dup a = (a,a) in add0 (dup n) Programmieren in Haskell 34 Auswertung von Fallunterscheidungen case C e1 ...ek of {...; C x1 ...xk -> e;...} (case-Regel) ⇒ e[x1 /e1 , . . . , xn /en ] Programmieren in Haskell 35 abs :: (Ord a, Num a) => a -> a abs n = case n >= 0 of True -> n False -> -n encode :: (Num a) => Maybe a -> a encode x = case x of Nothing -> -1 Just n -> abs n encode (Just 5) ⇒ case Just 5 of {Nothing -> -1; Just n -> abs n} (Def. encode) ⇒ abs 5 (case − Regel) ⇒ case 5 >= 0 of {True -> 5; False -> -5} (Def. abs) ⇒ case True of {True -> 5; False -> -5} (Def. >=) ⇒ 5 Programmieren in Haskell (case − Regel) 36 Auswertung von Funktionsanwendungen (\x -> e) a ⇒ e[x/a] Programmieren in Haskell (β-Regel) (1) 37 abs = encode = \n -> case n >= 0 of {True -> n; False -> -n} \x -> case x of {Nothing -> -1; Just n -> abs n} encode (Just 5) (Def. encode) ⇒ (\x-> case x of {Nothing-> -1; Just n-> abs n}) (Just 5) ⇒ case Just 5 of {Nothing -> -1; Just n -> abs n} ⇒ abs 5 ⇒ (\n -> case n >= 0 of {True -> n; False -> -n}) 5 (Def. abs) ⇒ case 5 >= 0 of {True -> 5; False -> -5} (β − Regel) ⇒ case True of {True -> 5; False -> -5} (Def. (>=)) ⇒ 5 Programmieren in Haskell (β − Regel) (case − Regel) (case − Regel) 38 Auswertung von lokalen Definitionen let {x1 = e1 ;...;xn = en } in e ⇒ e[x1 /let {x1 = e1 ;...;xn = en } in e1 , . . . , (let-Regel) xn /let {x1 = e1 ;...;xn = en } in en ] Programmieren in Haskell 39 rep n a = let x = a:x in take n x rep 2 8 Programmieren in Haskell ⇒ let x = 8:x in take 2 x ⇒ take 2 (let x = 8:x in 8:x) (let − Regel) ⇒ take 2 (8:let x = 8:x in 8:x) (let − Regel) ⇒ 8:take 1 (let x = 8:x in 8:x) (Def. take) ⇒ 8:take 1 (8:let x = 8:x in 8:x) (let − Regel) ⇒ 8:8:take 0 (let x = 8:x in 8:x) (Def. take) ⇒ 8:8:[] (Def. take) (Def. rep) 40