Name & Lab:
Honor pledge:
1. | /20 |
2. | /20 |
3. | /20 |
4. | /20 |
5. | /20 |
tot | /100 |
Directions and tips:
define
d names to examples of data,
so it's easy to show test-cases on subsequent functions.
You may abbreviate define
as
(lower-case greek delta),
and define-struct
as
-struct
.
map foldr foldl filter list length append(Reminder:
filter
takes in a function keep?
,
and a list stuff
,
and returns
those elements of stuff
for which keep?
returns true
.)
Encoding | Assembly | Meaning |
---|---|---|
? ? ? x y z 1 0 | (add Rx Ry Rz) | Rx <-- Ry + Rz |
? ? ? x y z 2 0 | (sub Rx Ry Rz) | Rx <-- Ry - Rz |
? ? ? x y z 3 0 | (mul Rx Ry Rz) | Rx <-- Ry * Rz |
? ? ? x y z 4 0 | (div Rx Ry Rz) | Rx <-- Ry / Rz |
? ? ? x y z 5 0 | (mod Rx Ry Rz) | Rx <-- Ry % Rz |
Encoding | Assembly | Meaning |
---|---|---|
±d7...d2 x 9 | (ldi Rx ±d7...d2) | Rx <-- ±d7...d2 |
? ? 1 ? y x 6 0 | (ld Rx Ry) | Rx <-- mem[Ry] |
? ? 0 z y x 6 0 | (ldx Rx Ry Rz) | Rx <-- mem[Ry + Rz] |
? ? 1 ? y x 7 0 | (st Rx Ry) | mem[Ry] <-- Rx |
? ? 0 z y x 7 0 | (stx Rx Ry Rz) | mem[Ry + Rz] <-- Rx |
? ? ? ? y x 9 0 | (mov Rx Ry) | Ry <-- Rx |
Encoding | Assembly | Meaning |
---|---|---|
? ? ? ? ? x 8 0 | (jmpx Rx) | PC <-- Rx |
d7...d2 ? 1 | (jmpi d7...d2) | PC <-- d7...d2 |
d7...d2 x 2 | (jsr Rx d7...d2) | Rx <-- PC; PC <-- d7...d2 |
d7...d2 x 3 | (bez Rx d7...d2) | PC <-- d7...d2, if (Rx = 0) |
d7...d2 x 4 | (bnez Rx d7...d2) | PC <-- d7...d2, if (Rx != 0) |
d7...d2 x 5 | (blz Rx d7...d2) | PC <-- d7...d2, if (Rx < 0) |
d7...d2 x 6 | (blez Rx d7...d2) | PC <-- d7...d2, if (Rx <= 0) |
d7...d2 x 7 | (bgz Rx d7...d2) | PC <-- d7...d2, if (Rx > 0) |
d7...d2 x 8 | (bgez Rx d7...d2) | PC <-- d7...d2, if (Rx >= 0) |
p. ii
;; threes: natNum --> 1, or never finish? ;; No guarantee of termination. ;; (define (threes n) (cond [(<= n 1) n] [else (threes (if (even? n) (/ n 2) (+ (* 3 n) 1)))])) ; This function could be re-written in a form which separates ; the looping from the individual computation: ; (define (threes n) (while (lambda (m) (> m 1)) (lambda (m) (if (even? m) (/ m 2) (+ (* 3 m) 1))) n))
while
.
(Give one additional test case, more
trivial than the previous example or 1b.)
;; while: (alpha --> boolean), (alpha --> alpha), alpha --> alpha ;; Repeatedly call body; ;; Stop the moment (continue? init) is false. ;; That is, we call (body init), then (body (body init)), then ... ;; It's possible that body is called 0 times, if (continue? init) isn't true. ;; (define (while continue? body init) (cond [(not (continue? init)) init] [else (while continue? body (body init))])) ; A trivial example, that never even gets started: (while (lambda (z) false) (lambda (y) (error 'uh-oh "We should never get here")) 17) = 17
#i1.0
;
while [doubling-then-halving gives back the same number], [double it].
(As seen in an optional lab,
inexact numbers will eventually overflow.)
You can alternately start with pi
or (sqrt 2)
instead of #i1.0
;
these are also stored as inexact numbers --
scientific notation with ~20 significant figures,
and exponent in (-320,+320] or so.
(define accuracy (add1 1.0)) ; Within 100%. Set to 0.50 to be within 50%. ; Except -- if accuracy isn't a whole number, ; we'll detect x != 2x/2 because of round-off error, ; before overflow kicks in. ; (Update the while loop to check "nearly="?) (while (lambda (x) (= x (/ (* x accuracy) accuracy))) (lambda (x) (* accuracy x)) #i1.0) = +8.988..e+307 ; A large number, approx 1e308.
p. 1
(20pts) For every actor, a website can provide us with a list of all the other actors they've co-starred with. Somehow, it is a mark of pride for an actor to have co-starred with Kevin Bacon ("to have KB# of 1"). Almost as prestigious is having co-starred with somebody who has co-starred with Kevin Bacon ("to have a KB# of 2"). (Kevin Bacon himself has a KB# of 0.) In general, an actor's KB# is n if they co-starred with some actor with a KB# of n-1 (and they've not co-starred with somebody with a smaller KB#).
For example,
Kevin Bacon was in 24 Hours (2001) with Charlize Theron.
Charlize Theron was in The Devil's Advocate (1997) with Al Pacino
Thus, Charlize Theron has a KB# of 1,
while
Al Pacino has a KB# of 2.
Note that Matt Dillon has
co-starred with Al Pacino (Madonna: Truth or Dare, 1991),
but that doesn't make his KB# 3:
because Matt has also starred with somebody with a smaller KB#:
in this case, Mr. Bacon himself (Wild Things, 1998).
; An actor is: ; (make-actor [string] [list-of-actors]) ; (define-struct actor (name costars)) (define ct (make-actor "charlize theron" empty)) (define md (make-actor "matt-dillon" empty)) (define ap (make-actor "al pacino" empty)) (define kb (make-actor "kevin bacon" empty)) (set-actor-costars! ct (list kb ap)) (set-actor-costars! md (list kb ap)) (set-actor-costars! ap (list md ct)) (set-actor-costars! kb (list ct md))
Reminiscent of the airline-path-finding problem where we progressively marked cities as seen, include a field for each actor with their KB#, which we will fill-in with the correct KB value later. What might this value be initially, before we have computed the actual value? (What one actor is an exception to this?) (You can do this problem together with the previous one, if you like.)
kb#
,
which is either a number or the sentinel value kb#-unknown
.
; An actor is:
; (make-actor [string] [list-of-actors] [number-or-kb#unknown])
; where kb# is either a kevin-bacon number, or kb#-unknown if not yet computed.
(define-struct actor (name costars kb#))
; Sentinel value:
(define kb#-unknown 'infinity)
;; kb#-unknown?: kb# --> boolean
(define kb#-unknown? symbol?)
; Notice how this function lets us easily change
; our concept of unknown later on -- perhaps
; there could be several separate sentinels, all meaning
; unknown (but also conveying other nuances for other purposes).
;; create-actor: a wrapper around make-actor, for convenience.
;;
(define (create-actor name) (make-actor name empty kb#-unknown))
;
; Note: Must set Mr. Bacon's kb#
seperately.
(define ct (create-actor "charlize theron"))
(define md (create-actor "matt-dillon"))
(define ap (create-actor "al pacino"))
(define kb (create-actor "kevin bacon"))
(set-actor-costars! ct (list kb ap))
(set-actor-costars! md (list kb ap))
(set-actor-costars! ap (list md ct))
(set-actor-costars! kb (list ct md))
; Special:
(set-actor-kb#! kb 0)
Reminiscent of missionaries and cannibals,
write a function
one-step-more
that, if given a list of actors with some KB# k (say, 3),
would return a list of actors with KB# k+1 (4), and no less.
(Compare to:
taking a list of states reachable in (say) 3 boat-trips,
and returning the list of states reachable in 4.)
It can modify these structures with their newly-discovered KB#.
You can use map-append
, provided.
Write a second function/expression to repeatedly call this one,
to compute everybody's KB#.
(To think about: when is this second function done?)
(You may use a looping construct if you like, though you don't have to; for this exam (only), it's acceptable to blend together the code which handles the looping, with the code which computes KB#.)
;; map-append: (alpha --> list-of-beta), list-of-alpha --> list-of-beta. ;; Like map, but the individual results are lists, so append them all. ;; (define (map-append f lst) (cond [(empty? lst) empty] [(cons? lst) (append (f (first lst)) (map-append f (rest lst)))])) ; ; NB Or, "(foldr (lambda (frst rr) (append (f frst) rr)) empty lst))"
map actor-costars
onto the list
(well, upon a bit of reflection, some sort of map-append),
followed by filter
ing in those with a still-uknown kb#.
Repeat.
;; increase-kb#!: list-of-actors --> list-of-actors. ;; Takes in a list of actors with the same kb# (call it k), ;; and returns a list of those who've co-starred with any in "actors", ;; with their kb# set to k+1. ;; Side-effect: set the kb# of some actors as mentioned. ;; (define (increase-kb#! actors) (cond [(empty? actors) empty] [else (local [(define k (actor-kb# (first actors)))] (map (lambda (act) (begin (set-actor-kb#! act (add1 k)) act)) (filter (lambda (act) (kb#-unknown? (actor-kb# act))) (map-append actor-costars actors))))])) ; Tests: (Comment out before testing the main function though -- side-effected!) ;(increase-kb#! (list kb)) = (list ct md) ; These are now tagged with kb# 1. ;(increase-kb#! (list ct md)) = (list ap) ; Note that kb, md filtered out. ;(increase-kb#! (list ap)) = empty ; All ap's costars already have kb#. ; Now, repeatedly call increase-kb#! until it's empty, ; starting from (just) kb. ; (while cons? increase-kb#! (list kb)) = empty ; Check results: (actor-kb# kb) = 0 (actor-kb# md) = 1 (actor-kb# ct) = 1 (actor-kb# ap) = 2Here is another version which keeps track of k, rather than always re-computing it. (To use this, we need to retreat from using while, since having the loop value be a list-of-two-items would work but starts to become cumbersome.)
;; increase-kb#!: list-of-actors, number --> list-of-actors. ;; Takes in a list of actors with kb# k, ;; and returns a list of those who've co-starred with any in "actors", ;; with their kb# set to k+1. ;; Side-effect: set the kb# of some actors as mentioned. ;; (define (increase-kb#! k actors) (map (lambda (act) (begin (set-actor-kb#! act (add1 k)) act)) (filter (lambda (act) (kb#-unknown? (actor-kb# act))) (map-append actor-costars actors)))) ; Tests: (comment out before testing the main function though -- side-effected!) ;(increase-kb#! 0 (list kb)) = (list ct md) ; These are now tagged with kb# 1. ;(increase-kb#! 1 (list ct md)) = (list ap) ; kb#2. Note that kb, md removed. ;(increase-kb#! 2 (list ap)) = empty ; ap's costars already have kb#. ;; solve-kb!: list-of-actors, natnum --> (void) ;; Repeatedly call increase-kb#! with increasing k. ;; Stop when there aren't any unseen actors left. ;; ;; This corresponds to the while-loop, in the previous version. ;; (define (solve-kb! actors k) (if (empty? actors) (void) (solve-kb (increase-kb#! k actors) (add1 k)))) ; Get the whole thing going: (solve-kb! (list kb) 0) ; Check results: (actor-kb# kb) = 0 (actor-kb# md) = 1 (actor-kb# ct) = 1 (actor-kb# ap) = 2
p. 2
(20pts) For this problem, you may use a looping construct if you like, though you don't have to; for this exam, it's acceptable to blend together the code which walks to the end of the first list, with the code which changes the list.
; Use the more sensible names: ; For exam purposes only, allow short names: (define set-cons-first! set-car!) (define scf! set-cons-first!) (define set-cons-rest! set-cdr!) (define scr! set-cons-rest!)
append!
,
which takes in two lists
a
and
b
,
and it
modifies
a
so that it contains
all the elements of both lists.
In particular, it changes a
's last cons
structure
so that it refers to
b
.
(You can have any return value you like for this function,
though scheme's real append!
returns the
(newly modified)
a
.)
a
is guaranteed to be non-empty.
(How does this effect the contract?)
We have seen many examples of
a function modifying the contents of a structure.
;; length1?: list --> boolean. ;; Does l contain exactly one item? ;; (define (length1? l) (and (cons? l) (empty? (rest l)))) (length1? empty) = false (length1? (list 'hi)) = true (length1? (list 'hi 'bye)) = false ;; append!: cons, list --> list ;; Side-effect: modify a's content so that it includes b. ;; ;; Implementation: recur down a until a's last cons cell, ;; then set it's "rest" field to be b, rather than empty. ;; (define (append! a b) (cond [(length1? a) (set-cons-rest! a b)] [else (append! (rest a) b)]))] ; Alternately, a one-liner: ; (set-cons-rest! (while (compose not length1?) rest a)) b) (define x (list 'hi 'bye)) (append! x empty) = (list 'hi 'bye) x = (list 'hi 'bye) ; Further tests in next part.A note on the contract: If you tried to use alpha as a type variable, the you'd want:
;; append!: (cons alpha list-of-alpha), list-of-alpha --> list-of-alphaThough note that it's not quite correct to assert that everything in the list is all the same type -- the lists could be "heterogenous", so that substituting "alpha" with "number" wouldn't work, nor would any other single value for "alpha".
(define uno (list 'blue 'aqua)) (define dos (list 'teal 'azure)) (append! uno dos) ; Draw a picture of what things look like right now, ; including what uno and dos refer to.; For exam soln set only, we'll stress how cons cells are structures: (define make-cons cons) (define bluey^ (make-cons 'blue aqueous^)) (define aqueous^ (make-cons 'aqua tealy^)) (define tealy^ (make-cons 'teal azul^)) (define azul^ (make-cons 'azure empty)) (define dos tealy^) (define uno bluey^) ; Note that the third cons cell of uno ; and the first cons cell of dos ; are the same identical cons cell. ; That is: (eq? dos (rest (rest uno))) (append! uno dos) ; A second time. ; Draw a picture of what things look like right now ; including what uno and dos refer to. ; (Careful, it may not be quite what you first thought!)(define bluey^ (make-cons 'blue aqueous^)) (define aqueous^ (make-cons 'aqua tealy^)) (define tealy^ (make-cond 'teal azul^)) (define azul^ (make-cons 'azure tealy^)) (define dos tealy^) (define uno bluey^) ; ; Note that we now have circular lists: (eq? dos (rest (rest dos)))
p. 3
(20pts)
You are P,
the chief engineer at
Mission Imperceptible headquarters.
Mission Imperceptible agents are briefed about each new mission
by listening to a CD with their mission statement.
After playing the message up to 2 times,
the CD will (call) self-destruct!
.
(Some of the agents are a bit slow,
and need a second listening.)
Each CD has a unique serial-number: the first CD you create at headquarters has the number 1, the next has the number 2, etc.
You have decided to implement
a CD as a function (of no arguments):
the first two times the function is called,
the message is returned;
if further called, the CD will
call self-destruct!
(a function with zero arguments)
and
just return its serial number.
(Presume self-destruct!
has already been provided --
you don't need to write it, just call it.)
You shall write two functions,
CD-maker
, which takes in a message.
Each time CD-maker
is called,
a new CD is returned with a new serial number.
CDs-issued
,
which returns the number of times
CD-maker
has been called.
set!
any other placeholder
to subvert either of your functions.
local
,
you return a list of those values,
and then attach names to them outside the local:
(define two-funcs (local [(define (f ..) ...) (define (g ..) ...) (define (h ..) ...)] (list f g))) ; Export only two functions from the local. (define my-func1 (first two-funcs)) (define my-func2 (second two-funcs))
(CDs-issued) = 0 (define mission1 (CD-maker "take candy from baby")) mission1 ; = (lambda () ...) (CDs-issued) = 1 (define mission2 (CD-maker "stir martini")) (mission1) = "take candy from baby" (mission1) = "take candy from baby" (mission1) = 1 (mission1) = 1 (mission2) = "stir martini" (mission2) = "stir martini" (mission2) = 2 (mission1) = 1 (mission2) = 2 (CDs-issued) = 2 (CDs-issued) = 2 (CDs-issued) = 2
p. 4
CD-maker
, CDs-issued
.
; Assumed Provided (not required for you to write): ;; self-destruct!: --> (void) ;; side-effect: print a message of destruction and carnage. ;; (define (self-destruct!) (printf " (boom) ")) ; Data definitions: ;; A message is a string. ;; A CD is a function: ( --> message-or-number) (define both-functions (local [(define TOO_LATE 2) ; Number of calls before self-destructing. (define num-made 0) ; CDs made so far ;; CDs-made: --> number ;; (define (CDs-made) num-made) ; Easy. ;; CDs-made: string --> CD ;; (define (CD-maker msg) (begin (set! num-made (add1 num-made)) ; Increment count. (local [(define calls-so-far 0) ; Make variables local for this CD: (define my-id# num-made)] (lambda () ; Now make the actual CD. (begin (set! calls-so-far (add1 calls-so-far)) (if (<= calls-so-far TOO_LATE) msg (begin (self-destruct!) my-id#))))))))] (list CDs-made CD-maker))) (define CDs-issued (first both-functions)) (define CD-maker (second both-functions))
If you don't want to have a local which returns a list of two functions
which you then globally name,
you can also use the approach used in one of the homeworks:
having a password object take in one of two messages
'play-message
or
'CDs-issued
.
If you wanted to implement CDs as structures,
that'd be okay,
but how would you prevent
a user from just calling
(CD-message some-cd)
as many times as they like,
bypassing any security mechanisms?
You would have to make the define-struct
hidden
inside some local
;
this leaves the user with a structure they can't do anything with.
You'd provide another function play
(defined inside that same local
),
which could be used.
Of course, if it is suddenly decided to
change the allowed number of listenings from 2 to (say) 3,
this should only require changing a single define
in your code.
set!
the allowed number of listenings
in DrScheme's interactions window
(the lower one -- without pressing "Execute"),
would it affect previously-made CDs,
carried by agents currently in the field?
(Yes or No).
(define TOO_LATE ..)
CD-maker
,
so that every CD had its own version of TOO_LATE
(just as they have their own version of my-id#
).
You don't need to worry about this, but:
HQ will have to be careful to make sure
we always call the actual functions written here,
even if some double agent
set!
s
our function names.
p. 5
;; gcd: natnum, natnum --> natnum ;; Given two numbers, return their greatest common divisor. ;; We must have bigger >= smaller, to work. ;; ;; Argument of termination (you don't need to repeat this): ;; every time this function is called,smaller
decreases ;; by at least one, and we stop when it reaches 0, so termination ;; guaranteed in at mostsmaller
steps.;; ;; For mathematicians (difficult): prove this algorithm actualy terminates ;; much more quickly than the previous argument suggests. ;; Hint: how much do the arguments decrease over two consecutive calls? ;; (define (gcd bigger smaller) (if (zero? smaller) bigger (gcd smaller (remainder bigger smaller)))) ; Tests -- you don't need to repeat: (gcd 5 0) = 5 (gcd 5 1) = 1 (gcd 5 4) = 1 (gcd 4 2) = 2 (gcd 12 8) = 4 (gcd 2515 814) = 1 (gcd 2516 814) = 74 ; 814 = 74*11; 2516=74*34. Notice how the second argument (smaller
) is used as the first argument of the recursive call.(In real life we'd write a user-friendly version where the arguments could be in either order; that wrapper would just call this as a helper, providing the bigger number as the first argument.)
- Rewrite this function in Scheme, in an imperative fashion (i.e. your function takes no arguments, it uses
set!
, it expects the input values to be in the variablesbigger
andsmaller
, and it stores the output in a variable namedgcd-result
).(define bigger 2516) ; These "inputs" must be changed for each test-case. (define smaller 814) (define gcd-result 'not-yet-computed) ; Our function will set! this. (define tmp 'swap-space) ;; A temporary variable. ;; do-gcd!: --> (void) ;; ;; As gcd, but it modifies smaller, bigger, gcd-result, and tmp. ;; (define (do-gcd!) (if (zero? smaller) (set! gcd-result bigger) (begin (set! tmp smaller) (set! smaller (remainder bigger smaller)) (set! bigger tmp) (do-gcd!)))) (do-gcd!) ; Run the program. gcd-result = 74 ; Running the program again with bigger set to be 2515 yields 1. (set! bigger 2515) (set! smaller 814) (do-gcd!) gcd-result = 1Rewrite your imperative version in Jam2000 assembly code. (The Jam2000 reference sheet is attached near front page.) You specify where
bigger
,smaller
,gcd-result
and your program are located in main-memory. (The user will make sure those memory locations contain the inputs, when they execute your assembly code.) Be sure to provide comments, showing the relation between your Jam2000 code and the imperative function whence it derived.
Note that the "%
" (see Jam'smod
) is the same as Scheme'sremainder
. ; Program will inhabit addresses 50 through 61. ; We'll have smaller in R0 and at address 100 ; bigger in R1 and at address 101 ; tmp in R8 ; and gcd-result at address 102. ; (ldi R0 100) ; mem[50] ; R0 <-- address of smaller (ld R0 R0) ; mem[51] ; R0 <-- smaller (ldi R1 101) ; mem[52] ; R1 <-- address of bigger (ld R1 R1) ; mem[53] ; R1 <-- bigger ; do-gcd: (bez R0 59) ; mem[54] ; (if (zero? smaller) branch to store-result. Else.. (mov R8 R0) ; mem[55] ; (set! tmp smaller) (mod R0 R1 R0) ; mem[56] ; (set! smaller (remainder bigger smaller)) (mov R1 R8) ; mem[57] ; (set! bigger tmp) (jmpi 54) ; mem[58] ; (do-gcd) ; store-result: (ldi R2 102) ; mem[59] ; R2 <-- address of gcd-result (st R2 R1) ; mem[60] ; (set! gcd-result bigger) (halt) ; mem[61]Usinggcd-result
as your temporary variable is acceptable: a low-level hack that doesn't quite reflect your thinking, but that's exactly what low-level langauges don't help with.
p. 6