Haskell i all enkelhet

Thu 31 December 2009

For denne artikkelen går vi ut ifra at du har installert en fungerende Haskell implementasjon med interaktiv sessions. Hvordan du gjør dette kan du lese om

Aritmetikk:

Ikke uventet kan vi gjøre aritmetiske operasjoner med Haskell. Skriv inn et gyldig aritmetisk uttrykk og trykk enter.

Hugs> 1+2+3
6 :: Integer

Hugs> sqrt 2
1.4142135623731 :: Double

Hugs> pi
3.14159265358979 :: Double

Hugs> 10/0
1.#INF :: Double

Vi ser i eksempelene over at Haskell Interpreteren gir oss svaret, samt hvilken type den har interferensert svaret til. Mer om typer og inferens senere.

Par, tripletter, tupler:

Paranteser brukes for å gruppere verdier av samme type. To verdier er et par, tre er en triplett, og n verdier er en n-tuple.

Hugs> (0,0)
(0,0) :: (Integer,Integer)

Par med Integers.

Hugs> ('a','b')
('a','b') :: (Char,Char)

Par med Characters, merk ''.

Hugs> ("ab","cd")
("ab","cd") :: ([Char],[Char])

Par med Characterlister, d.v.s streng, merk "".

Hugs> ('a','b', 'c', 'd')
('a','b','c','d') :: (Char,Char,Char,Char)

En tuple med Characters, merk ''.

Vi ser noen tupler og hvordan Haskell Interpreteren interferer typene. Vi kan ha alle gyldige typer i en tupel.

Vi har noen innebygde funksjoner som virker på par. fst (first) og snd (second). Merk at verdiene i et par ikke trenger å være av samme type. Dette gjelder alle tupler.

Hugs> fst (0, 1)
0 :: Integer
Hugs> snd (0,1)
1 :: Integer
Hugs> fst ("string", 0)
"string" :: [Char]
snd (0, 'a')
'a' :: Char

Dersom du prøver disse funksjonene på annet en par vil du få en feilmelding fra Interpreteren.

Oppgave: Skriv en funksjon som returnerer 4 fra tuplet: (('a',4),"string")?

Løsning:

Hugs> snd (fst(('a',4),"string"))

4 :: Integer

Lister:

I motsetning til par, tripletter og tupler kan lister inneholde et ubegrenset antall elementer. Men lister krever derimot at alle elementer i listen er av samme type.

Hugs> [0,1,2,3]
[0,1,2,3] :: [Integer]

Liste med Integers.

Hugs> ['a','b','c','d']
"abcd" :: [Char]

Liste med Characters. D.v.s en streng.

Hugs> ["ab","cd","ef","gh"]
["ab","cd","ef","gh"] :: [[Char]]

Liste med strenger.

Hugs> []
[] :: [a]

Den tomme listen.

Lister defineres ved hjelp av [ ] (eng:square brackets). Tupler ble definert ved hjelp av ( ) (eng:paranthesis).

Vi konstruerer lister ved hjelp av operatoren con / : (tenk: construct) og den tomme listen.

Hugs> 'a' : 'b' : 'c' : []
"abc" :: [Char]
Hugs> 1 : 2 : 3 :
[1,2,3] :: [Integer]
Hugs> True : False : []
[True,False] :: [Bool]
Hugs> [] : []
[[]] :: [[a]]

Noen definerte funksjoner på lister.

Hugs> length [1,2,3]
3 :: Int
Hugs> head[1,2,3]
1 :: Integer
Hugs> tail[1,2,3]
[2,3] :: [Integer]

Length: Returnerer lengden på listen. D.v.s antall elementer som er i listen.

Head: Returnerer første elementet i listen.

Tail: Returnerer alt utenom første elementet i listen.

Som nevnt lenger oppe er en streng i Haskell en liste med karakterer.

Hugs> ['h','e','i']
"hei" :: [Char]
Hugs> 'h':'e':'i':[]
"hei" :: [Char]

