Introduktion till programmering Men först: Några funktioner ur preluden Vad är skillnaden mellan head str och take 1 str? Föreläsning 4 Ett större exempel. head :: [a] -> a take :: Int -> [a] -> [a] tail :: [a] -> [a] head xs ger första elementet i xs. tail xs ger listan som återstår då det första elementet tas bort. Bägge ger fel om argumentlistan är tom. fst :: (a,b) -> a snd :: (a,b) -> b Funktioner för att välja ut komponenterna i ett par. Uppgiften Variationer av uppgiften Givet en text, skapa en frekvenslista som för varje ord anger hur många gånger ordet förekommer i texten. Listan ska vara sorterad efter frekvens. För Röda rummet börjar listan och 3247 att 1892 han 1694 en 1671 det 1633 i 1604 som 1275 är 992 jag 990 den 963 med 899 Många varianter möjliga listan alfabetiskt i stället. Ange relativa frekvenser i stället. Ignorera interpunktion; han och han. ska vara samma ord. Ignorera skillnaden mellan stora och små bokstäver; han och Han ska vara samma ord.... Hur kan vi lösa problemet? Detta verkar betydligt svårare än vad vi gjort tidigare.
igen (efter frekvens) [("vet",3),("han",2),("allt",1),("att",1),("inte",1),("jag",1)] igen (efter frekvens) [("vet",3),("han",2),("allt",1),("att",1),("inte",1),("jag",1)] Formattera Standardbibliotek Försök hitta delproblem som redan är lösta Att sortera är en mycket vanlig uppgift. Finns inte en funktion för detta i preluden? Nej(!) Men i biblioteksmodulen Data.List, som dokumenteras på http://www.haskell.org/ghc/docs/6.12.2/html/libraries/ API Ett bibliotek beskriver de funktioner som ingår i form av ett API (Application Programming Interface), bestående av namn, typ och en kort beskrivning av vad funktionen gör, t ex sort :: Ord a => [a] -> [a] The sort function implements a stable sorting algorithm. Hur ska man gruppera? En till hjälpfunktion från Data.List group :: Eq a => [a] -> [[a]] The group function takes a list and returns a list of lists such that the concatenation of the result is equal to the argument. Moreover, each sublist in the result contains only equal elements. For example, group "Mississippi" = ["M","i","ss","i","ss","i","pp","i"] Nästa steg Definiera den grupperingsfunktion vi vill ha med hjälp av group.
Kan man sortera par? Den fullständiga lösningen Ja, det kan man! Sortering sker i första hand efter första komponenten, i andra hand efter den andra. Så vi gör om freqs genom att byta plats på de två komponenterna i varje par. Ett litet problem Sortering sker nu i växande ordning; vi vill ha avtagande. Lätt fixat! Vi får lägga till en reversering av resultatet. Sedan återstår endast formattering. import Data.List main = interact translate translate s = format(reverse(sort(freqs(group(sort(words s)))))) freqs xs = [(length x, head x) x <- xs] format xs = unlines [snd x ++ " " ++ show (fst x) x <- xs] Funktionskomposition Variationer Vi har definierat translate genom successiva transformationer, litet analogt med rör (pipes) i Unix. Det finns en operator för detta i Haskell, men funktionerna skrivs i omvänd ordning: translate = format. reverse. sort. freqs. group. sort. words Vi slipper alla parenteserna, vi ser lättare de olika stegen och vi slipper hitta på ett variabelnamn för indata! Normalisering Vi kan normalisera indata genom att ta bort alla tecken utom bokstäver, blanktecken och radbyten; ersätta stora bokstäver med små. normalize s = [tolower c c <- s, isalpha c isspace c] Vi har utnyttjat tre funktioner från Data.Char: tolower :: Char -> Char isalpha :: Char -> Bool isspace :: Char -> Bool
En visualisering: histogram och ********************************** det *********************** han ********************* att ******************* en ***************** i **************** som *************** jag ************** på ************* den *********** är ********** så ********* med ********* för ******** Bara en annan formattering! Att upprepa ett tecken Vi behöver preludfunktionen replicate :: Int -> a -> [a] Ex: replicate 10 a = "aaaaaaaaaa" En ny variant av format format xs = unlines [ snd x ++ "\t" ++ replicate (div (fst x) 100) * x <- xs ] Tab-tecknet \t gör att asteriskerna (för det mesta) börjar i samma kolumn. Varje asterisk betyder 100 förekomster av ordet. Hur fås detta?