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

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

DD1361 Programmeringsparadigm. Carina Edlund

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

Föreläsning 5 i programmeringsparadigm.

Funktionell programmering DD1361

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

Programmering i C++ En manual för kursen Datavetenskaplig introduktionskurs 5p

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

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.

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

Nada Tentamensdag 2004 okt 18 Tentamen Programmeringsparadigm Skrivtid 5 h

Enjoy Vattenfallsmodellen i funktionella språk

Enjoy Vattenfallsmodellen i funktionella språk

Introduktion till Haskell

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

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

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

Välkomna till DIT012 IPGO. Tyvärr en bug i Google Docs: Sidnummer stämmer inte alltid. Alla anteckningar börjar på sidan 1.

Använda Python Laboration 1 GruDat, DD1344

TDDC74 Programmering: Abstraktion och modellering Tenta, kl 14 18, 11 juni 2014

Föreläsning 4 i programmeringsparadigm.

Föreläsning 8 i programmeringsparadigm. Kommentarer kring schacklabben.

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

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.

Föreläsning 2. Täcker material från lektion 1, 2, 3 och 4:

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

kl Tentaupplägg

MMA132: Laboration 2 Matriser i MATLAB

Program & programmering

Labb i Datorsystemteknik och programvaruteknik Programmering av kalkylator i Visual Basic

F4. programmeringsteknik och Matlab

Programmering med Java. Grunderna. Programspråket Java. Programmering med Java. Källkodsexempel. Java API-exempel In- och utmatning.

Användarhandledning Version 1.2

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

Sätt att skriva ut binärträd

Eclipse. Avsikt. Nu ska ett fönster liknande figuren till höger synas.

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

Objektorienterad programmering i Java I. Uppgifter: 2 Beräknad tid: 5-8 timmar (OBS! Endast ett labbtillfälle) Att läsa: kapitel 5 6

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

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

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

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

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

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

Objektorienterad programmering Föreläsning 2

FrontPage Express. Ämne: Datorkunskap (Internet) Handledare: Thomas Granhäll

NetBeans 7. Avsikt. Projektfönster

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?

Föreläsning 2 Programmeringsteknik DD1310. Programmering. Programspråk

Klassdeklaration. Metoddeklaration. Parameteröverföring

Introduktion till programmering D0009E. Föreläsning 5: Fruktbara funktioner

Imperativ programmering. Föreläsning 2

Föreläsning 6: Introduktion av listor

TDDD78, TDDE30, 729A Introduktion till Java -- för Pythonprogrammerare

NetBeans 5.5. Avsikt. Projektfönster

Alla filer som bearbetar PHP script ska avslutas med ändelsen.php, exempelvis ska en indexsida till en hemsida heta index.php

Repetition i Python 3. Exemplen fac. Exemplen fac motivering. Exemplen fac i Python

Variabler och konstanter

729G04 Programmering och diskret matematik. Python 2: Villkorssatser, sanningsvärden och logiska operatorer

Del : Paradigmer allmänt.

732G Linköpings universitet 732G11. Johan Jernlås. Översikt. Repetition. Exempelduggan. Luffarschack. Koda spel

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

Funktionell programmering. Haskell. Ge#ng started...

Några inbyggda funktioner (med resultat!) Introduktion till programmering D0009E. Föreläsning 4: Villkor och rekursion. Modulus-operatorn.

Programmering I Tobias Wrigstad fredag, 2009 augusti 28

Introduktion till programmering SMD180. Föreläsning 5: Fruktbara funktioner

Textsträngar från/till skärm eller fil

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

TDDD78, TDDE30, 729A Introduktion till Java -- för Pythonprogrammerare

Del : Paradigmer allmänt.

Tentamen i TDP004 Objektorienterad Programmering Praktisk del

Backcode. Jonathan Crusoe TDP019 Projekt: Datorspråk Linköpings universitet

BEGREPP HITTILLS FÖRELÄSNING 2 SAMMANSATTA UTTRYCK - SCHEME DATORSPRÅK

Objektorienterad programmering E. Telefonboken, än en gång. Gränssnitt. Telefonboken med gränssnitt specificerat, del 1.

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

TDIU01 - Programmering i C++, grundkurs

TDDC74 Lab 04 Muterbara strukturer, omgivningar