Vi konkatinerer (setter sammen) lister med ++ operatoren.

Hugs> "Hei " ++ "verden"
"Hei verden" :: [Char]

Konverter string til numeriske verdier.

read "1" + 2
3 :: Integer
Hugs> read "1" + read "2"
3 :: Integer

Konverter numeriske verdier til string.

Hugs> "en plus to er lik " ++ show 3
"en plus to er lik 3" :: [Char]

Bruk en funksjon på alle elementer v.h.a listefunksjonen map. Map tar inn en funksjon, samt en liste med verdier og anvender den gitte funksjonen på alle elementer i listen. Merk at når vi mapper over en liste vil lengden på innlisten og utlisten være like. Vi endrer kun selve elementene i listen.

Char> map toUpper "hei verden"
"HEI VERDEN" :: [Char]
Char> map toLower "Hei Verden"
"hei verden" :: [Char]

Om du bruker en standard WinHugs implementasjon er det mulig du må laste modulen Char for å få tilgang til toUpper / toLower.

Char> :load Char

Hugs session for:
file:{Hugs}\packages\hugsbase\Hugs\Prelude.hs
file:{Hugs}\packages\base\Prelude.hs
file:{Hugs}\packages\hugsbase\Hugs.hs
file:{Hugs}\packages\hugsbase\Hugs\Char.hs
file:{Hugs}\packages\base\Data\Char.hs
file:{Hugs}\packages\haskell98\Char.hs
Char>

Vi kan filtrere ut elementer i en liste ved å bruke listefunksjonen filter.

Char> filter isUpper "FJERN meg"
"FJERN" :: [Char]
Char> filter isLower "FJERN meg"
"meg" :: [Char]

Vi kan gjøre operasjoner mellom alle elementene i en liste ved å bruke listefunksjonen foldr / foldl. Denne funksjonen tar tre argumenter: en funksjon, en start verdi og en liste.

foldr (+) 0 [1, 2, 3]
6 :: Integer

0+(1+2+3 )= 6.

foldr (*) 1 [1, 2, 3]
6 :: Integer

1*(1*2*3) = 6

Merk at vi i (*) eksempelet har satt 1 som start verdi istedenfor 0. Dette fordi 0*(1*2*3) blir 0, og dermed ikke det svaret vi er ute etter.

Ved å bruke foldr går vi ut ifra at uttrykket som skal evalueres er høyre assosiativt. Men dette er ikke alltid tilfelle, f.eks ved subtraksjon ( - ).

foldr (-) 1 [4,8,5]
0 :: Integer
foldl (-) 1 [4,8,5]
-16 :: Integer

Vi kan bruke foldl for å assosiere uttrykket mot venstre som vist i eksempelet over.

Merk at foldr evaluerer uttrykket forløpende og kan derfor i teorien brukes på uendelige lister, mens foldl evaluerer v.h.a en rekursiv stabel, og en vil derfor kun få resultat på endelige mengder.

foldr (-) 1 [4,8,5]
==> 4 - (foldr (-) 1 [8,5])
==> 4 - (8 - foldr (-) 1 [5])
==> 4 - (8 - (5 - foldr (-) 1 []))
==> 4 - (8 - (5 - 1))
==> 4 - (8 - 4)
==> 4 - 4
==> 0
foldl (-) 1 [4,8,5]
==> foldl (-) (1 - 4) [8,5]
==> foldl (-) ((1 - 4) - 8) [5]
==> foldl (-) (((1 - 4) - 8) - 5) []
==> ((1 - 4) - 8) - 5
==> ((-3) - 8) - 5
==> (-11) - 5
==> -16

Da avslutter vi denne artikkelen her, og gleder oss til neste artikkel kommer. Da skal vi se litt på hvordan vi kan utvide mulighetene våre ved å programmere egne funksjoner, legge de i en ekstern fil som vi laster inn i Interpreteren.