Dugga 3 (provkod TEN1), Tid: kl 14-16, Datum: 2013-03-12 Lösningsförslag Dugga 3 (provkod TEN1), Tid: kl 14-16, Datum: 2013-03- 12 Läs alla frågorna först och bestäm dig för den ordning som passar dig bäst. Även om det i uppgi;en står a< du skall skriva en procedur/funk?on, så får du gärna skriva y<erligare hjälpfunk?oner som kan vara nödvändiga. Det finns tre uppgi;er i denna dugga. Poängen per uppgi;/deluppgi; anges i samband med varje uppgi;. Skriv tydligt för a< öka läsbarheten. Använd väl valda namn på parametrar och indentera din kod. Sammanlagt två poäng är reserverade för väl- skriven kod, dvs, kod som innehåller bra namngivning, korrekt indentering, användning av dataabstrak?on vid behov, och andra principer för bra Scheme- kod. Betygsgradering: Det finns tre duggor i kursen. Varje dugga ger 12p, dvs totalt 36p för alla tre. För a< passera en dugga krävs minst 3p på duggan. Totalt skall du på de tre duggorna för betyget 3 ha minst 18p, för betyget 4 minst 23p och för betyget 5 minst 27p. Lycka?ll!
Uppgift 1 (2 poäng) Studera följande kod som implementerar enkla objekt (lampor). En lampa kan an?ngen vara på eller av. (define *lamps* ()) (define (make-lamp) (define status 'off) (define (turn-me-off) (set! status 'off)) (define (turn-me-on) (set! status 'on)) (define (dispatch message) (cond ((eq? message 'status) status) ((eq? message 'on) (turn-me-on)) ((eq? message 'off) (turn-me-off)) (else (error "Invalid message: " message)))) (set! *lamps* (cons dispatch *lamps*)) dispatch) Som du har observerat har man en global variabel *lamps* där alla lamp- objekt sparas i en lista. På så sä< kan man all?d få tag i alla lampor som skapats. Antag a< vi har skapat e< antal lampor genom a< anropa make- lamp några gånger och sedan ändrat status på vissa?ll on. Din uppgi; är nu a< skriva en procedur som går genom *lamps* och ändrar varje lamps status, dvs om status är off då ändras det?ll on annars ändras det från off?ll on. on?ll off. 2
Lösning Uppgift 1 ;on? och off? nedan är ej nödvändiga, men kan snygga upp koden ;något. Den ena räcker. (define (on? lamp) (eq? 'on (lamp 'status))) (define (off? lamp) (eq? 'off (lamp 'status))) ;lösningen till Uppgift 1 (define (turn-onoff lamps) (if (null? lamps) 'done (begin (let ((lamp (car lamps))) (if (on? lamp) (lamp 'turnoff) (lamp 'turnon))) (turn-onoff (cdr lamps))))) 3
Alternativa lösningar till Uppgift 1 + testkörning (define (turn-onoff lamps) (if (null? lamps) 'done (begin (let ((lamp (car lamps))) (lamp (if (on? lamp) 'turnoff 'turnon))) (turn-onoff (cdr lamps))))) (define (turn-onoff lamps) (if (null? lamps) 'done (begin ((car lamps) (if (on? (car lamps)) 'turnoff 'turnon)) (turn-onoff (cdr lamps))))) (define (turn-onoff lamps) (for-each (lambda (lamp) (lamp (if (on? lamp) 'turnoff 'turnon))) lamps)) ;Följande är för att testa lösningen, ej del av lösningen (define (show-all-lamp-status) (map (lambda (lamp) (lamp 'status)) *lamps*)) ;Skapa några lampor (define lamp-1 (make-lamp)) (define lamp-2 (make-lamp)) (define lamp-3 (make-lamp)) (define lamp-4 (make-lamp)) (show-all-lamp-status) => (off off off off) ;tänd några lampor (lamp-1 'turnon) (lamp-2 'turnon) (show-all-lamp-status) => (off off on on) (turn-onoff *lamps*) (show-all-lamp-status) => (on on off off) 4
Uppgift 2 Rita omgivningsdiagram som fås e;er de följande u<rycken är evaluerade i den ordning som de förekommer nedan. När värdet på en variabel ändras kryssa över det gamla värdet och skriv det nya bredvid. (define age 1) (lambda (age) (+ age age)) (set! age (+ age 1)) ((lambda (age) (set! age (+ age 1))) age) (define f (let ((state 0)) (lambda (n) (if (= state 0) n (begin (set! state (- 1 state)) (+ state (f (- n 1)))))))) (* (f 1) (f age)) 5
GE CE age: 1 2 E0 f: para: (age) kropp: (+ age age) para: (age) kropp: (set! age...) age: 2 3 E1 CE para: (state) kropp: (lambda (n)... state: 0 para: (n) kropp: (if (= state... E2 CE E3 n: 1 CE E4 n: 2 CE 6
Uppgift 3 I den här uppgi;en skall du jobba med en så kallad ringstruktur. Ringen kommer a< innehålla noll eller flera element som pekar fram och?llbaka?ll varandra. På så sä< är det möjligt a< fly<a sig både framåt och bakåt i strukturen. Varje element i ringen kommer a< innehålla e< värde (e< heltal) och två pekare: en som pekar?ll nästa element och en som pekar?ll föregående element i ringen. Du måste se?ll a< elementen hålls i s?gande ordning (när man fly<ar sig framåt i strukturen). Det första elementet kommer a< innehålla det minsta värdet och det sista elementet i ringen det största värdet i ringen. En ring (ring- 1) som inte har några element ser ut så här, ring- 1 ring () Dvs, en mcons- struktur där mcar- delen innehåller symbolen ring och mcdr- delen pekar på första elementet i ringen. För närvarande finns inga element varför mcdr- delen är lika med (). Vi lägger in e< element i ringen genom a< anropa (ring- insert! ring- 1 12) som skapar e< element med värdet 12: ring- 1 prev ring 12 next Observera hur pekarna (förkortad next och prev) pekar ut nästa resp föregående element. Än så länge pekar de på e< och samma element (elementet själv). E;er y<erligare anrop (ring- insert! ring- 1 43) får vi följande struktur. 7
ring- 1 ring 12 next prev prev next 43 Här ser vi a< elementet med värdet 12 pekar ut elementet med värdet 43 som si< nästa element och elementet med värdet 43 pekar ut första elementet som si< nästa element. Sedan anropar vi ring- insert! igen med värdet 25, (ring- insert! ring- 1 25). Observera a< elementet med värdet 25 har hamnat mellan 12 och 45 e;ersom vi ville hålla ringen i ökande ordning. ring- 1 ring 12 next next 43 25 next Som Ni ser från exemplen ovan, har en ring e< antal element där sista elementet pekar?llbaka?ll det första och på så sä< bildas en cirkulär struktur. Vi definierar ring som en dataabstrak?on. Här är några av de procedurer som ingår i implementa?onen av denna dataabstrak?on: 8
(define (make-ring) (mcons 'ring ())) (define (ring-label ring) (mcar ring)) (define (ring-first ring) (mcdr ring)) (define (empty-ring? ring) (null? (mcdr ring))) Komple<era och använd dessa vid behov i samband med din lösning?ll uppgi; (b) nedan. a) (2 poäng) Skapa en dataabstrak?on för elementen i ringen. E< sådant element har tre delar som kan lämpligen kallas för previous, value och next. Dataabstrak?onen skall inkludera lämpliga konstruktor-, selektor-, igenkännar- och mutatorfunk?oner för den tredelade strukturen. b) (3 poäng) Skriv proceduren (ring- insert! ring value) som lägger in e< element som innehåller värdet value på rä< ställe i ringen så a< den ökande ordningen behålls. De värden som finns i ringens element är inte unika, dvs, det kan finnas fler än e< element med e< och samma värde. 9
Lösningsförslag till Uppgift 3a Implementa?on av triple nedan motsvarar triple- defini?onen från deque- exemplet från en?digare föreläsning. Igenkännaren triple? kan skriva lite olika och utgör ingen vik?g del av denna uppgi;. (define (make-triple prev value next) (mcons prev (mcons value next))) (define (triple-prev triple) (mcar triple)) (define (triple-value triple) (mcar (mcdr triple))) (define (triple-next triple) (mcdr (mcdr triple))) (define (triple? element) (and (mpair? (mcar triple)) (mpair? (mcdr triple)) (integer? (mcar (mdr triple))))) (define (triple-set-prev! triple element) (set-mcar! triple element)) (define (triple-set-value! triple value) (set-mcar! (mcdr triple) value)) (define (triple-set-next! triple element) (set-mcdr! (mcdr triple) element)) 10
Lösningsförslag till Uppgift 3b Implementa?on av ring. Strukturen liknar samma typ av struktur som fanns i förra årets Dugga3. Skillnaden är a< nu har vi en triple per element och inte en MCONS- cell. Exemplet förekommer som en uppgi; i föreläsningsmaterialen. (define (make-ring) (mcons 'ring ())) (define (ring-label ring) (mcar ring)) (define (ring-first ring) (mcdr ring)) (define (empty-ring? ring) (null? (mcdr ring))) (define (ring-set-first! ring element) (set-mcdr! ring element)) (define (ring-insert! ring value) (define (insert-before! new-element next) ;lägg in new-element före next och uppdatera länkarna (triple-set-next! new-element next) (triple-set-prev! new-element (triple-prev next)) (triple-set-next! (triple-prev next) new-element) (triple-set-prev! next new-element)) (define (ring-insert-help! current-element new-element) ;new-element skall läggas in i ringen på rätt ställe ;current-element är nästa element på tur som skall undersökas (cond ((< (triple-value new-element) (triple-value current-element)) (insert-before! new-element current-element)) ((eq? (triple-next current-element) (ring-first ring)) ;Är current-element sista elementet i ringen? (insert-before! new-element (ring-first ring))) (else (ring-insert-help! (triple-next current-element) new-element)))) (let ((new-element (make-triple () value ()))) (cond ((empty-ring? ring) (ring-set-first! ring new-element) (triple-set-prev! new-element new-element) (triple-set-next! new-element new-element)) ((< (triple-value new-element) (triple-value (ring-first ring))) ;Skall det nya elementet hamna först i ringen? (insert-before! new-element (ring-first ring)) (ring-set-first! ring new-element)) (else (ring-insert-help! (triple-next (ring-first ring)) new-element))))) 11
;följande kod är inte en del av lösningen utan för att testa ;ring-insert! ovan (define (ring->list ring) ;Gå genom en ring och skapa en lista som innehåller ;de värden som finns i ringen (define (ring-elements next) (if (eq? next (ring-first ring)) () (cons (triple-value next) (ring-elements (triple-next next))))) (cons (triple-value (ring-first ring)) (ring-elements (triple-next (ring-first ring))))) (define ring-1 (make-ring)) (define (test-example) (ring-insert! ring-1 3) (ring-insert! ring-1 6) (ring-insert! ring-1 5) (ring-insert! ring-1 5) (ring-insert! ring-1 2) (ring-insert! ring-1 9)) (test-example) (ring->list ring-1) => (2 3 5 5 6 9) 12
;En alternativ lösning som innehåller lite kodmönster som förekommer två gånger. (define (ring-insert! ring value) (define (ring-insert-help! current-element new-element) ;new-element skall läggas in i ringen på rätt ställe ;current-element är nästa element på tur som skall undersökas (cond ((< (triple-value new-element) (triple-value current-element)) (triple-set-next! new-element current-element) (triple-set-prev! new-element (triple-prev current-element)) (triple-set-next! (triple-prev current-element) new-element) (triple-set-prev! current-element new-element)) ((eq? (triple-next current-element) (ring-first ring)) ;Är current-element sista elementet i ringen? (triple-set-next! new-element (ring-first ring)) (triple-set-prev! new-element (triple-prev (ring-first ring))) (triple-set-next! (triple-prev (ring-first ring)) new-element) (triple-set-prev! (ring-first ring) new-element)) (else (ring-insert-help! (triple-next current-element) new-element)))) (let ((new-element (make-triple () value ()))) (cond ((empty-ring? ring) (ring-set-first! ring new-element) (triple-set-prev! new-element new-element) (triple-set-next! new-element new-element)) ((< (triple-value new-element) (triple-value (ring-first ring))) ;Skall det nya elementet hamna först i ringen? (triple-set-next! new-element (ring-first ring)) (triple-set-prev! new-element (triple-prev (ring-first ring))) (triple-set-next! (triple-prev (ring-first ring)) new-element) (ring-set-first! ring new-element)) (else (ring-insert-help! (triple-next (ring-first ring)) new-element))))) 13