Skip to content

Commit 90f3dc6

Browse files
committed
3.1.2 Benefits of introducing assignment
1 parent 1929193 commit 90f3dc6

File tree

1 file changed

+203
-0
lines changed

1 file changed

+203
-0
lines changed

031_assignment_and_local_state.scm

Lines changed: 203 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,37 @@
158158

159159

160160
;; 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+
161192
;; To realize the annoyance that would be to explicitly remember the current
162193
;; value of 'x' passed as an argument to 'random-update', we will consider
163194
;; using random numbers to implement a technique called Monte Carlo simulation
@@ -168,3 +199,175 @@
168199
;; a large set and then making deductions on the basis of the probabilities
169200
;; estimated from tabulating the results of those experiments."
170201
;; 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

Comments
 (0)