|
158 | 158 |
|
159 | 159 |
|
160 | 160 | ;; 3.1.2 Benefits of introducing assignment |
| 161 | +;; Viewing systems as collections of objects with local state is a powerfull |
| 162 | +;; technique for maintaining modular desing. As a simple example consider the |
| 163 | +;; desing of a procedure 'rand' that choses an integer at random. |
| 164 | + |
| 165 | +;; It's not at all clear what it is meant "chosen at random". Successive calls |
| 166 | +;; to rand should produce a sequence of numbers that has statistical properties |
| 167 | +;; of uniform distribution. We can assume that we have a procedure rand-update |
| 168 | +;; that has the property that if we start with a given number x1 and form: |
| 169 | +;; x2 = (rand-update x1) |
| 170 | +;; x3 = (rand-update x2) |
| 171 | +;; then the sequence of values x1, x2, x3 ... will have the desired statistical |
| 172 | +;; properties. |
| 173 | +;; We can implement rand as a procedure with a local state variable x, that is |
| 174 | +;; initialized to some fixed value rand-init. Each call to rand: |
| 175 | +;; - computes rand-update of the current value of x |
| 176 | +;; - stores the value as the new value of x |
| 177 | +;; - returns this as the random number |
| 178 | +(define rand-init 137) |
| 179 | +(define (rand-update x) |
| 180 | + (let ((a 27) (b 26) (m 127)) |
| 181 | + (modulo (+ (* a x) b) m))) |
| 182 | +;; Compute rand value as stated above |
| 183 | +(define rand |
| 184 | + (let ((x rand-init)) |
| 185 | + (lambda ( . args) |
| 186 | + (if (pair? args) |
| 187 | + (set! x (car args)) |
| 188 | + (begin |
| 189 | + (set! x (rand-update x)) |
| 190 | + x))))) |
| 191 | + |
161 | 192 | ;; To realize the annoyance that would be to explicitly remember the current |
162 | 193 | ;; value of 'x' passed as an argument to 'random-update', we will consider |
163 | 194 | ;; using random numbers to implement a technique called Monte Carlo simulation |
|
168 | 199 | ;; a large set and then making deductions on the basis of the probabilities |
169 | 200 | ;; estimated from tabulating the results of those experiments." |
170 | 201 | ;; I don't get it ... |
| 202 | +;; For example we can approximate Pi by using the fact, that 6/Pi^2 is the |
| 203 | +;; probability that two integers chosen at random will have no factors in |
| 204 | +;; common - their greatest common divisor will be 1. |
| 205 | +;; To obtain the approximation to Pi, we perform a large number of experiments. |
| 206 | +;; In each experiment we choose two integers at random and perform a test to |
| 207 | +;; see if their GCD is 1. The fraction of times, that the test passes, gives us |
| 208 | +;; our estimate of 6/Pi^2, and from this we obtain our approximation to Pi. |
| 209 | + |
| 210 | +;; The heart of our program is the procedure monte-carlo, which takes as |
| 211 | +;; arguments the number of times to try and experiment together with the |
| 212 | +;; experiment procedure. The procedure doesn't have any arguments and returns |
| 213 | +;; true or false each time it is run. The monte-carlo procedure returns |
| 214 | +;; a number telling the fraction of the trials in which the experiment was |
| 215 | +;; found true. |
| 216 | +(define (monte-carlo trials experiment) |
| 217 | + (define (iter-trials trials-remaining trials-passed) |
| 218 | + (cond ((= trials-remaining 0) |
| 219 | + (/ trials-passed trials)) |
| 220 | + ((experiment) |
| 221 | + (iter-trials (- trials-remaining 1) |
| 222 | + (+ trials-passed 1))) |
| 223 | + (else |
| 224 | + (iter-trials (- trials-remaining) |
| 225 | + trials-passed)))) |
| 226 | + (iter-trials trials 0)) |
| 227 | + |
| 228 | +(define (cesaro-test) |
| 229 | + (= (gcd (rand) (rand)) 1)) |
| 230 | + |
| 231 | +(define (estimate-pi trials) |
| 232 | + (sqrt (/ 6 (monte-carlo trials cesaro-test)))) |
| 233 | + |
| 234 | +;; Will this work? |
| 235 | +;;(say (estimate-pi 10)) |
| 236 | + |
| 237 | +;; The general phenomenon illustrated by the Monte Carlo example is this: |
| 238 | +;; From the point of view of one part of a complex process, the other parts |
| 239 | +;; appear to change within time. They have hidden time-varying local state. |
| 240 | +;; We make computational objects (such as random-number generators) whose |
| 241 | +;; behaviour changes within time. We model state with local state variables, |
| 242 | +;; and we model the changes of state with assignments to those variables. |
| 243 | + |
| 244 | +;; Exercise 3.5 TODO |
| 245 | + |
| 246 | +;; Exercise 3.6 |
| 247 | +(define (rand2 . args) |
| 248 | + (if (pair? args) |
| 249 | + ;; Argument given |
| 250 | + (let ((operation (car args))) |
| 251 | + (cond ;; Generate new random numer |
| 252 | + ((and (symbol? operation) |
| 253 | + (eq? operation 'generate)) |
| 254 | + (rand)) |
| 255 | + ;; Rests the internal state variable |
| 256 | + ((and (symbol? operation) |
| 257 | + (eq? operation 'reset)) |
| 258 | + (lambda (new-rand-init) |
| 259 | + (rand new-rand-init))))) |
| 260 | + ;; No arguments - by default return next rand value |
| 261 | + (rand))) |
| 262 | + |
| 263 | +;;(say "(rand) test cases:") |
| 264 | +;;(say (rand2)) ;; 42 |
| 265 | +;;(say (rand2)) ;; 17 |
| 266 | +;;(say ((rand2 'reset) rand-init)) |
| 267 | +;;(say (rand2)) ;; 42 |
| 268 | +;;(say (rand2)) ;; 17 |
| 269 | + |
| 270 | +;; 3.1.3 The cost of introducing assignment |
| 271 | +;; The set! operation enables us to model objects that have local state. |
| 272 | +;; But so long as we don't use assignments, two evaluations of the same |
| 273 | +;; procedure with the same arguments will produce the same result. So that |
| 274 | +;; procedures can be viewed as computing mathematical functions. |
| 275 | +;; Programming without any use of assignments is accordingly known as (tadam...) |
| 276 | +;; functional programming. |
| 277 | +;; Substitution is based on the notion, that the symbols in our language are |
| 278 | +;; essentially names for values. But as soon as we introduce set! and the |
| 279 | +;; idea that the value of a variable can change, a variable can no longer be |
| 280 | +;; simply a name. Now a variable refers to a place, where a value can be stored |
| 281 | +;; and the value at this place can change. |
| 282 | +;; As soon as we introduce change into our computational models, many notions |
| 283 | +;; that were previously straightforward become problematical. One of them is |
| 284 | +;; the concept of being "the same". |
| 285 | +(define (make-decrementer balance) |
| 286 | + (lambda (amount) (- balance amount))) |
| 287 | +;; We can call (make-decrementer) twice with the same argument to create two |
| 288 | +;; procedures: |
| 289 | +(define D1 (make-decrementer 25)) |
| 290 | +(define D2 (make-decrementer 25)) |
| 291 | +;; Are D1 and D2 the same ? An acceptable answer is yes, because D1 and D2 have |
| 292 | +;; the same computational behavior - each substracts 25 from its input. |
| 293 | +;; D1 could be substituted for D2 in any computation without changing the |
| 294 | +;; result. |
| 295 | +;; Contrast this with making two calls to (make-simplified-withdraw). |
| 296 | +(define (make-simplified-withdraw balance) |
| 297 | + (lambda (amount) |
| 298 | + (set! balance (- balance amount)) |
| 299 | + balance)) |
| 300 | +(define W1 (make-simplified-withdraw 25)) |
| 301 | +(define W2 (make-simplified-withdraw 25)) |
| 302 | +;; W1 and W2 are not the same, because subsequent calls to them have different |
| 303 | +;; effects: |
| 304 | +;;(say (W1 20)) ;; 5 |
| 305 | +;;(say (W1 20)) ;; -15 |
| 306 | +;;(say (W2 20)) ;; 5, should be -35 |
| 307 | +;; Even though W1 and W2 were created by evaluating the same expression: |
| 308 | +;; (make-simplified-withdraw 25) |
| 309 | +;; it is not true, that W1 could be substituted for W2 in any expression |
| 310 | +;; without changing the result of evaluating the expression. |
| 311 | +;; A language is said to be referentially transparent when is supports the |
| 312 | +;; concept, that a procedure call can be replaced by the call value in an |
| 313 | +;; expression without changing the value of the expression. Referential |
| 314 | +;; transparency is violated when we include set! in our programming language. |
| 315 | +;; This makes it tricky to determine when we can simplify expressions by |
| 316 | +;; substituting equivalent expressions. |
| 317 | + |
| 318 | +;; Pitfalls of imperative programming |
| 319 | +;; In contrast to functional programming, programming that makes extensive use |
| 320 | +;; of assignment is known as imperative programming. Programs written in this |
| 321 | +;; style are open to bugs that cannot occur in functional programs. Programming |
| 322 | +;; with assignments forces us to carefully consider the relative orders of the |
| 323 | +;; assignments to make sure that each statement is using the correct version of |
| 324 | +;; the variables that have been changed. For example, recall the iterative |
| 325 | +;; factorial program: |
| 326 | +(define (factorial n) |
| 327 | + (define (factorial-iter step result) |
| 328 | + (if (> step n) |
| 329 | + result |
| 330 | + (factorial-iter (+ step 1) |
| 331 | + (* result step)))) |
| 332 | + |
| 333 | + (factorial-iter 1 1)) |
| 334 | +;;(say (factorial 5)) |
| 335 | + |
| 336 | +;; Instead of passing arguments in the internal loop we could adopt a more |
| 337 | +;; imperative style by using assignmen to update the values of variables: |
| 338 | +(define (factorial n) |
| 339 | + (let ((result 1) |
| 340 | + (step 1)) |
| 341 | + (define (iter) |
| 342 | + (if (> step n) |
| 343 | + result |
| 344 | + (begin (set! result (* result step)) |
| 345 | + (set! step (+ step 1)) |
| 346 | + (iter)))) |
| 347 | + (iter))) |
| 348 | +;;(say (factorial 5)) |
| 349 | + |
| 350 | +;; This does not change the results produced by the program, but does introduce |
| 351 | +;; a subtle trap. If the order of assignments was different, then the program |
| 352 | +;; would generate an incorrect result. This issue does not arise in functional |
| 353 | +;; programs. The complexity of imperative programs becomes even worse if we |
| 354 | +;; consider applications in which several processes execute concurrently. |
| 355 | + |
| 356 | +;; Exercise 3.7 |
| 357 | +;; TODO |
| 358 | + |
| 359 | +;; Exercise 3.8 |
| 360 | +;; (f 0) + (f 1) = 0; |
| 361 | +;; (f 1) + (f 0) = 1; |
| 362 | +;; (1 + value) * factor |
| 363 | +(define (make-f) |
| 364 | + (let ((factor '())) |
| 365 | + (lambda (value) |
| 366 | + (and (null? factor) (set! factor value)) |
| 367 | + (* factor value)))) |
| 368 | + |
| 369 | +;;(define f (make-f)) |
| 370 | +;;(say (+ (f 0) (f 1))) |
| 371 | +;;(define f (make-f)) |
| 372 | +;;(say (+ (f 1) (f 0))) |
| 373 | + |
0 commit comments