Praktische Informatik 3: Funktionale Programmierung Vorlesung 8 vom 04.12.2018: Abstrakte Datentypen
Christoph Lüth Universität Bremen Wintersemester 2018/19
16:03:11 2018-12-18 1 [42]
Organisatorisches
I Morgen istTag der Lehre
IMittwochs-Tutorienfallen aus
IDonnerstags-Tutorien finden statt.
I Abgabe des 8. Übungsblattes in Gruppen zudreiStudenten.
IBittejetzteine Gruppe suchen!
I Klausurtermine:
IÜbungsklausur: 17.12.2018 10– 12
IHauptklausur: 08.03.2019 10– 14
PI3 WS 18/19 2 [42]
Fahrplan
I Teil I: Funktionale Programmierung im Kleinen
I Teil II: Funktionale Programmierung im Großen
I Abstrakte Datentypen
I Signaturen und Eigenschaften
I Teil III: Funktionale Programmierung im richtigen Leben
PI3 WS 18/19 3 [42]
Inhalt
I Abstrakte Datentypen
IAllgemeine Einführung
IRealisierung in Haskell
IBeispiele
PI3 WS 18/19 4 [42]
Warum Modularisierung?
I Übersichtlichkeit der Module
Lesbarkeit
I Getrennte Übersetzung
technischeHandhabbarkeit
I Verkapselung
konzeptionelleHandhabbarkeit
PI3 WS 18/19 5 [42]
Abstrakte Datentypen
Definition (Abstrakter Datentyp)
Einabstrakter Datentyp(ADT) besteht aus einem (oder mehreren) TypenundOperationendarauf, mit folgenden Eigenschaften:
I Werte des Typen können nur über die bereitgestellten Operationen erzeugt werden
I Eigenschaften von Werten des Typen werden nur über die bereitgestellten Operationen beobachtet
I Einhaltung vonInvariantenüber dem Typ kann garantiert werden
Implementationvon ADTs in einer Programmiersprache:
I benötigt Möglichkeit derKapselung(Einschränkung der Sichtbarkeit) I bspw. durchModuleoderObjekte
PI3 WS 18/19 6 [42]
ADTs vs. algebraische Datentypen
I Algebraische Datentypen
I Frei erzeugt
I Keine Einschränkungen
I Insbesondere keine Gleichheiten ([ ]6= x : xs,x : l s6= y : l setc.) I ADTs:
I Einschränkungen und Invarianten möglich
I Gleichheiten möglich
ADTs in Haskell: Module
I Einschränkung der Sichtbarkeit durchVerkapselung
I Modul: Kleinste verkapselbareEinheit
I EinModulumfaßt:
IDefinitionenvon Typen, Funktionen, Klassen
IDeklarationder nach außensichtbarenDefinitionen
I Gleichzeitig: Modul ˆ= Übersetzungseinheit (getrennte Übersetzung)
Module: Syntax
I Syntax:
moduleName( Bezeichner )whereRumpf
I Bezeichner können leer sein (dann wird alles exportiert) I Bezeichner sind:
I Typen:T,T(c1 , . . . , cn),T( . . )
I Klassen:C,C( f1 , . . . , fn ),C( . . )
I Andere Bezeichner:Werte,Felder,Klassenmethoden
I ImportierteModule:moduleM
I Typsynonyme und Klasseninstanzen bleiben sichtbar I Module könnenrekursivsein(don’t try at home)
PI3 WS 18/19 9 [42]
Refakturierung im Einkaufsparadies
module Shoppe4 where import Data.Maybe
−−Modellierung der Artikel.
data Apfelsorte = Boskoop | CoxOrange | GrannySmith deriving (Eq, Show) apreis :: Apfelsorte→Int apreis Boskoop = 55 apreis CoxOrange = 60 apreis GrannySmith = 50 data Kaesesorte = Gouda | Appenzeller
deriving (Eq, Show) kpreis :: Kaesesorte→Double kpreis Gouda = 1450 kpreis Appenzeller = 2270 data Bio = Bio | Konv
deriving (Eq, Show) data Art ikel =
Apfel Apfelsorte | Eier
| Kaese Kaesesorte | Schinken
| Salami | Milch Bio deriving (Eq, Show) data Menge = Stueck Int | Gramm Int | Lit er Double
deriving (Eq, Show) type Preis = Maybe Int preis :: Art ikel→Menge→Preis preis ( Apfel a) ( Stueck n) = Just (n∗apreis a) preis Eier ( Stueck n)= Just (n∗20) preis (Kaese k)(Gramm g) = Just ( round( fromIntegral g∗1000∗kpreis k)) preis Schinken (Gramm g) = Just ( div (g∗199) 100) preis Salami (Gramm g)= Just ( div (g∗159) 100) preis ( Milch bio) ( Lit er l ) =
Just ( round ( l∗case bio of Bio→119; Konv→69)) preis = Nothing
−−Addition von Mengen addiere :: Menge→Menge→Menge addiere ( Stueck i ) ( Stueck j )= Stueck ( i + j ) addiere (Gramm g) (Gramm h) = Gramm (g+ h) addiere ( Lit er l ) ( Lit er m) = Lit er ( l + m) addiere m n = error (” addiere: ” ++ show m++ ” und ” ++ show n)
−−Posten:
data Posten = Posten Art ikel Menge deriving (Eq, Show) cent :: Posten→Int
cent ( Posten a m) = fromMaybe 0 ( preis a m)−−gibt keinen Laufzeitfehler!
−−Lagerhaltung:
data Lager = Lager [ Posten]
deriving (Eq, Show) leeresLager :: Lager leeresLager = Lager [ ]
suche :: Art ikel→Lager→Maybe Menge suche a ( Lager ps) =
listToMaybe [ m | Posten la m←ps,la == a ] einlagern :: Art ikel→Menge→Lager→Lager einlagern a m ( Lager ps) =
let hinein a m [ ] = [ Posten a m]
hinein a m ( Posten al ml: l )
| a == al= ( Posten a ( addiere m ml) : l )
| otherwise = ( Posten al ml: hinein a m l ) in case preis a m of
Nothing→Lager ps
→Lager ( hinein a m ps) data Einkaufswagen = Ekwg [ Posten]
deriving (Eq, Show) leererWagen :: Einkaufswagen leererWagen = Ekwg [ ]
einkauf :: Art ikel→Menge→Einkaufswagen→Einkaufswagen einkauf a m (Ekwg ps)
| isJust ( preis a m) = Ekwg ( Posten a m: ps)
| otherwise = Ekwg ps kasse :: Einkaufswagen→Int kasse (Ekwg ps) = sum (map cent ps) kassenbon :: Einkaufswagen→String kassenbon ew@(Ekwg ps) =
”Bob’ s Aulde Grocery Shoppe\n\n” ++
” Art ikel Menge Preis\n” ++
”−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−\n” ++
concatMap art ikel ps ++
”=====================================\n” ++
”Summe: ” ++ formatR 31 (showEuro ( kasse ew)) art ikel :: Posten→String art ikel p@( Posten a m) =
formatL 20 (show a) ++
formatR 7 (menge m) ++
formatR 10 (showEuro ( cent p)) ++ ” \n”
menge :: Menge→String menge ( Stueck n) = show n++ ” St”
menge (Gramm g) = show g++ ” g. ” menge ( Lit er l ) = show l++ ” l . ” formatL :: Int→String→String formatL n st r = take n ( st r++ replicat e n ’ ’ ) formatR :: Int→String→String formatR n st r =
take n ( replicat e (n−length st r ) ’ ’ ++ st r ) showEuro :: Int→String showEuro i =
show ( div i 100) ++ ” . ” ++
show (mod ( div i 10) 10) ++
show (mod i 10)++ ” EU”
inventur :: Lager→Int inventur ( Lager l ) = sum (map cent l )
Artikel
module Shoppe4 where import Data.Maybe
−−Modellierung der Artikel.
data Apfelsorte = Boskoop | CoxOrange | GrannySmith deriving (Eq, Show) apreis :: Apfelsorte→Int apreis Boskoop = 55 apreis CoxOrange = 60 apreis GrannySmith = 50 data Kaesesorte = Gouda | Appenzeller
deriving (Eq, Show) kpreis :: Kaesesorte→Double kpreis Gouda = 1450 kpreis Appenzeller = 2270 data Bio = Bio | Konv
deriving (Eq, Show) data Art ikel =
Apfel Apfelsorte | Eier
| Kaese Kaesesorte | Schinken
| Salami | Milch Bio deriving (Eq, Show) data Menge = Stueck Int | Gramm Int | Lit er Double
deriving (Eq, Show) type Preis = Maybe Int preis :: Art ikel→Menge→Preis preis ( Apfel a) ( Stueck n) = Just (n∗apreis a) preis Eier ( Stueck n)= Just (n∗20) preis (Kaese k)(Gramm g) = Just ( round( fromIntegral g∗1000∗kpreis k)) preis Schinken (Gramm g) = Just ( div (g∗199) 100) preis Salami (Gramm g)= Just ( div (g∗159) 100) preis ( Milch bio) ( Lit er l ) =
Just ( round ( l∗case bio of Bio→119; Konv→69)) preis = Nothing
−−Addition von Mengen addiere :: Menge→Menge→Menge addiere ( Stueck i ) ( Stueck j )= Stueck ( i + j ) addiere (Gramm g) (Gramm h) = Gramm (g+ h) addiere ( Lit er l ) ( Lit er m) = Lit er ( l + m) addiere m n = error (” addiere: ” ++ show m++ ” und ” ++ show n)
−−Posten:
data Posten = Posten Art ikel Menge deriving (Eq, Show) cent :: Posten→Int
cent ( Posten a m) = fromMaybe 0 ( preis a m)−−gibt keinen Laufzeitfehler!
−−Lagerhaltung:
data Lager = Lager [ Posten]
deriving (Eq, Show) leeresLager :: Lager leeresLager = Lager [ ]
suche :: Art ikel→Lager→Maybe Menge suche a ( Lager ps) =
listToMaybe [ m | Posten la m←ps,la == a ] einlagern :: Art ikel→Menge→Lager→Lager einlagern a m ( Lager ps) =
let hinein a m [ ] = [ Posten a m]
hinein a m ( Posten al ml: l )
| a == al= ( Posten a ( addiere m ml) : l )
| otherwise = ( Posten al ml: hinein a m l ) in case preis a m of
Nothing→Lager ps
→Lager ( hinein a m ps) data Einkaufswagen = Ekwg [ Posten]
deriving (Eq, Show) leererWagen :: Einkaufswagen leererWagen = Ekwg [ ]
einkauf :: Art ikel→Menge→Einkaufswagen→Einkaufswagen einkauf a m (Ekwg ps)
| isJust ( preis a m) = Ekwg ( Posten a m: ps)
| otherwise = Ekwg ps kasse :: Einkaufswagen→Int kasse (Ekwg ps) = sum (map cent ps) kassenbon :: Einkaufswagen→String kassenbon ew@(Ekwg ps) =
”Bob’ s Aulde Grocery Shoppe\n\n” ++
” Art ikel Menge Preis\n” ++
”−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−\n” ++
concatMap art ikel ps ++
”=====================================\n” ++
”Summe: ” ++ formatR 31 (showEuro ( kasse ew)) art ikel :: Posten→String art ikel p@( Posten a m) =
formatL 20 (show a) ++
formatR 7 (menge m) ++
formatR 10 (showEuro ( cent p)) ++ ” \n”
menge :: Menge→String menge ( Stueck n) = show n++ ” St”
menge (Gramm g) = show g++ ” g. ” menge ( Lit er l ) = show l++ ” l . ” formatL :: Int→String→String formatL n st r = take n ( st r++ replicat e n ’ ’ ) formatR :: Int→String→String formatR n st r =
take n ( replicat e (n−length st r ) ’ ’ ++ st r ) showEuro :: Int→String showEuro i =
show ( div i 100) ++ ” . ” ++
show (mod ( div i 10) 10) ++
show (mod i 10)++ ” EU”
inventur :: Lager→Int inventur ( Lager l ) = sum (map cent l )
Posten Artikel
module Shoppe4 where import Data.Maybe
−−Modellierung der Artikel.
data Apfelsorte = Boskoop | CoxOrange | GrannySmith deriving (Eq, Show) apreis :: Apfelsorte→Int apreis Boskoop = 55 apreis CoxOrange = 60 apreis GrannySmith = 50 data Kaesesorte = Gouda | Appenzeller
deriving (Eq, Show) kpreis :: Kaesesorte→Double kpreis Gouda = 1450 kpreis Appenzeller = 2270 data Bio = Bio | Konv
deriving (Eq, Show) data Art ikel =
Apfel Apfelsorte | Eier
| Kaese Kaesesorte | Schinken
| Salami | Milch Bio deriving (Eq, Show) data Menge = Stueck Int | Gramm Int | Lit er Double
deriving (Eq, Show) type Preis = Maybe Int preis :: Art ikel→Menge→Preis preis ( Apfel a) ( Stueck n) = Just (n∗apreis a) preis Eier ( Stueck n)= Just (n∗20) preis (Kaese k)(Gramm g) = Just ( round( fromIntegral g∗1000∗kpreis k)) preis Schinken (Gramm g) = Just ( div (g∗199) 100) preis Salami (Gramm g)= Just ( div (g∗159) 100) preis ( Milch bio) ( Lit er l ) =
Just ( round ( l∗case bio of Bio→119; Konv→69)) preis = Nothing
−−Addition von Mengen addiere :: Menge→Menge→Menge addiere ( Stueck i ) ( Stueck j )= Stueck ( i + j ) addiere (Gramm g) (Gramm h) = Gramm (g+ h) addiere ( Lit er l ) ( Lit er m) = Lit er ( l + m) addiere m n = error (” addiere: ” ++ show m++ ” und ” ++ show n)
−−Posten:
data Posten = Posten Art ikel Menge deriving (Eq, Show) cent :: Posten→Int
cent ( Posten a m) = fromMaybe 0 ( preis a m)−−gibt keinen Laufzeitfehler!
−−Lagerhaltung:
data Lager = Lager [ Posten]
deriving (Eq, Show) leeresLager :: Lager leeresLager = Lager [ ]
suche :: Art ikel→Lager→Maybe Menge suche a ( Lager ps) =
listToMaybe [ m | Posten la m←ps,la == a ] einlagern :: Art ikel→Menge→Lager→Lager einlagern a m ( Lager ps) =
let hinein a m [ ] = [ Posten a m]
hinein a m ( Posten al ml: l )
| a == al= ( Posten a ( addiere m ml) : l )
| otherwise = ( Posten al ml: hinein a m l ) in case preis a m of
Nothing→Lager ps
→Lager ( hinein a m ps) data Einkaufswagen = Ekwg [ Posten]
deriving (Eq, Show) leererWagen :: Einkaufswagen leererWagen = Ekwg [ ]
einkauf :: Art ikel→Menge→Einkaufswagen→Einkaufswagen einkauf a m (Ekwg ps)
| isJust ( preis a m) = Ekwg ( Posten a m: ps)
| otherwise = Ekwg ps kasse :: Einkaufswagen→Int kasse (Ekwg ps) = sum (map cent ps) kassenbon :: Einkaufswagen→String kassenbon ew@(Ekwg ps) =
”Bob’ s Aulde Grocery Shoppe\n\n” ++
” Art ikel Menge Preis\n” ++
”−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−\n” ++
concatMap art ikel ps ++
”=====================================\n” ++
”Summe: ” ++ formatR 31 (showEuro ( kasse ew)) art ikel :: Posten→String art ikel p@( Posten a m) =
formatL 20 (show a) ++
formatR 7 (menge m) ++
formatR 10 (showEuro ( cent p)) ++ ” \n”
menge :: Menge→String menge ( Stueck n) = show n++ ” St”
menge (Gramm g) = show g++ ” g. ” menge ( Lit er l ) = show l++ ” l . ” formatL :: Int→String→String formatL n st r = take n ( st r++ replicat e n ’ ’ ) formatR :: Int→String→String formatR n st r =
take n ( replicat e (n−length st r ) ’ ’ ++ st r ) showEuro :: Int→String showEuro i =
show ( div i 100) ++ ” . ” ++
show (mod ( div i 10) 10) ++
show (mod i 10)++ ” EU”
inventur :: Lager→Int inventur ( Lager l ) = sum (map cent l )
Lager
Lager Posten Artikel
module Shoppe4 where import Data.Maybe
−−Modellierung der Artikel.
data Apfelsorte = Boskoop | CoxOrange | GrannySmith deriving (Eq, Show) apreis :: Apfelsorte→Int apreis Boskoop = 55 apreis CoxOrange = 60 apreis GrannySmith = 50 data Kaesesorte = Gouda | Appenzeller
deriving (Eq, Show) kpreis :: Kaesesorte→Double kpreis Gouda = 1450 kpreis Appenzeller = 2270 data Bio = Bio | Konv
deriving (Eq, Show) data Art ikel =
Apfel Apfelsorte | Eier
| Kaese Kaesesorte | Schinken
| Salami | Milch Bio deriving (Eq, Show) data Menge = Stueck Int | Gramm Int | Lit er Double
deriving (Eq, Show) type Preis = Maybe Int preis :: Art ikel→Menge→Preis preis ( Apfel a) ( Stueck n) = Just (n∗apreis a) preis Eier ( Stueck n)= Just (n∗20) preis (Kaese k)(Gramm g) = Just ( round( fromIntegral g∗1000∗kpreis k)) preis Schinken (Gramm g) = Just ( div (g∗199) 100) preis Salami (Gramm g)= Just ( div (g∗159) 100) preis ( Milch bio) ( Lit er l ) =
Just ( round ( l∗case bio of Bio→119; Konv→69)) preis = Nothing
−−Addition von Mengen addiere :: Menge→Menge→Menge addiere ( Stueck i ) ( Stueck j )= Stueck ( i + j ) addiere (Gramm g) (Gramm h) = Gramm (g+ h) addiere ( Lit er l ) ( Lit er m) = Lit er ( l + m) addiere m n = error (” addiere: ” ++ show m++ ” und ” ++ show n)
−−Posten:
data Posten = Posten Art ikel Menge deriving (Eq, Show) cent :: Posten→Int
cent ( Posten a m) = fromMaybe 0 ( preis a m)−−gibt keinen Laufzeitfehler!
−−Lagerhaltung:
data Lager = Lager [ Posten]
deriving (Eq, Show) leeresLager :: Lager leeresLager = Lager [ ]
suche :: Art ikel→Lager→Maybe Menge suche a ( Lager ps) =
listToMaybe [ m | Posten la m←ps,la == a ] einlagern :: Art ikel→Menge→Lager→Lager einlagern a m ( Lager ps) =
let hinein a m [ ] = [ Posten a m]
hinein a m ( Posten al ml: l )
| a == al= ( Posten a ( addiere m ml) : l )
| otherwise = ( Posten al ml: hinein a m l ) in case preis a m of
Nothing→Lager ps
→Lager ( hinein a m ps) data Einkaufswagen = Ekwg [ Posten]
deriving (Eq, Show) leererWagen :: Einkaufswagen leererWagen = Ekwg [ ]
einkauf :: Art ikel→Menge→Einkaufswagen→Einkaufswagen einkauf a m (Ekwg ps)
| isJust ( preis a m) = Ekwg ( Posten a m: ps)
| otherwise = Ekwg ps kasse :: Einkaufswagen→Int kasse (Ekwg ps) = sum (map cent ps) kassenbon :: Einkaufswagen→String kassenbon ew@(Ekwg ps) =
”Bob’ s Aulde Grocery Shoppe\n\n” ++
” Art ikel Menge Preis\n” ++
”−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−\n” ++
concatMap art ikel ps ++
”=====================================\n” ++
”Summe: ” ++ formatR 31 (showEuro ( kasse ew)) art ikel :: Posten→String art ikel p@( Posten a m) =
formatL 20 (show a) ++
formatR 7 (menge m) ++
formatR 10 (showEuro ( cent p)) ++ ” \n”
menge :: Menge→String menge ( Stueck n) = show n++ ” St”
menge (Gramm g) = show g++ ” g. ” menge ( Lit er l ) = show l++ ” l . ” formatL :: Int→String→String formatL n st r = take n ( st r++ replicat e n ’ ’ ) formatR :: Int→String→String formatR n st r =
take n ( replicat e (n−length st r ) ’ ’ ++ st r ) showEuro :: Int→String showEuro i =
show ( div i 100) ++ ” . ” ++
show (mod ( div i 10) 10) ++
show (mod i 10)++ ” EU”
inventur :: Lager→Int inventur ( Lager l ) = sum (map cent l )
Einkaufswagen Lager
Lager Posten Artikel
module Shoppe4 where import Data.Maybe
−−Modellierung der Artikel.
data Apfelsorte = Boskoop | CoxOrange | GrannySmith deriving (Eq, Show) apreis :: Apfelsorte→Int apreis Boskoop = 55 apreis CoxOrange = 60 apreis GrannySmith = 50 data Kaesesorte = Gouda | Appenzeller
deriving (Eq, Show) kpreis :: Kaesesorte→Double kpreis Gouda = 1450 kpreis Appenzeller = 2270 data Bio = Bio | Konv
deriving (Eq, Show) data Art ikel =
Apfel Apfelsorte | Eier
| Kaese Kaesesorte | Schinken
| Salami | Milch Bio deriving (Eq, Show) data Menge = Stueck Int | Gramm Int | Lit er Double
deriving (Eq, Show) type Preis = Maybe Int preis :: Art ikel→Menge→Preis preis ( Apfel a) ( Stueck n) = Just (n∗apreis a) preis Eier ( Stueck n)= Just (n∗20) preis (Kaese k)(Gramm g) = Just ( round( fromIntegral g∗1000∗kpreis k)) preis Schinken (Gramm g) = Just ( div (g∗199) 100) preis Salami (Gramm g)= Just ( div (g∗159) 100) preis ( Milch bio) ( Lit er l ) =
Just ( round ( l∗case bio of Bio→119; Konv→69)) preis = Nothing
−−Addition von Mengen addiere :: Menge→Menge→Menge addiere ( Stueck i ) ( Stueck j )= Stueck ( i + j ) addiere (Gramm g) (Gramm h) = Gramm (g+ h) addiere ( Lit er l ) ( Lit er m) = Lit er ( l + m) addiere m n = error (” addiere: ” ++ show m++ ” und ” ++ show n)
−−Posten:
data Posten = Posten Art ikel Menge deriving (Eq, Show) cent :: Posten→Int
cent ( Posten a m) = fromMaybe 0 ( preis a m)−−gibt keinen Laufzeitfehler!
−−Lagerhaltung:
data Lager = Lager [ Posten]
deriving (Eq, Show) leeresLager :: Lager leeresLager = Lager [ ]
suche :: Art ikel→Lager→Maybe Menge suche a ( Lager ps) =
listToMaybe [ m | Posten la m←ps,la == a ] einlagern :: Art ikel→Menge→Lager→Lager einlagern a m ( Lager ps) =
let hinein a m [ ] = [ Posten a m]
hinein a m ( Posten al ml: l )
| a == al= ( Posten a ( addiere m ml) : l )
| otherwise = ( Posten al ml: hinein a m l ) in case preis a m of
Nothing→Lager ps
→Lager ( hinein a m ps) data Einkaufswagen = Ekwg [ Posten]
deriving (Eq, Show) leererWagen :: Einkaufswagen leererWagen = Ekwg [ ]
einkauf :: Art ikel→Menge→Einkaufswagen→Einkaufswagen einkauf a m (Ekwg ps)
| isJust ( preis a m) = Ekwg ( Posten a m: ps)
| otherwise = Ekwg ps kasse :: Einkaufswagen→Int kasse (Ekwg ps) = sum (map cent ps) kassenbon :: Einkaufswagen→String kassenbon ew@(Ekwg ps) =
”Bob’ s Aulde Grocery Shoppe\n\n” ++
” Art ikel Menge Preis\n” ++
”−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−\n” ++
concatMap art ikel ps ++
”=====================================\n” ++
”Summe: ” ++ formatR 31 (showEuro ( kasse ew)) art ikel :: Posten→String art ikel p@( Posten a m) =
formatL 20 (show a) ++
formatR 7 (menge m) ++
formatR 10 (showEuro ( cent p)) ++ ” \n”
menge :: Menge→String menge ( Stueck n) = show n++ ” St”
menge (Gramm g) = show g++ ” g. ” menge ( Lit er l ) = show l++ ” l . ” formatL :: Int→String→String formatL n st r = take n ( st r++ replicat e n ’ ’ ) formatR :: Int→String→String formatR n st r =
take n ( replicat e (n−length st r ) ’ ’ ++ st r ) showEuro :: Int→String showEuro i =
show ( div i 100) ++ ” . ” ++
show (mod ( div i 10) 10) ++
show (mod i 10)++ ” EU”
inventur :: Lager→Int inventur ( Lager l ) = sum (map cent l )
PI3 WS 18/19 10 [42]
Refakturierung im Einkaufsparadies:
Modularchitektur
Artikel
Shoppe
Lager Einkaufswagen
Posten
PI3 WS 18/19 11 [42]
Refakturierung im Einkaufsparadies I: Artikel
I Es wirdallesexportiert I Reine Datenmodellierung module A r t i k e l where
data Apfelsorte = Boskoop | CoxOrange | GrannySmith apreis :: Apfelsorte → Int
data Kaesesorte = Gouda | Appenzeller kpreis :: Kaesesorte →Double
dataMenge = Stueck Int | Gramm Int | L i t e r Double addiere :: Menge→Menge→Menge
PI3 WS 18/19 12 [42]
Refakturierung im Einkaufsparadies II: Posten
modulePosten(
Posten , a r t i k e l , menge, posten , cent , hinzu )where
I Implementiert ADTPosten:
data Posten = Posten { a r t i k e l :: A r t i k e l , menge :: Menge }
I Konstruktor wirdnichtexportiert I Garantierte Invariante:
I Postenhat immer die korrekte Menge zu Artikel posten :: A r t i k e l→Menge→Maybe Posten posten a m =
case p r e i s a mof
Just _ → Just (Posten a m) Nothing →Nothing
PI3 WS 18/19 13 [42]
Refakturierung im Einkaufsparadies III: Lager
module Lager(
Lager , leeresLager , einlagern , suche , l i s t e , inventur ) where import A r t i k e l import Posten
I Implementiert ADTLager dataLager
I Signatur der exportierten Funktionen:
leeresLager :: Lager
einlagern :: A r t i k e l→Menge→ Lager→ Lager suche :: A r t i k e l→ Lager→Maybe Menge
l i s t e :: Lager→ [ ( Artikel , Menge) ] inventur :: Lager→ Int
I GarantierteInvariante:
I Lagerenthält keine doppelten Artikel
PI3 WS 18/19 14 [42]
Refakturierung IV: Einkaufswagen
moduleEinkaufswagen(
Einkaufswagen , leererWagen , einkauf , kasse , kassenbon ) where
IImplementiert ADTEinkaufswagen data Einkaufswagen = Ekwg [ Posten ] IGarantierte Invariante:
IKorrekte Menge zu Artikel im Einkaufswagen einkauf :: A r t i k e l→Menge
→ Einkaufswagen
→ Einkaufswagen einkauf a m (Ekwg ps ) =
case posten a mof Just p→ Ekwg (p : ps ) Nothing→Ekwg ps
INutzt dazu ADTPosten
PI3 WS 18/19 15 [42]
Refakturierung im Einkaufsparadies V: Hauptmodul
moduleShoppewhere import A r t i k e l import Lager import Einkaufswagen
I Nutzt andere Module l0= leeresLager
l1= einlagern ( Apfel Boskoop) (Stueck 1) l0 l2= einlagern Schinken (Gramm 50) l1 l3= einlagern (Milch Bio) ( L i t e r 6) l2 l4= einlagern ( Apfel Boskoop) (Stueck 4) l3 l5= einlagern (Milch Bio) ( L i t e r 4) l4 l6= einlagern Schinken (Gramm 50) l5
PI3 WS 18/19 16 [42]
Benutzung von ADTs
I OperationenundTypenmüssenimportiertwerden
I Möglichkeiten des Imports:
I Allesimportieren
I Nur bestimmteOperationen und Typenimportieren
I BestimmteTypen und Operationennichtimportieren
PI3 WS 18/19 17 [42]
Importe in Haskell
I Syntax:
import [qualified] M [ as N] [hiding] [ ( Bezeichner ) ] I Bezeichnergeben an,wasimportiert werden soll:
IOhne Bezeichner wirdallesimportiert
IMithidingwerden Bezeichnernichtimportiert
I Für jeden exportierten BezeichnerfausMwird importiert
I fundqualifizierterBezeichnerM. f
I qualified:nur qualifizierterBezeichnerM. f
IUmbenennung bei Import mitas(dannN. f)
IKlasseninstanzen und Typsynonyme werden immer importiert I Alle Importe stehen immer amAnfangdes Moduls
PI3 WS 18/19 18 [42]
Beispiel
moduleM(a , b) where. . .
Import(e) Bekannte Bezeichner
importM a,b,M. a,M. b
importM() (nothing)
importM(a) a,M. a
import qualifiedM M. a,M. b import qualifiedM() (nothing) import qualifiedM(a) M. a importMhiding () a,b,M. a,M. b importMhiding (a) b,M. b import qualifiedMhiding () M. a,M. b import qualifiedMhiding (a) M. b importM as B a,b,B. a,B. b
importM as B(a) a,B. a
import qualifiedM as B B. a,B. b
Quelle: Haskell98-Report, Sect. 5.3.4
PI3 WS 18/19 19 [42]
Ein typisches Beispiel
I Modul implementiert Funktion, die auch importiert wird I Umbenennung nicht immer praktisch
I Qualifizierter Import führt zulangenBezeichnern
I Einkaufswagenimplementiert Funktionen a r t i k e lundmenge, die auch ausPostenimportiert werden:
importPosten hiding ( a r t i k e l , menge) import qualified Posten as P( a r t i k e l , menge) a r t i k e l p =
formatL 20 (show (P. a r t i k e l p)) ++ formatR 7 (menge (P.menge p)) ++ formatR 10 (showEuro ( cent p)) ++ "\n"
PI3 WS 18/19 20 [42]
Schnittstelle vs. Implementation
I GleicheSchnittstellekann unterschiedlicheImplementationenhaben
I Beispiel: (endliche) Abbildungen
PI3 WS 18/19 21 [42]
Endliche Abbildungen
I Viel gebraucht, oft in Abwandlungen (Hashtables, Sets, Arrays) I Abstrakter Datentyp fürendliche Abbildungen:
IDatentyp dataMapα β
ILeere Abbildung:
empty :: Mapα β
IAbbildung auslesen:
lookup :: Ordα⇒α→Mapα β→Maybeβ
IAbbildung ändern:
i n s e r t :: Ordα⇒α→β→Mapα β→Mapα β
IAbbildung löschen:
delete :: Ordα⇒α→Mapα β→Mapα β
PI3 WS 18/19 22 [42]
Eine naheliegende Implementation
I Modellierung als Haskell-Funktion:
dataMapα β= Map (α→Maybeβ) I Damit einfacheslookup,i n s e r t,delete:
empty = Map (λx→ Nothing) lookup a (Map s ) = s a i n s e r t a b (Map s ) =
Map (λx→ i f x == athen Just b else s x) delete a (Map s ) =
Map (λx→ i f x == athenNothing else s x) I Instanzen von Eq, Shownicht möglich
I Speicherleck: überschriebene Zellen werden nicht freigegeben
Endliche Abbildungen: Anwendungsbeispiel
I Lager als endliche Abbildung:
dataLager = Lager (M.Map A r t i k e l Menge) I Artikel suchen:
suche :: A r t i k e l→Lager→Maybe Menge suche a (Lager l ) = M. lookup a l I Ins Lager hinzufügen:
einlagern :: A r t i k e l→Menge→Lager→Lager einlagern a m (Lager l ) =
caseposten a mof
Just _ →caseM. lookup a l of
Just q →Lager (M. i n s e r t a ( addiere m q) l ) Nothing→Lager (M. i n s e r t a m l )
Nothing→Lager l
I Für Inventur fehlt Möglichkeit zurIteration I Daher: Map alsAssoziativliste
Map als sortierte Assoziativliste
dataMapα β= Map { toList :: [ (α, β) ] } I Einfache Implementierung:
i n s e r t :: Ordα⇒α→β→Mapα β→Mapα β i n s e r t a v (Map s ) = Map ( i n s e r t ’ s )where
i n s e r t ’ [ ] = [ ( a , v ) ]
i n s e r t ’ s0@((b , w) : s ) | a> b = (b , w) : i n s e r t ’ s
| a == b = (a , v ) : s
| a< b = (a , v ) : s0 I Zusatzfunktionalität:
I Iteration (SelektortoList)
I Instanzen vonEqundShow(abgeleitet)
I . . . ist aberineffizient(Zugriff/Löschen inO(n)) I Deshalb:balancierte Bäume
PI3 WS 18/19 25 [42]
AVL-Bäume und Balancierte Bäume
AVL-Bäume
Ein Baum istausgeglichen, wenn I alle Unterbäume ausgeglichen sind, und
I der Höhenunterschied zwischen zwei Unterbäumen höchstens eins beträgt.
Balancierte Bäume
Ein Baum istbalanciert, wenn I alle Unterbäume balanciert sind, und I für den linken und rechten Unterbauml,rgilt:
size(l) ≤ w·size(r) (1)
size(r) ≤ w·size(l) (2)
w—Gewichtung(Parameter des Algorithmus)
PI3 WS 18/19 26 [42]
Implementation von balancierten Bäumen
I Der Datentyp data Treeα= Null
| Node Int (Treeα) α(Treeα) derivingEq I Gewichtung (Parameter des Algorithmus):
weight :: Int
I Hilfskonstruktor, setzt Größe (l,rbalanciert) node :: Tree α→α→Treeα→ Treeα I Selektor: Größe des Baumes (0 fürNull)
s i z e :: Tree α→ Int
PI3 WS 18/19 27 [42]
Implementation von balancierten Bäumen
I Hilfskonstruktor, balanciert ggf. neu aus:
mkNode :: Treeα→α→ Treeα→ Treeα
IVoraussetzungen:
I l,rbalanciert
IGesamtbaum “fast” balanciert:
size(l)−1 ≤ w·size(r) (3)
size(r)−1 ≤ w·size(l) (4)
IWird beim Löschen und Einfügen benutzt
PI3 WS 18/19 28 [42]
Balance sicherstellen
I Problem:
Nach Löschen oder Einfügen zu großes Ungewicht
I Lösung:
Rotierender Unterbäume
sx
sy sz
PI3 WS 18/19 29 [42]
Linksrotation
xt
yt zt
=⇒
xt yt
zt
r o t l :: Treeα→ Treeα
r o t l (Node _ xt y (Node _ yt x zt )) = node (node xt y yt ) x zt
PI3 WS 18/19 30 [42]
Rechtsrotation
xt yt
zt ⇒
xt
yt zt
r o t r :: Treeα→ Treeα
r o t r (Node _ (Node _ ut y vt ) x r t ) = node ut y (node vt x r t )
PI3 WS 18/19 31 [42]
Balanciertheit sicherstellen
I Fall 1: Äußerer Unterbaum zu groß I Lösung: Linksrotation
sx sy
sz
⇒
PI3 WS 18/19 32 [42]
Balanciertheit sicherstellen
I Fall 2: Innerer Unterbaum zu groß oder gleich groß
I Reduktion auf vorherigen Fall durch Rechtsrotation des Unterbaumes
sx sy
sz
⇒
sx sy
sz
⇒. . .
PI3 WS 18/19 33 [42]
Balance sicherstellen
I Hilfsfunktion:Balanceeines Baumes bias :: Treeα→ Ordering bias Null = EQ
bias (Node _ l t _ r t ) = compare ( s i z e l t ) ( s i z e r t ) I Zu implementieren:mkNode lt y rt
IVoraussetzung:l t,r tbalanciert
IKonstruiert neuen balancierten Baum mit Knoteny I Fallunterscheidung:
I r tzugroß, zweiUnterfälle:
ILinker Unterbaum vonr tkleiner (Fall 1):bias r t == LT
ILinker Unterbaum vonr tgrößer/gleich groß (Fall 2):bias r t == EQ, bias r t == GT
I l tzugroß, zweiUnterfälle(symmetrisch).
PI3 WS 18/19 34 [42]
Konstruktion eines ausgeglichenen Baumes
I Voraussetzung:l t,r tbalanciert mkNode :: Treeα→α→Treeα→ Treeα mkNode l t x r t
| l s + r s<2 = node l t x r t
| weight∗ l s <r s =
i f bias r t == LTthen r o t l (node l t x r t ) else r o t l (node l t x ( r o t r r t ))
| l s>weight∗ r s =
i f bias l t == GTthen r o t r (node l t x r t ) else r o t r (node ( r o t l l t ) x r t )
| otherwise = node l t x r t where l s = s i z e l t ; r s= s i z e r t
PI3 WS 18/19 35 [42]
Balancierte Bäume als Maps
I Endliche Abbildung: Bäume mit(key , value )Paaren I lookup ’liest Element aus:
lookup ’ :: Ordα⇒α→ Tree (α,β)→Maybeβ I i n s e r t ’fügt neues Element ein:
i n s e r t ’ :: Ordα⇒α→β→ Tree (α, β)→Tree (α, β) i n s e r t ’ k v Null = node Null (k , v) Null
i n s e r t ’ k v (Node n l a@(kn , _) r )
| k<kn = mkNode ( i n s e r t ’ k v l ) a r
| k == kn = Node n l (k , v) r
| k>kn = mkNode l a ( i n s e r t ’ k v r ) I remove ’löscht ein Element
IBenötigt Hilfsfunktion join :: Treeα→Treeα→Treeα
PI3 WS 18/19 36 [42]
Zusammenfassung Balancierte Bäume
I Verkapselung des Datentypen:
dataMapα β= Map { tree :: Tree (α, β) } i n s e r t :: Ordα⇒α→β→Mapα β→Mapα β i n s e r t k v (Map t ) = Map ( i n s e r t ’ k v t )
I Auslesen, einfügen und löschen: logarithmischer Aufwand (O(logn)) I Fold: linearer Aufwand (O(n))
I Guten durchschnittlichen Aufwand
I Auch in der Haskell-Bücherei:Data .Map(mit vielen weiteren Funktionen)
PI3 WS 18/19 37 [42]
Benchmarking: Setup
I Wieschnellsind die Implementationenwirklich?
I Benchmarking: nicht trivial
IVerzögerte Auswertung und optimierender Compiler
IMessen wir dasrichtige?
IBenchmarking-Tool: Criterion
I Setup:Map Int Stringmit 50000 zufälligen Einträgen erzeugen I Darin:
IEinmal zufällig lesen (lookup), schreiben (i n s e r t), löschen (delete)
ISequenz aus fünfmal löschen und schreiben, zweihundertmal lesen (mixed)
PI3 WS 18/19 38 [42]
Benchmarking: Resultate
create lookup i n s e r t delete mixed (1) 358.4 ms
7.483 ms
2.66 ms 134.70µs
10.75 ns 159.70 ps
10.90 ns 170.90 ps
1.83 ms 111.80µs (2) 6.20 s
37.59 ms
11.57µs 351.3 ns
133.8µs 2.36µs
148.40µs 1.99µs
5.67 ms 128.10µs (3) 470.00 ms
2.69 ms
265.10 ns 4.54 ns
138.90µs 2.35µs
137.60µs 3.00µs
2.18 ms 81.68µs (4) 392.7 ms
5.02 ms
189.2 ns 13.41 ns
135.7µs 2.00µs
134.50µs 3.10µs
2.08 ms 80.22µs
(1)MapFun, (2)MapList, (3)MapWeighted, (4)Data .Map. Lazy Einträge: durchschnittl. Ausführungszeit, Standardabweichung
Defizite von Haskells Modulsystem
I Signatur ist nurimplizit
IExportliste enthält nur Bezeichner
IWünschenswert: Signatur an der Exportliste annotierbar, oder Signaturen in separater Datei
IIn Java:Interfaces
I Klasseninstanzen werdenimmerexportiert.
I KeinPaket-System
ADTs vs. Objekte
I ADTs (Haskell):TypplusOperationen I Objekte (z.B. Java):Interface,Methoden.
I Gemeinsamkeiten:
I Verkapselung(information hiding) der Implementation I Unterschiede:
I Objekte habeninternen Zustand, ADTs sindreferentiell transparent;
I Objekte habenKonstruktoren, ADTs nicht (Konstruktoren nicht unterscheidbar)
I Vererbungsstrukturauf Objekten (Verfeinerungfür ADTs)
I Java:interfaceeigenes Sprachkonstrukt
I Java:packagesfür Sichtbarkeit
PI3 WS 18/19 41 [42]
Zusammenfassung
I Abstrakte Datentypen(ADTs):
IBesteht ausTypenundOperationendarauf
I Realisierung in Haskell durchModule
I Beispieldatentypen: endliche Abbildungen
I Nächste Vorlesung: ADTs durchEigenschaftenspezifizieren
PI3 WS 18/19 42 [42]