Funktionell programmering Haskell Ge#ng started...
Installera Haskell För a/ installera Haskell på egen maskin, ladda ned Haskell Pla9orm från h/p://www.haskell.org/pla9orm/ Där finns instrukdoner, dokumentadon, etc. Den interakdva tolken startas med kommandot ghci $ ghci GHCi, version 7.6.3: http://www.haskell.org/ghc/ :? for help Loading package ghc- prim... linking... done. Loading package integer- gmp... linking... done. Loading package base... linking... done. Prelude>
GHCi som kalkylator Här kan man skriva in u/ryck som utvärderas och resultatet skrivs ut Prelude> 6 * 7 42 Prelude> sqrt 2 1.4142135623730951 Prelude> 2^8 256
Bindningar i GHCi Det är möjligt a/ göra Dllfälliga bindningar av värden Dll namn med hjälp av let OBS! Bindningar är inte variabler. Det är inte möjligt att ändra värdet, bara att binda ett annat till samma namn. Tidigare beräkningar påverkas inte. Prelude> let tau = 2 * pi Prelude> let unitcircumference = tau * 1 Prelude> unitcircumference 6.283185307179586 Prelude> let tau = 17 Prelude> unitcircumference 6.283185307179586
Att arbeta med GHCi Det förmodade arbetsflödet är a/ skriva definidoner i filer som laddas i GHCi OBS! Ställ in editorn du använder på a/ använda mellanslag för indragningar, inte tabbar Filer laddas med kommandot :load och kan sedan laddas om med :reload
Att ladda :iler Givet en fil med namnet Temps.hs och följande innehåll: convconst = 273.15 (nu behövs inte let ) Kan den laddas och användas på följande sä/: Prelude> :load Temps.hs [1 of 1] Compiling Main ( Temps.hs, interpreted ) Ok, modules loaded: Main. *Main> convconst 273.15 *Main> 0 - convconst - 273.15 *Main> 100 - convconst - 173.14999999999998
En första funktion DefiniDonen placeras i filen Temps.hs kommentar - - konvertera Kelvin till grader Celsius celsius x = x - convconst funkdonens namn namn på argument u/ryck för a/ räkna ut resultatet
Att använda funktionen *Main> :reload [1 of 1] Compiling Main ( Temps.hs, interpreted ) Ok, modules loaded: Main. *Main> celsius 0-273.15 *Main> celsius 100-173.14999999999998
Konvertera andra vägen - - konvertera grader Celsius till Kelvin kelvin x = x + convconst *Main> :reload [1 of 1] Compiling Main ( Temps.hs, interpreted ) Ok, modules loaded: Main. *Main> kelvin 0 273.15 *Main> celsius (kelvin 0) 0.0
Testning och egenskaper Vi kan u/rycka en allmän egenskap om vad som förväntas hända när man konverterar fram och Dllbaka prop_celsiuskelvin x = celsius (kelvin x) == x Det här är en vanlig funktion som uttrycker en egenskap som förväntas hålla oberoende av värdet på x *Main> :reload [1 of 1] Compiling Main ( Temps.hs, interpreted ) Ok, modules loaded: Main. *Main> prop_celsiuskelvin 0 True *Main> prop_celsiuskelvin 42 True
Att skriva egenskaper i :iler KonvenDon: Namnet på funkdoner som u/rycker en egenskap börjar med prop_ (från property) och förväntas vara sanna (utvärderas Dll värdet True oberoende av argumentens värden) För a/ automadskt testa dessa används e/ bibliotek som heter QuickCheck Det kan importeras i filer med: import Test.QuickCheck eller i GHCi: :module +Test.QuickCheck
Att köra test *Main> :reload [1 of 1] Compiling Main ( Temps.hs, interpreted ) Ok, modules loaded: Main. *Main> quickcheck prop_celsiuskelvin *** Failed! Falsifiable (after 2 tests and 1 shrink): 8.316199327150231e- 2 De/a är en klassiskt problem med fly/alsaritmedk och logiska jämförelser
Lösning på problemet Resultatet ska vara nästan det samma Det räcker a/ skillnaden är Dllräckligt liten, säg <0.000001 Vi kan använda operatorn < för a/ jämföra storlek *Main> 4 < 7 True *Main> 7 < 4 False *Main> 3 < 3 False
De:inition av nästan lika epsilon = 0.000001 x ~== y = x y < epsilon definierar operatorn ~== med argumenten x och y som utvärderas Dll True om skillnaden mellan värdena är Dllräckligt liten
Testa ~== *Main> 2 ~== (2 + epsilon) True *Main> 2 ~== 3 True *Main> 3 ~== 2 False Vad är problemet? (Svar på nästa sida...)
Testa ~== x ~== y = abs(x - y) < epsilon
Korrekt egenskap prop_celsiuskelvin x = celsius (kelvin x) ~== x *Main> prop_celsiuskelvin 0 True *Main> prop_celsiuskelvin 42 True *Main> prop_celsiuskelvin (- 10) True *Main> quickcheck prop_celsiuskelvin +++ OK, passed 100 tests.
Typer Hi#lls har vi inte behövt bry oss om datatyper, men om man gör följande så stöter man på problem. *Main> let utetemp = 15 *Main> kelvin utetemp <interactive>:32:8: Couldn't match expected type `Double' with actual type `Integer' In the first argument of `kelvin', namely `utetemp' In the expression: kelvin utetemp In an equation for `it': it = kelvin utetemp Vad är det som händer?
Typinformation i GHCi Vi kan använda tolken för a/ ta redan på vilken typ värden har *Main> :type utetemp utetemp :: Integer *Main> :type convconst convconst :: Double *Main> :type celsius celsius :: Double - > Double pilen anger a/ det är frågan om en funkdonstyp *Main> :type True True :: Bool
Typinferens Genom en process som kallas typinferens beräknas den slutgildga typen för e/ u/ryck Den förväntade typen kan påverka beräkningen av värdet Operatorn :: läses har typen och kan användas för a/ tvinga resultatet a/ ha en given typ *Main> 2^42 :: Double 4.398046511104e12 *Main> 2^42 :: Integer 4398046511104
Typkontroll Använder typinferens för a/ beräkna den förväntade typen Kontrollerar a/ alla typsignaturer passar ihop före programmet körs
Varför fungerade allt tidigare? Tal kan anta flera typer, men när de binds Dll e/ namn fixeras typen Prelude> 15 :: Integer 15 Prelude> 15 :: Double 15.0 Prelude> let utetemp = 15 Prelude> :type utetemp utetemp :: Integer Prelude> utetemp :: Double <interactive>:16:1: Couldn't match expected type `Double' with actual type `Integer' In the expression: utetemp :: Double In an equation for `it': it = utetemp :: Double
Lösningar på problemet Man kan andngen ange den typ som önskas vid bindningsdllfället I GHCi: let utetemp = 15 :: Double I filer: utetemp :: Double utetemp = 15 AlternaDvt kan funkdoner som möjliggör typkonvertering användas *Main> kelvin (fromintegral utetemp) 288.15