Föreläsning 4 (och 5?) i programmeringsparadigm.

Relevanta dokument
Föreläsning 4 (och 5?) i programmeringsparadigm.

Föreläsning 5 i programmeringsparadigm.

DD1361 Programmeringsparadigm. Carina Edlund

Enjoy Vattenfallsmodellen i funktionella språk

Funktionell programmering DD1361

Enjoy Vattenfallsmodellen i funktionella språk

Del : Funktionell programmering. I alla deluppgifterna, använd Haskell och skriv typen för de identifierare du definierar.

Föreläsning 7 i programmeringsparadigm. Ytterligare Högre ordningens funktioner: filter, foldr foldl. Hutton 7.2, 7.3 och 7.4.

Föreläsning 4 i programmeringsparadigm.

Tentamensdag 2002-aug-20 Tentamen i Funktionell Programmering Skrivtid 5 h

Föreläsning 6 i programmeringsparadigm. Tips kring programmering i Haskell och kring labbarna.

Nada KTH 2004 jan 12 Tentamen Programmeringsparadigm 2D1350 Skrivtid 5 h 8-13

Kap9. Operatorn.. Hudak 9.4 sid 11. Fermats förmodan, eller Fermats stora sats säger att. xm + ym == zm har heltalslösningar om och endast om m == 2.

Introduktion till Haskell

Föreläsning 5 i programmeringsparadigm. Tips kring programmering i Haskell och kring labbarna.

Föreläsning 8. Paradigmöversikt, paradigmhistoria, paradigmgeografi. Se även föreläsning 1.

Programkonstruktion och datastrukturer. Moment 9 Om högre ordningens funktioner. PKD 2010/11 moment 9 Sida 1 Uppdaterad

F6: Högre ordningens funktioner. Mönster för rekursion (1) Mönster för rekursion (1b) Mönster för rekursion (2) Högre Ordningens Funktioner

LÖSNINGSFÖRSLAG TENTAMEN PROGRAMMERING I ETT FUNKTIONELLT SPRÅK ML, 5P

Nada KTH 2003 okt 23 Tentamen Programmeringsparadigm 2D1350 Skrivtid 5 h 8-13

Föreläsning 8. newtype Chess = Chess [(Square, Chessman)] -- data ist f newtype OK -- data istället för newtype krävs om >1 konstruerare.

Tentamen. Datalogi I, grundkurs med Java 10p, 2D4112, Lördagen den 30 november 2002 kl , salar E33, E34

Del : Paradigmer allmänt.

Föreläsning 9 i programmeringsparadigm. Paradigmöversikt, paradigmhistoria, paradigmgeografi. Se även föreläsning 1.

Nada Tentamensdag 2004 okt 18 Tentamen Programmeringsparadigm Skrivtid 5 h

Kungliga Tekniska Högskolan Ämneskod 2D1370 Tentamensdag 2001-maj-31 Tentamen i Funktionell Programmering Skrivtid 4 h

Del : Paradigmer allmänt.

TDDC74 Programmering: Abstraktion och modellering Tentamen, lördag 27 augusti 2016, kl 8 12

Del : Funktionell programmering. I alla deluppgifterna, använd Haskell och skriv typen för de identifierare du definierar.

Kompilering och exekvering. Föreläsning 1 Objektorienterad programmering DD1332. En kompilerbar och körbar java-kod. Kompilering och exekvering

Universitetet i Linköping Institutionen för datavetenskap Anders Haraldsson 2

Dagens föreläsning Programmering i Lisp Fö 5

Programmering II (ID1019) :00-17:00

TDDC74 Programmering: Abstraktion och modellering Datortenta , kl 14-18

TDIU01 - Programmering i C++, grundkurs

Programkonstruktion och datastrukturer. Moment 9 Om högre ordningens funktioner. PKD 2011/12 moment 9 Sida 1 Uppdaterad

TDDC74 Programmering, abstraktion och modellering DUGGA 2

Imperativ programmering. Föreläsning 2

Dagens föreläsning Programmering i Lisp Fö 7. Sammanfattning funktionell programmering Exempel på funktionella programspråk

Föreläsning 3-4 Innehåll. Diskutera. Metod. Programexempel med metod

Föreläsning 3 i Programmeringsparadigm

Våra enkla funktioner eller procedurer

