Eksamen i HUMIT 2710, Funksjonell programmering, våren 2005 Ingen hjelpemidler er tillatt. <resten av forsiden> Side 1 av 7
Oppgave 1 Rekursjon Fakultetsfunksjonen, her kalt Fak, kan defineres rekursivt slik: Fak(0) = 1, Fak(n) = n Fak(n 1), for n > 0 Implementer Fak først uten å bruke halerekursjon, og så ved hjelp av halerekursjon. Prosedyren rest tar to heltallsargumenter a og b og returnerer det som er igjen av a etter at alle hele b'er er tatt vekk, (rest 15 6) ==> 3 (rest 15 5) ==> 0 (rest 6 15) ==> 6 (rest 6 0) feilmelding: null-divisjon Implementer rest ved hjelp av subtraksjon, og uten å bruke halerekursjon. Prosedyren int-log-2 tar ett heltallsargument a og returnerer det helttallet 2 må opphøyes i, for å komme nærmest a nedenfra. (int-log-2 6) ==> 2, fordi 2 2 6 < 2 3, dvs. 4 6 < 8 (int-log-2 8) ==> 3, fordi 2 3 = 8 (int-log-2 12) ==> 3, fordi 2 3 12 < 2 4, dvs. 8 12 < 16 Implementer int-log-2. d) Prosedyren potens tar to heltallsargumenter a og b og returnerer 1 hvis b = 0, og ellers resultatet av at a ganges med seg selv b ganger. (potens 3 0) ==> 1 (potens 5 4) ==> 625 (potens 0 4) ==> 0 Implementer potens slik at antallet multiplikasjoner er av størrelsesorden (int-log-2. F.eks. skal kallene (potens 3 16) og (potens 3 20) begge kreve omtrent 4 multiplikasjoner. Side 2 av 7
Oppgave 2 Prosedyreobjekter og lambda-uttrykk Prosedyren neste-kombinasjon tar en kombinator k og et objekt x som argument og returnerer en prosedyre som tar et objekt y som argument og selv returnerer kombinasjonen k(x, y) (du kan gå ut fra at x og y lar seg kombinere av k). ((neste-kombinasjon + 2) 2) ==> 4 ((neste-kombinasjon cons 2) 3) ==> (2. 3) ((neste-kombinasjon / 1) 2) ==> 1/2 Fyll ut nedenstående kall med et passende lambda-uttrykk. ((neste-kombinasjon <???> 2) '(3. 4)) ==> (6. 8) Prosedyren delsum tar en unær prosedyre f og to heltall a og b som argumenter, og returnerer summen av leddene f(... f( i rekken S, når S har formen S = f( + f(a+1) + f(a+2) + Eksempel: (delsum (lambda (k) (/ 1 k)) 1 4) ==> 1 + 1/2 + 1/3 + 1/4 = 25/12 Implementer delsum ved hjelp av en lokal halerekursiv prosedyre. d) Kall delsum med passende lambdauttrykk som prosedyreargumenter, for å beregne summene av de 100 første leddene i hver av rekkene S og T. 1 S = 1 1 1 1 + 1 + + + + 2 2 3 2 3 4 T = 1 1 1 1 + + 3 5 7 1 S og T konvergerer hhv. mot e og π /4 uten at det har noen betydning her. Side 3 av 7
Oppgave 3 Lister og binære trær Prosedyren halvdeler deler en gitt liste i to deler, slik at andre del er like lang som eller har ett element mer enn første del. Prosedyrene tar de to listene del-1 og del-2 som argumenter. Ved kallet skal del-1 være tom, mens del-2 skal være den listen som skal deles. Returverdien fra prosedyren skal være paret med de to listene. (halvdeler '() '(1 2 3 4 5)) ==> ((1 2) 3 4 5) (halvdeler '() '(1 2 3 4 5 6)) ==> ((1 2 3) 4 5 6) Implementer prosedyren halvdeler. I det følgende ser vi på binære trær der hver node har to subtrær, og alle blader er tomme lister. Videre er hvert datum i et tre unikt; og, gitt en node N, er alle data i Ns venstre subtre mindre enn, og alle data i Ns høyre subtre større enn Ns datum. Endelig skal alle data være tall. Vi tar utgangspunkt i følgende abstraksjon: (define (lag-tre datum v-sub h-su (list datum v-sub h-su) (define (datum tre) (car tre)) (define (v-sub tre) (cadr tre)) (define (h-sub tre) (caddr tre)) Eksempel: BT = 10 (10 (6 (5 (1 () ()) ()) 6 15 (9 (8 () ()) ())) 5 9 13 17 (15 (13 (12 () ()) ()) 1 8 12 (17 () ()))) Prosedyren plasser-i-bintre tar et heltall og et binært tre som argumenter, og plasserer tallet i treet, eller returnerer en melding, i form av en streng, om at tallet evt. finnes i treet. Implementer plasser-i-bintre. Prosedyren bintre->liste tar et binært tre som argument og returnerer listen med treets data i stigende orden Implementer bintre->liste. Side 4 av 7
d) Prosedyren liste->bintre tar en liste med data i stigende orden som argument, og returnerer et binært tre med listens data Implementer liste->bintre bl.a. vha. prosedyren halvdeler fra punkt a (uavhengig av om du har implementert denne eller ikke). e) Vis og forklar kort hvordan vi ved hjelp av (deler av) ovenstående kan omforme et ubalansert binært tre til et balansert. Oppgave 4 Strømmer Definer et strømobjekt gertrud som genererer den implisitte strømmen (a rose is a rose is a rose is a rose...). Prosedyren fusjoner tar to strømmer S og T som argumenter og returnerer fusjonen F av disse, slik at første element i F er første elelement i S, andre element i F er første element i T osv. Eksempel: (fusjoner (1 2 3...) (a b c...)) ==> (1 a 2 b 3 c...) Implementer fusjoner. Prosedyren feedback tar en initialverdi a og to unære prosedyrer f og g som argument og returnerer den strømmen som dannes ved at de to prosedyrene mater hverandre gjensidig. Prosedyrene har følgende lokale objekter: - Prosedyren f-strøm tar en strøm s som argument og returnerer strømobjektet der a er første og løftet om avbildningen av s ved f er andre element. - Strømobjektet g-strøm er definert ved et kall på f-strøm med avbildningen av en strøm x ved g som argument. Hvilken strøm x må være, er en del av oppgaven. For at dette skal virke, kan ikke argumentet til f-strøm evalueres før definisjonen av både f-strøm og g-strøm er evaluert. Implementer feedback. Side 5 av 7
d) Prosedyren prosedyrenøste tar en unær prosedyre f som argument og returnerer strømmen av suksessivt tykkere nøster av f f.eks. slik at anvendelsen av tredje element på x = f ( f ( f (x) ) ). ((strømelement 1 (prosedyrenøste legg-til-1)) 1) ==> 2 ((strømelement 3 (prosedyrenøste legg-til-1)) 1) ==> 4 ((strømelement 4 (prosedyrenøste doble)) 1) ==> 16 ((strømelement 6 (prosedyrenøste doble)) 1) ==> 64 når (strømelement k s) ==> element nummer k i strømmen s, når vi teller fra 1. (legg-til-1 x) ==> x + 1, (doble x) ==> x 2, Implementer prosedyrenøste. e) Prosedyren feedback kan brukes til å utvikle nøstede brøker, som f.eks. brøken B under 2, og det samme kan vi oppnå vha. prosedyrenøste, ved passende valg av prosedyreargument. B = 1 + 1 1 + 1 1 1 + = 1 (1 + 1 (1 + 1 (1 + Vis hvordan vi kan utvikle B frem til ledd nummer 40, først ved hjelp av feedback og så ved hjelp av prosedyrenøste. (Merk at Scheme-primitiven for divisjon kan brukes som en unær prosedyre, slik at f.eks. (/ 5) ==> 1/5.) 2 B konvergerer mot φ 1, når φ er det gylne snitt uten at det har noen betydning her. Slutt på oppgaveteksten Side 6 av 7
Vedlegg 1 Relevante Scheme-primitiver (minus de aritmetiske operatorene) og annet (cons x y) ==> paret der x er første og y er andre element (x. y). (car x) ==> første element i paret x. (cdr x) ==> andre element i paret x. (list x y z) ==> det nøstede paret (x. (y. (z. ()))) = listen (x y z). (null? x) ==> true hvis x er den tomme listen. (length x) ==> antall elementer i listen x. (append x y) ==> sammenskjøtingen av listene x og y. (c<x>r x) kombinasjoner av car og cdr der <X> kan være inntil 4 c'er og/eller d'er, f.eks. slik at (cadar x) er en sammentrekning av (car (cdr (car x))). (error melding <evt. flere meldinger>) Skriver ut de gitte meldingene og gir kjøravbrudd. Strømabstraksjonen plus en nyttig strømprosedyre Spesialformen delay tar et uttrykk som "argument" og produserer et løfte om en mulig fremtidig evaluering av uttrykket, dvs. et objekt med det ennå ikke evaluerte uttrykket også kalt et utsatt objekt. Prosedyren force tar et utsatt objekt som argument og fremtvinger evalueringen av dettes uttrykk. Spesialformen cons-stream tar to "argumenter" x og y og returnerer strømobjektet (paret) der første element er x og andre element er et løfte om en mulig fremtidig evalueringen av y. Prosedyren stream-car tar et strømobjekt som argument og returnerer dettes første element. Prosedyren stream-cdr tar et strømobjekt som argument, fremtvinger evalueringen av dettes andre element og returnerer resultatet av evalueringen. Følgende strømprosedyre kan tas for gitt: stream-map (i en begrenset versjon) tar en prosedyre og en strøm som argument, anvender prosedyren på elementene i strømmen og returnerer strømmen med resultatene av disse anvendelsene. Side 7 av 7