TDDC74 Programmering: Abstraktion och modellering Datortenta - 2019-05-27, kl 08-12 Läs alla frågorna först och bestäm dig för i vilken ordning du vill lösa uppgifterna. Uppgifterna är inte nödvändigtvis i svårighetsordning. Använd väl valda namn på parametrar och indentera din kod. Väl valda namn inkluderar bland annat konsekvent språk. Du behöver inte skriva kodkommentarer, annat än för väldigt svårförklarade hjälpfunktioner (som bör undvikas). Namngivning ska vara tillräcklig (och följa konventioner). Skriv inte onödigt komplicerade lösningar. Om det är naturligt, definiera gärna hjälpfunktioner! Hjälpfunktioner ska som vanligt lösa tydliga och lättförklarade uppgifter. Du får använda alla tillgängliga primitiver och språkkonstruktioner i Racket, om annat inte anges i uppgiftens text. Frågor Är något otydligt i uppgifterna kan du använda meddelande-funktionen i tentaklienten för att skicka frågor till jourlärare. Att lämna in Skicka in uppgifterna med hjälp av tentaklienten, när du är klar med dem! Kontrollera att det står Begäran mottagen eller dylikt i tentaklienten, så att du vet att koden tagits mot av servern! Vänta inte på att alla uppgifter är klara med att lämna in. När du har lämnat in en uppgift, fortsätt arbeta på nästa. Du har en inlämning per uppgift (så skicka inte ev a- och b-uppgifter separat). Följ angivna namn, och testa att alla körexempel i uppgiften fungerar exakt som de är inskrivna! Betyg: För trea räcker ca 50% av det totala poängen. För en fyra räcker ca 65% och för en femma ca 80%. Ett resultat man inte är nöjd med, kan plussas vid ett senare tentamenstillfälle. Visning Ingen allmän visning. Kontakta examinator för visning, och för att diskutera din skrivning. Lycka till!
Uppgift 1. Enkelrekursion, liststrukturer (6p) Lämna in dina svar i fil uppg1.rkt. a) I denna uppgift ska du byta ett element på en given plats i en lista. Skapa den rekursiva proceduren replace-at som har parametrarna seq, idx (där idx 0, heltal) och val (valfri typ). Proceduren ska returnera en kopia av listan seq, men där heltalet på indexet idx har bytts mot val. Du ska inte gå in i eventuella underlistor. List-index börjar på 0. Det vill säga, om listan seq har n element så är 0 första elementet och n 1 det sista. Detta ska fungera: > (replace-at (a b c d e f) 0 NYTT) ;; Listindex börjar på noll. (NYTT b c d e f) > (replace-at (a b c d e f) 5 NYTT) (a b c d e NYTT) > (replace-at (a b c d e f) 99999 NYTT) ;; Index utanför listlängd. (a b c d e f) > (replace-at () 0 NYTT) ;; Inget speciellt med tomma listan. () b) Definiera en funktion replace-several som tar in en lista seq, en lista av index och ett värde. Din funktion ska returnera en ny lista med samma värden, men där elementen på platser i indexlistan har ersatts av det angivna värdet. Så här ska den fungera: > (replace-several (a b c d e f) (0 5 9999) NYTT) (NYTT b c d e NYTT) > (replace-several (a b c d e f) () NYTT) ;; Tom indexlista ok. (a b c d e f) > (replace-several () (0 1 2 3) NYTT) ;; Tom seq såklart ok. () Ni får självklart anropa lösning från föregående uppgift. 2
Uppgift 2. Dubbelrekursion över tal (3p) Mönstret nedan kallas Pascals triangel: Rad 0: 1 Rad 1: 1 1 Rad 2: 1 2 1 Rad 3: 1 3 3 1 Rad 4: 1 4 6 4 1... och så vidare... De yttersta siffrorna är alltid 1 och varje tal inuti triangeln är summan av de två tal som står över det. Implementera en rekursiv procedur pascal som beräknar talet på plats row, col. Ni kan räkna med att indata alltid är korrekt, så row col 0, och båda är heltal. Så här ska den fungera: > (pascal 0 0) 1 > (pascal 4 0) 1 > (pascal 4 1) 4 > (pascal 4 4) 1 > (pascal 5 3) 10 > (pascal 15 2) 105 3
Uppgift 3, Liststrukturer, grafisk representation (3 poäng) Lämna in dina svar i fil uppg3.rkt. a) (1,5p) Skriv Schemeuttryck som skapar följande struktur. Du får inte använda set-mcar! och set-mcdr! för denna deluppgift eftersom de skall inte behövas. Använd gärna define, let och/eller let* för att tillfälligt benämna delstrukturer som du skall använda flera gånger. b) (1,5p) Skriv Schemeuttryck som skapar följande struktur. Du får använda set-mcar! och set-mcdr! vid behov. Använd gärna define, let och/eller let* även här. 4
Uppgift 4, Bankkonton (ADT, closures) (4+2 poäng) Skriv dina svar i filen uppg4.rkt. Det är viktigt att alla funktioner som anges nedan har exakt samma namn som i uppgiften. Inkludera raden (provide (all-defined-out)) i din fil. a) Din uppgift är att implementera en datatyp som representerar enkla bankkonton för personer. Varje konto har ett nummer (account#) som är unikt och ett saldo (balance). Vi skapar en konstruktor make-account som tar kontonummer och saldo för ett konto och returnerar en procedur som sköter bankärenden för det kontot 1. Vi antar att make-account alltid får ett unikt kontonummer som inte skapats tidigare, därför slipper vi testa om ett konto med det numret finns. Varje konto ska kunna hantera meddelandena withdraw, deposit, balance. Hur de fungerar ser du i exemplet nedan. Läs detta noga! > (define acc1 (make-account 1 0)) ;; Account 1 > acc1 ;; Vad är detta? #<procedure> > (define acc2 (make-account 2 2000)) ;; Account 2, separat från account 1. > (acc1 deposit) ;; Vad returneras när vi ger meddelandet deposit? #<procedure:deposit> > ((acc1 deposit) 50000) ;; Sätt in 50000kr och returnera nya saldot. 50000 > (acc1 balance) ;; Returnera kontosaldo. 50000 > (acc2 balance) ;; Konto 2:s saldo ändrades inte. 2000 > ((acc1 withdraw) 999) ;; Ta ut 999kr från konto 1. Returnera nya saldot. 49001 > ((acc2 withdraw) 99999999) ;; Försök att ta ut mer än det finns på kontot. -1 > (acc2 balance) ;; Konto 2:s saldo ändrades inte. 2000 b) Skriv en procedur transfer som tar två konton - src och dest - och ett belopp amount. Om det finns tillräckligt med pengar på kontot src ska beloppet överföras till dest och det nya beloppet på dest returneras. Annars ska -1 returneras. 1 Sättet vi implementerar datatypen är alltså som ett lexikaliskt closure, eller mer informellt och handviftande en procedur och en bunt lokala variabler. 5
Uppgift 5. Enkelrekursion, tal (1+2+2p) Skriv dina svar i filen uppg5.rkt. Det är viktigt att alla funktioner som anges nedan har exakt samma namn som i uppgiften. Inkludera raden (provide (all-defined-out)) i din fil. a) I samband med laborationsarbetet har du arbetat med funktioner som bearbetar enskilda siffror i ett positivt heltal (> 0). Då definierade du funktioner såsom last-digit, but-last-digit. number-of-digits (som returnerar antalet siffror i talet). Dessa kommer att behövas till uppgifterna nedan. Definiera dem! > (last-digit 123) 3 > (but-last-digit 123) 12 b) Implementera en procedur number-of-digits som tar ett tal (heltal, 0) och returnerar antalet siffror i det. Så här ska det fungera: > (number-of-digits 3421) 4 > (number-of-digits 000003421) 4 > (number-of-digits 0) 1 Du ska använda dina funktioner från a-uppgiften, där det är relevant. c) Du skall nu definiera add-digit-first. add-digit-first tar ett heltal ( 0) och returnerar ett heltal bestående av siffran följt av de tidigare siffrorna i heltalet. Med andra ord lägger till siffran först enligt exemplen nedan. Lägger vi 0 först i ett tal ges ingen effekt. Ett heltal kan ej ha inledande nollor. Så här ska det fungera: > (add-digit-first 1 234) 1234 > (add-digit-first 1 0) 10 > (add-digit-first 1 10) 110 6
Uppgift 6. Användande av högre ordningens procedurer (2+1p) Skriv dina svar i filen uppg6.rkt. Det är viktigt att alla funktioner som anges nedan har exakt samma namn som i uppgiften. Inkludera raden (provide (all-defined-out)) i din fil. a) Skriv en funktion replace-hop som tar tre argument, seq, old och new och returnerar en ny lista med samma innehåll som en given lista seq, förutom att alla förekomster av old har ersatts med new. Att namnet slutar på -hop beror på att denna funktion ska använda den högre ordningens proceduren map. Din lösning måste vara skriven på formen (define (replace-hop seq old new) (map??????)) Den får inte innehålla någon kod utanför. Så här ska den fungera: > (replace-hop (a b a b) b cat) (a cat a cat) Använd eqv? för jämförelser. OBS! Din lösning ska inte gå in i listor-i-listor! b) Vad är det som gör den inbyggda funktionen map till en högre ordningens procedur (till skillnad från till exempel + eller null?)? Svara kortfattat, max en-två meningar, i en kodkommentar. 7
Några vanliga inbyggda funktioner i Racket Ett urval av Racketfunktioner som kan användas listas nedan. Listan är naturligtvis minimal - det finns många andra funktioner i Racket. Att en procedur eller operation finns med på denna lista betyder inte nödvändigtvis att den behöver användas. Arbete med tal + - * / = < > >= =< number? even? odd? remainder quotient expt negative? positive? zero? random Arbete med listor och cons-strukturer car cdr cons mcar mcdr mcons set-mcar! set-mcdr! mlist eq? eqv? equal? list? null? pair? append length list first second rest Kommentar: För att kunna se muterbara strukturer som listor inkludera (print-as-expression #f) i ditt program. För att kunna använda mlist, som skapar muterbara listor, skriv (require racket/mpair) innan. Diverse funktioner, operationer och språkkonstruktioner and not or cond define if let let* require provide Trace för att spåra processer (require racket/trace) (trace f) (untrace f) ;För att spåra en funktion f ;Tar bort spårning Sekvenshantering map filter foldr foldl 8
In- och utmatning display newline printf error (error "Value does not exist" arg) (printf "Course Code ~a, Course Name ~a~n" TDDC74 PRAM) Associationslistor Associationslistor är listor som avbildar tabeller där varje element relaterar en nyckel till ett värde. Funktionen assoc används för att hitta element som har en viss nyckel. Om inget element med given nyckel hittas, returneras #f. assoc använder equal? för att jämföra det vi söker med elementnycklarna. > (define a-list (((3. 3). 1) ;; Nyckeln (3. 3) är relaterad till värdet 1 osv. ((3. 5). 4))) > (assoc (3. 5) a-list) ((3. 5). 4) > (assoc (4. 4) a-list) #f