Men först: Några funktioner ur preluden. Introduktion till programmering. Uppgiften. Variationer av uppgiften. Föreläsning 4

F4. programmeringsteknik och Matlab

Föreläsning 3-4 Innehåll

Föreläsning 2 Programmeringsteknik och C DD1316. Mikael Djurfeldt

Laboration: Whitebox- och blackboxtesting

Övningsuppgifter kapitel 8

Användarhandledning Version 1.2

Tentamen Grundläggande programmering

732G Linköpings universitet 732G11. Johan Jernlås. Översikt. Repetition. Muddy. Funktioner / metoder. Punktnotation. Evalueringsordning

Lambdas. (och fler design patterns) Objekt-orienterad programmering och design (DIT952) Niklas Broberg, 2017

Outline. For. I istället för att följa det normala ordningen, man ändra den. I i Java får man inte göra hopp hur som helst

5. En metod som anropar sig själv a) får inte förekomma i Java-program b) kallas destruktiv c) kallas iterativ d) kallas rekursiv 6. Vilka värden har

Föreläsning 8. Hudak kapitel 13. 2D1370 Funktionell programmering v15 torsdag

Programmera i C Varför programmera i C när det finns språk som Simula och Pascal??

Anmälningskod: Lägg uppgifterna i ordning. Skriv uppgiftsnummer (gäller B-delen) och din kod överst i högra hörnet på alla papper

Pythons standardbibliotek

Senast. Idag. Icke-strikt evaluering. Strikt evaluering. Testa latheten! Lat evaluering. Plus och minus med lat evaluering. Testa latheten!

Övning2. Variabler. Data typer

Datatyper och kontrollstrukturer. Skansholm: Kapitel 2) De åtta primitiva typerna. Typ Innehåll Defaultvärde Storlek

Introduktion till programmering. Standardfunktioner. Vad används datorer till? Standardfunktioner. Föreläsning 2. Prelude. $ ghci...

Föreläsning 10 Datalogi 1 DA2001. Utskrift på skärmen. Syntax. print( Hej ) Hur är det? Hej. print( Hej,end= ) print( Hur är det? ) HejHur är det?

Programkonstruktion. Tentamen,

String [] argv. Dagens Agenda. Mer om arrayer. Mer om arrayer forts. String [] argv. argv är variabelnamnet. Arrayer och Strängar fortsättning

Föreläsning 6: Metoder och fält (arrays)

Programmering I Tobias Wrigstad fredag, 2009 augusti 28

EDAA20 Programmering och databaser. Mål komprimerat se kursplanen för detaljer. Om att lära sig programmera. Föreläsning 1-2 Innehåll.

TDDC74 Lab 04 Muterbara strukturer, omgivningar

Programmering B med Visual C

Se utdraget ur "AGentle Introduktion.." Appendix (bihang) till Hutton.

Repetition i Pascal. Exemplen fac. Exemplen fac i Pascal. Exemplen fac motivering. Orginalet

TDDC30. Kursledning Kursledare: Jonas Lindgren. Labassistent: Jonas Lindgren Labassistent: Niklas Holma Labassistent: Erik Nilsson

Föreläsning 6: Introduktion av listor

Föreläsning 7 Innehåll. Rekursion. Rekursiv problemlösning. Rekursiv problemlösning Mönster för rekursiv algoritm. Rekursion. Rekursivt tänkande:

Introduktion till formella metoder Programmeringsmetodik 1. Inledning

TDP002 - Imperativ programmering

Programkonstruktion. Tentamen,

Programmeringsmetodik DV1 Programkonstruktion 1. Moment 9 Om högre ordningens funktioner. PK1&PM1 HT-06 moment 9 Sida 1 Uppdaterad

Klassdeklaration. Metoddeklaration. Parameteröverföring

Bakgrund. Bakgrund. Bakgrund. Håkan Jonsson Institutionen för systemteknik Luleå tekniska universitet Luleå, Sverige

Lite om felhantering och Exceptions Mer om variabler och parametrar Fält (eng array) och klassen ArrayList.

Multipel tilldelning. Introduktion till programmering D0009E. Föreläsning 6: Iteration. while-satsen. Kom ihåg. Snurror kontra rekursion

KOMPLETTERANDE HEMTENTAMEN TDDB53

Tentamen i kurserna Beräkningsmodeller (TDA181/INN110) och Grundläggande Datalogi (TDA180)

