Uitwerking tentamen Functioneel Programmeren 29 januari 2009
1. Een van de functies die door de ontwikkelaars van Wall-E is gebruikt is iterate: iterate :: (a a) a [a] iterate f a = [a : iterate f (f a) ] 1.1. Leg uit wat iterate uitrekent. Antwoord: iterate f a bouwt een oneindig lange lijst waarvan het i-e element het resultaat is van de functie f i keer toepassen op a, ofwel: iterate f a = [a, f a, f (f a), f (f (f a)),...]
1.2. Neem de volgende toepassing van iterate: iterate (drop 5) [1..50] Geef de uitkomst van deze functie aanroep. Antwoord: iterate (drop 5) [1..50] =[[1..50], drop 5 [1..50], drop 5 (drop 5 [1..50]), drop 5 (drop 5 (drop 5 [1..50])), drop 5 (drop 5 (drop 5 (drop 5 [1..50]))),...] =[[1..50], [6..50], [11..50], [16..50],..., [46..50], [], [], [], [],...]
1.3. De hoeveelheid afval die Wall-E moet opruimen is schier oneindig. Leg uit waarom het tweede argument van bovenstaande functie aanroep wel of niet een oneindig lange lijst kan zijn. Bijvoorbeeld: iterate (drop 5) [1..]. Antwoord: Het argument kan wél een oneindige lijst zijn. Door de luie evaluatiestrategie van Clean wordt alleen dát deel van deze resultaatlijst geëvalueerd dat nodig is, en niet verder. Bijv.: take 3 (map hd (iterate (drop 5) [1..])) = [1,6,11]
2. De ontwikkelaars van Wall-E hebben de volgende functie gebouwd met behulp van een aantal standaard Clean functies: unfold h p t = map h o takeWhile p o iterate t 2.1. Leid het meest algemene type af van unfold. Antwoord: iterate :: (a a) a [a],dan t :: (a a) takeWhile :: (a Bool) [a] [a],dan p :: (a Bool) map :: (a b) [a] [b],dan h :: (a b) (g o f):: (b c) (a b) (a c) dan: ((takeWhile p) o (iterate t)) :: a [a] en: ((map h) o ((takeWhile p) o (iterate t))) :: a [b] dus: unfold h p t :: (a b) (a Bool) (a a) (a [b])
2.2. Leg uit wat unfold uitrekent. Antwoord: unfold h p t is een functie die een aantal andere functies aan elkaar koppelt middels functie compositie (o). Gegeven een argument x :: a, wordt hier eerst de functie t geïtereerd zoals uitgelegd in onderdeel 1.1. Hier komt dus een oneindig lange lijst uit. Hiervan wordt het (mogelijk oneindige) beginstuk genomen tot een element niet aan predikaat p voldoet. Daarna worden al deze elementen omgezet m.b.v. de functie h.
2.3. Neem de volgende toepassing van unfold: Start = groepeer 5 afval groepeer n= unfold (take n) (not o isEmpty) (drop n) afval= [1..50] Geef de uitkomst van deze Start regel. Antwoord: Start= groepeer 5 afval = unfold (take 5) (not o isEmpty) (drop 5) [1..50] = map (take 5) (takeWhile (not o isEmpty) (iterate (drop 5) [1..50])) = map (take 5) (takeWhile (not o isEmpty) [[1..50], [6..50],..., [46..50], [], [], [], [],...]) = map (take 5) [[1..50], [6..50],..., [46..50]] = [[1..5], [6..10], [11..15],..., [46..50]].
3. Omdat Wall-E autonoom en eeuwenlang moet kunnen opereren, is het van belang dat zijn software correct is. Bewijs voor de Wall-E ontwikkelaars dat de volgende stelling geldt voor ieder mogelijk getal n 0, functie f en waarde x: take n (iterate f (f x)) = take n (map f (iterate f x)). Antwoord: Bewijs met inductie naar n. Basis: toon aan voor n = 0. Inductie: neem aan dat eigenschap geldt voor zekere n 0 en toon aan voor (n+1).
Benodigde functie-definities (iterate, take, map): iterate :: (a a) a [a] iterate f a = [a : iterate f (f a) ](1) take :: Int [a] [a] take 0 _ = [](2) take n [x : xs] = [x : take (n-1) xs](3) map :: (a b) [a] [b] map f []= [](4) map f [x : xs]= [f x : map f xs](5)
Basis: bewijs eigenschap voor n = 0 (aanname) take n (iterate f (f x)) = take n (map f (iterate f x)) (aanname) (2) take 0 (iterate f (f x)) = take 0 (map f (iterate f x)) (2) [] = [] (qed)
Inductie: eigenschap geldt voor zekere n 0, ofwel: voor iedere functie f en waarde x: take n (iterate f (f x)) = take n (map f (iterate f x))(IH) Te bewijzen: take (n+1) (iterate f (f x)) = take (n+1) (map f (iterate f x)) Bewijs: take (n+1) (iterate f (f x))(1) =take (n+1) [f x : iterate f (f (f x))](3 want (n+1) 0) =[f x : take n (iterate f (f (f x))](IH) =[f x : take n (map f (iterate f (f x)))](htk wiskunde) =[f x : take ((n+1)-1) (map f (iterate f (f x)))](3 ) =take (n+1) [f x : map f (iterate f (f x))](5 ) =take (n+1) (map f [x : iterate f (f x)])(1 ) =take (n+1) (map f (iterate f x))(qed)
4. In Wall-E’s wereld wordt afval natuurlijk gescheiden verwerkt. Om dit zo algemeen mogelijk te maken, hebben zijn makers gebruik gemaakt van een route die Wally-E keurig vertelt naar welke afvalhoop hij een volgend stuk afval moet brengen. De datastructuren zijn als volgt gedefiniëerd: :: Richting = Links | Rechts :: Route afval = Y (afval Richting) (Route afval) (Route afval) | Hoop [afval] Een Y-splitsing vertelt Wall-E of hij naar Links of Rechts verder moet gaan. Bij een Hoop aangekomen, moet hij die hoop uitbreiden met het stuk afval. Een hoop afval mag echter niet hoger worden dan een zekere gegeven max_hoogte (> 0). Als Wall-E bij een dergelijke hoop aankomt, dan ontstaat er een nieuwe Y-splitsing, waarvan de linker-route naar de maximaal gevulde hoop leidt, en de rechter- route bij een nieuwe, lege, hoop waar het stuk afval alsnog gelegd kan worden.
4.1. Schrijf een functie berg_afval :: afval (Route afval) Route afval die Wall-E een stuk afval laat bergen op de juiste hoop zoals hierboven beschreven. Antwoord: berg_afval afval (Y richting links rechts) = case richting afval of Links= Y richting (berg_afval afval links) rechts Rechts= Y richting links (berg_afval afval rechts) berg_afval afval (Hoop hoop) | length hoop < max_hoogte = Hoop [afval:hoop] | otherwise= Y ( Rechts) (Hoop hoop) (Hoop [afval])
4.2. Schrijf een functie kleinste_hoop :: (Route afval) [Richting] dieWall-E de route oplevert naar een hoop met het minst aantal afval elementen. Antwoord: kleinste_hoop (Hoop afval)= ([], length afval) kleinste_hoop (Y _ links rechts) | h_links < h_rechts= ([Links : r_links], h_links ) | otherwise= ([Rechts:r_rechts],h_rechts) where (r_links, h_links )= kleinste_hoop links (r_rechts,h_rechts)= kleinste_hoop rechts