Praktische Informatik 3: Funktionale Programmierung Vorlesung 8 vom 21.12.2020: Abstrakte Datentypen
Christoph Lüth
Wintersemester 2020/21
11:51:27 2021-02-22 1 [44]
Organisatorisches
IAbgabe des 7. Übungsblattes in Gruppen zudreiStudenten.
IBittejetzteine Gruppe suchen!
IKlausurtermine:
IKlausur: 03.02.2020, 10:00/11:30/15:00 IWiederholungstermin: 21.04.2020, 10:00/11:30/15:00
PI3 WS 20/21 2 [44]
Fahrplan
ITeil I: Funktionale Programmierung im Kleinen ITeil II: Funktionale Programmierung im Großen
IAbstrakte Datentypen ISignaturen und Eigenschaften
ITeil III: Funktionale Programmierung im richtigen Leben
PI3 WS 20/21 3 [44]
Inhalt
IAbstrakte Datentypen IAllgemeine Einführung IRealisierung in Haskell IBeispiele
PI3 WS 20/21 4 [44]
I. Modularisierung und Abstrakte Datentypen
PI3 WS 20/21 5 [44]
Warum Modularisierung?
IÜbersichtlichkeit der Module Lesbarkeit
IGetrennte Übersetzung technischeHandhabbarkeit
IVerkapselung konzeptionelleHandhabbarkeit
PI3 WS 20/21 6 [44]
Abstrakte Datentypen
Definition (Abstrakter Datentyp)
Einabstrakter Datentyp(ADT) besteht aus einem (oder mehreren)Typenund Operationendarauf, mit folgenden Eigenschaften:
1Werte des Typen können nur über die Operationenerzeugtwerden
2Eigenschaften von Werten des Typen werden nur über die Operationenbeobachtet 3Einhaltung vonInvariantenüber dem Typ kann garantiert werden
Implementationvon ADTs in einer Programmiersprache:
Ibenötigt Möglichkeit derKapselung(Einschränkung der Sichtbarkeit) Ibspw. durchModuleoderObjekte
PI3 WS 20/21 7 [44]
ADTs vs. algebraische Datentypen
IAlgebraische Datentypen IFrei erzeugtdurchKonstruktoren IKeine Einschränkungen
IInsbesondere keine Gleichheiten der Konstruktoren ([ ]6=x:xs,x:ls6=y:lsetc.) IADTs:
IKeine ausgezeichneten Konstruktoren IEinschränkungen und Invarianten möglich IGleichheiten möglich
PI3 WS 20/21 8 [44]
ADTs vs. Objekte
IADTs (z.B. Haskell):TypplusOperationen IObjekte (z.B. Java):Interface,Methoden.
IGemeinsamkeiten:
IVerkapselung(information hiding) der Implementation IUnterschiede:
IObjekte habeninternen Zustand, ADTs sindreferentiell transparent;
IObjekte habenKonstruktoren, ADTs nicht
IVererbungsstrukturauf Objekten (Verfeinerungfür ADTs) IJava:interfaceeigenes Sprachkonstrukt
IJava:packagesfür Sichtbarkeit
PI3 WS 20/21 9 [44]
ADTs in Haskell: Module
IEinschränkung der Sichtbarkeit durchVerkapselung IModul: Kleinste verkapselbareEinheit
IEinModulumfaßt:
IDefinitionenvon Typen, Funktionen, Klassen IDeklarationder nach außensichtbarenDefinitionen
IGleichzeitig: Modul ˆ= Übersetzungseinheit (getrennte Übersetzung)
PI3 WS 20/21 10 [44]
Module: Syntax
ISyntax:
moduleName(Bezeichner)whereRumpf IBezeichner können leer sein (dann wird alles exportiert) IBezeichner sind:
ITypen:T,T(c1,..., cn),T(..) IKlassen:C,C(f1,...,fn),C(..)
IAndere Bezeichner:Werte,Felder,Klassenmethoden IImportierteModule:moduleM
ITypsynonyme und Klasseninstanzen bleiben sichtbar IModule könnenrekursivsein(don’t try at home)
PI3 WS 20/21 11 [44]
Refakturierung im Einkaufsparadies
module Shoppe4 where import Data.Maybe
−−Modellierung der Artikel.
data Apfelsorte = Boskoop | CoxOrange | GrannySmithderiving (Eq, Show) apreis :: Apfelsorte→Int apreis Boskoop = 55 apreis CoxOrange = 60 apreis GrannySmith = 50 data Kaesesorte = Gouda | Appenzellerderiving (Eq, Show) kpreis :: Kaesesorte→Double kpreis Gouda = 1450 kpreis Appenzeller = 2270 data Bio = Bio | Konv
deriving (Eq, Show) data Artikel =
Apfel Apfelsorte | Eier
| Kaese Kaesesorte | Schinken
| Salami | Milch Bio deriving (Eq, Show) data Menge = Stueck Int | Gramm Int | Liter Double
deriving (Eq, Show) type Preis = Maybe Int preis :: Artikel→Menge→Preis preis (Apfel a) (Stueck n) = J ust (n∗apreis a) preis Eier (Stueck n)= J ust (n∗20) preis (Kaese k)(Gramm g) = J ust (round( fromIntegral g∗1000∗kpreis k)) preis Schinken (Gramm g) = J ust ( div (g∗199) 100) preis Salami (Gramm g)= J ust ( div (g∗159) 100) preis (Milch bio) ( Liter l ) =
J ust (round ( l∗case bio of Bio→119; Konv→69)) preis = Nothing
−−Addition von Mengenaddiere :: Menge→Menge→Menge addiere (Stueck i ) (Stueck j )= Stueck ( i + j ) addiere (Gramm g) (Gramm h) = Gramm (g+ h) addiere ( Liter l ) ( Liter m) = Liter ( l +m) addiere m n = error (”addiere: ”++ show m++ ” und ”++ show n)
−−Posten:
data Posten = Posten Artikel Mengederiving (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 :: Artikel→Lager→Maybe Menge suche a (Lager ps) =
listToMaybe [ m | Posten la m←ps,la == a ] einlagern :: Artikel→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 :: Artikel→Menge→Einkaufswagen→Einkaufswagen einkauf a m (Ekwg ps)
| isJ ust ( 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”++
”Artikel Menge Preis\ n”++
”−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−\ n”++
concatMap artikel ps ++
”=====================================\ n”++
”Summe:”++ formatR 31 (showEuro (kasse ew)) artikel :: Posten→String artikel 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 ( Liter l ) = show l++ ” l .”
formatL :: Int→String→String formatL n str = take n ( str++ replicate n ’ ’) formatR :: Int→String→String formatR n str =take n ( replicate (n−length str) ’ ’++ str) 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 | GrannySmithderiving (Eq, Show) apreis :: Apfelsorte→Int apreis Boskoop = 55 apreis CoxOrange = 60 apreis GrannySmith = 50 data Kaesesorte = Gouda | Appenzellerderiving (Eq, Show) kpreis :: Kaesesorte→Double kpreis Gouda = 1450 kpreis Appenzeller = 2270 data Bio = Bio | Konv
deriving (Eq, Show) data Artikel =
Apfel Apfelsorte | Eier
| Kaese Kaesesorte | Schinken
| Salami | Milch Bio deriving (Eq, Show) data Menge = Stueck Int | Gramm Int | Liter Double
deriving (Eq, Show) type Preis = Maybe Int preis :: Artikel→Menge→Preis preis (Apfel a) (Stueck n) = J ust (n∗apreis a) preis Eier (Stueck n)= J ust (n∗20) preis (Kaese k)(Gramm g) = J ust (round( fromIntegral g∗1000∗kpreis k)) preis Schinken (Gramm g) = J ust ( div (g∗199) 100) preis Salami (Gramm g)= J ust ( div (g∗159) 100) preis (Milch bio) ( Liter l ) =
J ust (round ( l∗case bio of Bio→119; Konv→69)) preis = Nothing
−−Addition von Mengenaddiere :: Menge→Menge→Menge addiere (Stueck i ) (Stueck j )= Stueck ( i + j ) addiere (Gramm g) (Gramm h) = Gramm (g+ h) addiere ( Liter l ) ( Liter m) = Liter ( l +m) addiere m n = error (”addiere: ”++ show m++ ” und ”++ show n)
−−Posten:
data Posten = Posten Artikel Mengederiving (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 :: Artikel→Lager→Maybe Menge suche a (Lager ps) =
listToMaybe [ m | Posten la m←ps,la == a ] einlagern :: Artikel→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 :: Artikel→Menge→Einkaufswagen→Einkaufswagen einkauf a m (Ekwg ps)
| isJ ust ( 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”++
”Artikel Menge Preis\ n”++
”−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−\ n”++
concatMap artikel ps ++
”=====================================\ n”++
”Summe:”++ formatR 31 (showEuro (kasse ew)) artikel :: Posten→String artikel 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 ( Liter l ) = show l++ ” l .”
formatL :: Int→String→String formatL n str = take n ( str++ replicate n ’ ’) formatR :: Int→String→String formatR n str =take n ( replicate (n−length str) ’ ’++ str) 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 | GrannySmithderiving (Eq, Show) apreis :: Apfelsorte→Int apreis Boskoop = 55 apreis CoxOrange = 60 apreis GrannySmith = 50 data Kaesesorte = Gouda | Appenzellerderiving (Eq, Show) kpreis :: Kaesesorte→Double kpreis Gouda = 1450 kpreis Appenzeller = 2270 data Bio = Bio | Konv
deriving (Eq, Show) data Artikel =
Apfel Apfelsorte | Eier
| Kaese Kaesesorte | Schinken
| Salami | Milch Bio deriving (Eq, Show) data Menge = Stueck Int | Gramm Int | Liter Double
deriving (Eq, Show) type Preis = Maybe Int preis :: Artikel→Menge→Preis preis (Apfel a) (Stueck n) = J ust (n∗apreis a) preis Eier (Stueck n)= J ust (n∗20) preis (Kaese k)(Gramm g) = J ust (round( fromIntegral g∗1000∗kpreis k)) preis Schinken (Gramm g) = J ust ( div (g∗199) 100) preis Salami (Gramm g)= J ust ( div (g∗159) 100) preis (Milch bio) ( Liter l ) =
J ust (round ( l∗case bio of Bio→119; Konv→69)) preis = Nothing
−−Addition von Mengenaddiere :: Menge→Menge→Menge addiere (Stueck i ) (Stueck j )= Stueck ( i + j ) addiere (Gramm g) (Gramm h) = Gramm (g+ h) addiere ( Liter l ) ( Liter m) = Liter ( l +m) addiere m n = error (”addiere: ”++ show m++ ” und ”++ show n)
−−Posten:
data Posten = Posten Artikel Mengederiving (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 :: Artikel→Lager→Maybe Menge suche a (Lager ps) =
listToMaybe [ m | Posten la m←ps,la == a ] einlagern :: Artikel→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 :: Artikel→Menge→Einkaufswagen→Einkaufswagen einkauf a m (Ekwg ps)
| isJ ust ( 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”++
”Artikel Menge Preis\ n”++
”−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−\ n”++
concatMap artikel ps ++
”=====================================\ n”++
”Summe:”++ formatR 31 (showEuro (kasse ew)) artikel :: Posten→String artikel 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 ( Liter l ) = show l++ ” l .”
formatL :: Int→String→String formatL n str = take n ( str++ replicate n ’ ’) formatR :: Int→String→String formatR n str =take n ( replicate (n−length str) ’ ’++ str) 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 | GrannySmithderiving (Eq, Show) apreis :: Apfelsorte→Int apreis Boskoop = 55 apreis CoxOrange = 60 apreis GrannySmith = 50 data Kaesesorte = Gouda | Appenzellerderiving (Eq, Show) kpreis :: Kaesesorte→Double kpreis Gouda = 1450 kpreis Appenzeller = 2270 data Bio = Bio | Konv
deriving (Eq, Show) data Artikel =
Apfel Apfelsorte | Eier
| Kaese Kaesesorte | Schinken
| Salami | Milch Bio deriving (Eq, Show) data Menge = Stueck Int | Gramm Int | Liter Double
deriving (Eq, Show) type Preis = Maybe Int preis :: Artikel→Menge→Preis preis (Apfel a) (Stueck n) = J ust (n∗apreis a) preis Eier (Stueck n)= J ust (n∗20) preis (Kaese k)(Gramm g) = J ust (round( fromIntegral g∗1000∗kpreis k)) preis Schinken (Gramm g) = J ust ( div (g∗199) 100) preis Salami (Gramm g)= J ust ( div (g∗159) 100) preis (Milch bio) ( Liter l ) =
J ust (round ( l∗case bio of Bio→119; Konv→69)) preis = Nothing
−−Addition von Mengenaddiere :: Menge→Menge→Menge addiere (Stueck i ) (Stueck j )= Stueck ( i + j ) addiere (Gramm g) (Gramm h) = Gramm (g+ h) addiere ( Liter l ) ( Liter m) = Liter ( l +m) addiere m n = error (”addiere: ”++ show m++ ” und ”++ show n)
−−Posten:
data Posten = Posten Artikel Mengederiving (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 :: Artikel→Lager→Maybe Menge suche a (Lager ps) =
listToMaybe [ m | Posten la m←ps,la == a ] einlagern :: Artikel→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 :: Artikel→Menge→Einkaufswagen→Einkaufswagen einkauf a m (Ekwg ps)
| isJ ust ( 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”++
”Artikel Menge Preis\ n”++
”−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−\ n”++
concatMap artikel ps ++
”=====================================\ n”++
”Summe:”++ formatR 31 (showEuro (kasse ew)) artikel :: Posten→String artikel 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 ( Liter l ) = show l++ ” l .”
formatL :: Int→String→String formatL n str = take n ( str++ replicate n ’ ’) formatR :: Int→String→String formatR n str =take n ( replicate (n−length str) ’ ’++ str) 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 | GrannySmithderiving (Eq, Show) apreis :: Apfelsorte→Int apreis Boskoop = 55 apreis CoxOrange = 60 apreis GrannySmith = 50 data Kaesesorte = Gouda | Appenzellerderiving (Eq, Show) kpreis :: Kaesesorte→Double kpreis Gouda = 1450 kpreis Appenzeller = 2270 data Bio = Bio | Konv
deriving (Eq, Show) data Artikel =
Apfel Apfelsorte | Eier
| Kaese Kaesesorte | Schinken
| Salami | Milch Bio deriving (Eq, Show) data Menge = Stueck Int | Gramm Int | Liter Double
deriving (Eq, Show) type Preis = Maybe Int preis :: Artikel→Menge→Preis preis (Apfel a) (Stueck n) = J ust (n∗apreis a) preis Eier (Stueck n)= J ust (n∗20) preis (Kaese k)(Gramm g) = J ust (round( fromIntegral g∗1000∗kpreis k)) preis Schinken (Gramm g) = J ust ( div (g∗199) 100) preis Salami (Gramm g)= J ust ( div (g∗159) 100) preis (Milch bio) ( Liter l ) =
J ust (round ( l∗case bio of Bio→119; Konv→69)) preis = Nothing
−−Addition von Mengenaddiere :: Menge→Menge→Menge addiere (Stueck i ) (Stueck j )= Stueck ( i + j ) addiere (Gramm g) (Gramm h) = Gramm (g+ h) addiere ( Liter l ) ( Liter m) = Liter ( l +m) addiere m n = error (”addiere: ”++ show m++ ” und ”++ show n)
−−Posten:
data Posten = Posten Artikel Mengederiving (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 :: Artikel→Lager→Maybe Menge suche a (Lager ps) =
listToMaybe [ m | Posten la m←ps,la == a ] einlagern :: Artikel→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 :: Artikel→Menge→Einkaufswagen→Einkaufswagen einkauf a m (Ekwg ps)
| isJ ust ( 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”++
”Artikel Menge Preis\ n”++
”−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−\ n”++
concatMap artikel ps ++
”=====================================\ n”++
”Summe:”++ formatR 31 (showEuro (kasse ew)) artikel :: Posten→String artikel 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 ( Liter l ) = show l++ ” l .”
formatL :: Int→String→String formatL n str = take n ( str++ replicate n ’ ’) formatR :: Int→String→String formatR n str =take n ( replicate (n−length str) ’ ’++ str) 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 20/21 12 [44]
Refakturierung im Einkaufsparadies: Modularchitektur
Artikel
Shoppe
Lager Einkaufswagen
Posten
PI3 WS 20/21 13 [44]
Refakturierung im Einkaufsparadies I: Artikel
IEs wirdallesexportiert IReine Datenmodellierung
moduleArtikelwhere
data Apfelsorte=Boskoop | CoxOrange | GrannySmith apreis :: Apfelsorte →Int
data Kaesesorte=Gouda | Appenzeller kpreis :: Kaesesorte →Double
data Menge=Stueck Int | Gramm Int | Liter Double addiere :: Menge→Menge→Menge
PI3 WS 20/21 14 [44]
Refakturierung im Einkaufsparadies II: Posten
modulePosten(
Posten, artikel, menge, posten, cent, hinzu)where
IImplementiert ADTPosten:
data Posten=Posten Artikel Menge deriving(Eq, Show) artikel :: Posten→ Artikel artikel (Posten a _)=a IKonstruktor wirdnichtexportiert
IInvariante:Postenhat immer die korrekte Menge zu Artikel posten :: Artikel→Menge→Maybe Posten posten a m=
casepreis a mof
Just _ →Just (Posten a m) Nothing→Nothing
Refakturierung im Einkaufsparadies III: Lager
moduleLager(
Lager, leeresLager, einlagern, suche, liste, inventur )where importArtikel importPosten
IImplementiert ADTLager dataLager
ISignatur der exportierten Funktionen:
leeresLager :: Lager
einlagern :: Artikel→Menge→Lager→Lager suche a (Lager l)= M.lookup a l
liste (Lager m)=M.toList m
inventur=sum◦map (fromJust◦uncurry preis)◦liste IInvariante:Lagerenthält keine doppelten Artikel
Refakturierung im Einkaufsparadies IV: Einkaufswagen
moduleEinkaufswagen(
Einkaufswagen, leererWagen, einkauf, kasse, kassenbon )where
IADT durchVerkapselung:
data Einkaufswagen=Ekwg [Posten]
deriving(Eq, Show) IEin Typsynmonym würde exportiert
IInvariante: Korrekte Menge zu Artikel im Einkaufswagen einkauf :: Artikel→ Menge→Einkaufswagen
→Einkaufswagen einkauf a m (Ekwg ps)=caseposten a mof
Just p→ Ekwg (p: ps) Nothing →Ekwg ps INutzt dazu ADTPosten
PI3 WS 20/21 17 [44]
Refakturierung im Einkaufsparadies V: Hauptmodul
moduleShoppewhere importArtikel importLager importEinkaufswagen
INutzt andere Module w0= leererWagen
w1= einkauf ( Apfel Boskoop) (Stueck 3) w0 w2= einkauf Schinken (Gramm 50) w1 w3= einkauf (Milch Bio) ( L i t e r 1) w2 w4= einkauf Schinken (Gramm 50) w3
PI3 WS 20/21 18 [44]
Benutzung von ADTs
IOperationenundTypenmüssenimportiertwerden
IMöglichkeiten des Imports:
IAllesimportieren
INur bestimmteOperationen und Typenimportieren IBestimmteTypen und Operationennichtimportieren
PI3 WS 20/21 19 [44]
Importe in Haskell
ISyntax:
import[qualified] M [as N] [hiding][(Bezeichner)]
IBezeichnergeben an,wasimportiert werden soll:
IOhne Bezeichner wirdallesimportiert IMithidingwerden Bezeichnernichtimportiert IFür jeden exportierten BezeichnerfausMwird importiert
IfundqualifizierterBezeichnerM.f Iqualified:nur qualifizierterBezeichnerM.f IUmbenennung bei Import mitas(dannN.f)
IKlasseninstanzen und Typsynonyme werden immer importiert IAlle Importe stehen immer amAnfangdes Moduls
PI3 WS 20/21 20 [44]
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 20/21 21 [44]
Ein typisches Beispiel
IModul implementiert Funktion, die auch importiert wird IUmbenennung nicht immer praktisch
IQualifizierter Import führt zulangenBezeichnern
IEinkaufswagenimplementiert Funktionenartikelundmenge, die auch ausPosten importiert werden:
importPostenhiding(artikel, menge) import qualifiedPosten as P(artikel, menge) artikel :: Posten→String
artikel p=
formatL 20 (show (P.artikel p)) ++ formatR 7 (menge (P.menge p))++ formatR 10 (showEuro (cent p))++"\n"
PI3 WS 20/21 22 [44]
Was zum Nachdenken
Übung 8.1: Import Warum schreibt man
importPreludehiding(repeat)
und was bewirkt das? (Hinweis:Preludeist das Modul der vordefinierten Funktionen.) Lösung:Die Import-Anweisung import alle vordefinierten Funktionenbis aufrepeat.
Dadurch können wirrepeatselber (anders) definieren.
PI3 WS 20/21 23 [44]
II. Schnittstelle vs. Implementation
PI3 WS 20/21 24 [44]
Schnittstelle vs. Implementation
IGleicheSchnittstellekann unterschiedlicheImplementationenhaben
IBeispiel: (endliche) Abbildungen
PI3 WS 20/21 25 [44]
Endliche Abbildungen
IViel gebraucht, oft in Abwandlungen (Hashtables, Sets, Arrays) IAbstrakter Datentyp fürendliche Abbildungen:
IDatentyp dataMapα β ILeere Abbildung:
empty :: Mapα β IAbbildung auslesen:
lookup :: Ordα⇒α→Mapα β→Maybeβ IAbbildung ändern:
insert :: Ordα⇒α→β→Mapα β→Mapα β IAbbildung löschen:
delete :: Ordα⇒α→Mapα β→Mapα β
PI3 WS 20/21 26 [44]
Eine naheliegende Implementation
IModellierung als Haskell-Funktion:
data Mapα β=Map (α→Maybeβ) IDamit einfacheslookup,insert,delete:
empty=Map (λx→Nothing) lookup a (Map s)=s a
insert a b (Map s)=Map (λx→ifx==a thenJust belses x) delete a (Map s)=Map (λx→if x==athen Nothingelses x) IInstanzen von Eq, Shownicht möglich
ISpeicherleck: überschriebene Zellen werden nicht freigegeben
PI3 WS 20/21 27 [44]
Endliche Abbildungen: Anwendungsbeispiel
ILager als endliche Abbildung:
dataLager=Lager (M.Map Artikel Menge) IArtikel suchen:
suche a (Lager l)= M.lookup a l IIns Lager hinzufügen:
einlagern :: Artikel→Menge→Lager→Lager einlagern a m (Lager l)=caseposten a mof
Just _ →case M.lookup a lof
Just q → Lager (M.insert a (addiere m q) l) Nothing→ Lager (M.insert a m l)
Nothing→Lager l
IFür Inventur fehlt Möglichkeit zurIteration IDaher: Map alsAssoziativliste
PI3 WS 20/21 28 [44]
Mitmachfolie
Übung 8.2: DieMapals Assoziativliste dataMapα β=Map [(α,β)]
insert :: Ordα⇒α→β→Mapα β→ Mapα β insert a b m=(a,b):m
Was ist der Nachteil dieser einfachen Implementation?
Lösung:Erzeugt ein Speicherleck — überschriebene Elemente bleiben in der Liste. Besser:
beim Einfügen alte Elemente entfernen
insert :: Ordα⇒α→β→Mapα β→ Mapα β insert a b xs=(a, b): filter ((a6=).fst) xs Nicht sehr effizient. Besser:MapalssortierteListe.
PI3 WS 20/21 29 [44]
Map als sortierte Assoziativliste
data Mapα β=Map { toList :: [(α,β)] }
IInvariante: Liste ist in der ersten Komponente aufsteigend sortiert Ilookupist vordefiniert; beim einfügen auch überschreiben;
insert :: Ordα⇒α→β→Mapα β→Mapα β insert a v (Map s)=Map (insert’ s)where
insert’ [ ] =[(a, v)]
insert’ s0@((b, w):s) | a> b=(b, w): insert’ s
| a==b=(a, v): s
| a< b=(a, v): s0 I. . . ist aberineffizient(Zugriff/Löschen inO(n)) IDeshalb:balancierte Bäume
PI3 WS 20/21 30 [44]
AVL-Bäume und Balancierte Bäume
AVL-Bäume
Ein Baum istausgeglichen, wenn Ialle Unterbäume ausgeglichen sind, und
Ider Höhenunterschied zwischen zwei Unterbäumen höchstens eins beträgt.
Balancierte Bäume Ein Baum istbalanciert, wenn Ialle Unterbäume balanciert sind, und Ifür den linken und rechten Unterbauml,rgilt:
size(l) ≤ w·size(r) (1)
size(r) ≤ w·size(l) (2)
w—Gewichtung(Parameter des Algorithmus)
Implementation
IBalanciertheit istInvariante
INach Einfügen oder Löschen: Balanciertheit wiederherstellen IDabei drei Fälle:
1 Linker Unterbaum größersize(l)>w·size(r)
2 Rechter Unterbaum größersize(r)>w· · ·size(l) 3 Keiner größer — Baum balanciert
Balanciertheit durch Einfache Rotation
ISei der rechte Unterbaum größer IZwei Unterfälle:
1 Linkes Enkelkindt2größer 2 Rechtes Enkelkindt3größer
IEinfacheLinksrotationheilt (2) IAnsonsten:Doppelrotationreduziert (1)
zu (2)
k1
t1 k2
t2 t3
Linksrotation
−−−−−−−−−−−−−−→
Rechtsrotation
←−−−−−−−−−−−−−−
k2
k1
t1 t2
t3
PI3 WS 20/21 33 [44]
Balanciertheit durch Doppelrotation
Falls linkes Enkelkind um Faktorαgrößer als rechtes:
INach einer einfachen Rechtsrotation des Unterbaumes ist rechtes Enkelkind größer IDanach Linksrotation des gesamten Baumes
k1
t1 k2
k3 t2 t3
t4
Rechtsrotation
−−−−−−−−→
k1 t1 k3
t2 k2 t3 t4
Linksrotation
−−−−−−−−→ k3 k1 t1 t2
k2 t3 t4
PI3 WS 20/21 34 [44]
Implementation in Haskell
IDer Datentyp data Mapα β=Empty
| Nodeα βInt (Mapα β) (Mapα β) derivingEq
IParameter:
IweightGewichtsfaktorw(für Einfachrotation) IratioGewichtsfaktorα(für Doppelrotation) IHilfskonstruktornode, setzt Größe (l,rbalanciert) ISelektorsizefür Größe des Baumes (0 fürEmpty)
PI3 WS 20/21 35 [44]
Hauptfunktion
Ibalance k x l rkonstruiert balancierten Baum Il,rsind balanciert und höchstens um einen Knoten unbalanciert IVier Fälle:
1 Beide Bäume zusammen höchstens einen Knoten−→keine Rotation 2 w·size(l)<size(r):−→Linksrotation
3 size(l)>w·size(r):−→Rechtsrotation 4 Ansonsten: keine Rotation
IbalanceL k x l rrotiert nach links. Seirlundrrrechter und linker Unterbaum vonr:
1 size(rl)< α·size(rr), dann einfache Linksrotation
2 size(rl)≥α·size(rr) dann Doppelrotation (Rechtsrotationr, dann Linksrotation)
PI3 WS 20/21 36 [44]
Hilfsfunktion join beim Löschen
IZwei balancierte Bäume zusammenfügen (nachdem Wurzel gelöscht wurde) ILinkester Knoten des rechten Unterbaumes wird neue Wurzel
IMitbalancewieder ausbalancieren
join
−−−−−−→
PI3 WS 20/21 37 [44]
Was zum Selbermachen
Übung 8.3: Use the Source, Luke!
Ladet euch von der Webseite der Veranstaltung die Quellen für die 8. Vorlesung herunter, und öffnet die DateiMapTree.hs.
Vergleicht die Haskell-Implementation mit den Beschreibung der Folien.
Welche der Funktionenlookup,insert,deletekönnte man alsfoldrealisieren?
Lösung:lookupläßt sich falten:
lookup’ k=fold (λak ax l r→ ifk==ak thenJust ax elsemaybe r Just l) Nothing
Ist aber nicht so effizient (linear statt logarithmisch), weil es immer erst links, dann rechts sucht.
PI3 WS 20/21 38 [44]
Zusammenfassung Balancierte Bäume
IAuslesen, einfügen und löschen: logarithmischer Aufwand (O(logn))
IFold: linearer Aufwand (O(n))
IGuten durchschnittlicher Aufwand
IAuch in der Haskell-Bücherei:Data.Map(schwer optimiert, mit vielen weiteren Funktionen)
PI3 WS 20/21 39 [44]
Benchmarking: Setup
IWieschnellsind die Implementationenwirklich?
IBenchmarking: nicht trivial
IVerzögerte Auswertung und optimierender Compiler IMessen wir dasrichtige?
IBenchmarking-Tool: Criterion
ISetup:Map Int Stringmit 50000 zufälligen Einträgen erzeugen IDarin:
IEinmal zufällig lesen (lookup), schreiben (insert), löschen (delete) ISequenz aus fünfmal löschen und schreiben, zweihundertmal lesen (mixed)
PI3 WS 20/21 40 [44]
Benchmarking: Resultate
create lookup insert delete mixed MapFun 333,3 ms
13,58 ms 1,634 ms
52,25µs 11,27 ns
130,8 ps 11,20 ns
120,3 ps 1,659 ms
79,22µs MapList 5,629 s
168,7 ms 32,70µs
9,625µs 96,12µs
1,294µs 101,4µs
18,47µs 6,182 ms
2,059µs MapTree 383,9 ms
19,62 ms 404,1 ns
135,3 ns 119,4µs
13,18µs 117,1µs
42,82µs 2,803 ms
521,5µs Data.Map.Lazy 473,0 ms
44,97 ms 221,6 ns
59,58 ns 104,7µs
49,66µs 112,7µs
11,39µs 2,396 ms
278,8µs
Einträge: durchschnittl. Ausführungszeit, Standardabweichung
PI3 WS 20/21 41 [44]
Defizite von Haskells Modulsystem
ISignatur ist nurimplizit IExportliste enthält nur Bezeichner
IWünschenswert: Signatur an der Exportliste annotierbar, oder Signaturen in separater Datei IIn Java:Interfaces
IKlasseninstanzen werdenimmerexportiert.
IKeinPaket-System
PI3 WS 20/21 42 [44]
Zusammenfassung
IAbstrakte Datentypen(ADTs):
IBesteht ausTypenundOperationendarauf
IRealisierung in Haskell durchModule
IBeispieldatentypen: endliche Abbildungen
INächste Vorlesung: ADTs durchEigenschaftenspezifizieren
PI3 WS 20/21 43 [44]
Frohe Weihnachten und einen Guten Rutsch!
PI3 WS 20/21 44 [44]