TDDC74 Programming: Abstraction and Modelling PRAM, Tutorial 5

advertisement
TDDC74 Programming: Abstraction and Modelling
PRAM, Tutorial 5
This tutorial should cover the following topics:
• An introduction to mutable linked structures (mpairs).
• Changing what names mean versus changing values.
• A brief object-oriented example.
1
Mutable Structures
In later versions of Scheme1 , there are two types of pairs: mutable and immutable. Mutable conscells are created using mcons, and the parts are selected with mcar and mcdr. Changing the pointer
in the first cell of an mpair is done using set-mcar!.
In DrRacket we find a set of useful functions for mutable lists. These are basically a mirror image
of our standard functions for ordinary lists, but in versions that work with mpair-based structures.
Add (require racket/mpair) in the beginning of your file and you will get functions like mlist?,
mlist, mappend, mreverse, mmap. mmember etc.
Note This is one of the few things that one can get wrong when reading SICP. There we use the
old standards’ cons, set-car! etc. Just remember the require above, add an m in front of the pair
functions (m cons, set-m car!,. . . ) and it should work. The illustrations and discussion is still of
interest.
Problem 1 ------------------------------------------------------------------------------------------------------------------What is the result of each of the following definitions and expressions?
(define *list* (mcons ’fir (mcons ’pine ’())))
(set-mcar! *list* ’spruce)
(define *new-list* (mcdr *list*))
(set-mcdr! (mcdr *list*) (mcons ’birch (mcons ’oak ’())))
*new-list*
*list*
(eq? *new-list* *list*)
(eq? *new-list* (mcdr *list*))
Draw box-and-pointer diagrams to illustrate how the structures are bound/changed by each definition/expression. Do not draw new structures, unless a new structure is defined!
Problem 2 ------------------------------------------------------------------------------------------------------------------We want a procedure that takes a pair and changes the first element to the number one. Something
that works along the lines of:
> (require racket/mpair)
> (define my-pair (mcons ’x ’y))
> my-pair
{ x . y }
> (one-car! my-pair)
> my-pair
{ 1 . y }
1
R6RS and onward, including Racket.
1
We make sure that we have mpairs instead of immutable pairs, and try the following definition:
(define (one-car! p)
(set! p (mcons 1 (mcdr p))))
However, we get
>
{
>
>
{
my-pair
x . y }
(one-car! my-pair)
my-pair
x . y }
Why is this?
Secondly, rewrite the procedure so that it works as intended.
Problem 3 ------------------------------------------------------------------------------------------------------------------What are the results of evaluating the following code?
(define *tail* (mlist 1 2))
(define *snake* (mcons ’head *tail*))
(set-mcar! *tail* 57)
(set! *tail* (mcons 1000 8001))
Again, draw box-and-pointer diagrams that show the changes that occur.
Problem 4 ------------------------------------------------------------------------------------------------------------------We want to write a procedure insert! which takes a mutable nonempty list, an element and a
position and inserts the element after that position in the list.
First, draw up a few examples of how this will work (in terms of box-and-pointer representations of
lists). In particular: what happens if you want to insert an element between two existing elements?
If you want to insert it after the last element? How should the structure change.
Second, write the procedure. Getting the logic of the first step right is much more important than
being able to do this on paper, at this stage.
(define *names* (mlist ’adam ’bertil ’david ’fredrik))
(insert! *names* ’caesar 2)
; Inserts caesar efter the second element bertil
*names*
; = (adam bertil caesar david fredrik)
(insert! *names* ’erik 4)
*names*
; = (adam bertil caesar david erik fredrik)
The position given is always within the bounds of the list (that is, no too big
2
Object orientation
This is a very brief introduction, just to get you started with the OO system.
Problem 5 ------------------------------------------------------------------------------------------------------------------Let’s remind ourselves of how a class definition can look:
2
#lang racket
(require racket/base)
(define thing%
(class object%
(init-field name
colour)
(define/public (get-name)
name)
(define/public (get-colour)
colour)
(define/public (set-colour! new-colour)
(set! colour new-colour))
(super-new)))
We have a way of creating a thing object, and we have getter and setter methods to retrieve
and change certain aspects of the state. The aspects exposed to the outside world are called the
interface. An example:
> (define casper (new thing% [name ’Casper] [colour ’red]))
> casper
#(struct:object:thing% ...)
> (send casper get-colour)
red
> (send casper set-colour! ’white)
> (send casper get-colour)
white
Now, we want to define a class box%, which we can put things in. We want the following methods
in the interface:
1. insert!. Takes a thing object like the one created above, and stores it in the box.
2. thing-names. Yields a list of all the names of objects in the box.
3. empty-box!. Removes all items from the box.
4. all-of-colour. Takes a colour (here, a symbol such as green) and returns a list of all things
of that colour.
Here is how it should work:
> (define ghost1
(new thing%
[name ’Christmas-Ghost]
[colour ’red]))
> ghost1
#(struct:object:thing% ...)
> (define ghost2
(new thing%
[name ’Sea-Ghost]
[colour ’blue]))
> (define box1 (new box%))
> (send box1 insert! ghost1)
> (send box1 insert! ghost2)
> (send box1 thing-names)
(’Sea-Ghost ’Christmas-Ghost)
> (define red-ghosts (send box1 all-of-colour ’red))
> red-ghosts
(#(struct:object:thing% ...))
> (send box1 empty-box!)
> (send box1 thing-names)
()
3
Problem 6 ------------------------------------------------------------------------------------------------------------------Now extend this so that we have a advanced-box where one can remove all the items of a certain
colour.
The interface of advanced-box should contain the following:
1. insert!. Takes a thing object like the one created above, and stores it in the box.
2. thing-names. Yields a list of all the names of objects in the box.
3. empty-box!. Removes all items from the box.
4. all-of-colour. Takes a colour (here, a symbol such as green) and returns a list of all things
of that colour.
5. remove-items!. Takes a colour, and removes all the items of that colour from the box.
Given the definitions of ghost1, ghost2 above:
> (define advbox (new advanced-box%))
> (send advbox insert! ghost1)
> (send advbox insert! ghost2)
> (send advbox thing-names)
(’Red-Ghost ’Sea-Ghost)
> (send advbox remove-items! ’red)
> (send advbox thing-names)
(’Sea-Ghost)
Make sure not to repeat code unnecessarily!
4
3
Extra material on mutable structures
Problem 7 ------------------------------------------------------------------------------------------------------------------Write a procedure make-circular which takes a mutable list as argument and makes it into a
circular list. By this we mean that the mcdr of the last pair in list should point at the first pair
in list.
Here we will use mlist to create a list of elements, as we could do with list.
In DrRacket this should work as follows:
(require racket/mpair)
(define *lst3* (mlist 1 2 3 4 5 6))
*lst3* ; = (1 2 3 4 5 6)
(make-circular *lst3*)
*lst3* ; = #0=(1 2 3 4 5 6 . #0#)
Problem 8 ------------------------------------------------------------------------------------------------------------------Now, implement a procedure real-length which takes a mutable list as argument and returns the
“correct” length even if the list is circular:
(define *lst4* (mlist ’a
(real-length *lst4*) ; =
(make-circular *lst4*)
*lst4* ; = #0=(a b c d 1
(real-length *lst4*) ; =
’b ’c ’d 1 2 3 4))
8
2 3 4 . #0#)
8
5
Download