System.out.println("Jaså du har "+ antalhusdjur+ " husdjur"); if ( antalhusdjur > 5 ) System.out.println("Oj det var många);

Instruktioner - Datortentamen TDDD73 Funktionell och imperativ programmering i Python TDDE24 Funktionell och imperativ programmering del 2

Programmeringsteknik med C och Matlab

Labb LIVE. Exempelkod från föreläsningen. Plushögskolan Frågeutveckling inom MSSQL - SU14

Repetition C-programmering

Föreläsning 2 Programmeringsteknik och C DD1316. Programmering. Programspråk

Introduktion till algoritmer - Lektion 4 Matematikgymnasiet, Läsåret Lektion 4

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

Rekursiva algoritmer sortering sökning mönstermatchning

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

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

Introduktion till programmering SMD180. Föreläsning 4: Villkor och rekursion

Översikt MERA JAVA OCH ECLIPSE. Uttryck och tilldelning. Uttryck och tilldelning. Uttryck och tilldelning. Uttryck och tilldelning

729G04 Programmering och diskret matematik. Föreläsning 7

TDDC74 Programmering: Abstraktion och modellering Dugga 2, kl 8 10, 5 mars 2015

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

TDP002 - Imperativ programmering

Föreläsning 2 Programmeringsteknik DD1310. Programmering. Programspråk

kl Tentaupplägg

Tentamen Grundläggande programmering

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

Imperativ och Funktionell Programmering i Python #TDDD73. Fredrik Heintz,

Transkript:

Föreläsning 6 i programmeringsparadigm. Tips kring programmering i Haskell och kring labbarna. Att arbeta med två fönster. Hugs är ju en tolk (interpreter) vilket har stora fördelar vid programutveckling. Man kan ju efter en inledande inladdning till Hugs med :l fortsätta med att göra :r när man gör någon ändring i filen med Haskellkod (spara först!). I Hugs kan man sedan skriva olika uttryck och göra små experiment med sina definierade funktioner och se vad funktionerna får för resultat med olika (enkla) argument. För att få information om något namn som man definierat eller som definierats i preluden, t ex en funktions typ, kan man göra :i <namn> eller : info <namn>: Prelude> :i length length :: [a] -> Int Man kan också helt enkelt skriva funktionsnamnet som ett uttryck (en funktion är ju ett värde, och ett värde är det enklaste formen av uttryck): Prelude> length ERROR - Cannot find "show" function for: *** Expression : length *** Of type : [a] -> Int Värdet skrivs inte ut då funktioner inte ingår i typklassen Show, varför någon funktionen show inte finns för funktionsvärden. Man kan också använda :t (eller :type ) : Prelude> :t length length :: [a] -> Int Att själv skriva typer: Jag rekommenderar att när man skall skriva t ex en ny funktion först tänka ut typen och skriva in den i Haskell-texten, trots att det i regel egentligen inte behövs då Haskell själv härleder typen. Sedan definierar man funktionen / värdet. Protesterar Haskell, t ex om är typen för generell eller om man får något annat typfel, kan man kommentera bort typuttrycket (med en-raders-kommentar : --, OBS mellanslag efter -- ) och försöka igen. Går det bra kollar man vad Haskell föreslår för typ. Sedan tänker man. Observera: I definitioner med flera ekvationer att Haskell bestämmer sig för typen i första ekvationen om man inte typar själv. Hittar hugs en annan typ i andra ekvationer blir det fel. Typiskt är att man stirrar på ekvationen för det rekursiva fallet, och inte kan finna något fel, pga att man skrivit basfallet först med något trivialt fel. Flerraders-kommentarer skivs med {- -}.

Krånglig funktion: Definiera en hjälpfunktion! Om vikten av att definiera funktionerna för hela definitionsområdet: Hugs kollar inte att man definierat en funktion så att alla tänkbara argument "matchar" någon ekvation. Glömda fall resulterar i körfel. Till exempel : f :: Int -> Int Körning: Main> f 14 f 14 = 2 2 Bättre definiton: Main> f 3 Program error: {f 3} f :: Int -> Int f 14 = 2 f i = error ("f " ++ show i ++ " odefinierat") Det är ofta bra att som sista ekvation skriva ett sköns-fall, default-fall, som alltid "matchar", t ex när man håller på med legalmove för ADT Chessman: legalmove =...... legalmove = error "Inte implementerat än i legalmove" När man tycker man är klar kan man byta texten till "Inträffar aldrig i legalmove". Inträffar det i alla fall har man tänkt fel. När man t ex skriver chessquaretographics:: Square -> Chessman -> [Graphic] i steg tre är det viktigt att komma ihåg NoChessman. Hur skriver man en lista med ett enda element (kan vara basfall ibland)? Det finns två sätt: [1] eller 1 :[] är en [Int] med ett enda element med värdet 1. Båda sätten [x] eller (bättre tycker jag) (x :[]) fungerar också i möster ("patterns"). Funktionen concat i steg 6. I steg 6 får man lätt typen[[graphics]] (64 element i listan där elementen består av listor med bilden av en ruta och ibland också bilden av en schackpjäs). I stället vill man ha [Graphics]. Att "platta till" listan gör man med en funktion: concat concat [] :: [[a]] -> [a] = [] concat (x:xs) = x ++ concat xs -- finns förstås färdig i Prelude.

Innehållet i en ADT. Ex i steg 6. En förfrågan : Jag och min labbkompis håller på och kämpar med schacket och har kommit till steg 6, d.v.s vi håller på och implementerar funktionen chesstographics() och har stött på lite patrull. Frågan är hur man fiskar ur (Square, Chessman)-paren från Chess-typen Chess [(Square, Chessman)]? Vi har satsat på att, precis som du tipsar om i kompendiet, att mappa listan i Chess mot chessquaretographics() funktionen via en hjälpfunktion. Problemet är att vi lyckas inte få typerna att matcha. Detta är vår ansats till lösning, som vi tycker ligger närmast till hands: ----------------------------------------------------------- -- Creates a Graphic list for the chessboard. chesstographics :: Chess -> [Graphic] chesstographics chess = map helper chess helper :: (Square, Chessman) -> [Graphic] helper (sq, cman) = chessquaretographics sq cman ----------------------------------------------------------- Svar : Använd mönsterpassning (pattern matchning)! Lösning: chesstographics :: Chess -> [Graphic] chesstographics (Chess squarechessmans) = map helper squarechessmans Kommentar : I de flesta språk "plockar man sönder en ADT" med selektorer (motsatsen till konstruerare) sådana funktioner kan man skriva i Haskell också förstås. För den inbyggda typen listor finns head :: [a] -> a tail:: [a] -> [a] och för par finns fst :: (a,b) -> a snd : (a,b) -> b Det är ck enklare att använda mönsterpassning (pattern match), t ex (för listor [], (x:xs), för par(a,b) ). Detta gör vana Haskell-programmerare alltid i den modul som definierar ADT-en. Vill man använda mönsterpassning i andra moduler (och det vill man nog ofta), måste man exportera konstruerarna för den konkreta datatypen. Datatypen är då inte längre abstrakt tyvärr. Konstruerarna definieras ju så här: data <Nytyp> = <Konstuerare>.. <Konstuerare> Tre sätt att exportera konstruerarna: module <NytypDT> where -- allt exporteras, enklast module <NytypDT> (<här räknar man upp det som skall exporteras inkl <Nytyp> och konstuerare> ) where module <NytypDT> (<här räknar man upp det som skall exporteras inkl <Nytyp> (..)>) where

"uncurring" mm. Ex helper ovan steg 6. Funktionen helper (sq, cman) = chessquaretographics sq cman i teknologlösningen ovan behövs för att chessquaretographics har typen chessquaretographics :: Square -> Chessman -> [Graphic] men skulle kunna funktionen direkt som argument till map om den hade typen chessquaretographics:: (Square, Chessman) -> [Graphic] Finns det andra lösningar än att använda en hjälpfunktion? Jomenvisst : 1. Använder man listomfattning är det inga problem: chesstographics (Chess rl) = concat [ chessquaretographics square cm (square, cm) <- rl] 2. Skriva om chessquaretographics.(tar mindre än en minut.) 3. Vi kan definera helper = \(sq, cman) -> chessquaretographics sq cman Lamda-uttrycket kan då "smackas in direkt" i map-uttrycket (Tar ca 20 sekunder att göra.) chesstographics (Chess squarechessmans) = map ( \(sq, cman) -> chessquaretographics sq cman) squarechessmans 4. Det skulle vara bra att slippa göra sådana här förvandlingar varje gång problemet dyker upp. Hur löser man det? Med funktioner förstås: Två användbara funktioner (finns förstås i Prelude) (överkurs i fjol) : 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 -- Om du tvilar på att det uncurry kan vara så enkel, förenkla detta uttryck! I vårt fall : (tar 5 sekunder om man känner till att uncurry finns i Prelude) chesstographics (Chess squarechessmans) = map (uncurry chessquaretographics) squarechessmans

Interaktiva program. Inför steg 8 och 9. Se först labb-häftet 30 <= sid <=33. Inför steg 8 och 9. Interaction main =... loop =... styrning (control) modell Chess Square Chessman startstatus, vy firstgraphs ChessGraphic i PlayChess Computational "Man ska inte gråta över spilld mjölk" "Tja, man blir inte yngre med åren " Lite mer om IO-programmering. Kernel ("Vanlig Haskell") Shell -> IO a "tiden går" ireversibilitet S > 0 "gjort är gjort och kan inte göras ogjort" World omvärlden När vi programmerade vårt interaktiva skal använde vi ADT IO b med dessa operationer/funktioner: (>>=) :: IO a -> (a -> IO b) -> IO b (>>) :: IO a -> IO b -> IO b -- ofta IO ()->IO () -> IO () return :: a -> IO a -- alla finns i Prelude för IO b (>>) är en variant av (>>=) och används när vi inte behöver det värde som kom ut ur åtgärden IO a. (>>) kan definieras som action1 >> action2 = action1 >>=(\_ -> action2) return används när man vill tillverka en åtgärd, till exempel en hjälpfunktion av typ IO a, där värde med typen a beräknas ur vanliga Haskell- värden. Omvärden är oförändrad efteråt. a a w1:world return w1:world

För att visa att IO-programmering är riktig Haskell används operatorerna, men normalt använder man -syntaxen, då man skriver åtgärder under varandra efter reserverade ordet på ungefär detta sätt: operator-syntax -sekvenser med "satser" e >>= \x -> s x <- e -- x:: a, e :: IO a s -- s oftast typen IO (), -- exvis s1 x e >> s e -- e oftast typen IO() s -- s oftast typen IO() let d in e let d -- d ofta vanlig Haskell e Ett exempel : I vårt Schack-program steg 8 och 9 föreslås hur IO skall programmeras med givna funktioner main och loop.all interaktion med omvärden i loop på dessa ställen (fet stil ); all användning av "vanlig Haskell "i loop, ddvs uttryck med annan typ än IO a, är kursiverat: loop :: Winw -> (Colour, Chess) -> [Graphic] -> IO () loop w (cl, chess) graphs = sequence_ (map (drawinwinw w) graphs) -- rita ändringar (1.a gången allt) (i1, j1) <- getlbp w let sq1 = tosquare (i1, j1) -- flera def efter let OK chessman1 = chessmanat sq1 chess if not (correctcolour cl chessman1) then loop w (cl, chess) (mess1 cl "Wrong chessman colour. ") else sequence_ (map (drawinwinw w) messs) -- rita ny info på skärmen (i2, j2) <- getlbp w -- klick 2 let sq2 = tosquare (i2, j2) -- målrutan chessman2 = chessmanat sq2 chess -- pjäs på målrutan if not... Har man börjat programmera med åtgärder, dvs värde med typ IO b, kan man alltså använda beräkningar i beräkningskärnan som vi gjort ovan, - dels efter let, - dels när vi beräknar argument, men man kan inte få ett slutresultat av annan typ än IO a, eftersom >>, >>= och return alla ger resultat av typen IO, dvs skalet skyddar beräkningskärnan. Om denna funktion unsafeperformio :: IO a -> a skulle finnas i ADT IO i Haskell 98 skulle vi kunna skriva funktioner med sieffekter, världen av typ Word skulle kunna påverka resultatet av typ a. För en imperativ programmerare känns det ovant att man inte kan programmera vanliga funktioner med sieffekter, men det är avsiktligt; det är nämligen detta förhållande som gör att vi inte förstör Haskells funktionella karaktär. (Men funktionen unsafeperformio finns i ett paket man kan importera, så man kan synda, men det skall man naturligtvis inte göra för det är farligt, farligt (unsafe)...).

Man kan programmera vårt exempel med en hjälpfunktion IO :: Winw -> Chess -> [Graphic] -> IO (Square, Chessman) så här : loop :: Winw -> (Colour, Chess) -> [Graphic] -> IO () loop w (cl, chess) graphs = (sq1, chessman1) <- IO w chess graphs if not (correctcolour cl chessman1) then loop w (cl, chess) (mess1 cl "Wrong chessman colour. ") else (sq2, chessman2) <- IO w chess messs if not... För att definiera IO behövs return : IO :: Winw -> Chess -> [Graphic] -> IO (Square, Chessman) IO w chess graphs = sequence_ (map (drawinwinw w) graphs) (i, j) <- getlbp w let sq = tosquare (i, j) chessman = chessmanat sq chess return (sq, chessman) -- :: IO (Square, Chessman) En -sekvensen har den typ som sista åtgärden, "satsen" har. Så fort en åtgärds-sekvens har minst två åtgärder krävs ett. Egentligen är en -sekvens ett uttryck, "satser" saknas alltså i funktionella språk. Paret (i, j) är en parametrar; -syntaxen ovan är en omskrivning av IO w chess graphs = sequence_ (map (drawinwinw w) graphs) >> getlbp w >>= \ (i, j) ->... -- resten av IO kan använda parametern (1,j) I praktiken programmerar man det interaktiva skalet på liknade sätt som i imperativa programmering. Om man använder hugs behövs ju överhuvud taget inget interaktiv skal så länge inte inmatning och utmatning är "inflätade" i varandra eller man behöver använda filer, ska rita, mm. Man försöker förstås göra skalet "tunt", eftersom det är så trevligt att programmera med vanlig Haskell i "beräkningskärnan", "the Computional Kernel". Men när man använder IO-programmering hävdar Pyton-Jones: "In short, Haskell is the world s finest imperative language" Hemligheten med IO-monad-programmering är precis som i vanliga Haskell att ha ordning på typerna. I programfragmentet nedan är den kursiverade texten vanlig Haskell, dvs uttryck som har andra typer än IO b, dvs vi lämnar "skalet" och använder oss av "beräkningskärnan". Typerna för "satserna" i sekvenserna är kommenterade.

loop :: Winw -> (Colour, Chess) -> [Graphic] -> IO () loop w (cl, chess) graphs = (sq1, chessman1) <- IO w chess graphs -- (sq1, chessman1) :: (Square,Chessman) -- IO w chess graphs :: IO (Square,Chessman) if not (correctcolour cl chessman1) -- IO () hela if-uttrycket then loop w (cl, chess) (mess1 cl "Wrong..") -- IO () uttrycket efter then else -- IO () uttrycket efter else (sq2, chessman2) <- IO w chess messs -- (sq1, chessman1) :: (Square,Chessman) -- IO w chess graphs :: IO (Square,Chessman) if not... -- IO () hela if-uttrycket then loop... -- IO() uttrycket efter then else... let newcolor = othercolor cl... in loop.. -- IO () uttrycket efter else Varför värden beter sig korrekt i ADT IO b. enda sättet att kombinera åtgärder, världen förändras stegvis, som den ska Haskell och andra språk. Haskell hade troligen varit ett mer använt språk om det tidigt funnits standard för samverkan med kod skrivit i andra språk. Standard för detta börjar komma först nu. Man kan kanske tycka att språket är så bra att man inte behöver beblanda sig med underlägsna imperativa språk, men de finns personer som anser en sådan inställning orealistisk. Eftersom kod skrivna i andra språk ofta har sieffekter bör Haskell betrakta de flesta importerade funktioner i skrivna i "främmande" språk som funktioner med resultat av typ IO (). Kompilering och interpretering. Hugs är en interpretator, och i början skrev vi uttryck i hugs som hugs då beräknade. Från steg 3 i schacklabben har vi ibland skrivit funktioner av typen IO (), som vi döpt till main. I dessa funktioner har vi kunnat växelverka med omvärden. I definitionen av Haskell står att det alltid skall finnas en funktion main :: IO(), dvs main :: World -> ((), World). Programmet startar med att beräkna main och tar alltså hänsyn till världen och påverkar värden. Hugs klar ju detta, men dessutom kan vi få hugs att beräkna uttryck av godtycklig typ. På liknade sätt som Blue J kan använda java på ett mer avancerat sätt än en java-kompilator, kan hugs mer än en Haskell-kompilator. Den mest använda kompilatorn för Haskell heter ghc och program för ghc skall ha en funktion main :: IO()som startar körningen.