Föreläsning 6. Hudak kapitel 8, 9, 10, 11, 23, Appendix B. Grafik med klickning. 2D1370 Funktionell programmering v14 torsdag Kap 8. Ett exempel på användning av listomfattningar. Fermats förmodan, eller Fermats stora sats säger att xm + ym == zm har heltalslösningar om och endast om m == 2. Om vi kan finna ett enda motexempel har vi motbevisat denna förmodan. Fermats fömodan framfördes på 1600-talet och förblev obevisad i över 300 år. Här är några funktioner som skulle kunna finna eventuella motexempel: triples :: Integer -> [(Integer, Integer, Integer)] -- generated triples triples n = [(x, y, z) x <- [1..n], y <- [x..n], z <- [y..n] ] {- Main> triples 3 [(1,1,1),(1,1,2),(1,1,3),(1,2,2),(1,2,3),(1,3,3),(2,2,2),(2,2,3),(2,3,3),(3,3,3)] -} pyt :: Integer -> (Integer, Integer, Integer) -> Bool -- testtriple pyt m (x, y, z) = x^m + y^m == z^m fermattriples :: Integer -> Integer ->[(Integer, Integer, Integer)] fermattriples n m = [trip trip <- triples n, pyt m trip] {- Main> fermattriples 15 2 -- list of tested triples n <= 15, m == 2 [(3,4,5),(5,12,13),(6,8,10),(9,12,15)] -} firsttriple :: Integer -> (Integer, Integer, Integer) firsttriple m = head ([ list n <- [1..], list <- fermattriples n m ]) {- Main> firsttriple 2 -- first tested triples m == 2 (3,4,5) Main> firsttriple 3 -- first tested triples m == 3 {Interrupted!} -- No answer after a wile Styr-c, "Controll -c" -} fermatsprop :: IO() -- nice output fermatsprop = putstr ( concat [ "\n m == " ++ show m ++ " " ++ show (firsttriple m) m <- [2..] ]) {- Main> fermatsprop {- m == 2 (3,4,5) m == 3 {Interrupted!} Ett klyftigare program skulle söka på ett bättre sätt. Detta program försöker ju i all evighet söka ett motexempel för m == 3. Kap9. Operatorn.. Hudak 9.4 sid 11.
Kap 23. PreludeList. Olika sätt att programmera words. Hudak kap 23 innehåller en genomgång av olika funktioner i PreludeList, som är en del av Prelude och vars definitioner således alltid finns tillgängliga när man kör Hugs. Många funktioner har definitioner som är enkla att förstå och genom att läsa definitionerna förstår man funktionernas syfte. Bl a definieras span och break för att bryta isär listor i två dellistor, brytpunkten definieras som första elementet i listan som uppfyller ett predikat (dvs en funktion elementtypen -> Bool) span :: (a -> Bool) -> [a] -> ([a],[a]) -- OBS fel i Hudak sid 235 span p [] = ([],[]) span p xs@(x:xs ) -- namn på helheten xs och delarna (x:xs ) p x = (x:ys, zs) otherwise = ([],xs) where (ys,zs) = span p xs break :: (a -> Bool) -> [a] -> ([a],[a]) break = span (not.p) -- span (\x -> not (p x)) break används av funktionen words (Hudak sid 238) som delar upp en sträng i ord: Main> words "Haskell is a functional programming language" ["Haskell","is","a","functional","programming","language"] words :: String -> [String] -- split? verb? substantiv? words s = case dropwhile isspace s of "" -> [] s -> w : words s where (w,s ) = break isspace s --(1740 reductions, 2616 cells) Här används case, som man inte använder så ofta. case gör att man kan göra mönsterpassning (pattern match) på resultatet av ett uttryck. Oftast använder vi ju mönsterpassning när vi gör en funktionsapplikation. Man kan (förstås, allting går att göra med funktioner, det är det som är funktionell programmering) använda en hjälpfunktion istället för case : words2 :: String -> [String] words2 s = wf (dropwhile isspace s) where wf "" = [] wf s = let (w,s ) = break isspace s in w : words2 s --(1740 reductions, 2616 cells) Hudak skriver en del om case på sidan 351 i Appendix B, som handlar om reglerna för mönsterpassning. Man kan skriva word direkt utan att gå via break, men det är inte helt lätt. Låt oss studera några olika sätt (utförligare på föreläsninjgen, om du tycker det är svårt att förstå detta : gör små reduktioner så ser du hur det fungerar): Om vi tänker som vanligt, dvs studerar definitionsmämngden, dvs att en lista är tom eller består av minst ett element och en svans (som kan vara tomma listan) får vi som vanligt två ekvationer. Emellertid avgränsas ju orden av mellanslag, så med mönsterpassning kan vi genast dela på fallet "ett element och en svans" till två ekvationer. Det är sedan inte så lätt att inse att man vid fallet mellanslag skall börja med en ny tom sträng "". Vi behöver också en hjälpfunktion addon som fungerar så här: addon o ("d":"dag":[]) blir "od":"dag":[].
Dessutom behöver vi en ekvation för att behandla (fler)dubbla mellanslag som ett mellanslag: words3 :: String -> [String] --"god dag" => ["god", "dag"] words3 [] = [] -- med hjälpfunktion words3 ( : :cs) = words3 ( :cs) words3 ( :cs) = "":words3 cs -- words3 (c:cs) = addon c (words3 cs) where addon :: Char -> [String] -> [String] addon c (w:ws) = (c:w):ws addon c [] = [c:[]] -- (861 reductions, 1278 cells) Bäst! En alternativ lösning är att försöka klara sig utan addon med hälp av en lokal bindning (w:ws) = words4 cs så att vi får "loss" början till det främsta ordet w och kan lägga iill tecknet c. Men om man har en sträng med ett enda tecken fungerar inte detta om man inte lägger till en femte ekvation. Detta är långt ifrån lätt att inse. words4 :: String -> [String] words4 [] = [] words4 (c:[]) = [[c]] words4 ( : :cs) = words4 ( :cs) words4 ( :cs) = "":words4 cs words4 (c:cs) = (c:w):ws -- med lokal bindning (935 reductions, 1504 cells) where (w:ws) = words4 cs En accumulerande lösning är lättare att förstå: words5 :: String -> [String] words5 str = sp str [[]] -- med hjälpfunktion med accumulator where sp :: String -> [String] -> [String] sp [] acc = reverse acc sp ( : :cs) acc = sp ( :cs) acc sp ( :cs) acc = sp cs ([] : acc) sp (c:cs) (w:ws) = sp cs ((w++[c]):ws) -(1025 reductions, 1834 cells)- En ackumulerande lösning kan också skrivas med foldl : words6 :: String -> [String] words6 str = reverse (foldl sp1 [[]] str) -- Ackumulering med foldl where sp1 :: [String] -> Char -> [String] sp1 ([]:ws) = "":ws -- måste stå först. Varför? sp1 acc = [] : acc sp1 (w:ws) c = (w++[c]):ws -- (1070 reductions, 1882 cells) En del av lösningarna ovan ger ordet "" om stängen slutar med blanktecken, vilket borde åtgärdas. Förvånande att en av mina lösningar verkar vara effektivare än definitionen i Prelude. words har vi användning av i labben om typklasser.
Kap 10. DT Picture. (Float, Float) Vertex : [] [ ]... DT Shape Kap 2 distbetween [Vertex] Polygon Float Side Radius area circle Rectangle RtTriangle square Ellipse Bool Shape String show containss Region Kap 8 containsr Shape Complement Union Intersect (Float, Float) Vector Coordinate Scale Translate Red Magenta Yellow White Region show Empty String Color Over EmptyPic IO() Region draw Black Blue Green Cyan Picture Kap 10 Picture show String Bättre bild i den tryckta upplagan. Vi fortsätter nu bokens "huvudspår". Våra regioner får färg och färgade regioner kan överlappa varandra.
Hur skriver man draw? Åt detta ägnar Hudak större delen av kapitel 10. Han börjar med ytterligare funktioner i SOEGraphics. SOEGraphics kap 10 andregion orregion xorregion diffregion createrectangle createellipse (Int, Int) Point [] : String G.Region createpolygon [Point] drawregion ellipse polygon text Red Magenta Yellow White openwindow Color Graphic withcolor IO Window Black Blue Green Cyan SOEGraphics kap 3 kap 4 drawinwindow Window IO() rungraphics <-
För att kombinera datatypen Picture med funktionerna i SOEGraphics skriver Hudak funktionerna draw och draw2 som använder sig av drawpic som använder sig av drawregioninwindow som använder sig av regiontogregion som använder sig av regtogreg som användersig av shapetogregion. Översiktsbild i den tryckta upplagan.
Kapitel 9. curry och uncurry. Två användbara funktioner (finns förstås i Prelude) : curry :: ((a,b) -> c) -> a -> b -> c curry f x y = f (x, y) uncurry:: (a -> b -> c) -> (a,b) -> c uncurry f (x,y) = f x y Vi kan nu göra sånt som Prelude> uncurry (+) (3, 4) 7 Allting går att göra med funktioner! Tycker du det är märkligt att det fungerar, gör en reduktion av till exempel uncurry (+) (3, 5). Fler exempel. Från tidigare föreläsningsanteckningar har vi en hel del som vi inte hunnit med, t ex en Luleåtenta, övningarna i kapitel 9, övning 5.9, anagram-problemet. Här är ytterligare en tentauppgift: 2. Tentabetyget på en kurs beror på dels antalet poäng p på tentan, 0<= p <= 100, dels på om man har gjort ett labmoment steg9 vilket markeras med G eller _. Tentabetyg ges till de personer som har minst 50 p på tentan. Föjande betygsgränser gäller för dessa: Tentabetyg Tentapoäng p om Tentapoäng p om man gjort steg 9 man inte gjort steg 9 3 50 <= p < 70 50<= p < 75 4 70 <= p < 80 75 <= p < 98 5 80 <= p <= 100 98 <= p <= 100 Skriv ett program tentabetyg :: [(String, Int,Char)] -> [(String, Int)] som givet en lista av typ [(String, Int,Char)] med personers namn, tentamenspoäng och om man gjort steg9 retunerar tentabetyg för de personer som skall ha tentabetyg 3, 4, eller 5. Exempel : res = [("Per", 74, G ), ("Siv", 74, _ ), ("Eva", 95, G )].. >tentabetyg res [("Per",4),("Siv",3),("Eva",5)] a) Skriv programmet utan att använda några högre ordningens funktioner eller listomfattning. b) Skriv programmet med map och filter. c) Skriv programmet med listomfattning. 8p)
Förslag till svar : tentabetyg [] = [] tentabetyg ((namn, p, _ ):nps) p < 50 = tentabetyg nps p < 75 = (namn, 3) : tentabetyg nps p < 98 = (namn, 4) : tentabetyg nps otherwise = (namn, 5) : tentabetyg nps tentabetyg ((namn, p, G ):nps) p < 50 = tentabetyg nps p < 70 = (namn, 3) : tentabetyg nps p < 80 = (namn, 4) : tentabetyg nps otherwise = (namn, 5) : tentabetyg nps tentabetyg _ = error "felaktiga indata" -- 2 a tentabetyg lista = map betyg1 -- b (filter ( \(namn, tentap, steg9) -> tentap > 50) lista) betyg1:: (String, Int, Char) -> (String, Int) betyg1 (namn, p, _ ) p < 75 = (namn, 3) p < 98 = (namn, 4) otherwise = (namn, 5) betyg1 (namn, p, G ) p < 70 = (namn, 3) p < 80 = (namn, 4) otherwise = (namn, 5) betyg1 _ = error "felaktiga indata" tentabetyg lista = -- c [ (namn, betyg tentap steg9) (namn, tentap, steg9) <- lista, tentap >= 50] betyg:: Int -> Char -> Int betyg p _ p < 75 = 3 p < 98 = 4 otherwise = 5 betyg p G p < 70 = 3 p < 80 = 4 otherwise = 5 betyg = error "felaktiga indata" Också kanske på föreläsningen. Grafik med interaktion från labhäftet. Något om kapitel 11 och något om strikta funktioner.