Lecture 5 - Haskell Exercises
Auto-generated from
haskell/lecture05/*.hsbysync-notes.sh- edit the .hs files, not this note.
Related: Lecture 1 - Intro to Functional Programming, Haskell - Polymorphism Rule of Thumb
preparation.hs
-- Lecture 5 - Preparation
-- Rekursion
-- Læsning: Kapitel 6 i Programming in Haskell + podcast.
--
-- Læringsmål:
-- * Forstå strukturen af en rekursiv funktionsdefinition
-- * Læse og skrive rekursive funktionsdefinitioner på lister
-- * Læse og skrive rekursive definitioner med flere rekursive kald eller
-- gensidig rekursion
-- * Skrive rekursive funktionsdefinitioner på en struktureret måde (Afsnit 6.6)
-- ===== Prep 1 =====
-- Definér funktionen replicate ved hjælp af rekursion - og brug mønstre i din
-- løsning. Denne funktion tager et heltal n og et element x og returnerer en liste
-- med n elementer, hvor x er gentaget præcis n gange. Som eksempel skal
-- replicate 3 5 give [5,5,5]
-- Hvilken type skal replicate have?
--replicatee :: (Eq n, Num n) => n -> [e] -> [e]
replicatee 0 [e] = []
replicatee n [e] = replicatee (n-1) [e] ++ [e]
replicateee n e = take n (repeat e)
-- >>> replicateee 4 1
-- >>> replicatee 4 1
-- No instance for `Num [()]' arising from a use of `it_aHYh'
-- In the first argument of `evalPrint', namely `it_aHYh'
-- In a stmt of an interactive GHCi command: evalPrint it_aHYh
-- >>> replicatee 3 5
-- [5,5,5]
-- ===== Prep 2 =====
-- Definér funktionen improve ved hjælp af rekursion - og brug mønstre i din
-- løsning. Den tager en liste xs og, hvis xs indeholder mindst to elementer,
-- returnerer den en liste, hvor hvert andet element er fjernet. Som eksempel skal
-- improve [1,2,3,4,5,6,7] give [1,3,5,7]
-- Hvilken type skal improve have?
--improve :: [a] -> [a]
improve ([]) = []
improve (x:[]) = [x]
improve (x:xs) = [x] ++ improve (drop 1 xs)
-- >>> improve [1,1,2,2,3,3,4,4]
-- [1,2,3,4]
-- >>> improve [1,2,3,4,5,6,7]
-- [1,3,5,7]
exercises.hs
-- Lecture 5 - Exercises
-- Rekursion
-- ===== Task 1 ===== (10 min)
-- Funktionen reverse findes i Haskell-præludiet. Den vender en liste, således at
-- f.eks. reverse [1,2,3] evaluerer til [3,2,1]. Din opgave er nu at definere din
-- egen version af denne funktion, rev. Prøv først at finde ud af, hvilken type rev
-- skal have, og følg den generelle tilgang beskrevet i Afsnit 6.6.
rev :: [a] -> [a]
rev ([]) = []
rev (x:xs) = rev xs ++ [x]
-- >>> rev [1,2,3,4,5]
-- [5,4,3,2,1]
-- >>> tail "ABCD"
-- "BCD"
-- ===== Task 2 ===== (15 min)
-- En liste [a1, a2, ..., an] er aftagende, hvis a1 >= a2 >= ... >= an. Skriv en
-- funktion descending, der giver os True, når den liste, den får som argument, er
-- aftagende, og False ellers. Eksempelvis skal descending [6,5,5,1] give True, og
-- descending ["plip","pli","ppp"] skal give False.
-- Hvad er dens type?
descending [] = True
descending (x:[]) = True
descending (x:y:xs)
| x >= y = descending xs
| otherwise = False
-- >>> descending [3,2,1]
-- >>> descending [3,2,2,1]
-- >>> descending [1,2,3]
-- >>> descending ["a","b","c"]
-- >>> descending ["c","b","a"]
-- True
-- True
-- False
-- False
-- True
-- >>> :t descending
-- >>> descending [6,5,5,1]
-- >>> descending ["plip","pli","ppp"]
-- ===== Task 3 ===== (20 min)
-- Funktionen wrapup er en funktion, der tager en liste og giver os en liste af
-- lister. Hver liste i denne liste indeholder de på hinanden følgende elementer
-- fra den oprindelige liste, der er identiske. For eksempel skal
-- wrapup [1,1,1,2,3,3,2] give [[1,1,1],[2],[3,3],[2]]
-- wrapup [True,True,False,False,False,True] give [[True,True],[False,False,False],[True]]
-- Definer wrapup i Haskell ved hjælp af rekursion (ikke listeabstraktion - det var
-- sidste uge!), men uden at bruge fst, snd, head eller tail.
-- Vink med vognstang: Husk definitionen af isolate-funktionen fra før.
--wrapup :: [a] -> [[a]]
wrapup (x:[]) = [x]
wrapup (x:xs)
| take 1 x == head xs = wrapup ([x ++ head xs] ++ tail xs)
| otherwise = [x] ++ wrapup xs
-- >>> wrapup ["a","a","b","c", "c"]
-- ["aa","b","cc"]
-- >>> wrapup [1,1,1,2,3,3,2]
-- >>> wrapup [True,True,False,False,False,True]
-- ===== Task 4 ===== (15 min)
-- En tidligere videnskabs- og uddannelsesminister har besluttet at tage en
-- universitetsuddannelse og forsøger nu at definere en Haskell-funktion triples,
-- der tager en liste af tupler (hver tupel har præcis 3 elementer) og konverterer
-- denne liste af tupler til en tupel af lister.
-- triples [(1,2,3),(4,5,6),(7,8,9)] skal producere ([1,4,7],[2,5,8],[3,6,9])
-- Ministeren skrev følgende stykke kode og en typespecifikation, men løb ind i
-- problemer. Hvad er der mon i vejen?
--
-- triples :: Num a => [(a,a,a)] -> ([a],[a],[a])
-- triples [] = ()
-- triples [(a,b,c)] = ([a],[b],[c])
-- triples (x:xs,y:ys,z:zs) = [x,y,z] : Triples [(xs,ys,zs)]
--
-- Kan du gøre noget ved ministerens problemer? Hvordan kan Afsnit 6.6 hjælpe dig her?
--
-- Hvad er der galt:
--
-- triples ()
triples' ((a,b,c):xs) = [a,b,c] ++ triples' xs
-- >>> triples [(1,2,3),(4,5,6),(7,8,9)]
-- ([1,4,7],[2,5,8],[3,6,9])
-- ===== Alphabet Worksheet =====
-- Her er Afsnit 6.6 rigtig nyttigt!
-- === A) ===
-- Funktionen rle er en funktion, der, når den får en liste xs, producerer en liste
-- af par af elementer fra xs og heltal (run-length encoding). Denne liste af par
-- har sine elementer i den rækkefølge, de oprindeligt optrådte, og indeholder
-- (x, n), hvis der er n på hinanden følgende forekomster af x i listen.
-- For eksempel skal
-- rle ['a','a','a','g','g','b','a','a'] give [('a',3),('g',2),('b',1),('a',2)]
-- rle [1,1,1,2,2,1,3,3] give [(1,3),(2,2),(1,1),(3,2)]
-- Definer rle i Haskell. Prøv først at finde ud af, hvilken type rle skal have,
-- og følg den generelle tilgang beskrevet i Afsnit 6.6.
-- rle ::
-- >>> rle ['a','a','a','g','g','b','a','a']
-- >>> rle [1,1,1,2,2,1,3,3]
-- [('a',3),('g',2),('b',1),('a',2)]
-- [(1,3),(2,2),(1,1),(3,2)]
-- === B) ===
-- Definer en funktion amy, der fortæller os, om nogle elementer i en liste
-- opfylder et givet prædikat. For eksempel, hvis
-- odd x = ((x `mod` 2) == 1)
-- så skal
-- amy odd [2,5,8,3,7,4] returnere True, mens
-- amy odd [2,8,42] skal returnere False.
-- amy ::
-- >>> amy odd [2,5,8,3,7,4]
-- >>> amy odd [2,8,42]
-- True
-- False
-- === C) ===
-- Lav en funktion frequencies, der, givet en streng s, opretter en liste af par
-- [(x1,f1),...,(xk,fk)] således, at hvis tegnene xi forekommer et samlet antal af
-- fi gange i listen s, så vil listen af par indeholde parret (xi,fi).
-- Som eksempel skal
-- frequencies "regninger"
-- returnere listen
-- [('r',2),('e',2),('g',2),('n',2),('i',1)]
-- Find først ud af, hvilken type funktionen skal have.
-- frequencies ::
-- >>> frequencies "regninger"
-- [('r',2),('e',2),('g',2),('n',2),('i',1)]
-- === D) ===
-- En sætning inden for talteori siger, at ethvert ikke-nul reelt tal x kan skrives
-- som en kædebrøk. Det er et potentielt uendeligt udtryk på formen
--
-- 1
-- x = a0 + ---------------------
-- 1
-- a1 + ----------------
-- 1
-- a2 + -----------
-- 1
-- a3 + ------
-- ...
-- 1
-- + ----
-- an
--
-- For rationale tal vil ai'erne til sidst alle være 0, så kædebrøken er endelig;
-- for irrationale tal vil den fortsatte brøk være uendelig.
-- Se f.eks. https://en.wikipedia.org/wiki/Continued_fraction for mere.
-- Målet med dette problem er at skrive en Haskell-funktion cfrac, der, givet et
-- reelt tal r og et naturligt tal n, finder listen af de første n tal i
-- kædebrøken for r. Hvad skal typen af cfrac være?
-- cfrac ::
-- >>> cfrac pi 5
-- [3,7,15,1,292]