Föreläsning 2 sept 05 (Onsdag v 36). DD Chapter 2.

Välkommen till. Datastrukturer, algoritmer och programkonstruktion. eller DOA

Datalogi för E Övning 3

MATLAB. Python. Det finns flera andra program som liknar MATLAB. Sage, Octave, Maple och...

ITK:P1 Föreläsning 1. Programmering. Programmeringsspråket Java. Stark typning Explicit typning Strukturerat Hög säkerhet

Rekursiva algoritmer sortering sökning mönstermatchning

Tentamen Datastrukturer D DAT 035/INN960 (med mycket kortfattade lösningsförslag)

SMD 134 Objektorienterad programmering

Generiska konstruktioner. Kursbokens kapitel 13

Föreläsning 3: Booleans, if, switch

JavaScript del 3 If, Operatorer och Confirm

Programmering II (ID1019) :00-12:00

Objektorienterad programmering i Java, datastrukturer och algoritmer. Föreläsning 4 Jonas Lindgren, Institutionen för Datavetenskap, LiU

Tentamen ID1004 Objektorienterad programmering May 29, 2012

C++ - En introduktion

Transkript:

Föreläsning 4 (och 5?) i programmeringsparadigm. Korrekta bilnummer. Programmet, som visar att man ibland använder smarta mönster för att skriva eleganta program, finns på /info/progp02/haskelllectures/f4. module Bilnummer where ischarinrange :: Char -> (Char, Char) -> Bool ischarinrange c (cfrom, cto) = cfrom <= c && c <= cto {- eller? ischarinrange :: Char -> Char - > Char -> Bool ischarinrange c cfrom cto = cfrom <= c && c <= ct -- anropas som ischarinrange c A Z -} isuppercase :: Char -> Bool isuppercase c = ischarinrange c ( A, Z ) notusedletters :: String -- det samma som [Char] notusedletters = "IQV" -- == [ I, Q, U ] == I :( Q :( U : [] )) islegalletter :: Char -> Bool islegalletter c = isuppercase c && (not (elem c notusedletters)) -- Snyggare? c elem notusedletters islegal :: String -> Bool islegal (b1:b2:b3: :s1:s2:s3:[]) --"avancerat mönster " = ischarinrange b1 ( A, T ) && isdigit s2 && isdigit s1 && isdigit s3 && islegalletter b1 && islegalletter b2 && islegalletter b3 islegal _ = False -- isdigit :: Char -> Bool i Prelude -- elem :: Eq a => a -> [a] -> Bool i Prelude -- Man kan tittat på Prelude genom att läsa filen -- "/pkg/hugs98/feb00/share/hugs/lib/prelude.hs" -- och finna någon definiton genom -- t ex ladda in filen i emacs och göra Search och Repeat Search i Search-menyn -- Def av elem i Prelude är emellertid för avancerad för oss just nu -- När man startar hugs står det just -- Reading file "/pkg/hugs98/feb00/share/hugs/lib/prelude.hs": {- En tänkbar definition av elem är elemlk :: Eq a => a -> [a] -> Bool elemlk x (y:ys) = (x == y) (x elemlk ys) elemlk x [] = False -} ADT och export från moduler. Se "A Gentle Introduction.." Labhäftet sid 18. Exempel från schacklabben.

