|
home—lectures—recipe—exams—hws—D2L—breeze (snow day)
1 #lang racket 2 3 4 ; NOTE: you must use #lang racket ('language: as determined in source') 5 ; rather than advanced-student, to get the var-args syntax: 6 (define (test-language-level a b . otherArgs) 7 (length otherArgs)) 8 9 (test-language-level 91 92 93 94 95) 10 11 12 ;;; We implement a random-number generator. 13 ;;; We need (want) state to do this; 14 ;;; version 1 will use (and set!) a global variable `seed`. 15 ;;; 16 17 (define seed 1) 18 (define MAX-RAND 123) 19 20 (define (next-rand-v1) 21 (begin (set! seed (remainder (+ (* 23 seed) 17) MAX-RAND)) 22 seed)) 23 24 (define (set-seed!-v1 new-val) 25 (set! seed new-val)) 26 27 28 29 "v1:" 30 (next-rand-v1) 31 (next-rand-v1) 32 (next-rand-v1) 33 (next-rand-v1) 34 (next-rand-v1) 35 "re-setting v1:" 36 (set-seed!-v1 1) 37 (next-rand-v1) 38 (next-rand-v1) 39 40 41 ;;;;;;;;;;;;;;;; 42 ;; v1 is nice, but it has a major problem: 43 ;; since 'seed' is global, anybody can muss with that variable. 44 45 ;; We want a variable that is local to just those two functions (methods). 46 ;; At first, it seems like `let*` doesn't help -- we can't put it in just 47 ;; one function (since the other needs access to that same local variable). 48 ;; But: put a `let*` around *both* functions! 49 50 ;; v2: a version where `seed` is 'private'. 51 52 (define two-rng-funcs 53 (let* {[seed 1] 54 [MAX-RAND 123] 55 [next-rand (lambda () 56 (begin (set! seed (remainder (+ (* 23 seed) 17) MAX-RAND)) 57 seed))] 58 [set-seed! (lambda (new-val) (set! seed new-val))]} 59 (list next-rand set-seed!))) 60 61 (define next-rand-v2 (first two-rng-funcs)) 62 (define set-seed!-v2 (second two-rng-funcs)) 63 ;;; Note: the above is common enough that scheme provides 'match-define': 64 ;;; (match-define (list next-rand-v2 set-seed!-v2) two-rng-funcs) 65 ;;; 66 ;;; In python: (x,y) = (3,4) 67 ;;; (nextRandv2, setSeedv2) = two-rng-funcs 68 69 70 "v2:" 71 (next-rand-v2) 72 (next-rand-v2) 73 (next-rand-v2) 74 (set-seed!-v2 1) 75 (next-rand-v2) 76 (next-rand-v2) 77 78 ;;; DEFINITION: the "closure" of a function: 79 ;;; the set of all 80 ;;; bindings(identifiers) which the function can refer to. 81 ;;; Note that from the top-level, 82 ;;; the id `next-rand-v2` is in scope, 83 ;;; the id `seed` is not in scope, 84 ;;; but it *is* in the function's scope. 85 ;;; (Put another way: even though we finished eval'ing the let* 86 ;;; a long time ago, the variable it created might live on inside 87 ;;; a function's closure, so it can't be garbage collected. 88 ;;; Hopefully such variables were allocated on the heap, 89 ;;; not on the stack!) 90 91 92 ;;;;;;;;;;;;;; 93 ;; A version where we can make 94 ;; *multiple* pairs-of-functions-which-each-share-a-local-`seed`. 95 96 97 (define (rng-factory) 98 (let* {[sseed 1] 99 [MAX-RAND 123] 100 [next-rand (lambda () 101 (begin (set! sseed (remainder (+ (* 23 sseed) 17) MAX-RAND)) 102 sseed))] 103 [set-seed! (lambda (new-val) (set! sseed new-val))]} 104 (list next-rand set-seed!))) 105 106 ;; The only difference in code between v2 and v3: 107 ;; the parens around 'rng-factory'! 108 109 110 (match-define (list next-rand-v3a set-seed!-v3a) (rng-factory)) 111 (match-define (list next-rand-v3b set-seed!-v3b) (rng-factory)) 112 113 114 115 "v3a:" 116 (next-rand-v3a) 117 (next-rand-v3a) 118 (next-rand-v3a) 119 (set-seed!-v3a 1) 120 (next-rand-v3a) 121 "v3b:" 122 (next-rand-v3b) 123 (next-rand-v3b) 124 (next-rand-v3b) 125 (set-seed!-v3b 1) 126 (next-rand-v3b) 127 (next-rand-v3b) 128 129 "continue using next-rand-v3a" 130 (next-rand-v3a) 131 (next-rand-v3a) 132 133 134 ;;;;;;;;;;;;;;;;;; 135 ;; Currently, we have pairs of coupled functions; 136 ;; we don't have one individual objects. 137 ;; Let's make one object, and we'll send "messages" to that 138 ;; object, asking it to do stuff for us (This is the flavor of O.O.!) 139 ;; 140 ;; 141 ;; A version where instead of returning a list-of-functions, 142 ;; we return one "meta function" which dispatches to the 143 ;; function that is being asked for: 144 ;; 145 (define (new-rng) 146 (let* {[sseed 1] 147 [MAX-RAND 123] 148 [next-rand (lambda () 149 (begin (set! sseed (remainder (+ (* 23 sseed) 17) MAX-RAND)) 150 sseed))] 151 [set-seed! (lambda (new-val) (set! sseed new-val))] 152 } 153 (lambda (msg . other-args) 154 (cond [(symbol=? msg 'next) (apply next-rand other-args)] 155 [(symbol=? msg 'seed!) (apply set-seed! other-args)] 156 [else (error 'rng (format "No such method recognized: ~a" msg))])))) 157 158 159 160 161 "v4a: (objects)" 162 (define r (new-rng)) 163 (define s (new-rng)) 164 (r 'next) 165 (r 'next) 166 (r 'next) 167 (r 'seed! 1) 168 (r 'next) 169 (r 'next) 170 "v4b:" 171 (s 'next) 172 (s 'next) 173 (s 'next) 174 (s 'next) 175 (s 'seed! 1) 176 (s 'next) 177 (s 'next) 178 179 180 ;;;;;;;;;;;;;;;; 181 ;;; A sub-class: 182 ;;; "class niftier-rng extends rng": 183 ;;; 184 ;;; We add a new method `skip` (which advances the seed, but returns nothing useful), 185 ;;; and we override `next` so that it doubles the superclass's result. 186 ;;; We also add a new field, `name`. 187 ;;; 188 189 (define (new-niftier-rng) 190 (let* {[super (new-rng)] ; The superclass object. 191 [name "hello"]} ; A new field, only in the subclass. 192 (lambda (msg . other-args) 193 (cond [(symbol=? msg 'skip) (begin (super 'next) "skipped")] 194 [(symbol=? msg 'next) (* 2 (super 'next))] 195 #;[(symbol=? msg 'get-seed) sseed] 196 ; This is what we *want* to return, but it'd be an error: sseed 197 ; is in super's scope, but not ours! 198 ; Our approach to implementing an object system can do most things, 199 ; but it can't emulate Java's 'protected' access 200 ; (since 'subclassing' this way is something any function can do). 201 ; One solution: In the superclass, have a 'secret key' that 202 ; must be provided to access protected fields/methods; 203 ; have a mechanism which provides that key only 204 ; via a construct 'build-valid-subclass'. 205 [else (apply super msg other-args)])))) 206 207 208 ;;; Exercise: our methodology for faking objects (by using closures and 209 ;;; a 'dispatcher' function) does allow for calling superclass methods 210 ;;; (through a variable we conveniently named `super`). 211 ;;; However, how could we call methods in the *same* class? 212 ;;; Hint: include the dispatcher method inside the let*, 213 ;;; perhaps naming it `this`. 214 ;;; But `let*` won't quite work; you'll need `letrec`. 215 216 217 218 219 "v5 (subclassing)" 220 (define ss (new-niftier-rng)) 221 (ss 'next) 222 (ss 'next) 223 (ss 'skip) 224 (ss 'next) 225 (ss 'seed! 1) 226 (ss 'next) 227 228 229 230 #| "Let over lambda": 231 The sandwiching of 'lambda' and 'let' is doing interesting things for us. 232 (And fwiw, recall that let can be re-written in terms of lambda...so 233 lambda alone is enough to implement objects!) 234 235 Hey, our own language N4 has both 'let' and 'lambda' -- 236 that means we can essentially implement subclassing and polymorphism! 237 |# |
home—lectures—recipe—exams—hws—D2L—breeze (snow day)
©2013, Ian Barland, Radford University Last modified 2013.Dec.02 (Mon) |
Please mail any suggestions (incl. typos, broken links) to ibarlandradford.edu |