Programmierkurs II Mengen – ein weiteres Beispiel • DatentypfürLambda-Ausdrücke data LExpr = Var String | App LExpr LExpr | Lam String LExpr deriving (Eq, Show) LambdaAusdrücke- Übungsaufgabe • UnterschiedlicheLambdaAusdrücke 1. q 2. x 3. \x. x y 4. \f x y. f y y 5. \x z. y (\x. x y) (\y. x y) LambdaAusdrücke- Übungsaufgabe • UnterschiedlicheLambdaAusdrücke e1 e2 e3 e4 = = = = Var "q" Var "x" Lam "x" (App (Var "x") (Var "y")) Lam "f" (Lam "x" (Lam "y" (App (App (Var "f") (Var "y")) (Var "x")))) e5 = Lam "x" . Lam "z" $ Var "y" `App` (Lam "x" $ Var "x" `App` Var "y") `App` (Lam "y" $ Var "x" `App` Var "y") LambdaAusdrücke- Übungsaufgabe AllefreienVariablenfinden: free free free free :: LExpr -> MySet String (Var v) = singleton v (App e e') = free e # free e' (Lam v e) = delete v $ free e Test: *Main> free $ Lam "x" (App (Var "x") (Var "y")) {"y"} FreieVariablen- Mengen AllefreienVariablenfinden: free free free free :: LExpr -> MySet String (Var v) = singleton v Vereinigung (App e e') = free e # free e' (Lam v e) = delete v $ free e Test: *Main> free $ Lam "x" (App (Var "x") (Var "y")) {"y"} FreieVariablen- Mengen AllegebundenenVariablenfinden: bound :: LExpr -> MySet String bound (Var _) = Empty bound (App e1 e2) = bound e1 # bound e2 bound (Lam v e) | element v $ free e = insert v $ bound e | otherwise = bound e Test: *Main> bound $ Lam "x" (App (Var "x") (Var "y")) {"x"} FreieVariablen- Mengen MusseineAlphakonversiondurchgeführtwerden? ImplementiertaufListen: safeList :: LExpr -> LExpr -> Bool safeList e1 e2 = null [ v | v <- boundList e1, v `elem` freeList e2 ] Alpha-Konversion- Beispiel MusseineAlphakonversiondurchgeführtwerden? ImplementiertaufListen: safeList :: LExpr -> LExpr -> Bool safeList e1 e2 = null [ v | v <- boundList e1, v `elem` freeList e2 ] ImplementiertaufMengen: safe :: LExpr -> LExpr -> Bool safe e1 e2 = isEmpty $ (bound e1) % (free e2) Alpha-Konversion- Beispiel Programmierkurs II Typklassen • EinigeTypklassenhabenwirschonkennengelernt – Eq,Ord,... • MankönnteTypklassenalsPendantzuInterfacesin objektorientiertenSprachen(e.g.,Java)bezeichnen. – EsmüssendiejeweiligenbereitgestelltenFunktionen ausimplementiertwerden. – DieseImplementierungenkönnensichunterscheiden. – EinDatentypkannmehrereTypklassenimplementieren. Typklassen SchauenwirunsmalkurzJavaScriptan: if(0){ console.log("YEAH!");} else { console.log("NO!");} if(""){ console.log("YEAH!");} else { console.log("NO!");} if(“String"){ console.log("YEAH!");} else { console.log("NO!");} ... Typklassen WirwollendiesesVerhalteninHaskell simulieren: class YesNo a where yesno :: a -> Bool Typklassen WirwollendiesesVerhalteninHaskell simulieren: class YesNo a where yesno :: a -> Bool StringsalsInstanzvonYesNo definieren: instance YesNo [a] where yesno [] = False yesno _ = True Typklassen Maybe alsInstanzvonYesNo definieren: instance YesNo (Maybe a) where yesno (Just _) = True yesno Nothing = False UnsereMengenalsInstanzvonYesNo definieren: instance YesNo (MySet a) where yesno Empty = False yesno _ = True Typklassen Testen: *Main> True *Main> False *Main> True *Main> False *Main> True *Main> False *Main> False *Main> True yesno "Hallo" yesno "" yesno [1,2] yesno [] yesno $ Just 10 yesno $ Nothing yesno Empty yesno $ 1:<:Empty Typklassen Funktion,welchedieJavaScriptif-Anweisungsimuliert: yesnoIf :: (YesNo y) => y -> a -> a -> a yesnoIf yesnoVal yesResult noResult = if yesno yesnoVal then yesResult else noResult Ausprobieren: *Main> yesnoIf [] "YEAH!" "NO!" "NO!" *Main> yesnoIf "Yes" "YEAH!" "NO!" "YEAH!" *Main> yesnoIf (Just 302) "YEAH!" "NO!" "YEAH!" Typklassen Programmierkurs II Module • DiePrelude umfassteinengroßenFunktionsumfang,kannaber durchzusätzlicheModulenocherweitertwerden. • ModulekönnendabeihelfenselbstgeschriebenenCodebesser zustrukturieren. • GenerischerCodekanndadurchinvielenunterschiedlichen Programmenverwendetwerden. Module- Motivation • BisherhabenwirlediglichmitFunktionenausderPrelude gearbeitet. • EsgibtaberModule – ZurListenverarbeitung – ZumparallelenProgrammieren – ZumArbeitenmitCharakter – ... • NatürlichschreibenwirselberauchModule Module- Einleitung • BevormanFunktionenschreibtmüssenModuleimportiert werden. • DeshalbbefindetsichderImportimmeramAnfangdesSkripts. • NatürlichkönnenmehrereModulegeladenwerden. • WirbekommendadurchZugriffaufalleexportiertenInhalte. – Esmussexplizitangegebenwerdenwasexportiertwerdensoll. – Esmussnichtallesexportiertwerden(z.B.Hilfsfunktionen) Module- Importieren ErweiternwirdieFunktionsvielfaltbzgl.Listenverarbeitung: import Data.List numUniques :: (Eq a) => [a] -> Int numUniques = length . nub Module ErweiternwirdieFunktionsvielfaltbzgl.Listenverarbeitung: import Data.List numUniques :: (Eq a) => [a] -> Int numUniques = length . nub Scant eineListeundentferntDuplikate Module EinModulkannauchdirektimGHCIgeladenwerden: Prelude> :m Data.List Prelude Data.List> Module EinModulkannauchdirektimGHCIgeladenwerden: Prelude> :m Data.List Prelude Data.List> Prelude Data.List> nub "hallo" "halo" Module FallsmannichteinganzesModul,sondernnureinpaarwenige Funktionenbenötigt: import Data.List (nub, sort) OdereinganzesModulohnebestimmteFunktionen: import Data.List hiding (nub) Module FallsmannichteinganzesModul,sondernnureinpaarwenige Funktionenbenötigt: import Data.List (nub, sort) OdereinganzesModulohnebestimmteFunktionen: import Data.List hiding (nub) Module ModuleimportierengleichnamigeFunktionen: import Data.Map Module– Namenskonflikte ModuleimportierengleichnamigeFunktionen: import Data.Map *Main> :t filter <interactive>:1:1: Ambiguous occurrence ‘filter’ It could refer to either ‘Data.List.filter’, imported from ‘Data.List’ (and originally defined in ‘GHC.List’) or ‘Data.Map.filter’, imported from ‘Data.Map’ at modules.hs:7:1-15 (and originally defined in ‘containers-0.5.6.2:Data.Map.Base’) Module– Namenskonflikte Lösungdurch„qualified“Import: import qualified Data.Map as M Zugriffauffilter: *Main> :t filter filter :: (a -> Bool) -> [a] -> [a] *Main> :t M.filter M.filter :: (a -> Bool) -> M.Map k a -> M.Map k a Module– Namenskonflikte SchauenwirunsmaleinigenützlicheFunktionenan: *Main> transpose ["hallo", "liebe", "Leute"] ["hlL","aie","leu","lbt","oee"] Module– Data.List SchauenwirunsmaleinigenützlicheFunktionenan: *Main> transpose ["hallo", "liebe", "Leute"] ["hlL","aie","leu","lbt","oee"] Matrixberechnungen: *Main> transpose [[1,2,3],[4,5,6],[7,8,9]] [[1,4,7],[2,5,8],[3,6,9]] Module– Data.List Polynomiale Gleichungberechnen: 3x2 +5x+9,10x3 +9,and 8x3 +5x2 +x- 1 Module– Data.List Polynomiale Gleichungberechnen: 3x2 +5x+9,10x3 +9,and 8x3 +5x2 +x- 1 *Main> transpose [[0,3,5,9],[10,0,0,9],[8,5,1,-1]] [[0,10,8],[3,0,5],[5,0,1],[9,9,-1]] *Main> map sum $ transpose [[0,3,5,9],[10,0,0,9],[8,5,1,-1]] [18,8,6,17] Module– Data.List Listensortieren: *Main> sort [1,2,5,4,3] [1,2,3,4,5] *Main> sort "unsorted" "denorstu" Listengruppieren: *Main> group [1,1,1,2,2,3,2,1] [[1,1,1],[2,2],[3],[2],[1]] Module– Data.List Beideskombinieren: *Main> group . sort $ [1,1,1,2,2,3,2,1] [[1,1,1,1],[2,2,2],[3]] WasbewirktdiesesSkript: *Main> map (\l@(x:xs) -> (x,length l)) . group . sort $ [1,1,1,2,2,3,2,1] Module– Data.List Beideskombinieren: *Main> group . sort $ [1,1,1,2,2,3,2,1] [[1,1,1,1],[2,2,2],[3]] WasbewirktdiesesSkript: *Main> map (\l@(x:xs) -> (x,length l)) . group . sort $ [1,1,1,2,2,3,2,1] [(1,4),(2,3),(3,1)] Module– Data.List ArbeitenmitStrings! NeueZeilenseparieren: *Main> lines "first line\nsecond line\nlast line" ["first line","second line","last line"] NeueZeileneinfügen: *Main> unlines ["first line","second line","last line"] "first line\nsecond line\nlast line\n" Module– Data.List ArbeitenmitStrings! Worteseparieren: *Main> words "hey these are the words in this sentence" ["hey","these","are","the","words","in","this","sentence"] NeueZeileneinfügen: *Main> unwords ["hey","these","are","the","words","in","this","sentence"] "hey these are the words in this sentence" Module– Data.List Modul,welchesFunktionenaufChar exportiert. *Main> False *Main> True *Main> True *Main> False isLetter '\n' isControl '\n' isSpace '\n' isPrint '\n' *Main> False *Main> False *Main> True *Main> True isLetter '€' isControl '€' isSymbol '€' isPrint '€' *Main> True *Main> False *Main> True *Main> True isLetter 'Z' isControl 'Z' isUpper 'Z' isPrint 'Z' EsgibtnochsehrvieleweiterePrädikatederForm:Char -> Bool Module– Data.Char ÜberprüfenobBenutzernamegültigist: *Main> isAlphaNum 'Z' True *Main> isAlphaNum '1' True *Main> isAlphaNum '?' False Module– Data.Char ÜberprüfenobBenutzernamegültigist: *Main> True *Main> True *Main> False *Main> True *Main> False isAlphaNum 'Z' isAlphaNum '1' isAlphaNum '?' all isAlphaNum "bobby283" all isAlphaNum "bobby!283" Module– Data.Char NützlicheInformationenzuChar: *Main> generalCategory LowercaseLetter *Main> generalCategory Space *Main> generalCategory OtherPunctuation *Main> generalCategory DecimalNumber 'a' ' ' '.' '2' Esgibtca.31Kategorien. Module– Data.Char NützlicheInformationenzuChar: *Main> map generalCategory " \t\nA9?|" [Space,Control,Control,UppercaseLetter,DecimalNumber,OtherPunctuation, MathSymbol] Module– Data.Char NützlicheInformationenzuChar: *Main> map generalCategory " \t\nA9?|" [Space,Control,Control,UppercaseLetter,DecimalNumber,OtherPunctuation, MathSymbol] Überprüfen,oballesinGroßbuchstabengeschriebenwurde: *Main> all (==UppercaseLetter) $ map generalCategory " \t\nA9?|" False *Main> all (==UppercaseLetter) $ map generalCategory "SILENCE" True Module– Data.Char Char indiedazugehörigeDezimaldarstellungüberführen: *Main> ord 'A' 65 *Main> ord ‘a' 97 *Main> chr 65 'A' Module– Data.Char PrimitiveVerschlüsselungeinesStrings encode :: Int -> String -> String encode shift msg = let ords = map ord msg shifted = map (+ shift) ords in map chr shifted decode :: Int -> String -> String decode shift msg = encode (negate shift) msg Module– Data.Char PrimitiveVerschlüsselungeinesStrings *Main> encode 0 "das kann man nicht lesen" "das kann man nicht lesen" *Main> encode 1 "das kann man nicht lesen" "ebt!lboo!nbo!ojdiu!mftfo" *Main> decode 1 "ebt!lboo!nbo!ojdiu!mftfo" "das kann man nicht lesen" Module– Data.Char • EsfindensichauchunterschiedlicheDatenstrukturenals Module. • BeimArbeitenmitWertepaaren(Identifier,Value)kanndas Data.Map Modulhilfreichsein. – OrganisiertdieDatenstrukturineinemBaum(schnellererZugriffals Listen) – StelltvielenützlicheFunktionenautomatischschonbereit Module– Data.Map Initialisierung: import qualified Data.Map as Map mapData = Map.insert 5 600 (Map.insert 4 200 ( Map.insert 3 100 Map.empty)) Schlüsselkönnenüberschriebenwerden: *Main> Map.insert 3 500 mapData fromList [(3,500),(4,200),(5,600)] Module– Data.Map - Beispiel Map undFilter aufData.Map: *Main> Map.map (*2) mapData fromList [(3,200),(4,400),(5,1200)] *Main> Map.filter (< 500) mapData fromList [(3,100),(4,200)] SchlüsselundWerteabfragen: *Main> Map.keys mapData [3,4,5] *Main> Map.elems mapData [100,200,600] Module– Data.Map - Beispiel • MankannauchMengenalsDatenstrukturenimportieren. • DieFunktionenorientierensichstarkandenvonunsbereits implementiertenFunktionen. – Vereinigung – Schnittmengen – Teilmengen – ... Module– Data.Set Initialisierung: import qualified Data.Set as Set set = Set.fromList text SchnittmengeundVereinigung *Main> Set.intersection set set2 fromList " abdehlmnrstuy" *Main> Set.union set set2 fromList " !.?BIRTWabcdefghijlmnoprstuvwy" Module– Data.Set - Beispiel • • • • EineMöglichkeitseinenCodezustrukturieren. ÄhnlicheFunktionengehörenineinModul. EinModulentsprichteinerDatei. WennwirModuleimportieren könnenwirnurdieFunktionen verwenden,welchevondementsprechendenModulexportiert wurden. Moduleselbererstellen ZuBeginndefinierenwirdenNamendesModuls(derDatei): module Geometry ( sphereVolume , sphereArea , cubeVolume , cubeArea , cuboidArea , cuboidVolume ) where Moduleselbererstellen ZuBeginndefinierenwirdenNamendesModuls(derDatei) module Geometry ( sphereVolume , sphereArea , cubeVolume , cubeArea , cuboidArea , cuboidVolume ) where NamenderFunktionen,welcheexportiert werdensollen. Moduleselbererstellen Funktionenausimplementieren: sphereVolume :: Float -> Float sphereVolume radius = (4.0 / 3.0) * pi * (radius ^ 3) sphereArea :: Float -> Float sphereArea radius = 4 * pi * (radius ^ 2) cubeVolume :: Float -> Float cubeVolume side = cuboidVolume side side side cubeArea :: Float -> Float cubeArea side = cuboidArea side side side cuboidVolume :: Float -> Float -> Float -> Float cuboidVolume a b c = rectangleArea a b * c cuboidArea :: Float -> Float -> Float -> Float cuboidArea a b c = rectangleArea a b * 2 + rectangleArea a c * 2 + rectangleArea c b * 2 rectangleArea :: Float -> Float -> Float rectangleArea a b = a * b Moduleselbererstellen Funktionenausimplementieren: sphereVolume :: Float -> Float sphereVolume radius = (4.0 / 3.0) * pi * (radius ^ 3) sphereArea :: Float -> Float sphereArea radius = 4 * pi * (radius ^ 2) cubeVolume :: Float -> Float cubeVolume side = cuboidVolume side side side cubeArea :: Float -> Float cubeArea side = cuboidArea side side side cuboidVolume :: Float -> Float -> Float -> Float cuboidVolume a b c = rectangleArea a b * c cuboidArea :: Float -> Float -> Float -> Float cuboidArea a b c = rectangleArea a b * 2 + rectangleArea a c * 2 + rectangleArea c b * 2 rectangleArea :: Float -> Float -> Float rectangleArea a b = a * b Moduleselbererstellen Funktionenausimplementieren: sphereVolume :: Float -> Float sphereVolume radius = (4.0 / 3.0) * pi * (radius ^ 3) sphereArea :: Float -> Float sphereArea radius = 4 * pi * (radius ^ 2) cubeVolume :: Float -> Float cubeVolume side = cuboidVolume side side side cubeArea :: Float -> Float cubeArea side = cuboidArea side side side cuboidVolume :: Float -> Float -> Float -> Float cuboidVolume a b c = rectangleArea a b * c cuboidArea :: Float -> Float -> Float -> Float cuboidArea a b c = rectangleArea a b * 2 + rectangleArea a c * 2 + rectangleArea c b * 2 rectangleArea :: Float -> Float -> Float rectangleArea a b = a * b Moduleselbererstellen WirkönnenausdenModuleneineHierarchischeStruktur aufbauen: module Geometry.Cuboid ( volume , area ) where volume :: Float -> Float -> Float -> Float volume a b c = rectangleArea a b * c area :: Float -> Float -> Float -> Float area a b c = rectangleArea a b * 2 + rectangleArea a c * 2 + rectangleArea c b * 2 rectangleArea :: Float -> Float -> Float rectangleArea a b = a * b Moduleselbererstellen WirkönnenausdenModuleneineHierarchischeStruktur aufbauen: IndemVerzeichnis‘Geometry‘befindetsichdieDatei‘Cuboid‘ module Geometry.Cuboid ( volume , area ) where volume :: Float -> Float -> Float -> Float volume a b c = rectangleArea a b * c area :: Float -> Float -> Float -> Float area a b c = rectangleArea a b * 2 + rectangleArea a c * 2 + rectangleArea c b * 2 rectangleArea :: Float -> Float -> Float rectangleArea a b = a * b Moduleselbererstellen WirkönnenausdenModuleneineHierarchischeStruktur aufbauen: module Geometry.Cube ( volume , area ) where import qualified Geometry.Cuboid as Cuboid volume :: Float -> Float volume side = Cuboid.volume side side side area :: Float -> Float area side = Cuboid.area side side side Moduleselbererstellen