Polymorfism. Fokker 1.5.5. När man skriver typer i typuttryck används stor initialbokstav för typer. Namn med små bokstäver i typuttryck betyder "vilken typ som helst". T ex length:: [a] -> Int. Dock : Om man till exempel skriver Num a => [a] -> a betyder a alla typer som är medlemmar i Num-gruppen, eller med Haskellterminologi : alla typer som är instanser i typklassen Num. Vi har inte sagt så mycket om typklasser än, hur de definieras och används kommer senare. Högre ordningens funktioner på listor; funktioner som argument: map, filter. Fokker 3.1.3 map -- förändra alla element i en lista på samma sätt. (i st f for-loopar)..fokker 3.1.3. filter -- filtera (välj ut) en del element ur en lista. (i st f for-loopar). Fokker 3.1.3. Exempel, vår kära power: power :: Int -> Int -> Int -- Om man kan map,[.. ] (i Prelude) power m n = product (map f [1..n]) --, n>0 ger 1 where f _ = m Listomfattning. Fokker 3.2.7 I stället för att använda map och filter kan man med listomfattning (List comprehension) skriva väldigt eleganta program, lätta att skriva och förstå. Några delar av Schack-labben ännu enklare att skriva med listomfattning än med map och filter. Vi ska ju ofta göra något med en listor, t ex listor med grafik. Exempel, vår kära power: power :: Int -> Int -> Int -- Om man kan listomfattning power m n = product [m x <- [1..n]] Användbara funktioner; until. Fokker 2.3.2 En mängd användbara funktioner finns som redan betonats i Preluden, där de också är definierade, de allra flesta med några få rader Haskell. Att använda dessa funktioner är bekvämt, man slipper göra ungefär samma sak gång på gång, programmering mindre tjatigt och programmeringen går ännu fortare, programmen kortare och programmering på "högre nivå", bekväm och lustfylld. Man kan naturligtvis själv också göra högre ordningens funktioner, och gör man dem bra (bl a så polymorfa som möjligt) så kan dom vara användbara gång på gång i en mängd olika sammanhang. Men många av dom högre ordningens funktioner som finns i Preluden är väldigt användbara. Ett exempel : until -- förvandla något gång på gång så länge något gäller loopar).

