Structure and Interpretation of Computer Programs notes III
08 Sep 2015
Chapter 3. Modularity, Objects and State
Local State Variables
We can make balance internal to withdraw:
Exercise 3.1
Exercise 3.2
Exercise 3.3
Exercise 3.4
Benefits of Introducing Assignment
Monte Carlo method
We can approximate pi using the fact that 6/pi^2 is the probability that two integers chosen at random will have no factors in common; that is, that their greatest common divisor will be 1. To obtain the approximation to pi, we perform a large number of experiments. In each experiment we choose two integers at random and perform a test to see if their GCD is 1.
Now let us try the same computation using rand-update directly rather than rand, the way we would be forced to proceed if we did not use assignment to model local state:
It betrays some painful breaches of modularity. In the first version of the program, assignment encapsulates the state of the random-number generator within the rand procedure, so that the details of random-number generation remain independent of the rest of the program.
Exercise 3.6
The Cost of Introducing Assignment
So long as we do not use assignments, two evaluations of the same procedure with the same arguments will produce the same result, so that procedures can be viewed as computing mathematical functions. Programming without any use of assignments is known as functional programming.
In contrast to functional programming, programming that makes extensive use of assignment is known as imperative programming. In addition to raising complications about computational models, programs written in imperative style are susceptible to bugs that cannot occur in functional programs (the order of assignment matters!).
Two key properties that make local procedure definitions a useful technique for modularizing program:
The names of the local procedures do not interfere with names external to the enclosing procedure, because the local procedure names will be bound in the frame that the procedure creates when it is run, rather than being bound in the global environment.
The local procedures can access the arguments of the enclosing procedure, simply by using parameter names as free variables. This is because the body of the local procedure is evaluated in an environment that is subordinate to the evaluation environment for the enclosing procedure.
Mutable List Structure
The Primitive mutators for pairs are set-car! and set-cdr!
Sharing and Identity
Streams are delayed Lists
If we represent sequences as lists, this elegance is bought at the price of severe inefficiency with respect to both the time and space required by our computations. When we represent manipulations on sequences as transformations of lists, our programs must construct and copy data structures (which may be huge) at every step of a process.
The filter cannot do any testing until enumerate-interval has constructed a complete list of the numbers in the interval. The filter generates another list, which in turn is passed to accumulate before being collapsed to form a sum. Such large intermediate storage is not needed.
Streams are a clever idea that allows one to use sequence manipulations without incurring the costs of manipulating sequences as lists. The basic idea is to arrange to construct a stream only partially, and to pass the partial construction to the program that consumes the stream.
Streams are the same as lists. The difference is the time at which the elements are evaluated. With ordinary lists, both the car and the cdr are evaluated at construction time. With streams, the cdr is evaluated at selection time.
Implementing delay and force
In other words, we implement delay as a special-purpose memoized procedure.
Exercise 3.50
Infinite Streams
Infinite stream of Fibonacci numbers:
Infinite stream of prime numbers:
Define streams implicitly
We can do more interesting things by manipulating streams with operations such as add-streams, which produces the elementwise sum of two given streams:
Now we can define the integers as follows:
We can define the Fibonacci numbers in the same style:
Scale-stream is another useful procedure in formulating such stream definitions. This multiplies each item in a stream by a given constant:
Exploiting the Stream Paradigm
the stream version sqrt procedure
Generate an approximation to pi, based on the alterating series:
pi/4 = 1-1/3+1/5-1/7+…
We can transform a stream with a sequence accelerator that converts a sequence of approximations to a new sequence that converges to the same value as the original, only faster.
One such accelerator, due to the eighteenth-century Swiss mathematician Leonhard Euler, works well with sequences that are partial sums of alternating series. If Sn is the nth term of the original sum sequence, then the accelerated sequence has terms:
Thus, if the original sequence is represented as a stream of values, the transformed sequence is given by
Modularity of Functional Programs and Modularity of Objects
One of the major benefits of introducing assignment is that we can increase the modularity of our systems by encapsulating. Stream models can provide an equivalent modularity without the use of assignment.
Reimplement the Monte Carlo estimation of pi:
Modeling with objects is powerful and intuitive. However, these models raise thorny problems of constraining the order of events and of synchronizing multiple processes. The possibility of avoiding these problems has stimulated the development of functional programming languages, which do not include any provision for assignment or mutable data. In such a language, all procedures implement well-defined mathematical functions of their arguments, whose behavior does not change. The functional approach is extremely attractive for dealing with concurrent systems.