I Prelude : until :: (a -> Bool) -> (a -> a) -> a -> a until p f x = if p x then x else until p f (f x) snyggare med vakter (tycker jag och Fokker) until p f x p x = x otherwise = until p f (f x) Obs. svansrekursiv. "loopar" (slinger, snurror) i imperativa språk kan programmeras med until, och många av våra lösningar med "ackumulering i parametrar" kan också lösas med until. De "imperativa programmeringsvariablerna" motsvaras av komponenterna i en tuppel, "<status>": Java : Haskell : while <not p> { until p f <status> <f > where p :: <statustyp> -> Bool } p <status> =.. - -funktion som True när vi skall sluta f :: <statustyp> -> <statustyp> f <status> =..- -funktion som beskriver "loopens kropp" Exempel, vår kära power: power :: Int -> Int -> Int -- accumulerande lösningar med until, power m n n<0 = error " second argument till power >= 0" otherwise = snd (until p f (0, 1)) where p (i, _) = ( i == n )) f(i, acc) = (i+1, acc*m) I Java : static int power (int m, int n) { //snurra, lokala variabler if (n < 0) { throw new RuntimeException("second argument till power >= 0"); } else { int acc = 1; for (int i = 0; i<n ; i = i+ 1) { acc = acc*m; } return acc; } } Alla sätt att resonera kan fångas med funktioner!

Hur fungerar det? Jo som vanligt så förklaras semantiken med att Haskell förenklar (reducerar) uttryck (vi struntar i att kolla n <0): power 2 3 snd (until p f (0,1)) snd ( if p (0, 1) then (0, 1) else until p f (f (0, 1))) snd ( if 0 == 3 then (0, 1) else until p f (f (0, 1))) snd ( if False then (0, 1) else until p f (f (0, 1))) snd (until p f (f (0, 1)) snd (until p f (f (0+1, 1*2)) snd (until p f (f (1, 2))... snd (until p f (f (2, 4))... snd (until p f (f (3, 8)) snd ( if p (3, 8) then (3, 8) else until p f (f (3, 8))) snd ( if 3 == 3 then (3, 8) else until p f (f (3, 8))) snd ((3, 8)) 8 Förenkling av power 2 100 skulle bli "längre", men inte "bredare", dvs minneseffektiv. Detta beror på att until är svansrekursiv, dvs högerledet i definitonen av until är ett enkelt anrop av until. Användbara funktioner; foldl foldr. Fokker 3.1.3 foldr -- vik ihop alla element från höger (i st f for-loopar). foldl -- vik ihop alla element från vänster (i st f for-loopar). Exempel, vår kära power: power :: Int -> Int -> Int -- Om man kan foldr (i Prelude) power m n = foldr (*) 1 [m x <- [1..n]] power :: Int -> Int -> Int -- Om man kan foldl (i Prelude) power m n = foldl (*) 1 [m x <- [1..n]] λ-notation (lamda-notation, anonyma funktioner). Fokker 2.3.4. Vi kan om vi vill definiera hjälpkonstanter, t ex i detta uttryck : kommunalskatt inkomst = skattesats * inkomst where skattesats = 0.30 -- skattesats definieras som 0.30. Ev globalt "skåp". Men om vi är för lata för att "döpa" 0.30 till skattesats så kan vi förstås helt enkelt skriva kommunalskatt inkomst = 0.30 * inkomst

Dvs i en definition namngivet begrepp kan ersättas med sin definition. Låt oss nu titta på mapf lista = map f lista where f i = 3*i + b b = 5 -- f definieras som en funktion med högerledet 3*i + b där i är parameter I en vanlig mattebok skulle en definition av f se ut så här f(i) = 3*i + b, vilket ju påminner om vår definition f i = 3*i + b. Men om vill kunna ersätta f i uttrycket mapf lista = map f lista kan vi ju inte bara skriva mapf lista = map (3*i + b) lista --- Fel!!!!!!!! eftersom (3*i + b) är ett uttyck men f är en funktion. I en sorts matematik som kallas λ-kalkyl skriver man definitonen av f som f = λi. 3*i + b Motsvarande sätt att definiera funktionen f går också att använda i funktionella språk: f = \i -> 3*i + b --- <namn> = <värde> som för konstanter (Teoretiskt sätt "det riktiga sättet", det sätt vi använt hitills är en eftergift till konventionell matematik) mapf lista = map f lista where f = \i -> 3*i + b b = 5 -- f definieras som en funktion med högerledet 3*i + b där i är parameter Men om vi är för lata för att "döpa" vår funktion till f så kan vi förstås helt enkelt skriva mapf lista = map (\i -> 3*i + b) lista Dvs ett i en definition namngivet begrepp kan ersättas med sin definition. Exempel, vår kära power: power :: Int->Int->Int -- accumulerande lösningar med until, lamda power m n -- Fokker 2.3.2, Fokker 2.3.4 n<0 = error " second argument till power >= 0" otherwise = snd (until (\(i, _) -> ( i == n )) (\(i, acc) -> (i+1, acc*m) ) (0, 1)) Hit hann vi på föreläsning 4.

För den som är intresserad av teori: Att i en definition namngivet begrepp alltid kan ersättas med sin definition är en mycket viktig egenskap hos matematik och funktionella språk. I imperativa språk gäller inte detta, trots att det borde vara en självklarhet, ett exempel i Java: static int x = 4; int y = f(x) * f(x) ; Efter att kört dessa två satser är inte y säkert samma värde som när man kört detta program static int x = 4; int z = f(x) int y = z * z; Låt t ex f ha denna definition static int f(int u) { int result = 2*u; x = 5; //sidoeffekt return result; } Sidoeffekter ställer lätt till med en massa elände och gör att man i imperativ och objektorienterad programmering måste tänka sig in i vad som händer vid körningen. Sammanhanget (contexten), vad som hänt under körningen och inte bara vad som står i programkoden, spelar ofta roll när man skall tolka något i ett imperativt språk. Ordningen mellan satser spelar (en mycket stor) roll i imperativa språk. Att skriva bra imperativa program innebär att använda minimalt med sidoeffekter (eller i OO använda sidoeffekter på ett kontrollerat sätt) och se till att så få begrepp som möjligt samtidigt är relevanta i något sammanhang. Betydelsen av en funktionsdeklaration i ett funktionellt språk beror int på vad som händer vid en körnming. Funktionsdeklarationer kan stå i godtycklig ordning. Funktionella språk har inga sidoeffekter, programmeringen innebär att definiera begrepp, dvs lämpliga funktioner och datatyper (och typklasser). Bl a Haskell Curry arbetade med λ-kalkyl på 1930-talet, en gren av matematiken där λ-notationen är ett viktigt begrepp, dvs λ-kalkyl är äldre än datortekniken. Kärnan i funktionell programmering är helt enkelt λ-kalkyl. Haskell Curry har hedrats genom att hans fönamn har namngivit programspråket Haskell och hans efternamn namngivet för en viktig teknik för att skriva funktioner som behandlas i nästa avsnitt.

Högre ordningens funktioner, higer-order functions (HOF). Funktioner resultat: Currying. Fokker 2.2.1, 2.2.2, 2.2.3 Låt oss definiera add :: (Int, Int)->Int add (x,y) = x + y -- eller add = \ (x,y) -> x+ y add (3,5) 8 dvs vi säger att add har en parameter som är ett heltalspar och lämnar ett heltal som resultat. Detta påminner ju om hur vi skriver i matte brukar skriva add(x,y) = x + y, men det vanliga men egentligen felaktiga språkbruket är att add "är en funktion av två variabler", men Funktioner har alltid bara en enda parameter. När funktioner används har de alltid bara ett enda argument. Låt oss definiera plus :: Int -> (Int->Int) (plus x) y = x + y -- eller plus = \ x -> (\y -> x + y) (plus 3) 5 8 -- ganska löjligt, enklare((+) 3) 5, 3+5 eftersom plus = (+) dvs det korrekta sättet att beskriva plus är att plus har en parameter x som är ett par heltal och lämnar som resultat en anonym funktion (\y -> x + y). Detta syns tydligare om vi läser definitionen av plus med λ-notation. När vi t ex gör (plus 3) får vi som resultat funktionen (\y -> 3 + y). Denna funktion kan nu appliceras på t ex 5, dvs (\y -> 3 + y) 5 vilket 8. När funktioner används har de alltid bara ett enda argument. Är resultatet en funktion kan resultatet användas på ytterligare ett argument osv. Funktionsapplikation binder till höger, dvs f g x betyder (f g) x. -> binder till höger, dvs Int -> Int ->Int betyder Int -> (Int ->Int). Alltså kan vi skriva på detta sätt plus :: Int -> Int->Int plus x y = x + y -- eller plus = \ x -> (\y -> x + y) plus 3 5 8 vilket vi gjort hela tiden, men inte förklarat egentligen varför vi kan skriva på detta sätt. plus kalls en "a curried funtion", på svenska en partiellt applicerbar funktion. I detta exempel har vi visat att en funktion med ett par som parameter (add) har en motsvarande "curried" funktion (plus), och på liknade sätt kan man resonera för triplar och tupler i allmänhet.

Det är ingen nackdel att använda sådana funktioner i stället för tupler. Fördelen är att man kan ha användning av "mellanfunktionerna". När man använder t ex plus med endast ett argument gör man en partiell applikation. (Fokker kallar det partiell parametrisering). Även operatorer kan partiellt appliceras, vilket kallas operator snitt (operator sections). T ex är följande uttryck olika sätt att skriva samma sak, värdet alltid [2, 4, 6] : let f x = 2*x in map f [1, 2, 3] let f = \x -> 2*x in map f [1, 2, 3] map f (\x -> 2*x) [1, 2, 3] map (2*) [1, 2, 3] map (*2) [1, 2, 3] Ett exempel från Schack-labben, steg 3: Antag att vi har en lista med koordinater som skall räknas om, dvs flyttas till rätt schackruta. Flytten av ett koordnatpar till ett nytt kordinatpar i en viss ruta på ett schackbräde med enhetsrutor definieras med movedto :: Square -> (Float, Float) -> (Float, Float) movedto sq (ix, iy) = ( frominteger(tointeger (col sq)) + ix, frominteger(tointeger (row sq)) + iy ) Om vi vill flytta alla koordinater i en lista till en viss ruta kan vi skriva något som symmovedto :: Square -> [(Float, Float)] -> [(Float, Float)] symmovedto sq coords = map movedtosq coords where movedtosq = movedto sq -- enklare -- movedtosq coord = movedto sq coord -- sämre men det är bekvämt, enklast och bäst att slippa definiera movedtosq symmovedto sq coords = map (movedto sq) coords vilket är möjligt tack vare att movedto är partiellt applicerbar. Ytterligare exempel från steg 3 i schack- labben: Antag att vi har listan av koordinater som symmovedto ger som resultat. Vi vill nu skala dessa kordinater med en skalfaktor scale och vi har en funktion som skalar par: scaledwith :: Float -> (Float, Float) -> (Float, Float) scaledwith sc (ix, iy) = (sc*ix, sc*iy) scaledsym ::[(Float, Float)] -> [(Float, Float)] scaledsym list = map ( scaledwith scale) list

Funktionssammansättning. Fokker 2.3.3 Uttrycket f ( g x ) kan skrivas som (f. f) x eller let h = f. g in h x. Exempel från labben: Antag att vi har en lista med koordinater som skall räknas om, dvs flyttas till rätt schackruta och också skalas. Flytten av ett koordnatpar till ett nytt kordinatpar i en viss ruta på ett schackbräde med rutor med storlek squaresize definieras med symtoandscaled loc sy = map ( scaledwith squaresize) list where list = map (movedto sq) coords vilket enklare skrivs som symtoandscaled loc sy = map (scaledwith squaresize) (map (movedto sq) coords) men istället för att gå igenom listan gång på gång och första gången flytta och andra gången skala kan vi gå igenom listan en gång och göra båda sakerna på en gång med den anonyma namnlösa funktionen (scaledwith squaresize).(movedto sq) -- paranteserna behövs ej symtoandscaled loc sy = map ((scaledwith squaresize).(movedto sq)) coords) I labben skall man slutligen förvandla alla kordinater från mm till pixels. Resonemanget ovan kan då också tillämpas på topixels som inte behöver något argument för att passa in. Slutresultatet en lättläst och lättförståelig enradersdefinition på symtographic, mycket bättre än den omständiga naiva lösningen i appendex (ännu ej utdelad) steg 3 med alla sina "mellandefinitioner". Märk också likheten mellan Haskells. och i Unix för att göra "pipes" Vad vi nu har gått igenom i Fokker. Nästan allt i kapitel 1, 2, och 3 hoppas jag nu är förståeligt. Det som vi inte har behandlat än är lat evaluering och oändliga listor 3.2.4, 3.25, 3.26. Man behöver inte förstå detta för att klara schacklabben. Lat evaluering och oändliga listor möjliggör dock att det är möjligt att programmera ännu elgantare och effektivare i Haskell, så jag hoppas kunna prata om detta på någon av de sista Haskell-förläsningarna. Hur man ritar bilder m m. Inför Steg 3 och 4 i schacklabben Se Labbhäftet sid 28 <= sid <= 34

Gamla tentatal: 5 a) Definiera en datatyp för binära träd med information i "löven". b) Antag att löven är funktioner. Skriv en funktion app som kan applicera lövens funktioner på ett givet argument. Ange typen för app. c) Skriv en funktion sumtree som summerar alla värden i ett lövträd med heltal i löven. Ledning : Ett delvis skämtsamt exempel på användning av deklarationerna i a), b( och c) (träden ritade på botaniskt i stället för datalogiskt sätt, rötterna nedåt) : \m -> 2* m*m + 3 \m -> 7*m + 15 \m -> 10*m + 7 Vår. Ett äppelträd mytree. Olika äppelsorter inympade. Funktionerna anger hur tunga (i g) tre äpplen som funktion av tiden (i månader). 90 57 67 Höst. 6 månader senare. Vikt för äpplenen på träden. app 6 mytree 214 Skördens vikt. sumtree (app 6 mytree)

module Appletree where -- på /info/progp02/haskelllectures/f5/appletree.hs data LeafTree a = Leaf a Node (LeafTree a) (LeafTree a) -- a) deriving Show app :: a -> LeafTree (a->b) -> LeafTree b -- b) app x (Leaf f) = Leaf (f x) app x (Node left right) = Node (app x left) (app x right) sumtree :: LeafTree Int -> Int -- c) sumtree (Leaf i) = i sumtree (Node left right) = sumtree left + sumtree right mytree :: LeafTree (Int -> Int) mytree = Node (Leaf (\m -> 10*m+7)) (Node (Leaf (\m -> 2*m*m+3*m)) (Leaf (\m -> 7*m+15))) {- -} Appletree> app 6 mytree Node (Leaf 67) (Node (Leaf 90) (Leaf 57)) Appletree> sumtree (app 6 mytree) 214 4 Skriv en funktion occurestimes n som lämnar som resultat en lista som innehåller de element som förekommer minst n gånger i list med anropet occurestimes n list. Exempel: occurestimes 2 [, "a", "hej", "Luleå", "a", "b", "hej"] ger svaret ["hej, "a"] occ [] = [] --Tal 4 på tentan occ (x:xs) = add x (occ xs) -- på /info/progp02/haskelllectures/f5/appletree.hs add x [] = [(x, 1)] add x ((y,n):ys) x == y = (x, n+1): ys otherwise = (y, n) : add x ys occurstimes m list = [x (x, n) <- occ list, m == n] {- occ :: Eq a => [a] -> [(a, Int)] occ = foldr add []...> occurstimes 2 ["a", "hej", "Lul", "a", "b", "hej"] ["hej","a"] -}

Quicksort i Haskell: module Quicksort where -- på /info/progp02/haskelllectures/f5/quicksort.hs qsort :: Ord a => [a] -> [a] qsort [] = [] qsort (x:xs) = qsort (filter (<=x) xs) ++ [x] ++ qsort (filter (x<) xs) {- Körning : :s +ts Quicksort> qsort [1,23, 14, 5, 8, 2, 14] [1,2,5,8,14,14,23] (469 reductions, 642 cells) -} {- qsort :: Ord a => [a] -> [a] qsort [] = [] qsort (x:xs) = qsort small ++ [x] ++ qsort large where (small, large) = foldl dir ([], []) xs dir -- (332 reductions, 594 cells) -} (sm, la) y y <= x = (y:sm, la) otherwise = (sm, y:la) Ett försök att motbevisa Fermats stora sats: --på /info/progp02/haskelllectures/f5/quicksort.hs triples = [ (x, y, z) x <- [1.. ], y <- [x..3], z <- [x..3]] fermat n = [ (x, y, z) x <- [1..20], y <- [x.. 20], z <- [x..20], x<=y, z^n == x^n + y^n] fermata n = [ (x, y, z) p <- [2..], x <- [1..p], y <- [x.. p], z <- [x..p], x<=y, z^n == x^n + y^n] fermatb n = take 1 [ (x, y, z) p <- [2..], x <- [1..p], y <- [x.. p], z <- [x..p], x<=y, z^n == x^n + y^n] fermatstorasats = [ ("n = " ++ show n, fermatb n) n <- [2..]] {-...> fermatstorasats [("n = 2",[(3,4,5)]),("n = 3",{Interrupted!} Inget motbevis än...

Appendix till steg 3 i Schacklabben: Naivt sätt att skriva symtographic loc sy : Antag som exempel attt vi vill rita en springare med utseendet sym Knight till schackrutan loc. (förkortining aav location). En springares utseende är definierad som en triangel i enhetsrutan : sym Knight = [(0.5, 0.2), (0.4, 0.8),(0.6, 0.8)] Triangelns tre kordinater skall flyttas till tre skärmfönsterkoordinater. Låt oss först fundera hur vi flyttare en koordinat, dvs ett trangelhörn med en funktion coordtopixelsat :: Square -> (Float, Float) -> (Int, Int) Vi måste räkna om kordinaten dels för att vi skall -- flytta till schackrutan loc (förkorting för location, dåligt namn) -- ta hänsyn till rutstorleken som är definierad av konstanten squaresize = 20.0 -- mm -- räkna om från mm till pixels Vi börjar med att flytta till schackrutan loc: coordtopixelsat loc (i, j) =... movedto loc (i, j) Sedan är det dags att ta hänsyn till schack-rute-storleken: där coordtopixelsat loc (i, j) =... scalewithsquaresize (movedto loc scalewithsquaresize:: (Float, Float) -> (Float, Float) scalewithsquaresize (mi, mj) = scaledwith squaresize (mi, mj) Sedan är det dags att räkna om från mm till pixels coordtopixelsat loc (i, j) = topixels (scalewithsquaresize (movedto loc (i, j))) (i, j)) Vi vet nu har vi skall räkna om en koordinat. Men vi vill ju flytta alla kordinater. Låt oss skriva en funktion för detta: symtopixelsat loc [] = [] symtopixelsat loc ((i,j):ijs) = coordtopixelsat loc (i, j) : symtopixelsat loc ijs Nu kan vi förvandla listan med koordinater till en polygon med typen Graphic symtographic loc sy = polygon (symtopixelsat loc sy) Denna funktion kan användas för vilken pjässort som helst och även för en schackruta, som ju är en polygon. Förbättringar kan nu göras om man är van vid Haskell (bl a kan högre ordningens funktioner): - I mönstret (i,j) används inte i och j för sig i högerledet; räcker med att skriva till ex coord. - Ännu flottare läsningar kan helt undvara denna parametrar. - Genom att använda map eller listomfattning kan symtopixelsat skrivas mer kortfattat. - symtographic kan skrivas "direkt" utan att definiera coordtopixelsat, scalewithsquaresize, och symtopixelsat.ger en lösning på en rad som också är lättläst.