Sicp
Sicp
of Computer Programs
Harold Abelson and
Gerald Jay Sussman
with Julie Sussman,
foreword by Alan J. Perlis
Unocial Texinfo Format
2.andresraba5.3
second edition
199 by Te Massachuseus lnstitute of Technology
Stiuctuie and lnteipietation of Computei Piogiams,
second edition
Haiold Abelson and Geiald Jay Sussman
with Julie Sussman, foiewoid by Alan J. Peilis
Tis woik is licensed undei a Cieative Commons
Auiibution-ShaieAlike 3.0 Unpoited License
(cc nvs~ 3.0). Based on a woik at mitpiess.mit.edu.
Te xi1 Piess
Cambiidge, Massachuseus
London, England
McGiaw-Hill Book Company
New Yoik, St. Louis, San liancisco,
Montieal, Toionto
Unocial Texinfo loimat 2.andiesiaba.3 (Apiil , 2014),
based on 2.neilvandyke4 (Januaiy 10, 200).
Contents
Unocial Texinfo Format ix
Dedication xii
Foreword xiii
Preface to the Second Edition xix
Preface to the First Edition xxi
Anowledgments xxv
1 Building Abstractions with Procedures 1
1.1 Te Elements of Piogiamming . . . . . . . . . . . . . .
1.1.1 Expiessions . . . . . . . . . . . . . . . . . . . .
1.1.2 Naming and the Enviionment . . . . . . . . . . 10
1.1.3 Evaluating Combinations . . . . . . . . . . . . 12
1.1.4 Compound Pioceduies . . . . . . . . . . . . . . 1
1.1. Te Substitution Model foi Pioceduie Application 18
1.1. Conditional Expiessions and Piedicates . . . . 22
1.1. Example Squaie Roots by Newtons Method . . 28
iii
1.1.8 Pioceduies as Black-Box Abstiactions . . . . . 33
1.2 Pioceduies and the Piocesses Tey Geneiate . . . . . . 40
1.2.1 Lineai Recuision and lteiation . . . . . . . . . 41
1.2.2 Tiee Recuision . . . . . . . . . . . . . . . . . . 4
1.2.3 Oideis of Giowth . . . . . . . . . . . . . . . . . 4
1.2.4 Exponentiation . . . . . . . . . . . . . . . . . .
1.2. Gieatest Common Divisois . . . . . . . . . . . 2
1.2. Example Testing foi Piimality . . . . . . . . .
1.3 loimulating Abstiactions
with Highei-Oidei Pioceduies . . . . . . . . . . . . . . 4
1.3.1 Pioceduies as Aiguments . . . . . . . . . . . .
1.3.2 Constiucting Pioceduies Using Lambda . . . . . 83
1.3.3 Pioceduies as Geneial Methods . . . . . . . . . 89
1.3.4 Pioceduies as Retuined Values . . . . . . . . . 9
2 Building Abstractions with Data 107
2.1 lntioduction to Data Abstiaction . . . . . . . . . . . . . 112
2.1.1 Example Aiithmetic Opeiations
foi Rational Numbeis . . . . . . . . . . . . . . . 113
2.1.2 Abstiaction Baiiieis . . . . . . . . . . . . . . . 118
2.1.3 What ls Meant by Data` . . . . . . . . . . . . . 122
2.1.4 Extended Exeicise lnteival Aiithmetic . . . . . 12
2.2 Hieiaichical Data and the Closuie Piopeity . . . . . . . 132
2.2.1 Repiesenting Sequences . . . . . . . . . . . . . 134
2.2.2 Hieiaichical Stiuctuies . . . . . . . . . . . . . . 14
2.2.3 Sequences as Conventional lnteifaces . . . . . 14
2.2.4 Example A Pictuie Language . . . . . . . . . . 12
2.3 Symbolic Data . . . . . . . . . . . . . . . . . . . . . . . 192
2.3.1 Qotation . . . . . . . . . . . . . . . . . . . . . 192
iv
2.3.2 Example Symbolic Dieientiation . . . . . . . 19
2.3.3 Example Repiesenting Sets . . . . . . . . . . . 20
2.3.4 Example Human Encoding Tiees . . . . . . . 218
2.4 Multiple Repiesentations foi Abstiact Data . . . . . . . 229
2.4.1 Repiesentations foi Complex Numbeis . . . . . 232
2.4.2 Tagged data . . . . . . . . . . . . . . . . . . . . 23
2.4.3 Data-Diiected Piogiamming and Additivity . . 242
2. Systems with Geneiic Opeiations . . . . . . . . . . . . 24
2..1 Geneiic Aiithmetic Opeiations . . . . . . . . . 2
2..2 Combining Data of Dieient Types . . . . . . . 22
2..3 Example Symbolic Algebia . . . . . . . . . . . 24
3 Modularity, Objects, and State 294
3.1 Assignment and Local State . . . . . . . . . . . . . . . 29
3.1.1 Local State Vaiiables . . . . . . . . . . . . . . . 29
3.1.2 Te Benets of lntioducing Assignment . . . . 30
3.1.3 Te Costs of lntioducing Assignment . . . . . . 311
3.2 Te Enviionment Model of Evaluation . . . . . . . . . . 320
3.2.1 Te Rules foi Evaluation . . . . . . . . . . . . . 322
3.2.2 Applying Simple Pioceduies . . . . . . . . . . . 32
3.2.3 liames as the Repositoiy of Local State . . . . 330
3.2.4 lnteinal Denitions . . . . . . . . . . . . . . . . 33
3.3 Modeling with Mutable Data . . . . . . . . . . . . . . . 341
3.3.1 Mutable List Stiuctuie . . . . . . . . . . . . . . 342
3.3.2 Repiesenting Qeues . . . . . . . . . . . . . . . 33
3.3.3 Repiesenting Tables . . . . . . . . . . . . . . . 30
3.3.4 A Simulatoi foi Digital Ciicuits . . . . . . . . . 39
3.3. Piopagation of Constiaints . . . . . . . . . . . 38
3.4 Concuiiency Time ls of the Essence . . . . . . . . . . . 401
v
3.4.1 Te Natuie of Time in Concuiient Systems . . 403
3.4.2 Mechanisms foi Contiolling Concuiiency . . . 410
3. Stieams . . . . . . . . . . . . . . . . . . . . . . . . . . . 428
3..1 Stieams Aie Delayed Lists . . . . . . . . . . . . 430
3..2 lnnite Stieams . . . . . . . . . . . . . . . . . . 441
3..3 Exploiting the Stieam Paiadigm . . . . . . . . . 43
3..4 Stieams and Delayed Evaluation . . . . . . . . 40
3.. Modulaiity of lunctional Piogiams
and Modulaiity of Objects . . . . . . . . . . . . 49
4 Metalinguistic Abstraction 487
4.1 Te Metaciiculai Evaluatoi . . . . . . . . . . . . . . . . 492
4.1.1 Te Coie of the Evaluatoi . . . . . . . . . . . . 49
4.1.2 Repiesenting Expiessions . . . . . . . . . . . . 01
4.1.3 Evaluatoi Data Stiuctuies . . . . . . . . . . . . 12
4.1.4 Running the Evaluatoi as a Piogiam . . . . . . 18
4.1. Data as Piogiams . . . . . . . . . . . . . . . . . 22
4.1. lnteinal Denitions . . . . . . . . . . . . . . . . 2
4.1. Sepaiating Syntactic Analysis fiom Execution . 34
4.2 Vaiiations on a Scheme Lazy Evaluation . . . . . . . 41
4.2.1 Noimal Oidei and Applicative Oidei . . . . . . 42
4.2.2 An lnteipietei with Lazy Evaluation . . . . . . 44
4.2.3 Stieams as Lazy Lists . . . . . . . . . . . . . . .
4.3 Vaiiations on a Scheme Nondeteiministic Computing 9
4.3.1 Amb and Seaich . . . . . . . . . . . . . . . . . 1
4.3.2 Examples of Nondeteiministic Piogiams . . . .
4.3.3 lmplementing the Amb Evaluatoi . . . . . . . . 8
4.4 Logic Piogiamming . . . . . . . . . . . . . . . . . . . . 94
4.4.1 Deductive lnfoimation Retiieval . . . . . . . . 99
vi
4.4.2 How the Qeiy System Woiks . . . . . . . . . 1
4.4.3 ls Logic Piogiamming Mathematical Logic` . . 2
4.4.4 lmplementing the Qeiy System . . . . . . . . 3
4.4.4.1 Te Diivei Loop and lnstantiation . . 3
4.4.4.2 Te Evaluatoi . . . . . . . . . . . . . 38
4.4.4.3 linding Asseitions
by Pauein Matching . . . . . . . . . 42
4.4.4.4 Rules and Unication . . . . . . . . . 4
4.4.4. Maintaining the Data Base . . . . . . 1
4.4.4. Stieam Opeiations . . . . . . . . . . 4
4.4.4. Qeiy Syntax Pioceduies . . . . . . .
4.4.4.8 liames and Bindings . . . . . . . . . 9
5 Computing with Register Maines 666
.1 Designing Registei Machines . . . . . . . . . . . . . . . 8
.1.1 A Language foi Desciibing Registei Machines . 2
.1.2 Abstiaction in Machine Design . . . . . . . . . 8
.1.3 Subioutines . . . . . . . . . . . . . . . . . . . . 81
.1.4 Using a Stack to lmplement Recuision . . . . . 8
.1. lnstiuction Summaiy . . . . . . . . . . . . . . . 9
.2 A Registei-Machine Simulatoi . . . . . . . . . . . . . . 9
.2.1 Te Machine Model . . . . . . . . . . . . . . . . 98
.2.2 Te Assemblei . . . . . . . . . . . . . . . . . . 04
.2.3 Geneiating Execution Pioceduies
foi lnstiuctions . . . . . . . . . . . . . . . . . . 08
.2.4 Monitoiing Machine Peifoimance . . . . . . . 18
.3 Stoiage Allocation and Gaibage Collection . . . . . . . 23
.3.1 Memoiy as Vectois . . . . . . . . . . . . . . . . 24
.3.2 Maintaining the lllusion of lnnite Memoiy . . 31
vii
.4 Te Explicit-Contiol Evaluatoi . . . . . . . . . . . . . . 41
.4.1 Te Coie of the Explicit-Contiol Evaluatoi . . . 43
.4.2 Sequence Evaluation and Tail Recuision . . . . 1
.4.3 Conditionals, Assignments, and Denitions . .
.4.4 Running the Evaluatoi . . . . . . . . . . . . . . 9
. Compilation . . . . . . . . . . . . . . . . . . . . . . . .
..1 Stiuctuie of the Compilei . . . . . . . . . . . . 2
..2 Compiling Expiessions . . . . . . . . . . . . . . 9
..3 Compiling Combinations . . . . . . . . . . . . 88
..4 Combining lnstiuction Sequences . . . . . . . . 9
.. An Example of Compiled Code . . . . . . . . . 802
.. Lexical Addiessing . . . . . . . . . . . . . . . . 81
.. lnteifacing Compiled Code to the Evaluatoi . . 823
References 834
List of Exercises 844
List of Figures 846
Index 848
Colophon 855
viii
Unoicial Texinfo Format
Tis is the second edition sicv book, fiom Unocial Texinfo loimat.
You aie piobably ieading it in an lnfo hypeitext biowsei, such as the
lnfo mode of Emacs. You might alteinatively be ieading it T
E
X-foimaued
on youi scieen oi piintei, though that would be silly. And, if piinted,
expensive.
Te fieely-distiibuted ocial u1xi-and-cii foimat was ist con-
veited peisonally to Unocial Texinfo loimat (U1i) veision 1 by Lytha
Ayth duiing a long Emacs lovefest weekend in Apiil, 2001.
Te U1i is easiei to seaich than the u1xi foimat. lt is also much
moie accessible to people iunning on modest computeis, such as do-
nated 38-based PCs. A 38 can, in theoiy, iun Linux, Emacs, and a
Scheme inteipietei simultaneously, but most 38s piobably cant also
iun both Netscape and the necessaiy X Window System without pie-
matuiely intioducing budding young undeifunded hackeis to the con-
cept of |ros|:ng. UTl can also t uncompiessed on a 1.44xn oppy
diskeue, which may come in handy foi installing UTl on PCs that do
not have lnteinet oi LAN access.
Te Texinfo conveision has been a stiaight tiansliteiation, to the
extent possible. Like the T
E
X-to-u1xi conveision, this was not without
some intioduction of bieakage. ln the case of Unocial Texinfo loimat,
ix
guies have sueied an amateuiish iesuiiection of the lost ait of ~scii.
Also, its quite possible that some eiiois of ambiguity weie intioduced
duiing the conveision of some of the copious supeisciipts () and sub-
sciipts (). Divining +|:c| has been lef as an exeicise to the ieadei.
But at least we dont put oui biave astionauts at iisk by encoding the
greoer|onoreqvo| symbol as <u>></u>.
lf you modify sicp.texi to coiiect eiiois oi impiove the ~scii ait,
then update the @set utfversion utfversion line to ieect youi delta.
loi example, if you staited with Lythas veision 1, and youi name is
Bob, then you could name youi successive veisions 1.bob1, 1.bob2, . . .
1.bobn. Also update utfversiondate. lf you want to distiibute youi
veision on the Web, then embedding the stiing sicp.texi somewheie
in the le oi Web page will make it easiei foi people to nd with Web
seaich engines.
lt is believed that the Unocial Texinfo loimat is in keeping with
the spiiit of the giaciously fieely-distiibuted u1xi veision. But you
nevei know when someones aimada of lawyeis might need something
to do, and get theii shoits all in a knot ovei some benign liule thing,
so think twice befoie you use youi full name oi distiibute lnfo, uvi,
PostSciipt, oi vui foimats that might embed youi account oi machine
name.
Peo|, Ly|o Ay|
Addendum: See also the sicv video lectuies by Abelson and Sussman
at xi1 cs~ii oi xi1 ocv.
Second Addendum: Above is the oiiginal intioduction to the U1i fiom
2001. Ten yeais latei, U1i has been tiansfoimed mathematical symbols
and foimulas aie piopeily typeset, and guies diawn in vectoi giaph-
ics. Te oiiginal text foimulas and ~scii ait guies aie still theie in
x
the Texinfo souice, but will display only when compiled to lnfo output.
At the dawn of e-book ieadeis and tablets, ieading a vui on scieen is
ocially not silly anymoie. Enjoy'
A.R, Moy, 2011
xi
Dedication
T
uis noox is uruic~1ru, in iespect and admiiation, to the spiiit that
lives in the computei.
l think that its extiaoidinaiily impoitant that we in com-
putei science keep fun in computing. When it staited out,
it was an awful lot of fun. Of couise, the paying customeis
got shafed eveiy now and then, and afei a while we began
to take theii complaints seiiously. We began to feel as if we
ieally weie iesponsible foi the successful, eiioi-fiee peifect
use of these machines. l dont think we aie. l think weie
iesponsible foi stietching them, seuing them o in new di-
iections, and keeping fun in the house. l hope the eld of
computei science nevei loses its sense of fun. Above all, l
hope we dont become missionaiies. Dont feel as if youie
Bible salesmen. Te woild has too many of those alieady.
What you know about computing othei people will leain.
Dont feel as if the key to successful computing is only in
youi hands. Whats in youi hands, l think and hope, is in-
telligence the ability to see the machine as moie than when
you weie ist led up to it, that you can make it moie.
Alan J. Peilis (Apiil 1, 1922 lebiuaiy , 1990)
xii
Foreword
E
uUc~1ovs, crNrv~is, uir1ici~Ns, psychologists, and paients pio-
giam. Aimies, students, and some societies aie piogiammed. An
assault on laige pioblems employs a succession of piogiams, most of
which spiing into existence en ioute. Tese piogiams aie iife with is-
sues that appeai to be paiticulai to the pioblem at hand. To appieciate
piogiamming as an intellectual activity in its own iight you must tuin to
computei piogiamming, you must iead and wiite computei piogiams
many of them. lt doesnt mauei much what the piogiams aie about oi
what applications they seive. What does mauei is how well they pei-
foim and how smoothly they t with othei piogiams in the cieation
of still gieatei piogiams. Te piogiammei must seek both peifection
of pait and adequacy of collection. ln this book the use of piogiam is
focused on the cieation, execution, and study of piogiams wiiuen in a
dialect of Lisp foi execution on a digital computei. Using Lisp we ie-
stiict oi limit not what we may piogiam, but only the notation foi oui
piogiam desciiptions.
Oui tiac with the subject mauei of this book involves us with
thiee foci of phenomena the human mind, collections of computei pio-
giams, and the computei. Eveiy computei piogiam is a model, hatched
in the mind, of a ieal oi mental piocess. Tese piocesses, aiising fiom
xiii
human expeiience and thought, aie huge in numbei, intiicate in de-
tail, and at any time only paitially undeistood. Tey aie modeled to oui
peimanent satisfaction iaiely by oui computei piogiams. Tus even
though oui piogiams aie caiefully handciafed disciete collections of
symbols, mosaics of inteilocking functions, they continually evolve we
change them as oui peiception of the model deepens, enlaiges, gen-
eializes until the model ultimately auains a metastable place within
still anothei model with which we stiuggle. Te souice of the exhilaia-
tion associated with computei piogiamming is the continual unfolding
within the mind and on the computei of mechanisms expiessed as pio-
giams and the explosion of peiception they geneiate. lf ait inteipiets
oui dieams, the computei executes them in the guise of piogiams'
loi all its powei, the computei is a haish taskmastei. lts piogiams
must be coiiect, and what we wish to say must be said accuiately in ev-
eiy detail. As in eveiy othei symbolic activity, we become convinced of
piogiam tiuth thiough aigument. Lisp itself can be assigned a seman-
tics (anothei model, by the way), and if a piogiams function can be
specied, say, in the piedicate calculus, the pioof methods of logic can
be used to make an acceptable coiiectness aigument. Unfoitunately, as
piogiams get laige and complicated, as they almost always do, the ade-
quacy, consistency, and coiiectness of the specications themselves be-
come open to doubt, so that complete foimal aiguments of coiiectness
seldom accompany laige piogiams. Since laige piogiams giow fiom
small ones, it is ciucial that we develop an aisenal of standaid piogiam
stiuctuies of whose coiiectness we have become suiewe call them
idiomsand leain to combine them into laigei stiuctuies using oiga-
nizational techniques of pioven value. Tese techniques aie tieated at
length in this book, and undeistanding themis essential to paiticipation
in the Piomethean enteipiise called piogiamming. Moie than anything
xiv
else, the uncoveiing and masteiy of poweiful oiganizational techniques
acceleiates oui ability to cieate laige, signicant piogiams. Conveisely,
since wiiting laige piogiams is veiy taxing, we aie stimulated to invent
new methods of ieducing the mass of function and detail to be ued
into laige piogiams.
Unlike piogiams, computeis must obey the laws of physics. lf they
wish to peifoim iapidlya few nanoseconds pei state changethey
must tiansmit elections only small distances (at most 1
1
2
feet). Te heat
geneiated by the huge numbei of devices so concentiated in space has to
be iemoved. An exquisite engineeiing ait has been developed balancing
between multiplicity of function and density of devices. ln any event,
haidwaie always opeiates at a level moie piimitive than that at which
we caie to piogiam. Te piocesses that tiansfoim oui Lisp piogiams
to machine piogiams aie themselves abstiact models which we pio-
giam. Teii study and cieation give a gieat deal of insight into the oi-
ganizational piogiams associated with piogiamming aibitiaiy models.
Of couise the computei itself can be so modeled. Tink of it the behav-
ioi of the smallest physical switching element is modeled by quantum
mechanics desciibed by dieiential equations whose detailed behavioi
is captuied by numeiical appioximations iepiesented in computei pio-
giams executing on computeis composed of . . .'
lt is not meiely a mauei of tactical convenience to sepaiately iden-
tify the thiee foci. Even though, as they say, its all in the head, this
logical sepaiation induces an acceleiation of symbolic tiac between
these foci whose iichness, vitality, and potential is exceeded in human
expeiience only by the evolution of life itself. At best, ielationships be-
tween the foci aie metastable. Te computeis aie nevei laige enough oi
fast enough. Each bieakthiough in haidwaie technology leads to moie
massive piogiamming enteipiises, new oiganizational piinciples, and
xv
an eniichment of abstiact models. Eveiy ieadei should ask himself pe-
iiodically Towaid what end, towaid what end`but do not ask it too
ofen lest you pass up the fun of piogiamming foi the constipation of
biueisweet philosophy.
Among the piogiams we wiite, some (but nevei enough) peifoim a
piecise mathematical function such as soiting oi nding the maximum
of a sequence of numbeis, deteimining piimality, oi nding the squaie
ioot. We call such piogiams algoiithms, and a gieat deal is known of
theii optimal behavioi, paiticulaily with iespect to the two impoitant
paiameteis of execution time and data stoiage iequiiements. A pio-
giammei should acquiie good algoiithms and idioms. Even though some
piogiams iesist piecise specications, it is the iesponsibility of the pio-
giammei to estimate, and always to auempt to impiove, theii peifoi-
mance.
Lisp is a suivivoi, having been in use foi about a quaitei of a cen-
tuiy. Among the active piogiamming languages only loitian has had
a longei life. Both languages have suppoited the piogiamming needs
of impoitant aieas of application, loitian foi scientic and engineeiing
computation and Lisp foi aiticial intelligence. Tese two aieas con-
tinue to be impoitant, and theii piogiammeis aie so devoted to these
two languages that Lisp and loitian may well continue in active use foi
at least anothei quaitei-centuiy.
Lisp changes. Te Scheme dialect used in this text has evolved fiom
the oiiginal Lisp and dieis fiom the lauei in seveial impoitant ways,
including static scoping foi vaiiable binding and peimiuing functions to
yield functions as values. ln its semantic stiuctuie Scheme is as closely
akin to Algol 0 as to eaily Lisps. Algol 0, nevei to be an active language
again, lives on in the genes of Scheme and Pascal. lt would be dicult
to nd two languages that aie the communicating coin of two moie dif-
xvi
feient cultuies than those gatheied aiound these two languages. Pas-
cal is foi building pyiamidsimposing, bieathtaking, static stiuctuies
built by aimies pushing heavy blocks into place. Lisp is foi building
oiganismsimposing, bieathtaking, dynamic stiuctuies built by squads
uing uctuating myiiads of simplei oiganisms into place. Te oiganiz-
ing piinciples used aie the same in both cases, except foi one extiaoidi-
naiily impoitant dieience Te discietionaiy expoitable functionality
entiusted to the individual Lisp piogiammei is moie than an oidei of
magnitude gieatei than that to be found within Pascal enteipiises. Lisp
piogiams inate libiaiies with functions whose utility tianscends the
application that pioduced them. Te list, Lisps native data stiuctuie, is
laigely iesponsible foi such giowth of utility. Te simple stiuctuie and
natuial applicability of lists aie ieected in functions that aie amazingly
nonidiosynciatic. ln Pascal the plethoia of declaiable data stiuctuies in-
duces a specialization within functions that inhibits and penalizes ca-
sual coopeiation. lt is beuei to have 100 functions opeiate on one data
stiuctuie than to have 10 functions opeiate on 10 data stiuctuies. As a
iesult the pyiamid must stand unchanged foi a millennium, the oigan-
ism must evolve oi peiish.
To illustiate this dieience, compaie the tieatment of mateiial and
exeicises within this book with that in any ist-couise text using Pascal.
Do not laboi undei the illusion that this is a text digestible at xi1 only,
peculiai to the bieed found theie. lt is piecisely what a seiious book on
piogiamming Lisp must be, no mauei who the student is oi wheie it is
used.
Note that this is a text about piogiamming, unlike most Lisp books,
which aie used as a piepaiation foi woik in aiticial intelligence. Afei
all, the ciitical piogiamming conceins of sofwaie engineeiing and ai-
ticial intelligence tend to coalesce as the systems undei investigation
xvii
become laigei. Tis explains why theie is such giowing inteiest in Lisp
outside of aiticial intelligence.
As one would expect fiom its goals, aiticial intelligence ieseaich
geneiates many signicant piogiamming pioblems. ln othei piogiam-
ming cultuies this spate of pioblems spawns new languages. lndeed, in
any veiy laige piogiamming task a useful oiganizing piinciple is to con-
tiol and isolate tiac within the task modules via the invention of lan-
guage. Tese languages tend to become less piimitive as one appioaches
the boundaiies of the system wheie we humans inteiact most ofen. As
a iesult, such systems contain complex language-piocessing functions
ieplicated many times. Lisp has such a simple syntax and semantics that
paising can be tieated as an elementaiy task. Tus paising technology
plays almost no iole in Lisp piogiams, and the constiuction of language
piocessois is iaiely an impediment to the iate of giowth and change of
laige Lisp systems. linally, it is this veiy simplicity of syntax and se-
mantics that is iesponsible foi the buiden and fieedom boine by all
Lisp piogiammeis. No Lisp piogiam of any size beyond a few lines can
be wiiuen without being satuiated with discietionaiy functions. lnvent
and t, have ts and ieinvent' We toast the Lisp piogiammei who pens
his thoughts within nests of paientheses.
Alan J. Peilis
New Haven, Connecticut
xviii
Preface to the Second Edition
ls it possible that sofwaie is not like anything else, that it
is meant to be discaided that the whole point is to always
see it as a soap bubble`
Alan J. Peilis
T
ur x~1rvi~i iN 1uis noox has been the basis of xi1s entiy-level
computei science subject since 1980. We had been teaching this ma-
teiial foi foui yeais when the ist edition was published, and twelve
moie yeais have elapsed until the appeaiance of this second edition.
We aie pleased that oui woik has been widely adopted and incoipo-
iated into othei texts. We have seen oui students take the ideas and
piogiams in this book and build them in as the coie of new computei
systems and languages. ln liteial iealization of an ancient Talmudic pun,
oui students have become oui buildeis. We aie lucky to have such ca-
pable students and such accomplished buildeis.
ln piepaiing this edition, we have incoipoiated hundieds of claii-
cations suggested by oui own teaching expeiience and the comments of
colleagues at xi1 and elsewheie. We have iedesigned most of the ma-
joi piogiamming systems in the book, including the geneiic-aiithmetic
system, the inteipieteis, the iegistei-machine simulatoi, and the com-
xix
pilei, and we have iewiiuen all the piogiam examples to ensuie that
any Scheme implementation confoiming to the irrr Scheme standaid
(lEEE 1990) will be able to iun the code.
Tis edition emphasizes seveial new themes. Te most impoitant of
these is the cential iole played by dieient appioaches to dealing with
time in computational models objects with state, concuiient piogiam-
ming, functional piogiamming, lazy evaluation, and nondeteiministic
piogiamming. We have included newsections on concuiiency and non-
deteiminism, and we have tiied to integiate this theme thioughout the
book.
Te ist edition of the book closely followed the syllabus of oui
xi1 one-semestei subject. With all the new mateiial in the second edi-
tion, it will not be possible to covei eveiything in a single semestei,
so the instiuctoi will have to pick and choose. ln oui own teaching, we
sometimes skip the section on logic piogiamming (Section 4.4), we have
students use the iegistei-machine simulatoi but we do not covei its im-
plementation (Section .2), and we give only a cuisoiy oveiview of the
compilei (Section .). Even so, this is still an intense couise. Some in-
stiuctois may wish to covei only the ist thiee oi foui chapteis, leaving
the othei mateiial foi subsequent couises.
Te Woild-Wide-Web site hup//mitpiess.mit.edu/sicp piovides sup-
poit foi useis of this book. Tis includes piogiams fiom the book, sam-
ple piogiamming assignments, supplementaiy mateiials, and download-
able implementations of the Scheme dialect of Lisp.
xx
Preface to the First Edition
A computei is like a violin. You can imagine a novice tiy-
ing ist a phonogiaph and then a violin. Te lauei, he says,
sounds teiiible. Tat is the aigument we have heaid fiom
oui humanists and most of oui computei scientists. Com-
putei piogiams aie good, they say, foi paiticulai puiposes,
but they aient exible. Neithei is a violin, oi a typewiitei,
until you leain how to use it.
Maivin Minsky, Why Piogiamming ls a Good Medium
foi Expiessing Pooily-Undeistood and Sloppily-loimulated
ldeas
T
ur S1vUc1Uvr ~Nu lN1rvvvr1~1ioN oi CoxvU1rv Pvocv~xs
is the entiy-level subject in computei science at the Massachuseus
lnstitute of Technology. lt is iequiied of all students at xi1 who majoi
in electiical engineeiing oi in computei science, as one-fouith of the
common coie cuiiiculum, which also includes two subjects on ciicuits
and lineai systems and a subject on the design of digital systems. We
have been involved in the development of this subject since 198, and
we have taught this mateiial in its piesent foim since the fall of 1980 to
between 00 and 00 students each yeai. Most of these students have
xxi
had liule oi no piioi foimal tiaining in computation, although many
have played with computeis a bit and a few have had extensive pio-
giamming oi haidwaie-design expeiience.
Oui design of this intioductoiy computei-science subject ieects
two majoi conceins. liist, we want to establish the idea that a com-
putei language is not just a way of geuing a computei to peifoim opei-
ations but iathei that it is a novel foimal medium foi expiessing ideas
about methodology. Tus, piogiams must be wiiuen foi people to iead,
and only incidentally foi machines to execute. Second, we believe that
the essential mateiial to be addiessed by a subject at this level is not
the syntax of paiticulai piogiamming-language constiucts, noi clevei
algoiithms foi computing paiticulai functions eciently, noi even the
mathematical analysis of algoiithms and the foundations of computing,
but iathei the techniques used to contiol the intellectual complexity of
laige sofwaie systems.
Oui goal is that students who complete this subject should have a
good feel foi the elements of style and the aesthetics of piogiamming.
Tey should have command of the majoi techniques foi contiolling
complexity in a laige system. Tey should be capable of ieading a 0-
page-long piogiam, if it is wiiuen in an exemplaiy style. Tey should
know what not to iead, and what they need not undeistand at any mo-
ment. Tey should feel secuie about modifying a piogiam, ietaining the
spiiit and style of the oiiginal authoi.
Tese skills aie by no means unique to computei piogiamming. Te
techniques we teach and diaw upon aie common to all of engineeiing
design. We contiol complexity by building abstiactions that hide details
when appiopiiate. We contiol complexity by establishing conventional
inteifaces that enable us to constiuct systems by combining standaid,
well-undeistood pieces in a mix and match way. We contiol complex-
xxii
ity by establishing newlanguages foi desciibing a design, each of which
emphasizes paiticulai aspects of the design and deemphasizes otheis.
Undeilying oui appioach to this subject is oui conviction that com-
putei science is not a science and that its signicance has liule to do
with computeis. Te computei ievolution is a ievolution in the way we
think and in the way we expiess what we think. Te essence of this
change is the emeigence of what might best be called roceJvro| e:s
eo|ogythe study of the stiuctuie of knowledge fiom an impeiative
point of view, as opposed to the moie declaiative point of view taken
by classical mathematical subjects. Mathematics piovides a fiamewoik
foi dealing piecisely with notions of what is. Computation piovides a
fiamewoik foi dealing piecisely with notions of how to.
ln teaching oui mateiial we use a dialect of the piogiamming lan-
guage Lisp. We nevei foimally teach the language, because we dont
have to. We just use it, and students pick it up in a few days. Tis is
one gieat advantage of Lisp-like languages Tey have veiy few ways
of foiming compound expiessions, and almost no syntactic stiuctuie.
All of the foimal piopeities can be coveied in an houi, like the iules
of chess. Afei a shoit time we foiget about syntactic details of the lan-
guage (because theie aie none) and get on with the ieal issuesguiing
out what we want to compute, how we will decompose pioblems into
manageable paits, and how we will woik on the paits. Anothei advan-
tage of Lisp is that it suppoits (but does not enfoice) moie of the laige-
scale stiategies foi modulai decomposition of piogiams than any othei
language we know. We can make pioceduial and data abstiactions, we
can use highei-oidei functions to captuie common paueins of usage,
we can model local state using assignment and data mutation, we can
link paits of a piogiamwith stieams and delayed evaluation, and we can
easily implement embedded languages. All of this is embedded in an in-
xxiii
teiactive enviionment with excellent suppoit foi inciemental piogiam
design, constiuction, testing, and debugging. We thank all the geneia-
tions of Lisp wizaids, staiting with John McCaithy, who have fashioned
a ne tool of unpiecedented powei and elegance.
Scheme, the dialect of Lisp that we use, is an auempt to biing to-
gethei the powei and elegance of Lisp and Algol. liom Lisp we take the
metalinguistic powei that deiives fiom the simple syntax, the unifoim
iepiesentation of piogiams as data objects, and the gaibage-collected
heap-allocated data. liomAlgol we take lexical scoping and block stiuc-
tuie, which aie gifs fiom the pioneeis of piogiamming-language de-
sign who weie on the Algol commiuee. We wish to cite John Reynolds
and Petei Landin foi theii insights into the ielationship of Chuichs -
calculus to the stiuctuie of piogiamming languages. We also iecognize
oui debt to the mathematicians who scouted out this teiiitoiy decades
befoie computeis appeaied on the scene. Tese pioneeis include Alonzo
Chuich, Baikley Rossei, Stephen Kleene, and Haskell Cuiiy.
xxiv
Acknowledgments
W
r voUiu iixr 1o 1u~Nx the many people who have helped us
develop this book and this cuiiiculum.
Oui subject is a cleai intellectual descendant of .231, a wondeiful
subject on piogiamming linguistics and the -calculus taught at xi1 in
the late 190s by Jack Wozenciaf and Aithui Evans, Ji.
We owe a gieat debt to Robeit lano, who ieoiganized xi1s intio-
ductoiy cuiiiculum in electiical engineeiing and computei science to
emphasize the piinciples of engineeiing design. He led us in staiting
out on this enteipiise and wiote the ist set of subject notes fiomwhich
this book evolved.
Much of the style and aesthetics of piogiamming that we tiy to
teach weie developed in conjunction with Guy Lewis Steele Ji., who
collaboiated with Geiald Jay Sussman in the initial development of the
Scheme language. ln addition, David Tuinei, Petei Hendeison, Dan liied-
man, David Wise, and Will Clingei have taught us many of the tech-
niques of the functional piogiamming community that appeai in this
book.
Joel Moses taught us about stiuctuiing laige systems. His expeii-
ence with the Macsyma system foi symbolic computation piovided the
insight that one should avoid complexities of contiol and concentiate
xxv
on oiganizing the data to ieect the ieal stiuctuie of the woild being
modeled.
Maivin Minsky and Seymoui Papeit foimed many of oui auitudes
about piogiamming and its place in oui intellectual lives. To them we
owe the undeistanding that computation piovides a means of expies-
sion foi exploiing ideas that would otheiwise be too complex to deal
with piecisely. Tey emphasize that a students ability to wiite and
modify piogiams piovides a poweiful medium in which exploiing be-
comes a natuial activity.
We also stiongly agiee with Alan Peilis that piogiamming is lots of
fun and we had beuei be caieful to suppoit the joy of piogiamming. Pait
of this joy deiives fiom obseiving gieat masteis at woik. We aie foitu-
nate to have been appientice piogiammeis at the feet of Bill Gospei and
Richaid Gieenblau.
lt is dicult to identify all the people who have contiibuted to the
development of oui cuiiiculum. We thank all the lectuieis, iecitation
instiuctois, and tutois who have woiked with us ovei the past feen
yeais and put in many extia houis on oui subject, especially Bill Siebeit,
Albeit Meyei, Joe Stoy, Randy Davis, Louis Biaida, Eiic Giimson, Rod
Biooks, Lynn Stein and Petei Szolovits. We would like to specially ac-
knowledge the outstanding teaching contiibutions of lianklyn Tuibak,
now at Wellesley, his woik in undeigiaduate instiuction set a standaid
that we can all aspiie to. We aie giateful to Jeiiy Saltzei and Jim Millei
foi helping us giapple with the mysteiies of concuiiency, and to Petei
Szolovits and David McAllestei foi theii contiibutions to the exposition
of nondeteiministic evaluation in Chaptei 4.
Many people have put in signicant eoit piesenting this mateiial
at othei univeisities. Some of the people we have woiked closely with
aie Jacob Katzenelson at the Technion, Haidy Mayei at the Univeisity
xxvi
of Califoinia at livine, Joe Stoy at Oxfoid, Elisha Sacks at Puidue, and
Jan Komoiowski at the Noiwegian Univeisity of Science and Technol-
ogy. We aie exceptionally pioud of oui colleagues who have ieceived
majoi teaching awaids foi theii adaptations of this subject at othei uni-
veisities, including Kenneth Yip at Yale, Biian Haivey at the Univeisity
of Califoinia at Beikeley, and Dan Huuenlochei at Coinell.
Al Moy aiianged foi us to teach this mateiial to engineeis at Hewleu-
Packaid, and foi the pioduction of videotapes of these lectuies. We
would like to thank the talented instiuctoisin paiticulai Jim Millei,
Bill Siebeit, and Mike Eisenbeigwho have designed continuing edu-
cation couises incoipoiating these tapes and taught themat univeisities
and industiy all ovei the woild.
Many educatois in othei countiies have put in signicant woik
tianslating the ist edition. Michel Biiand, Pieiie Chamaid, and An-
di Pic pioduced a liench edition, Susanne Daniels-Heiold pioduced
a Geiman edition, and lumio Motoyoshi pioduced a Japanese edition.
We do not know who pioduced the Chinese edition, but we considei
it an honoi to have been selected as the subject of an unauthoiized
tianslation.
lt is haid to enumeiate all the people who have made technical con-
tiibutions to the development of the Scheme systems we use foi in-
stiuctional puiposes. ln addition to Guy Steele, piincipal wizaids have
included Chiis Hanson, Joe Bowbeei, Jim Millei, Guilleimo Rozas, and
Stephen Adams. Otheis who have put in signicant time aie Richaid
Stallman, Alan Bawden, Kent Pitman, Jon Taf, Neil Mayle, John Lamp-
ing, Gwyn Osnos, Tiacy Laiiabee, Geoige Caiieue, Soma Chaudhuii,
Bill Chiaichiaio, Steven Kiisch, Leigh Klotz, Wayne Noss, Todd Cass,
Patiick ODonnell, Kevin Teobald, Daniel Weise, Kenneth Sinclaii, An-
thony Couitemanche, Heniy M. Wu, Andiew Beilin, and Ruth Shyu.
xxvii
Beyond the xi1 implementation, we would like to thank the many
people who woiked on the irrr Scheme standaid, including William
Clingei and Jonathan Rees, who edited the R
4
RS, and Chiis Haynes,
David Baitley, Chiis Hanson, and Jim Millei, who piepaied the irrr
standaid.
Dan liiedman has been a long-time leadei of the Scheme commu-
nity. Te communitys bioadei woik goes beyond issues of language
design to encompass signicant educational innovations, such as the
high-school cuiiiculum based on EdScheme by Schemeis lnc., and the
wondeiful books by Mike Eisenbeig and by Biian Haivey and Mauhew
Wiight.
We appieciate the woik of those who contiibuted to making this a
ieal book, especially Teiiy Ehling, Laiiy Cohen, and Paul Bethge at the
xi1 Piess. Ella Mazel found the wondeiful covei image. loi the second
edition we aie paiticulaily giateful to Beinaid and Ella Mazel foi help
with the book design, and to David Jones, T
E
X wizaid extiaoidinaiie.
We also aie indebted to those ieadeis who made penetiating comments
on the new diaf Jacob Katzenelson, Haidy Mayei, Jim Millei, and es-
pecially Biian Haivey, who did unto this book as Julie did unto his book
S:|y Sc|ee.
linally, we would like to acknowledge the suppoit of the oiganiza-
tions that have encouiaged this woik ovei the yeais, including suppoit
fiom Hewleu-Packaid, made possible by lia Goldstein and Joel Biin-
baum, and suppoit fiom u~vv~, made possible by Bob Kahn.
xxviii
will see that these same iules allowus to build pioceduies to manipulate
compound data as well.
1.1.1 Expressions
One easy way to get staited at piogiamming is to examine some typical
inteiactions with an inteipietei foi the Scheme dialect of Lisp. lmagine
that you aie siuing at a computei teiminal. You type an e:ress:on, and
the inteipietei iesponds by displaying the iesult of its e+o|vo:ng that
expiession.
One kind of piimitive expiession you might type is a numbei. (Moie
piecisely, the expiession that you type consists of the numeials that
iepiesent the numbei in base 10.) lf you piesent Lisp with a numbei
486
the inteipietei will iespond by piinting
486
giamming language. Some typical issues involved aie these Some computei systems
distinguish :negers, such as 2, fiom reo| nv|ers, such as 2.1. ls the ieal numbei 2.00
dieient fiom the integei 2` Aie the aiithmetic opeiations used foi integeis the same
as the opeiations used foi ieal numbeis` Does divided by 2 pioduce 3, oi 3.0` How
laige a numbei can we iepiesent` How many decimal places of accuiacy can we iepie-
sent` ls the iange of integeis the same as the iange of ieal numbeis` Above and beyond
these questions, of couise, lies a collection of issues conceining ioundo and tiunca-
tion eiioisthe entiie science of numeiical analysis. Since oui focus in this book is on
laige-scale piogiamdesign iathei than on numeiical techniques, we aie going to ignoie
these pioblems. Te numeiical examples in this chaptei will exhibit the usual ioundo
behavioi that one obseives when using aiithmetic opeiations that pieseive a limited
numbei of decimal places of accuiacy in nonintegei opeiations.
Tioughout this book, when we wish to emphasize the distinction between the
input typed by the usei and the iesponse piinted by the inteipietei, we will show the
lauei in slanted chaiacteis.
Lisp systems typically piovide featuies to aid the usei in foimauing expiessions.
Two especially useful featuies aie one that automatically indents to the piopei pieuy-
piint position whenevei a new line is staited and one that highlights the matching lef
paienthesis whenevei a iight paienthesis is typed.
Lisp obeys the convention that eveiy expiession has a value. Tis convention, to-
gethei with the old ieputation of Lisp as an inecient language, is the souice of the
quip by Alan Peilis (paiaphiasing Oscai Wilde) that Lisp piogiammeis knowthe value
of eveiything but the cost of nothing.
8
ln this book, we do not show the inteipieteis iesponse to evaluating denitions,
since this is highly implementation-dependent.
10
(* 5 size)
10
Heie aie fuithei examples of the use of define
(define pi 3.14159)
(define radius 10)
(* pi (* radius radius))
314.159
(define circumference (* 2 pi radius))
circumference
62.8318
Define is oui languages simplest means of abstiaction, foi it allows
us to use simple names to iefei to the iesults of compound opeiations,
such as the circumference computed above. ln geneial, computational
objects may have veiy complex stiuctuies, and it would be extiemely
inconvenient to have to iemembei and iepeat theii details each time we
want to use them. lndeed, complex piogiams aie constiucted by build-
ing, step by step, computational objects of incieasing complexity. Te
inteipietei makes this step-by-step piogiam constiuction paiticulaily
convenient because name-object associations can be cieated inciemen-
tally in successive inteiactions. Tis featuie encouiages the inciemental
development and testing of piogiams and is laigely iesponsible foi the
fact that a Lisp piogiam usually consists of a laige numbei of ielatively
simple pioceduies.
lt should be cleai that the possibility of associating values with sym-
bols and latei ietiieving them means that the inteipietei must maintain
some soit of memoiy that keeps tiack of the name-object paiis. Tis
memoiy is called the en+:ronen (moie piecisely the g|o|o| en+:ron
en, since we will see latei that a computation may involve a numbei
11
of dieient enviionments).
9
1.1.3 Evaluating Combinations
One of oui goals in this chaptei is to isolate issues about thinking pio-
ceduially. As a case in point, let us considei that, in evaluating combi-
nations, the inteipietei is itself following a pioceduie.
To evaluate a combination, do the following
1. Evaluate the subexpiessions of the combination.
2. Apply the pioceduie that is the value of the lefmost subexpies-
sion (the opeiatoi) to the aiguments that aie the values of the
othei subexpiessions (the opeiands).
Even this simple iule illustiates some impoitant points about piocesses
in geneial. liist, obseive that the ist step dictates that in oidei to ac-
complish the evaluation piocess foi a combination we must ist pei-
foim the evaluation piocess on each element of the combination. Tus,
the evaluation iule is recvrs:+e in natuie, that is, it includes, as one of
its steps, the need to invoke the iule itself.
10
Notice how succinctly the idea of iecuision can be used to expiess
what, in the case of a deeply nested combination, would otheiwise be
viewed as a iathei complicated piocess. loi example, evaluating
9
Chaptei 3 will show that this notion of enviionment is ciucial, both foi undei-
standing how the inteipietei woiks and foi implementing inteipieteis.
10
lt may seem stiange that the evaluation iule says, as pait of the ist step, that
we should evaluate the lefmost element of a combination, since at this point that can
only be an opeiatoi such as + oi * iepiesenting a built-in piimitive pioceduie such as
addition oi multiplication. We will see latei that it is useful to be able to woik with
combinations whose opeiatois aie themselves compound expiessions.
12
15 26
24 2
390
7 5 3
6 4
+
*
*
+
Figure 1.1: Tiee iepiesentation, showing the value of each
subcombination.
(* (+ 2 (* 4 6))
(+ 3 5 7))
iequiies that the evaluation iule be applied to foui dieient combina-
tions. We can obtain a pictuie of this piocess by iepiesenting the combi-
nation in the foim of a tiee, as shown in liguie 1.1. Each combination is
iepiesented by a node with bianches coiiesponding to the opeiatoi and
the opeiands of the combination stemming fiom it. Te teiminal nodes
(that is, nodes with no bianches stemming fiom them) iepiesent eithei
opeiatois oi numbeis. Viewing evaluation in teims of the tiee, we can
imagine that the values of the opeiands peicolate upwaid, staiting fiom
the teiminal nodes and then combining at highei and highei levels. ln
geneial, we shall see that iecuision is a veiy poweiful technique foi
dealing with hieiaichical, tieelike objects. ln fact, the peicolate values
upwaid foim of the evaluation iule is an example of a geneial kind of
piocess known as ree occvv|o:on.
Next, obseive that the iepeated application of the ist step biings us
to the point wheie we need to evaluate, not combinations, but piimitive
expiessions such as numeials, built-in opeiatois, oi othei names. We
13
take caie of the piimitive cases by stipulating that
the values of numeials aie the numbeis that they name,
the values of built-in opeiatois aie the machine instiuction se-
quences that caiiy out the coiiesponding opeiations, and
the values of othei names aie the objects associated with those
names in the enviionment.
We may iegaid the second iule as a special case of the thiid one by stip-
ulating that symbols such as + and * aie also included in the global envi-
ionment, and aie associated with the sequences of machine instiuctions
that aie theii values. Te key point to notice is the iole of the enviion-
ment in deteimining the meaning of the symbols in expiessions. ln an
inteiactive language such as Lisp, it is meaningless to speak of the value
of an expiession such as (+ x 1) without specifying any infoimation
about the enviionment that would piovide a meaning foi the symbol
x (oi even foi the symbol +). As we shall see in Chaptei 3, the geneial
notion of the enviionment as pioviding a context in which evaluation
takes place will play an impoitant iole in oui undeistanding of piogiam
execution.
Notice that the evaluation iule given above does not handle deni-
tions. loi instance, evaluating (define x 3) does not apply define to
two aiguments, one of which is the value of the symbol x and the othei
of which is 3, since the puipose of the define is piecisely to associate x
with a value. (Tat is, (define x 3) is not a combination.)
Such exceptions to the geneial evaluation iule aie called sec:o| [ors.
Define is the only example of a special foim that we have seen so fai,
but we will meet otheis shoitly. Each special foim has its own evalu-
ation iule. Te vaiious kinds of expiessions (each with its associated
14
evaluation iule) constitute the syntax of the piogiamming language. ln
compaiison with most othei piogiamming languages, Lisp has a veiy
simple syntax, that is, the evaluation iule foi expiessions can be de-
sciibed by a simple geneial iule togethei with specialized iules foi a
small numbei of special foims.
11
1.1.4 Compound Procedures
We have identied in Lisp some of the elements that must appeai in any
poweiful piogiamming language
Numbeis and aiithmetic opeiations aie piimitive data and pioce-
duies.
Nesting of combinations piovides a means of combining opeia-
tions.
Denitions that associate names with values piovide a limited
means of abstiaction.
Now we will leain about roceJvre Jen::ons, a much moie poweiful
abstiaction technique by which a compound opeiation can be given a
name and then iefeiied to as a unit.
11
Special syntactic foims that aie simply convenient alteinative suiface stiuctuies
foi things that can be wiiuen in moie unifoim ways aie sometimes called synoc:c
svgor, to use a phiase coined by Petei Landin. ln compaiison with useis of othei lan-
guages, Lisp piogiammeis, as a iule, aie less conceined with maueis of syntax. (By
contiast, examine any Pascal manual and notice how much of it is devoted to desciip-
tions of syntax.) Tis disdain foi syntax is due paitly to the exibility of Lisp, which
makes it easy to change suiface syntax, and paitly to the obseivation that many con-
venient syntactic constiucts, which make the language less unifoim, end up causing
moie tiouble than they aie woith when piogiams become laige and complex. ln the
woids of Alan Peilis, Syntactic sugai causes cancei of the semicolon.
1
We begin by examining how to expiess the idea of squaiing. We
might say, To squaie something, multiply it by itself. Tis is expiessed
in oui language as
(define (square x) (* x x))
We can undeistand this in the following way
(define (square x) (* x x))
| | | | | |
To square something, multiply it by itself.
We have heie a coovnJ roceJvre, which has been given the name
square. Te pioceduie iepiesents the opeiation of multiplying some-
thing by itself. Te thing to be multiplied is given a local name, x, which
plays the same iole that a pionoun plays in natuial language. Evaluating
the denition cieates this compound pioceduie and associates it with
the name square.
12
Te geneial foim of a pioceduie denition is
(define (name formal parameters)
body)
Te noe is a symbol to be associated with the pioceduie denition in
the enviionment.
13
Te [oro| oroeers aie the names used within
the body of the pioceduie to iefei to the coiiesponding aiguments of
the pioceduie. Te |oJy is an expiession that will yield the value of
12
Obseive that theie aie two dieient opeiations being combined heie we aie cieat-
ing the pioceduie, and we aie giving it the name square. lt is possible, indeed impoitant,
to be able to sepaiate these two notionsto cieate pioceduies without naming them,
and to give names to pioceduies that have alieady been cieated. We will see how to do
this in Section 1.3.2.
13
Tioughout this book, we will desciibe the geneial syntax of expiessions by using
italic symbols delimited by angle biacketse.g., noeto denote the slots in the
expiession to be lled in when such an expiession is actually used.
1
the pioceduie application when the foimal paiameteis aie ieplaced by
the actual aiguments to which the pioceduie is applied.
14
Te noe
and the [oro| oroeers aie giouped within paientheses, just as they
would be in an actual call to the pioceduie being dened.
Having dened square, we can now use it
(square 21)
441
(square (+ 2 5))
49
(square (square 3))
81
We can also use square as a building block in dening othei pioceduies.
loi example, x
2
y
2
can be expiessed as
(+ (square x) (square y))
We can easily dene a pioceduie sum-of-squares that, given any two
numbeis as aiguments, pioduces the sum of theii squaies
(define (sum-of-squares x y)
(+ (square x) (square y)))
(sum-of-squares 3 4)
25
Now we can use sum-of-squares as a building block in constiucting
fuithei pioceduies
(define (f a)
(sum-of-squares (+ a 1) (* a 2)))
(f 5)
136
14
Moie geneially, the body of the pioceduie can be a sequence of expiessions. ln this
case, the inteipietei evaluates each expiession in the sequence in tuin and ietuins the
value of the nal expiession as the value of the pioceduie application.
1
Compound pioceduies aie used in exactly the same way as piimitive
pioceduies. lndeed, one could not tell by looking at the denition of
sum-of-squares given above whethei square was built into the intei-
pietei, like + and *, oi dened as a compound pioceduie.
1.1.5 The Substitution Model for Procedure Application
To evaluate a combination whose opeiatoi names a compound pioce-
duie, the inteipietei follows much the same piocess as foi combina-
tions whose opeiatois name piimitive pioceduies, which we desciibed
in Section 1.1.3. Tat is, the inteipietei evaluates the elements of the
combination and applies the pioceduie (which is the value of the opei-
atoi of the combination) to the aiguments (which aie the values of the
opeiands of the combination).
We can assume that the mechanism foi applying piimitive pioce-
duies to aiguments is built into the inteipietei. loi compound pioce-
duies, the application piocess is as follows
To apply a compound pioceduie to aiguments, evaluate the
body of the pioceduie with each foimal paiametei ieplaced
by the coiiesponding aigument.
To illustiate this piocess, lets evaluate the combination
(f 5)
wheie f is the pioceduie dened in Section 1.1.4. We begin by ietiieving
the body of f
(sum-of-squares (+ a 1) (* a 2))
Ten we ieplace the foimal paiametei a by the aigument
(sum-of-squares (+ 5 1) (* 5 2))
18
Tus the pioblem ieduces to the evaluation of a combination with two
opeiands and an opeiatoi sum-of-squares. Evaluating this combina-
tion involves thiee subpioblems. We must evaluate the opeiatoi to get
the pioceduie to be applied, and we must evaluate the opeiands to get
the aiguments. Now (+ 5 1) pioduces and (* 5 2) pioduces 10, so
we must apply the sum-of-squares pioceduie to and 10. Tese values
aie substituted foi the foimal paiameteis x and y in the body of sum-
of-squares, ieducing the expiession to
(+ (square 6) (square 10))
lf we use the denition of square, this ieduces to
(+ (* 6 6) (* 10 10))
which ieduces by multiplication to
(+ 36 100)
and nally to
136
Te piocess we have just desciibed is called the sv|s:v:on oJe| foi
pioceduie application. lt can be taken as a model that deteimines the
meaning of pioceduie application, insofai as the pioceduies in this
chaptei aie conceined. Howevei, theie aie two points that should be
stiessed
Te puipose of the substitution is to help us think about pioce-
duie application, not to piovide a desciiption of how the intei-
pietei ieally woiks. Typical inteipieteis do not evaluate pioce-
duie applications by manipulating the text of a pioceduie to sub-
stitute values foi the foimal paiameteis. ln piactice, the substi-
tution is accomplished by using a local enviionment foi the foi-
mal paiameteis. We will discuss this moie fully in Chaptei 3 and
19
Chaptei 4 when we examine the implementation of an inteipietei
in detail.
Ovei the couise of this book, we will piesent a sequence of in-
cieasingly elaboiate models of how inteipieteis woik, culminat-
ing with a complete implementation of an inteipietei and com-
pilei in Chaptei . Te substitution model is only the ist of these
modelsa way to get staited thinking foimally about the evalu-
ation piocess. ln geneial, when modeling phenomena in science
and engineeiing, we begin with simplied, incomplete models.
As we examine things in gieatei detail, these simple models be-
come inadequate and must be ieplaced by moie iened models.
Te substitution model is no exception. ln paiticulai, when we
addiess in Chaptei 3 the use of pioceduies with mutable data,
we will see that the substitution model bieaks down and must be
ieplaced by a moie complicated model of pioceduie application.
1
Applicative order versus normal order
Accoiding to the desciiption of evaluation given in Section 1.1.3, the
inteipietei ist evaluates the opeiatoi and opeiands and then applies
the iesulting pioceduie to the iesulting aiguments. Tis is not the only
way to peifoim evaluation. An alteinative evaluation model would not
evaluate the opeiands until theii values weie needed. lnstead it would
1
Despite the simplicity of the substitution idea, it tuins out to be suipiisingly com-
plicated to give a iigoious mathematical denition of the substitution piocess. Te
pioblem aiises fiom the possibility of confusion between the names used foi the foimal
paiameteis of a pioceduie and the (possibly identical) names used in the expiessions to
which the pioceduie may be applied. lndeed, theie is a long histoiy of eiioneous def-
initions of sv|s:v:on in the liteiatuie of logic and piogiamming semantics. See Stoy
19 foi a caieful discussion of substitution.
20
ist substitute opeiand expiessions foi paiameteis until it obtained an
expiession involving only piimitive opeiatois, and would then peifoim
the evaluation. lf we used this method, the evaluation of (f 5) would
pioceed accoiding to the sequence of expansions
(sum-of-squares (+ 5 1) (* 5 2))
(+ (square (+ 5 1)) (square (* 5 2)) )
(+ (* (+ 5 1) (+ 5 1)) (* (* 5 2) (* 5 2)))
followed by the ieductions
(+ (* 6 6) (* 10 10))
(+ 36 100)
136
Tis gives the same answei as oui pievious evaluation model, but the
piocess is dieient. ln paiticulai, the evaluations of (+ 5 1) and (* 5
2) aie each peifoimed twice heie, coiiesponding to the ieduction of the
expiession (* x x) with x ieplaced iespectively by (+ 5 1) and (* 5
2).
Tis alteinative fully expand and then ieduce evaluation method
is known as noro|orJer e+o|vo:on, in contiast to the evaluate the
aiguments and then apply method that the inteipietei actually uses,
which is called o|:co:+eorJer e+o|vo:on. lt can be shown that, foi
pioceduie applications that can be modeled using substitution (includ-
ing all the pioceduies in the ist two chapteis of this book) and that
yield legitimate values, noimal-oidei and applicative-oidei evaluation
pioduce the same value. (See Exeicise 1. foi an instance of an illegit-
imate value wheie noimal-oidei and applicative-oidei evaluation do
not give the same iesult.)
Lisp uses applicative-oidei evaluation, paitly because of the addi-
tional eciency obtained fiom avoiding multiple evaluations of expies-
sions such as those illustiated with (+ 5 1) and (* 5 2) above and, moie
21
signicantly, because noimal-oidei evaluation becomes much moie com-
plicated to deal with when we leave the iealm of pioceduies that can be
modeled by substitution. On the othei hand, noimal-oidei evaluation
can be an extiemely valuable tool, and we will investigate some of its
implications in Chaptei 3 and Chaptei 4.
1
1.1.6 Conditional Expressions and Predicates
Te expiessive powei of the class of pioceduies that we can dene at
this point is veiy limited, because we have no way to make tests and
to peifoim dieient opeiations depending on the iesult of a test. loi
instance, we cannot dene a pioceduie that computes the absolute value
of a numbei by testing whethei the numbei is positive, negative, oi zeio
and taking dieient actions in the dieient cases accoiding to the iule
|x| =
_
_
x if x > 0,
0 if x = 0,
x if x < 0.
Tis constiuct is called a cose ono|ys:s, and theie is a special foim in
Lisp foi notating such a case analysis. lt is called cond (which stands foi
conditional), and it is used as follows
(define (abs x)
(cond ((> x 0) x)
((= x 0) 0)
((< x 0) (- x))))
Te geneial foim of a conditional expiession is
1
ln Chaptei 3 we will intioduce sreo rocess:ng, which is a way of handling appai-
ently innite data stiuctuies by incoipoiating a limited foim of noimal-oidei evalu-
ation. ln Section 4.2 we will modify the Scheme inteipietei to pioduce a noimal-oidei
vaiiant of Scheme.
22
(cond (p
1
e
1
)
(p
2
e
2
)
. . .
(p
n
e
n
))
consisting of the symbol cond followed by paienthesized paiis of ex-
piessions
(p e)
called c|ovses. Te ist expiession in each paii is a reJ:coethat is, an
expiession whose value is inteipieted as eithei tiue oi false.
1
Conditional expiessions aie evaluated as follows. Te piedicate p
1
)))
3( 2)(2 )
.
Exercise 1.3: Dene a pioceduie that takes thiee numbeis
as aiguments and ietuins the sum of the squaies of the two
laigei numbeis.
Exercise 1.4: Obseive that oui model of evaluation allows
foi combinations whose opeiatois aie compound expies-
sions. Use this obseivation to desciibe the behavioi of the
following pioceduie
(define (a-plus-abs-b a b)
((if (> b 0) + -) a b))
Exercise 1.5: Ben Bitdiddle has invented a test to deteimine
whethei the inteipietei he is faced with is using applicative-
oidei evaluation oi noimal-oidei evaluation. He denes the
following two pioceduies
(define (p) (p))
(define (test x y)
(if (= x 0) 0 y))
Ten he evaluates the expiession
(test 0 (p))
What behavioi will Ben obseive with an inteipietei that
uses applicative-oidei evaluation` What behavioi will he
obseive with an inteipietei that uses noimal-oidei evalu-
ation` Explain youi answei. (Assume that the evaluation
2
iule foi the special foim if is the same whethei the in-
teipietei is using noimal oi applicative oidei Te piedi-
cate expiession is evaluated ist, and the iesult deteimines
whethei to evaluate the consequent oi the alteinative ex-
piession.)
1.1.7 Example: Square Roots by Newtons Method
Pioceduies, as intioduced above, aie much like oidinaiy mathematical
functions. Tey specify a value that is deteimined by one oi moie pa-
iameteis. But theie is an impoitant dieience between mathematical
functions and computei pioceduies. Pioceduies must be eective.
As a case in point, considei the pioblem of computing squaie ioots.
We can dene the squaie-ioot function as
_
0 if n = 0,
1 if n = 1,
lib(n 1) lib(n 2) otheiwise.
We can immediately tianslate this denition into a iecuisive pioceduie
foi computing libonacci numbeis
(define (fib n)
(cond ((= n 0) 0)
((= n 1) 1)
(else (+ (fib (- n 1))
(fib (- n 2))))))
Considei the pauein of this computation. To compute (fib 5), we com-
pute (fib 4) and (fib 3). To compute (fib 4), we compute (fib 3)
and (fib 2). ln geneial, the evolved piocess looks like a tiee, as shown
in liguie 1.. Notice that the bianches split into two at each level (ex-
cept at the bouom), this ieects the fact that the fib pioceduie calls
itself twice each time it is invoked.
Tis pioceduie is instiuctive as a piototypical tiee iecuision, but it
is a teiiible way to compute libonacci numbeis because it does so much
iedundant computation. Notice in liguie 1. that the entiie computation
of (fib 3)almost half the woikis duplicated. ln fact, it is not haid to
show that the numbei of times the pioceduie will compute (fib 1) oi
(fib 0) (the numbei of leaves in the above tiee, in geneial) is piecisely
lib(n 1). To get an idea of how bad this is, one can show that the value
of lib(n) giows exponentially with n. Moie piecisely (see Exeicise 1.13),
lib(n) is the closest integei to
n
/
, wheie
=
1
2
1.180
48
fib 5
fib 4 fib 3
fib 3 fib 2 fib 2 fib 1
1
fib 2 fib 1 fib 1 fib 0 fib 1 fib 0
1 1 0 1 0
fib 1 fib 0
1 0
Figure 1.5: Te tiee-iecuisive piocess geneiated in com-
puting (fib 5).
is the go|Jen ro:o, which satises the equation
2
= 1.
Tus, the piocess uses a numbei of steps that giows exponentially with
the input. On the othei hand, the space iequiied giows only lineaily
with the input, because we need keep tiack only of which nodes aie
above us in the tiee at any point in the computation. ln geneial, the
numbei of steps iequiied by a tiee-iecuisive piocess will be piopoi-
tional to the numbei of nodes in the tiee, while the space iequiied will
be piopoitional to the maximum depth of the tiee.
We can also foimulate an iteiative piocess foi computing the li-
bonacci numbeis. Te idea is to use a paii of integeis a and b, initialized
to lib(1) 1 and lib(0) 0, and to iepeatedly apply the simultaneous
49
tiansfoimations
a a b,
b a.
lt is not haid to show that, afei applying this tiansfoimation n times, a
and b will be equal, iespectively, to lib(n 1) and lib(n). Tus, we can
compute libonacci numbeis iteiatively using the pioceduie
(define (fib n)
(fib-iter 1 0 n))
(define (fib-iter a b count)
(if (= count 0)
b
(fib-iter (+ a b) a (- count 1))))
Tis second method foi computing lib(n) is a lineai iteiation. Te diei-
ence in numbei of steps iequiied by the two methodsone lineai in n,
one giowing as fast as lib(n) itselfis enoimous, even foi small inputs.
One should not conclude fiom this that tiee-iecuisive piocesses
aie useless. When we considei piocesses that opeiate on hieiaichically
stiuctuied data iathei than numbeis, we will nd that tiee iecuision is
a natuial and poweiful tool.
32
But even in numeiical opeiations, tiee-
iecuisive piocesses can be useful in helping us to undeistand and de-
sign piogiams. loi instance, although the ist fib pioceduie is much
less ecient than the second one, it is moie stiaightfoiwaid, being liule
moie than a tianslation into Lisp of the denition of the libonacci se-
quence. To foimulate the iteiative algoiithm iequiied noticing that the
computation could be iecast as an iteiation with thiee state vaiiables.
32
An example of this was hinted at in Section 1.1.3. Te inteipietei itself evaluates
expiessions using a tiee-iecuisive piocess.
0
Example: Counting change
lt takes only a bit of cleveiness to come up with the iteiative libonacci
algoiithm. ln contiast, considei the following pioblem How many dif-
feient ways can we make change of S1.00, given half-dollais, quaiteis,
dimes, nickels, and pennies` Moie geneially, can we wiite a pioceduie
to compute the numbei of ways to change any given amount of money`
Tis pioblem has a simple solution as a iecuisive pioceduie. Sup-
pose we think of the types of coins available as aiianged in some oidei.
Ten the following ielation holds
Te numbei of ways to change amount a using n kinds of coins
equals
the numbei of ways to change amount a using all but the ist
kind of coin, plus
the numbei of ways to change amount a d using all n kinds of
coins, wheie d is the denomination of the ist kind of coin.
To see why this is tiue, obseive that the ways to make change can be
divided into two gioups those that do not use any of the ist kind of
coin, and those that do. Teiefoie, the total numbei of ways to make
change foi some amount is equal to the numbei of ways to make change
foi the amount without using any of the ist kind of coin, plus the
numbei of ways to make change assuming that we do use the ist kind
of coin. But the lauei numbei is equal to the numbei of ways to make
change foi the amount that iemains afei using a coin of the ist kind.
Tus, we can iecuisively ieduce the pioblem of changing a given
amount to the pioblem of changing smallei amounts using fewei kinds
of coins. Considei this ieduction iule caiefully, and convince youiself
1
that we can use it to desciibe an algoiithm if we specify the following
degeneiate cases
33
lf a is exactly 0, we should count that as 1 way to make change.
lf a is less than 0, we should count that as 0 ways to make change.
lf n is 0, we should count that as 0 ways to make change.
We can easily tianslate this desciiption into a iecuisive pioceduie
(define (count-change amount) (cc amount 5))
(define (cc amount kinds-of-coins)
(cond ((= amount 0) 1)
((or (< amount 0) (= kinds-of-coins 0)) 0)
(else (+ (cc amount
(- kinds-of-coins 1))
(cc (- amount
(first-denomination
kinds-of-coins))
kinds-of-coins)))))
(define (first-denomination kinds-of-coins)
(cond ((= kinds-of-coins 1) 1)
((= kinds-of-coins 2) 5)
((= kinds-of-coins 3) 10)
((= kinds-of-coins 4) 25)
((= kinds-of-coins 5) 50)))
(Te first-denomination pioceduie takes as input the numbei of kinds
of coins available and ietuins the denomination of the ist kind. Heie
we aie thinking of the coins as aiianged in oidei fiom laigest to small-
est, but any oidei would do as well.) We can now answei oui oiiginal
question about changing a dollai
33
loi example, woik thiough in detail how the ieduction iule applies to the pioblem
of making change foi 10 cents using pennies and nickels.
2
(count-change 100)
292
Count-change geneiates a tiee-iecuisive piocess with iedundancies sim-
ilai to those in oui ist implementation of fib. (lt will take quite a while
foi that 292 to be computed.) On the othei hand, it is not obvious how
to design a beuei algoiithm foi computing the iesult, and we leave this
pioblem as a challenge. Te obseivation that a tiee-iecuisive piocess
may be highly inecient but ofen easy to specify and undeistand has
led people to piopose that one could get the best of both woilds by
designing a smait compilei that could tiansfoim tiee-iecuisive pio-
ceduies into moie ecient pioceduies that compute the same iesult.
34
Exercise 1.11: A function f is dened by the iule that
f (n) =
_
_
n if n < 3,
f (n 1) 2f (n 2) 3f (n 3) if n 3.
Wiite a pioceduie that computes f by means of a iecuisive
piocess. Wiite a pioceduie that computes f by means of an
iteiative piocess.
Exercise 1.12: Te following pauein of numbeis is called
Posco|s r:ong|e.
34
One appioach to coping with iedundant computations is to aiiange maueis so
that we automatically constiuct a table of values as they aie computed. Each time we
aie asked to apply the pioceduie to some aigument, we ist look to see if the value
is alieady stoied in the table, in which case we avoid peifoiming the iedundant com-
putation. Tis stiategy, known as o|v|o:on oi eo::o:on, can be implemented in a
stiaightfoiwaid way. Tabulation can sometimes be used to tiansfoim piocesses that
iequiie an exponential numbei of steps (such as count-change) into piocesses whose
space and time iequiiements giow lineaily with the input. See Exeicise 3.2.
3
1
1 1
1 2 1
1 3 3 1
1 4 6 4 1
. . .
Te numbeis at the edge of the tiiangle aie all 1, and each
numbei inside the tiiangle is the sum of the two numbeis
above it.
3
Wiite a pioceduie that computes elements of
Pascals tiiangle by means of a iecuisive piocess.
Exercise 1.13: Piove that lib(n) is the closest integei to
n
/
, wheie = (1
)/2. Hint Let = (1
)/2.
Use induction and the denition of the libonacci numbeis
(see Section 1.2.2) to piove that lib(n) (
n
n
)/
.
1.2.3 Orders of Growth
Te pievious examples illustiate that piocesses can diei consideiably
in the iates at which they consume computational iesouices. One con-
venient way to desciibe this dieience is to use the notion of orJer o[
gro+| to obtain a gioss measuie of the iesouices iequiied by a piocess
as the inputs become laigei.
3
Te elements of Pascals tiiangle aie called the |:no:o| coec:ens, because the
n
th
iow consists of the coecients of the teims in the expansion of (x y)
n
. Tis pat-
tein foi computing the coecients appeaied in Blaise Pascals 13 seminal woik on
piobability theoiy, Tro:e Jv r:ong|e or:|e:qve. Accoiding to Knuth (193), the same
pauein appeais in the S:vyven Yvc|:en (Te Piecious Miiioi of the loui Elements),
published by the Chinese mathematician Chu Shih-chieh in 1303, in the woiks of the
twelfh-centuiy Peisian poet and mathematician Omai Khayyam, and in the woiks of
the twelfh-centuiy Hindu mathematician Bhascaia Achaiya.
4
Let n be a paiametei that measuies the size of the pioblem, and let
R(n) be the amount of iesouices the piocess iequiies foi a pioblem of
size n. ln oui pievious examples we took n to be the numbei foi which
a given function is to be computed, but theie aie othei possibilities. loi
instance, if oui goal is to compute an appioximation to the squaie ioot of
a numbei, we might take n to be the numbei of digits accuiacy iequiied.
loi matiix multiplication we might take n to be the numbei of iows in
the matiices. ln geneial theie aie a numbei of piopeities of the pioblem
with iespect to which it will be desiiable to analyze a given piocess.
Similaily, R(n) might measuie the numbei of inteinal stoiage iegisteis
used, the numbei of elementaiy machine opeiations peifoimed, and so
on. ln computeis that do only a xed numbei of opeiations at a time, the
time iequiied will be piopoitional to the numbei of elementaiy machine
opeiations peifoimed.
We say that R(n) has oidei of giowth (f (n)), wiiuen R(n) (f (n))
(pionounced theta of f (n)), if theie aie positive constants k
1
and k
2
independent of n such that k
1
f (n) R(n) k
2
f (n) foi any suciently
laige value of n. (ln othei woids, foi laigen, the value R(n) is sandwiched
between k
1
f (n) and k
2
f (n).)
loi instance, with the lineai iecuisive piocess foi computing facto-
iial desciibed in Section 1.2.1 the numbei of steps giows piopoitionally
to the input n. Tus, the steps iequiied foi this piocess giows as (n).
We also saw that the space iequiied giows as (n). loi the iteiative
factoiial, the numbei of steps is still (n) but the space is (1)that
is, constant.
3
Te tiee-iecuisive libonacci computation iequiies (
n
)
3
Tese statements mask a gieat deal of oveisimplication. loi instance, if we count
piocess steps as machine opeiations we aie making the assumption that the numbei
of machine opeiations needed to peifoim, say, a multiplication is independent of the
size of the numbeis to be multiplied, which is false if the numbeis aie suciently laige.
Similai iemaiks hold foi the estimates of space. Like the design and desciiption of a
piocess, the analysis of a piocess can be caiiied out at vaiious levels of abstiaction.
steps and space (n), wheie is the golden iatio desciibed in Section
1.2.2.
Oideis of giowth piovide only a ciude desciiption of the behavioi
of a piocess. loi example, a piocess iequiiing n
2
steps and a piocess
iequiiing 1000n
2
steps and a piocess iequiiing 3n
2
10n 1 steps all
have (n
2
) oidei of giowth. On the othei hand, oidei of giowth piovides
a useful indication of how we may expect the behavioi of the piocess to
change as we change the size of the pioblem. loi a (n) (lineai) piocess,
doubling the size will ioughly double the amount of iesouices used. loi
an exponential piocess, each inciement in pioblemsize will multiply the
iesouice utilization by a constant factoi. ln the iemaindei of Section 1.2
we will examine two algoiithms whose oidei of giowth is logaiithmic,
so that doubling the pioblem size incieases the iesouice iequiiement
by a constant amount.
Exercise 1.14: Diaw the tiee illustiating the piocess gen-
eiated by the count-change pioceduie of Section 1.2.2 in
making change foi 11 cents. What aie the oideis of giowth
of the space and numbei of steps used by this piocess as
the amount to be changed incieases`
Exercise 1.15: Te sine of an angle (specied in iadians)
can be computed by making use of the appioximation sinx x
if x is suciently small, and the tiigonometiic identity
sinx = 3 sin
x
3
4 sin
3
x
3
to ieduce the size of the aigument of sin. (loi puiposes of
this exeicise an angle is consideied suciently small if its
magnitude is not gieatei than 0.1 iadians.) Tese ideas aie
incoipoiated in the following pioceduies
(define (expt b n)
(expt-iter b n 1))
(define (expt-iter b counter product)
(if (= counter 0)
product
(expt-iter b
(- counter 1)
(* b product))))
Tis veision iequiies (n) steps and (1) space.
We can compute exponentials in fewei steps by using successive
squaiing. loi instance, iathei than computing b
8
as
b (b (b (b (b (b (b b)))))) ,
we can compute it using thiee multiplications
b
2
= b b,
b
4
= b
2
b
2
,
b
8
= b
4
b
4
.
Tis method woiks ne foi exponents that aie poweis of 2. We can
also take advantage of successive squaiing in computing exponentials
in geneial if we use the iule
b
n
= (b
n/2
)
2
if n is even,
b
n
= b b
n1
if n is odd.
We can expiess this method as a pioceduie
(define (fast-expt b n)
(cond ((= n 0) 1)
((even? n) (square (fast-expt b (/ n 2))))
(else (* b (fast-expt b (- n 1))))))
8
wheie the piedicate to test whethei an integei is even is dened in teims
of the piimitive pioceduie remainder by
(define (even? n)
(= (remainder n 2) 0))
Te piocess evolved by fast-expt giows logaiithmically withn in both
space and numbei of steps. To see this, obseive that computing b
2n
us-
ing fast-expt iequiies only one moie multiplication than computing
b
n
. Te size of the exponent we can compute theiefoie doubles (appiox-
imately) with eveiy new multiplication we aie allowed. Tus, the num-
bei of multiplications iequiied foi an exponent of n giows about as fast
as the logaiithm of n to the base 2. Te piocess has (logn) giowth.
3
Te dieience between (logn) giowth and (n) giowth becomes
stiiking as n becomes laige. loi example, fast-expt foi n 1000 ie-
quiies only 14 multiplications.
38
lt is also possible to use the idea of
successive squaiing to devise an iteiative algoiithm that computes ex-
ponentials with a logaiithmic numbei of steps (see Exeicise 1.1), al-
though, as is ofen the case with iteiative algoiithms, this is not wiiuen
down so stiaightfoiwaidly as the iecuisive algoiithm.
39
Exercise 1.16: Design a pioceduie that evolves an iteia-
tive exponentiation piocess that uses successive squaiing
3
Moie piecisely, the numbei of multiplications iequiied is equal to 1 less than the
log base 2 of n plus the numbei of ones in the binaiy iepiesentation of n. Tis total
is always less than twice the log base 2 of n. Te aibitiaiy constants k
1
and k
2
in the
denition of oidei notation imply that, foi a logaiithmic piocess, the base to which
logaiithms aie taken does not mauei, so all such piocesses aie desciibed as (logn).
38
You may wondei why anyone would caie about iaising numbeis to the 1000th
powei. See Section 1.2..
39
Tis iteiative algoiithm is ancient. lt appeais in the C|onJo|svro by Achaiya
Pingala, wiiuen befoie 200 n.c. See Knuth 1981, section 4..3, foi a full discussion and
analysis of this and othei methods of exponentiation.
9
and uses a logaiithmic numbei of steps, as does fast-expt.
(Hint Using the obseivation that (b
n/2
)
2
= (b
2
)
n/2
, keep,
along with the exponent n and the base b, an additional
state vaiiable a, and dene the state tiansfoimation in such
a way that the pioduct ab
n
is unchanged fiomstate to state.
At the beginning of the piocess a is taken to be 1, and the
answei is given by the value of a at the end of the piocess.
ln geneial, the technique of dening an :n+or:on qvon:y
that iemains unchanged fiom state to state is a poweiful
way to think about the design of iteiative algoiithms.)
Exercise 1.17: Te exponentiation algoiithms in this sec-
tion aie based on peifoiming exponentiation by means of
iepeated multiplication. ln a similai way, one can peifoim
integei multiplication by means of iepeated addition. Te
following multiplication pioceduie (in which it is assumed
that oui language can only add, not multiply) is analogous
to the expt pioceduie
(define (* a b)
(if (= b 0)
0
(+ a (* a (- b 1)))))
Tis algoiithm takes a numbei of steps that is lineai in b.
Now suppose we include, togethei with addition, opeia-
tions double, which doubles an integei, and halve, which
divides an (even) integei by 2. Using these, design a mul-
tiplication pioceduie analogous to fast-expt that uses a
logaiithmic numbei of steps.
0
Exercise 1.18: Using the iesults of Exeicise 1.1 and Exei-
cise 1.1, devise a pioceduie that geneiates an iteiative pio-
cess foi multiplying two integeis in teims of adding, dou-
bling, and halving and uses a logaiithmic numbei of steps.
40
Exercise 1.19: Teie is a clevei algoiithm foi computing
the libonacci numbeis in a logaiithmic numbei of steps.
Recall the tiansfoimation of the state vaiiables a and b in
the fib-iter piocess of Section 1.2.2 a a b and b a.
Call this tiansfoimation T, and obseive that applying T
ovei and ovei againn times, staiting with 1 and 0, pioduces
the paii lib(n 1) and lib(n). ln othei woids, the libonacci
numbeis aie pioduced by applyingT
n
, the n
th
powei of the
tiansfoimationT, staiting with the paii (1, 0). Nowconsidei
T to be the special case of p = 0 and q = 1 in a family of
tiansfoimations T
pq
, wheie T
pq
tiansfoims the paii (a, b)
accoiding to a bq aq ap and b bp aq. Show
that if we apply such a tiansfoimationT
pq
twice, the eect
is the same as using a single tiansfoimation T
p
q
of the
same foim, and compute p
and q
?? , compute q
(/ count 2)))
(else (fib-iter (+ (* b q) (* a q) (* a p))
(+ (* b p) (* a q))
p
q
(- count 1)))))
1.2.5 Greatest Common Divisors
Te gieatest common divisoi (ccu) of two integeis a and b is dened to
be the laigest integei that divides both a and b with no iemaindei. loi
example, the ccu of 1 and 28 is 4. ln Chaptei 2, when we investigate
how to implement iational-numbei aiithmetic, we will need to be able
to compute ccus in oidei to ieduce iational numbeis to lowest teims.
(To ieduce a iational numbei to lowest teims, we must divide both the
numeiatoi and the denominatoi by theii ccu. loi example, 1/28 ie-
duces to 4/.) One way to nd the ccu of two integeis is to factoi them
and seaich foi common factois, but theie is a famous algoiithm that is
much moie ecient.
Te idea of the algoiithm is based on the obseivation that, if r is the
iemaindei when a is divided by b, then the common divisois of a and b
aie piecisely the same as the common divisois of b and r . Tus, we can
2
use the equation
GCD(a,b) = GCD(b,r)
to successively ieduce the pioblem of computing a ccu to the pioblem
of computing the ccu of smallei and smallei paiis of integeis. loi ex-
ample,
GCD(206,40) = GCD(40,6)
= GCD(6,4)
= GCD(4,2)
= GCD(2,0)
= 2
ieduces ccu(20, 40) to ccu(2, 0), which is 2. lt is possible to show that
staiting with any two positive integeis and peifoiming iepeated ieduc-
tions will always eventually pioduce a paii wheie the second numbei is
0. Ten the ccu is the othei numbei in the paii. Tis method foi com-
puting the ccu is known as Fvc|:Js A|gor:|.
42
lt is easy to expiess Euclids Algoiithm as a pioceduie
(define (gcd a b)
(if (= b 0)
a
(gcd b (remainder a b))))
Tis geneiates an iteiative piocess, whose numbei of steps giows as the
logaiithm of the numbeis involved.
42
Euclids Algoiithm is so called because it appeais in Euclids F|eens (Book , ca.
300 n.c.). Accoiding to Knuth (193), it can be consideied the oldest known nontiivial
algoiithm. Te ancient Egyptian method of multiplication (Exeicise 1.18) is suiely oldei,
but, as Knuth explains, Euclids algoiithm is the oldest known to have been piesented
as a geneial algoiithm, iathei than as a set of illustiative examples.
3
Te fact that the numbei of steps iequiied by Euclids Algoiithm
has logaiithmic giowth beais an inteiesting ielation to the libonacci
numbeis
Lams Theorem: lf Euclids Algoiithmiequiies k steps to
compute the ccu of some paii, then the smallei numbei in
the paii must be gieatei than oi equal to the k
th
libonacci
numbei.
43
We can use this theoiem to get an oidei-of-giowth estimate foi Euclids
Algoiithm. Let n be the smallei of the two inputs to the pioceduie. lf the
piocess takes k steps, then we must have n lib(k)
k
/
. Teiefoie
the numbei of steps k giows as the logaiithm(to the base ) of n. Hence,
the oidei of giowth is (logn).
43
Tis theoiem was pioved in 184 by Gabiiel Lam, a liench mathematician and
engineei known chiey foi his contiibutions to mathematical physics. To piove the
theoiem, we considei paiis (a
k
, b
k
), wheie a
k
b
k
, foi which Euclids Algoiithm
teiminates in k steps. Te pioof is based on the claim that, if (a
k1
, b
k1
) (a
k
, b
k
)
(a
k 1
, b
k 1
) aie thiee successive paiis in the ieduction piocess, then we must have
b
k1
b
k
b
k 1
. To veiify the claim, considei that a ieduction step is dened by
applying the tiansfoimation a
k 1
= b
k
, b
k 1
= iemaindei of a
k
divided by b
k
. Te
second equation means that a
k
= qb
k
b
k 1
foi some positive integei q. And since q
must be at least 1 we have a
k
= qb
k
b
k 1
b
k
b
k 1
. But in the pievious ieduction
step we have b
k1
= a
k
. Teiefoie, b
k1
= a
k
b
k
b
k 1
. Tis veiies the claim. Now
we can piove the theoiem by induction on k, the numbei of steps that the algoiithm
iequiies to teiminate. Te iesult is tiue foi k = 1, since this meiely iequiies that b be at
least as laige as lib(1) 1. Now, assume that the iesult is tiue foi all integeis less than oi
equal to k and establish the iesult foi k 1. Let (a
k1
, b
k1
) (a
k
, b
k
) (a
k 1
, b
k 1
)
be successive paiis in the ieduction piocess. By oui induction hypotheses, we have
b
k 1
lib(k 1) and b
k
lib(k). Tus, applying the claim we just pioved togethei
with the denition of the libonacci numbeis gives b
k1
b
k
b
k 1
lib(k) lib(k
1) = lib(k 1), which completes the pioof of Lams Teoiem.
4
Exercise 1.20: Te piocess that a pioceduie geneiates is
of couise dependent on the iules used by the inteipietei.
As an example, considei the iteiative gcd pioceduie given
above. Suppose we weie to inteipiet this pioceduie using
noimal-oidei evaluation, as discussed in Section 1.1.. (Te
noimal-oidei-evaluation iule foi if is desciibed in Exeicise
1..) Using the substitution method (foi noimal oidei), illus-
tiate the piocess geneiated in evaluating (gcd 206 40) and
indicate the remainder opeiations that aie actually pei-
foimed. How many remainder opeiations aie actually pei-
foimed in the noimal-oidei evaluation of (gcd 206 40)`
ln the applicative-oidei evaluation`
1.2.6 Example: Testing for Primality
Tis section desciibes two methods foi checking the piimality of an in-
tegei n, one with oidei of giowth (
n.
44
Tis means that the algo-
iithmneed only test divisois between 1 and
n).
The Fermat test
Te (logn) piimality test is based on a iesult fiom numbei theoiy
known as leimats Liule Teoiem.
4
44
lf d is a divisoi of n, then so is n/d. But d and n/d cannot both be gieatei than
n.
4
Pieiie de leimat (101-1) is consideied to be the foundei of modein numbei the-
oiy. He obtained many impoitant numbei-theoietic iesults, but he usually announced
just the iesults, without pioviding his pioofs. leimats Liule Teoiem was stated in a
leuei he wiote in 140. Te ist published pioof was given by Eulei in 13 (and an
eailiei, identical pioof was discoveied in the unpublished manusciipts of Leibniz). Te
most famous of leimats iesultsknown as leimats Last Teoiemwas joued down
in 13 in his copy of the book Ar:|e:c (by the thiid-centuiy Gieek mathematician
Diophantus) with the iemaik l have discoveied a tiuly iemaikable pioof, but this mai-
gin is too small to contain it. linding a pioof of leimats Last Teoiem became one of
the most famous challenges in numbei theoiy. A complete solution was nally given
in 199 by Andiew Wiles of Piinceton Univeisity.
. . ., is due
to Leibniz. Well see how to use this as the basis foi some fancy numeiical tiicks in
Section 3..3.
n=a
f (n) = f (a) f (b),
to expiess this concept. Te powei of sigma notation is that it allows
mathematicians to deal with the concept of summation itself iathei than
only with paiticulai sumsfoi example, to foimulate geneial iesults
about sums that aie independent of the paiticulai seiies being summed.
Similaily, as piogiam designeis, we would like oui language to be
poweiful enough so that we can wiite a pioceduie that expiesses the
concept of summation itself iathei than only pioceduies that compute
paiticulai sums. We can do so ieadily in oui pioceduial language by
taking the common template shown above and tiansfoiming the slots
into foimal paiameteis
(define (sum term a next b)
(if (> a b)
0
(+ (term a)
(sum term (next a) next b))))
Notice that sum takes as its aiguments the lowei and uppei bounds a
and b togethei with the pioceduies term and next. We can use sum just
as we would any pioceduie. loi example, we can use it (along with a
pioceduie inc that inciements its aigument by 1) to dene sum-cubes
(define (inc n) (+ n 1))
(define (sum-cubes a b)
(sum cube a inc b))
Using this, we can compute the sum of the cubes of the integeis fiom 1
to 10
(sum-cubes 1 10)
3025
With the aid of an identity pioceduie to compute the teim, we can dene
sum-integers in teims of sum
(define (identity x) x)
(define (sum-integers a b)
(sum identity a inc b))
Ten we can add up the integeis fiom 1 to 10
(sum-integers 1 10)
55
We can also dene pi-sum in the same way
0
0
Notice that we have used block stiuctuie (Section 1.1.8) to embed the denitions of
pi-next and pi-term within pi-sum, since these pioceduies aie unlikely to be useful
foi any othei puipose. We will see how to get iid of them altogethei in Section 1.3.2.
8
(define (pi-sum a b)
(define (pi-term x)
(/ 1.0 (* x (+ x 2))))
(define (pi-next x)
(+ x 4))
(sum pi-term a pi-next b))
Using these pioceduies, we can compute an appioximation to
(* 8 (pi-sum 1 1000))
3.139592655589783
Once we have sum, we can use it as a building block in foimulating fui-
thei concepts. loi instance, the denite integial of a function f between
the limits a and b can be appioximated numeiically using the foimula
b
a
f =
_
f
_
a
dx
2
_
f
_
a dx
dx
2
_
f
_
a 2dx
dx
2
_
. . .
_
dx
foi small values of dx. We can expiess this diiectly as a pioceduie
(define (integral f a b dx)
(define (add-dx x)
(+ x dx))
(* (sum f (+ a (/ dx 2.0)) add-dx b)
dx))
(integral cube 0 1 0.01)
.24998750000000042
(integral cube 0 1 0.001)
.249999875000001
(Te exact value of the integial of cube between 0 and 1 is 1/4.)
9
Exercise 1.29: Simpsons Rule is a moie accuiate method
of numeiical integiation than the method illustiated above.
Using Simpsons Rule, the integial of a function f between
a and b is appioximated as
h
3
(y
0
4y
1
2y
2
4y
3
2y
4
2y
n2
4y
n1
y
n
),
wheie h = (b a)/n, foi some even integei n, and y
k
=
f (a kh). (lncieasing n incieases the accuiacy of the ap-
pioximation.) Dene a pioceduie that takes as aiguments
f , a, b, and n and ietuins the value of the integial, com-
puted using Simpsons Rule. Use youi pioceduie to inte-
giate cube between 0 and 1 (with n = 100 and n = 1000),
and compaie the iesults to those of the integral pioceduie
shown above.
Exercise 1.30: Te sum pioceduie above geneiates a lineai
iecuision. Te pioceduie can be iewiiuen so that the sum
is peifoimed iteiatively. Show how to do this by lling in
the missing expiessions in the following denition
(define (sum term a next b)
(define (iter a result)
(if ??
??
(iter ?? ??)))
(iter ?? ??))
Exercise 1.31:
a. Te sum pioceduie is only the simplest of a vast num-
bei of similai abstiactions that can be captuied as highei-
80
oidei pioceduies.
1
Wiite an analogous pioceduie called
product that ietuins the pioduct of the values of a
function at points ovei a given iange. Showhowto de-
ne factorial in teims of product. Also use product
to compute appioximations to using the foimula
2
4
=
2 4 4 8
3 3
.
b. lf youi product pioceduie geneiates a iecuisive pio-
cess, wiite one that geneiates an iteiative piocess. lf
it geneiates an iteiative piocess, wiite one that gen-
eiates a iecuisive piocess.
Exercise 1.32:
a. Show that sum and product (Exeicise 1.31) aie both
special cases of a still moie geneial notion called accumulate
that combines a collection of teims, using some gen-
eial accumulation function
(accumulate combiner null-value term a next b)
1
Te intent of Exeicise 1.31 thiough Exeicise 1.33 is to demonstiate the expiessive
powei that is auained by using an appiopiiate abstiaction to consolidate many seem-
ingly dispaiate opeiations. Howevei, though accumulation and lteiing aie elegant
ideas, oui hands aie somewhat tied in using them at this point since we do not yet
have data stiuctuies to piovide suitable means of combination foi these abstiactions.
We will ietuin to these ideas in Section 2.2.3 when we show how to use seqvences as
inteifaces foi combining lteis and accumulatois to build even moie poweiful abstiac-
tions. We will see theie how these methods ieally come into theii own as a poweiful
and elegant appioach to designing piogiams.
2
Tis foimula was discoveied by the seventeenth-centuiy English mathematician
John Wallis.
81
Accumulate takes as aiguments the same teim and
iange specications as sum and product, togethei with
a combiner pioceduie (of two aiguments) that speci-
es how the cuiient teim is to be combined with the
accumulation of the pieceding teims and a null-value
that species what base value to use when the teims
iun out. Wiite accumulate and show how sum and
product can both be dened as simple calls to accumulate.
b. lf youi accumulate pioceduie geneiates a iecuisive
piocess, wiite one that geneiates an iteiative piocess.
lf it geneiates an iteiative piocess, wiite one that gen-
eiates a iecuisive piocess.
Exercise 1.33: You can obtain an even moie geneial vei-
sion of accumulate (Exeicise 1.32) by intioducing the no-
tion of a |er on the teims to be combined. Tat is, combine
only those teims deiived fiom values in the iange that sat-
isfy a specied condition. Te iesulting filtered-accumulate
abstiaction takes the same aiguments as accumulate, to-
gethei with an additional piedicate of one aigument that
species the ltei. Wiite filtered-accumulate as a pioce-
duie. Show how to expiess the following using filtered-
accumulate
a. the sum of the squaies of the piime numbeis in the
inteival a tob (assuming that you have a prime? pied-
icate alieady wiiuen)
b. the pioduct of all the positive integeis less thann that
aie ielatively piime ton (i.e., all positive integeisi < n
such that GCD(i , n) = 1).
82
1.3.2 Constructing Procedures Using Lambda
ln using sum as in Section 1.3.1, it seems teiiibly awkwaid to have to
dene tiivial pioceduies such as pi-term and pi-next just so we can
use them as aiguments to oui highei-oidei pioceduie. Rathei than de-
ne pi-next and pi-term, it would be moie convenient to have a way
to diiectly specify the pioceduie that ietuins its input inciemented by
4 and the pioceduie that ietuins the iecipiocal of its input times its
input plus 2. We can do this by intioducing the special foim lambda,
which cieates pioceduies. Using lambda we can desciibe what we want
as
(lambda (x) (+ x 4))
and
(lambda (x) (/ 1.0 (* x (+ x 2))))
Ten oui pi-sum pioceduie can be expiessed without dening any aux-
iliaiy pioceduies as
(define (pi-sum a b)
(sum (lambda (x) (/ 1.0 (* x (+ x 2))))
a
(lambda (x) (+ x 4))
b))
Again using lambda, we can wiite the integral pioceduie without hav-
ing to dene the auxiliaiy pioceduie add-dx
(define (integral f a b dx)
(* (sum f
(+ a (/ dx 2.0))
(lambda (x) (+ x dx))
b)
dx))
83
ln geneial, lambda is used to cieate pioceduies in the same way as
define, except that no name is specied foi the pioceduie
(lambda (formal-parameters) body)
Te iesulting pioceduie is just as much a pioceduie as one that is cie-
ated using define. Te only dieience is that it has not been associated
with any name in the enviionment. ln fact,
(define (plus4 x) (+ x 4))
is equivalent to
(define plus4 (lambda (x) (+ x 4)))
We can iead a lambda expiession as follows
(lambda (x) (+ x 4))
| | | | |
the procedure of an argument x that adds x and 4
Like any expiession that has a pioceduie as its value, a lambda expies-
sion can be used as the opeiatoi in a combination such as
((lambda (x y z) (+ x y (square z)))
1 2 3)
12
oi, moie geneially, in any context wheie we would noimally use a pio-
ceduie name.
3
3
lt would be cleaiei and less intimidating to people leaining Lisp if a name moie
obvious than lambda, such as make-procedure, weie used. But the convention is imly
entienched. Te notation is adopted fiom the -calculus, a mathematical foimalism in-
tioduced by the mathematical logician Alonzo Chuich (1941). Chuich developed the
-calculus to piovide a iigoious foundation foi studying the notions of function and
function application. Te -calculus has become a basic tool foi mathematical investi-
gations of the semantics of piogiamming languages.
84
Using let to create local variables
Anothei use of lambda is in cieating local vaiiables. We ofen need lo-
cal vaiiables in oui pioceduies othei than those that have been bound
as foimal paiameteis. loi example, suppose we wish to compute the
function
f (x, y) = x(1 xy)
2
y(1 y) (1 xy)(1 y),
which we could also expiess as
a = 1 xy,
b = 1 y,
f (x, y) = xa
2
yb ab.
ln wiiting a pioceduie to compute f , we would like to include as local
vaiiables not only x and y but also the names of inteimediate quantities
like a andb. One way to accomplish this is to use an auxiliaiy pioceduie
to bind the local vaiiables
(define (f x y)
(define (f-helper a b)
(+ (* x (square a))
(* y b)
(* a b)))
(f-helper (+ 1 (* x y))
(- 1 y)))
Of couise, we could use a lambda expiession to specify an anonymous
pioceduie foi binding oui local vaiiables. Te body of f then becomes
a single call to that pioceduie
(define (f x y)
((lambda (a b)
8
(+ (* x (square a))
(* y b)
(* a b)))
(+ 1 (* x y))
(- 1 y)))
Tis constiuct is so useful that theie is a special foim called let to make
its use moie convenient. Using let, the f pioceduie could be wiiuen as
(define (f x y)
(let ((a (+ 1 (* x y)))
(b (- 1 y)))
(+ (* x (square a))
(* y b)
(* a b))))
Te geneial foim of a let expiession is
(let ((var
1
exp
1
)
(var
2
exp
2
)
. . .
(var
n
exp
n
))
body)
which can be thought of as saying
let var
1
have the value exp
1
and
var
2
have the value exp
2
and
. . .
var
n
have the value exp
n
in body
Te ist pait of the let expiession is a list of name-expiession paiis.
When the let is evaluated, each name is associated with the value of
the coiiesponding expiession. Te body of the let is evaluated with
these names bound as local vaiiables. Te way this happens is that the
let expiession is inteipieted as an alteinate syntax foi
8
((lambda (var
1
. . . var
n
)
body)
exp
1
. . .
exp
n
)
No new mechanism is iequiied in the inteipietei in oidei to piovide
local vaiiables. A let expiession is simply syntactic sugai foi the un-
deilying lambda application.
We can see fiom this equivalence that the scope of a vaiiable spec-
ied by a let expiession is the body of the let. Tis implies that
Let allows one to bind vaiiables as locally as possible to wheie
they aie to be used. loi example, if the value of x is , the value
of the expiession
(+ (let ((x 3))
(+ x (* x 10)))
x)
is 38. Heie, the x in the body of the let is 3, so the value of the
let expiession is 33. On the othei hand, the x that is the second
aigument to the outeimost + is still .
Te vaiiables values aie computed outside the let. Tis maueis
when the expiessions that piovide the values foi the local vaii-
ables depend upon vaiiables having the same names as the local
vaiiables themselves. loi example, if the value of x is 2, the ex-
piession
(let ((x 3)
(y (+ x 2)))
(* x y))
8
will have the value 12 because, inside the body of the let, x will
be 3 and y will be 4 (which is the outei x plus 2).
Sometimes we can use inteinal denitions to get the same eect as with
let. loi example, we could have dened the pioceduie f above as
(define (f x y)
(define a (+ 1 (* x y)))
(define b (- 1 y))
(+ (* x (square a))
(* y b)
(* a b)))
We piefei, howevei, to use let in situations like this and to use inteinal
define only foi inteinal pioceduies.
4
Exercise 1.34: Suppose we dene the pioceduie
(define (f g) (g 2))
Ten we have
(f square)
4
(f (lambda (z) (* z (+ z 1))))
6
What happens if we (peiveisely) ask the inteipietei to eval-
uate the combination (f f)` Explain.
4
Undeistanding inteinal denitions well enough to be suie a piogiam means what
we intend it to mean iequiies a moie elaboiate model of the evaluation piocess than we
have piesented in this chaptei. Te subtleties do not aiise with inteinal denitions of
pioceduies, howevei. We will ietuin to this issue in Section 4.1., afei we leain moie
about evaluation.
88
1.3.3 Procedures as General Methods
We intioduced compound pioceduies in Section 1.1.4 as a mechanism
foi abstiacting paueins of numeiical opeiations so as to make them in-
dependent of the paiticulai numbeis involved. With highei-oidei pio-
ceduies, such as the integral pioceduie of Section 1.3.1, we began to
see a moie poweiful kind of abstiaction pioceduies used to expiess
geneial methods of computation, independent of the paiticulai func-
tions involved. ln this section we discuss two moie elaboiate examples
geneial methods foi nding zeios and xed points of functionsand
show how these methods can be expiessed diiectly as pioceduies.
Finding roots of equations by the half-interval method
Te |o|[:ner+o| e|oJ is a simple but poweiful technique foi nding
ioots of an equation f (x) = 0, wheie f is a continuous function. Te
idea is that, if we aie given points a and b such that f (a) < 0 < f (b),
then f must have at least one zeio between a and b. To locate a zeio,
let x be the aveiage of a and b, and compute f (x). lf f (x) > 0, then
f must have a zeio between a and x. lf f (x) < 0, then f must have a
zeio between x and b. Continuing in this way, we can identify smallei
and smallei inteivals on which f must have a zeio. When we ieach a
point wheie the inteival is small enough, the piocess stops. Since the
inteival of unceitainty is ieduced by half at each step of the piocess, the
numbei of steps iequiied giows as (log(L/T)), wheie L is the length
of the oiiginal inteival and T is the eiioi toleiance (that is, the size of
the inteival we will considei small enough). Heie is a pioceduie that
implements this stiategy
(define (search f neg-point pos-point)
(let ((midpoint (average neg-point pos-point)))
89
(if (close-enough? neg-point pos-point)
midpoint
(let ((test-value (f midpoint)))
(cond ((positive? test-value)
(search f neg-point midpoint))
((negative? test-value)
(search f midpoint pos-point))
(else midpoint))))))
We assume that we aie initially given the function f togethei with
points at which its values aie negative and positive. We ist compute
the midpoint of the two given points. Next we check to see if the given
inteival is small enough, and if so we simply ietuin the midpoint as oui
answei. Otheiwise, we compute as a test value the value of f at the mid-
point. lf the test value is positive, then we continue the piocess with a
new inteival iunning fiom the oiiginal negative point to the midpoint.
lf the test value is negative, we continue with the inteival fiom the mid-
point to the positive point. linally, theie is the possibility that the test
value is 0, in which case the midpoint is itself the ioot we aie seaiching
foi.
To test whethei the endpoints aie close enough we can use a pio-
ceduie similai to the one used in Section 1.1. foi computing squaie
ioots
(define (half-interval-method f a b)
(let ((a-value (f a))
(b-value (f b)))
(cond ((and (negative? a-value) (positive? b-value))
(search f a b))
((and (negative? b-value) (positive? a-value))
(search f b a))
(else
(error "Values are not of opposite sign" a b)))))
Te following example uses the half-inteival method to appioximate
as the ioot between 2 and 4 of sinx = 0
(half-interval-method sin 2.0 4.0)
3.14111328125
Heie is anothei example, using the half-inteival method to seaich foi a
ioot of the equation x
3
2x 3 = 0 between 1 and 2
(half-interval-method (lambda (x) (- (* x x x) (* 2 x) 3))
1.0
2.0)
1.89306640625
Tis can be accomplished using error, which takes as aiguments a numbei of items
that aie piinted as eiioi messages.
91
Finding fixed points of functions
A numbei x is called a :eJ o:n of a function f if x satises the equa-
tion f (x) = x. loi some functions f we can locate a xed point by
beginning with an initial guess and applying f iepeatedly,
f (x), f (f (x)), f (f (f (x))), . . . ,
until the value does not change veiy much. Using this idea, we can de-
vise a pioceduie fixed-point that takes as inputs a function and an
initial guess and pioduces an appioximation to a xed point of the func-
tion. We apply the function iepeatedly until we nd two successive val-
ues whose dieience is less than some piesciibed toleiance
(define tolerance 0.00001)
(define (fixed-point f first-guess)
(define (close-enough? v1 v2)
(< (abs (- v1 v2))
tolerance))
(define (try guess)
(let ((next (f guess)))
(if (close-enough? guess next)
next
(try next))))
(try first-guess))
loi example, we can use this method to appioximate the xed point of
the cosine function, staiting with 1 as an initial appioximation
Tiy this duiing a boiing lectuie Set youi calculatoi to iadians mode and then
iepeatedly piess the cos buuon until you obtain the xed point.
92
(fixed-point (lambda (y) (+ (sin y) (cos y)))
1.0)
1.2587315962971173
Te xed-point piocess is ieminiscent of the piocess we used foi nding
squaie ioots in Section 1.1.. Both aie based on the idea of iepeatedly
impioving a guess until the iesult satises some ciiteiion. ln fact, we can
ieadily foimulate the squaie-ioot computation as a xed-point seaich.
Computing the squaie ioot of some numbei x iequiies nding a y such
that y
2
= x. Puuing this equation into the equivalent foim y = x/y,
we iecognize that we aie looking foi a xed point of the function
8
y x/y, and we can theiefoie tiy to compute squaie ioots as
(define (sqrt x)
(fixed-point (lambda (y) (/ x y))
1.0))
Unfoitunately, this xed-point seaich does not conveige. Considei an
initial guess y
1
. Te next guess is y
2
= x/y
1
and the next guess is y
3
=
x/y
2
= x/(x/y
1
) = y
1
. Tis iesults in an innite loop in which the two
guesses y
1
and y
2
iepeat ovei and ovei, oscillating about the answei.
One way to contiol such oscillations is to pievent the guesses fiom
changing so much. Since the answei is always between oui guess y and
x/y, we can make a new guess that is not as fai fiom y as x/y by av-
eiaging y with x/y, so that the next guess afei y is
1
2
(y x/y) instead
of x/y. Te piocess of making such a sequence of guesses is simply the
piocess of looking foi a xed point of y
1
2
(y x/y)
(define (sqrt x)
(fixed-point (lambda (y) (average y (/ x y)))
1.0))
8
(pionounced maps to) is the mathematicians way of wiiting lambda. y x/y
means (lambda (y) (/ x y)), that is, the function whose value at y is x/y.
93
(Note that y =
1
2
(y x/y) is a simple tiansfoimation of the equation
y = x/y, to deiive it, add y to both sides of the equation and divide by
2.)
With this modication, the squaie-ioot pioceduie woiks. ln fact, if
we uniavel the denitions, we can see that the sequence of appioxi-
mations to the squaie ioot geneiated heie is piecisely the same as the
one geneiated by oui oiiginal squaie-ioot pioceduie of Section 1.1..
Tis appioach of aveiaging successive appioximations to a solution, a
technique that we call o+eroge Jo:ng, ofen aids the conveigence of
xed-point seaiches.
Exercise 1.35: Show that the golden iatio (Section 1.2.2)
is a xed point of the tiansfoimation x 1 1/x, and
use this fact to compute by means of the fixed-point
pioceduie.
Exercise 1.36: Modify fixed-point so that it piints the
sequence of appioximations it geneiates, using the newline
and display piimitives shown in Exeicise 1.22. Ten nd
a solution to x
x
= 1000 by nding a xed point of x
log(1000)/ log(x). (Use Schemes piimitive log pioceduie,
which computes natuial logaiithms.) Compaie the numbei
of steps this takes with and without aveiage damping. (Note
that you cannot stait fixed-point with a guess of 1, as this
would cause division by log(1) = 0.)
Exercise 1.37:
a. An innite con:nveJ [roc:on is an expiession of the
94
foim
f =
N
1
D
1
N
2
D
2
N
3
D
3
. . .
.
As an example, one can show that the innite con-
tinued fiaction expansion with the N
i
and the D
i
all
equal to 1 pioduces 1/, wheie is the golden iatio
(desciibed in Section 1.2.2). One way to appioximate
an innite continued fiaction is to tiuncate the expan-
sion afei a given numbei of teims. Such a tiuncation
a so-called |ern:e con:nveJ [roc:onhas the foim
N
1
D
1
N
2
.
.
.
N
k
D
k
.
Suppose that n and d aie pioceduies of one aigument
(the teim index i) that ietuin the N
i
and D
i
of the
teims of the continued fiaction. Dene a pioceduie
cont-frac such that evaluating (cont-frac n d k)
computes the value of the k-teimnite continued fiac-
tion. Check youi pioceduie by appioximating 1/ us-
ing
(cont-frac (lambda (i) 1.0)
(lambda (i) 1.0)
k)
9
foi successive values of k. How laige must you make
k in oidei to get an appioximation that is accuiate to
4 decimal places`
b. lf youi cont-frac pioceduie geneiates a iecuisive pio-
cess, wiite one that geneiates an iteiative piocess. lf
it geneiates an iteiative piocess, wiite one that gen-
eiates a iecuisive piocess.
Exercise 1.38: ln 13, the Swiss mathematician Leonhaid
Eulei published a memoii De Froc:on:|vs Con:nv:s, which
included a continued fiaction expansion foi e 2, wheie
e is the base of the natuial logaiithms. ln this fiaction, the
N
i
aie all 1, and the D
i
aie successively 1, 2, 1, 1, 4, 1, 1,
, 1, 1, 8, . . .. Wiite a piogiam that uses youi cont-frac
pioceduie fiom Exeicise 1.3 to appioximate e, based on
Euleis expansion.
Exercise 1.39: A continued fiaction iepiesentation of the
tangent function was published in 10 by the Geiman math-
ematician J.H. Lambeit
tanx =
x
1
x
2
3
x
2
. . .
,
wheie x is in iadians. Dene a pioceduie (tan-cf x k) that
computes an appioximation to the tangent function based
on Lambeits foimula. k species the numbei of teims to
compute, as in Exeicise 1.3.
9
1.3.4 Procedures as Returned Values
Te above examples demonstiate how the ability to pass pioceduies as
aiguments signicantly enhances the expiessive powei of oui piogiam-
ming language. We can achieve even moie expiessive powei by cieating
pioceduies whose ietuined values aie themselves pioceduies.
We can illustiate this idea by looking again at the xed-point exam-
ple desciibed at the end of Section 1.3.3. We foimulated a new veision
of the squaie-ioot pioceduie as a xed-point seaich, staiting with the
obseivation that
x is a xed-point of the function y x/y. Ten we
used aveiage damping to make the appioximations conveige. Aveiage
damping is a useful geneial technique in itself. Namely, given a function
f , we considei the function whose value at x is equal to the aveiage of
x and f (x).
We can expiess the idea of aveiage damping by means of the fol-
lowing pioceduie
(define (average-damp f)
(lambda (x) (average x (f x))))
Average-damp is a pioceduie that takes as its aigument a pioceduie
f and ietuins as its value a pioceduie (pioduced by the lambda) that,
when applied to a numbei x, pioduces the aveiage of x and (f x). loi
example, applying average-damp to the square pioceduie pioduces a
pioceduie whose value at some numbei x is the aveiage of x and x
2
.
Applying this iesulting pioceduie to 10 ietuins the aveiage of 10 and
100, oi
9
9
Obseive that this is a combination whose opeiatoi is itself a combination. Exeicise
1.4 alieady demonstiated the ability to foim such combinations, but that was only a toy
example. Heie we begin to see the ieal need foi such combinationswhen applying a
pioceduie that is obtained as the value ietuined by a highei-oidei pioceduie.
9
((average-damp square) 10)
55
Using average-damp, we can iefoimulate the squaie-ioot pioceduie as
follows
(define (sqrt x)
(fixed-point (average-damp (lambda (y) (/ x y)))
1.0))
Notice howthis foimulation makes explicit the thiee ideas in the method
xed-point seaich, aveiage damping, and the function y x/y. lt is in-
stiuctive to compaie this foimulation of the squaie-ioot method with
the oiiginal veision given in Section 1.1.. Beai in mind that these pio-
ceduies expiess the same piocess, and notice howmuch cleaiei the idea
becomes when we expiess the piocess in teims of these abstiactions. ln
geneial, theie aie many ways to foimulate a piocess as a pioceduie. Ex-
peiienced piogiammeis know how to choose pioceduial foimulations
that aie paiticulaily peispicuous, and wheie useful elements of the pio-
cess aie exposed as sepaiate entities that can be ieused in othei appli-
cations. As a simple example of ieuse, notice that the cube ioot of x is a
xed point of the function y x/y
2
, so we can immediately geneialize
oui squaie-ioot pioceduie to one that extiacts cube ioots
0
(define (cube-root x)
(fixed-point (average-damp (lambda (y) (/ x (square y))))
1.0))
Newtons method
When we ist intioduced the squaie-ioot pioceduie, in Section 1.1., we
mentioned that this was a special case of Ne+ons e|oJ. lf x (x)
0
See Exeicise 1.4 foi a fuithei geneialization.
98
is a dieientiable function, then a solution of the equation (x) = 0 is a
xed point of the function x f (x), wheie
f (x) = x
(x)
D(x)
and D(x) is the deiivative of evaluated at x. Newtons method is the
use of the xed-point method we saw above to appioximate a solution
of the equation by nding a xed point of the function f.
1
loi many functions and foi suciently good initial guesses foi x,
Newtons method conveiges veiy iapidly to a solution of (x) = 0.
2
ln oidei to implement Newtons method as a pioceduie, we must
ist expiess the idea of deiivative. Note that deiivative, like aveiage
damping, is something that tiansfoims a function into anothei function.
loi instance, the deiivative of the function x x
3
is the function x
3x
2
. ln geneial, if is a function and dx is a small numbei, then the
deiivative D of is the function whose value at any numbei x is given
(in the limit of small dx) by
D(x) =
(x J:) (x)
J:
.
Tus, we can expiess the idea of deiivative (takingdx to be, say, 0.00001)
as the pioceduie
(define (deriv g)
(lambda (x) (/ (- (g (+ x dx)) (g x)) dx)))
1
Elementaiy calculus books usually desciibe Newtons method in teims of the se-
quence of appioximations x
n1
= x
n
(x
n
)/D(x
n
). Having language foi talking about
piocesses and using the idea of xed points simplies the desciiption of the method.
2
Newtons method does not always conveige to an answei, but it can be shown
that in favoiable cases each iteiation doubles the numbei-of-digits accuiacy of the ap-
pioximation to the solution. ln such cases, Newtons method will conveige much moie
iapidly than the half-inteival method.
99
along with the denition
(define dx 0.00001)
Like average-damp, deriv is a pioceduie that takes a pioceduie as ai-
gument and ietuins a pioceduie as value. loi example, to appioximate
the deiivative of x x
3
at (whose exact value is ) we can evaluate
(define (cube x) (* x x x))
((deriv cube) 5)
75.00014999664018
With the aid of deriv, we can expiess Newtons method as a xed-point
piocess
(define (newton-transform g)
(lambda (x) (- x (/ (g x) ((deriv g) x)))))
(define (newtons-method g guess)
(fixed-point (newton-transform g) guess))
Te newton-transform pioceduie expiesses the foimula at the begin-
ning of this section, and newtons-method is ieadily dened in teims of
this. lt takes as aiguments a pioceduie that computes the function foi
which we want to nd a zeio, togethei with an initial guess. loi in-
stance, to nd the squaie ioot of x, we can use Newtons method to nd
a zeio of the function y y
2
x staiting with an initial guess of 1.
3
Tis piovides yet anothei foim of the squaie-ioot pioceduie
(define (sqrt x)
(newtons-method
(lambda (y) (- (square y) x)) 1.0))
3
loi nding squaie ioots, Newtons method conveiges iapidly to the coiiect solu-
tion fiom any staiting point.
100
Abstractions and first-class procedures
Weve seen two ways to expiess the squaie-ioot computation as an in-
stance of a moie geneial method, once as a xed-point seaich and once
using Newtons method. Since Newtons method was itself expiessed
as a xed-point piocess, we actually saw two ways to compute squaie
ioots as xed points. Each method begins with a function and nds a
xed point of some tiansfoimation of the function. We can expiess this
geneial idea itself as a pioceduie
(define (fixed-point-of-transform g transform guess)
(fixed-point (transform g) guess))
Tis veiy geneial pioceduie takes as its aiguments a pioceduie g that
computes some function, a pioceduie that tiansfoims g, and an initial
guess. Te ietuined iesult is a xed point of the tiansfoimed function.
Using this abstiaction, we can iecast the ist squaie-ioot computa-
tion fiom this section (wheie we look foi a xed point of the aveiage-
damped veision of y x/y) as an instance of this geneial method
(define (sqrt x)
(fixed-point-of-transform
(lambda (y) (/ x y)) average-damp 1.0))
Similaily, we can expiess the second squaie-ioot computation fiom this
section (an instance of Newtons method that nds a xed point of the
Newton tiansfoim of y y
2
x) as
(define (sqrt x)
(fixed-point-of-transform
(lambda (y) (- (square y) x)) newton-transform 1.0))
We began Section 1.3 with the obseivation that compound pioceduies
aie a ciucial abstiaction mechanism, because they peimit us to expiess
geneial methods of computing as explicit elements in oui piogiamming
101
language. Now weve seen how highei-oidei pioceduies peimit us to
manipulate these geneial methods to cieate fuithei abstiactions.
As piogiammeis, we should be aleit to oppoitunities to identify the
undeilying abstiactions in oui piogiams and to build upon them and
geneialize them to cieate moie poweiful abstiactions. Tis is not to
say that one should always wiite piogiams in the most abstiact way
possible, expeit piogiammeis know how to choose the level of abstiac-
tion appiopiiate to theii task. But it is impoitant to be able to think in
teims of these abstiactions, so that we can be ieady to apply them in
new contexts. Te signicance of highei-oidei pioceduies is that they
enable us to iepiesent these abstiactions explicitly as elements in oui
piogiamming language, so that they can be handled just like othei com-
putational elements.
ln geneial, piogiamming languages impose iestiictions on the ways
in which computational elements can be manipulated. Elements with
the fewest iestiictions aie said to have rsc|oss status. Some of the
iights and piivileges of ist-class elements aie
4
Tey may be named by vaiiables.
Tey may be passed as aiguments to pioceduies.
Tey may be ietuined as the iesults of pioceduies.
Tey may be included in data stiuctuies.
n
2
d
2
=
n
1
d
2
n
2
d
1
d
1
d
2
,
n
1
d
1
n
2
d
2
=
n
1
d
2
n
2
d
1
d
1
d
2
,
n
1
d
1
n
2
d
2
=
n
1
n
2
d
1
d
2
,
n
1
/d
1
n
2
/d
2
=
n
1
d
2
d
1
n
2
,
n
1
d
1
=
n
2
d
2
if and only if n
1
d
2
= n
2
d
1
.
We can expiess these iules as pioceduies
(define (add-rat x y)
(make-rat (+ (* (numer x) (denom y))
(* (numer y) (denom x)))
(* (denom x) (denom y))))
(define (sub-rat x y)
(make-rat (- (* (numer x) (denom y))
(* (numer y) (denom x)))
(* (denom x) (denom y))))
(define (mul-rat x y)
(make-rat (* (numer x) (numer y))
(* (denom x) (denom y))))
(define (div-rat x y)
(make-rat (* (numer x) (denom y))
(* (denom x) (numer y))))
114
(define (equal-rat? x y)
(= (* (numer x) (denom y))
(* (numer y) (denom x))))
Now we have the opeiations on iational numbeis dened in teims
of the selectoi and constiuctoi pioceduies numer, denom, and make-rat.
But we havent yet dened these. What we need is some way to glue
togethei a numeiatoi and a denominatoi to foim a iational numbei.
Pairs
To enable us to implement the conciete level of oui data abstiaction,
oui language piovides a compound stiuctuie called a o:r, which can
be constiucted with the piimitive pioceduie cons. Tis pioceduie takes
two aiguments and ietuins a compound data object that contains the
two aiguments as paits. Given a paii, we can extiact the paits using the
piimitive pioceduies car and cdr.
2
Tus, we can use cons, car, and cdr
as follows
(define x (cons 1 2))
(car x)
1
(cdr x)
2
Notice that a paii is a data object that can be given a name and manip-
ulated, just like a piimitive data object. Moieovei, cons can be used to
foim paiis whose elements aie paiis, and so on
2
Te name cons stands foi constiuct. Te names car and cdr deiive fiom the oiig-
inal implementation of Lisp on the lBM 04. Tat machine had an addiessing scheme
that allowed one to iefeience the addiess and deciement paits of a memoiy location.
Car stands foi Contents of Addiess pait of Registei and cdr (pionounced could-ei)
stands foi Contents of Deciement pait of Registei.
11
(define x (cons 1 2))
(define y (cons 3 4))
(define z (cons x y))
(car (car z))
1
(car (cdr z))
3
ln Section 2.2 we will see how this ability to combine paiis means that
paiis can be used as geneial-puipose building blocks to cieate all soits
of complex data stiuctuies. Te single compound-data piimitive o:r,
implemented by the pioceduies cons, car, and cdr, is the only glue we
need. Data objects constiucted fiom paiis aie called |:ssrvcvreJ data.
Representing rational numbers
Paiis oei a natuial way to complete the iational-numbei system. Sim-
ply iepiesent a iational numbei as a paii of two integeis a numeiatoi
and a denominatoi. Ten make-rat, numer, and denom aie ieadily im-
plemented as follows
3
3
Anothei way to dene the selectois and constiuctoi is
(define make-rat cons)
(define numer car)
(define denom cdr)
Te ist denition associates the name make-rat with the value of the expiession
cons, which is the piimitive pioceduie that constiucts paiis. Tus make-rat and cons
aie names foi the same piimitive constiuctoi.
Dening selectois and constiuctois in this way is ecient lnstead of make-rat co||
:ng cons, make-rat :s cons, so theie is only one pioceduie called, not two, when make-
rat is called. On the othei hand, doing this defeats debugging aids that tiace pioceduie
calls oi put bieakpoints on pioceduie calls You may want to watch make-rat being
called, but you ceitainly dont want to watch eveiy call to cons.
We have chosen not to use this style of denition in this book.
11
(define (make-rat n d) (cons n d))
(define (numer x) (car x))
(define (denom x) (cdr x))
Also, in oidei to display the iesults of oui computations, we can piint
iational numbeis by piinting the numeiatoi, a slash, and the denomi-
natoi
4
(define (print-rat x)
(newline)
(display (numer x))
(display "/")
(display (denom x)))
Now we can tiy oui iational-numbei pioceduies
(define one-half (make-rat 1 2))
(print-rat one-half)
1/2
(define one-third (make-rat 1 3))
(print-rat (add-rat one-half one-third))
5/6
(print-rat (mul-rat one-half one-third))
1/6
(print-rat (add-rat one-third one-third))
6/9
As the nal example shows, oui iational-numbei implementation does
not ieduce iational numbeis to lowest teims. We can iemedy this by
changing make-rat. lf we have a gcd pioceduie like the one in Section
1.2. that pioduces the gieatest common divisoi of two integeis, we can
4
Display is the Scheme piimitive foi piinting data. Te Scheme piimitive newline
staits a new line foi piinting. Neithei of these pioceduies ietuins a useful value, so
in the uses of print-rat below, we show only what print-rat piints, not what the
inteipietei piints as the value ietuined by print-rat.
11
use gcd to ieduce the numeiatoi and the denominatoi to lowest teims
befoie constiucting the paii
(define (make-rat n d)
(let ((g (gcd n d)))
(cons (/ n g) (/ d g))))
Now we have
(print-rat (add-rat one-third one-third))
2/3
as desiied. Tis modication was accomplished by changing the con-
stiuctoi make-rat without changing any of the pioceduies (such as
add-rat and mul-rat) that implement the actual opeiations.
Exercise 2.1: Dene a beuei veision of make-rat that han-
dles both positive and negative aiguments. Make-rat should
noimalize the sign so that if the iational numbei is positive,
both the numeiatoi and denominatoi aie positive, and if
the iational numbei is negative, only the numeiatoi is neg-
ative.
2.1.2 Abstraction Barriers
Befoie continuing with moie examples of compound data and data ab-
stiaction, let us considei some of the issues iaised by the iational-numbei
example. We dened the iational-numbei opeiations in teims of a con-
stiuctoi make-rat and selectois numer and denom. ln geneial, the undei-
lying idea of data abstiaction is to identify foi each type of data object
a basic set of opeiations in teims of which all manipulations of data
objects of that type will be expiessed, and then to use only those opei-
ations in manipulating the data.
118
Programs that use rational numbers
Rational numbers in problem domain
add-rat sub-rat ...
Rational numbers as numerators and denominators
make-rat numer denom
Rational numbers as pairs
cons car cdr
However pairs are implemented
Figure 2.1: Data-abstiaction baiiieis in the iational-
numbei package.
We can envision the stiuctuie of the iational-numbei system as
shown in liguie 2.1. Te hoiizontal lines iepiesent o|sroc:on |orr:ers
that isolate dieient levels of the system. At each level, the baiiiei
sepaiates the piogiams (above) that use the data abstiaction fiom the
piogiams (below) that implement the data abstiaction. Piogiams that
use iational numbeis manipulate them solely in teims of the pioce-
duies supplied foi public use by the iational-numbei package add-
rat, sub-rat, mul-rat, div-rat, and equal-rat?. Tese, in tuin, aie
implemented solely in teims of the constiuctoi and selectois make-rat,
numer, and denom, which themselves aie implemented in teims of paiis.
Te details of how paiis aie implemented aie iiielevant to the iest of
the iational-numbei package so long as paiis can be manipulated by
the use of cons, car, and cdr. ln eect, pioceduies at each level aie the
119
inteifaces that dene the abstiaction baiiieis and connect the dieient
levels.
Tis simple idea has many advantages. One advantage is that it
makes piogiams much easiei to maintain and to modify. Any complex
data stiuctuie can be iepiesented in a vaiiety of ways with the piim-
itive data stiuctuies piovided by a piogiamming language. Of couise,
the choice of iepiesentation inuences the piogiams that opeiate on it,
thus, if the iepiesentation weie to be changed at some latei time, all
such piogiams might have to be modied accoidingly. Tis task could
be time-consuming and expensive in the case of laige piogiams unless
the dependence on the iepiesentation weie to be conned by design to
a veiy few piogiam modules.
loi example, an alteinate way to addiess the pioblem of ieducing
iational numbeis to lowest teims is to peifoim the ieduction whenevei
we access the paits of a iational numbei, iathei than when we constiuct
it. Tis leads to dieient constiuctoi and selectoi pioceduies
(define (make-rat n d) (cons n d))
(define (numer x)
(let ((g (gcd (car x) (cdr x))))
(/ (car x) g)))
(define (denom x)
(let ((g (gcd (car x) (cdr x))))
(/ (cdr x) g)))
Te dieience between this implementation and the pievious one lies
in when we compute the gcd. lf in oui typical use of iational numbeis
we access the numeiatois and denominatois of the same iational num-
beis many times, it would be piefeiable to compute the gcd when the
iational numbeis aie constiucted. lf not, we may be beuei o waiting
until access time to compute the gcd. ln any case, when we change fiom
120
one iepiesentation to the othei, the pioceduies add-rat, sub-rat, and
so on do not have to be modied at all.
Constiaining the dependence on the iepiesentation to a few in-
teiface pioceduies helps us design piogiams as well as modify them,
because it allows us to maintain the exibility to considei alteinate
implementations. To continue with oui simple example, suppose we
aie designing a iational-numbei package and we cant decide initially
whethei to peifoim the gcd at constiuction time oi at selection time.
Te data-abstiaction methodology gives us a way to defei that decision
without losing the ability to make piogiess on the iest of the system.
Exercise 2.2: Considei the pioblem of iepiesenting line
segments in a plane. Each segment is iepiesented as a paii
of points a staiting point and an ending point. Dene a
constiuctoi make-segment and selectois start-segment and
end-segment that dene the iepiesentation of segments in
teims of points. luitheimoie, a point can be iepiesented
as a paii of numbeis the x cooidinate and the y cooidi-
nate. Accoidingly, specify a constiuctoi make-point and
selectois x-point and y-point that dene this iepiesenta-
tion. linally, using youi selectois and constiuctois, dene a
pioceduie midpoint-segment that takes a line segment as
aigument and ietuins its midpoint (the point whose cooi-
dinates aie the aveiage of the cooidinates of the endpoints).
To tiy youi pioceduies, youll need a way to piint points
(define (print-point p)
(newline)
(display "(")
(display (x-point p))
(display ",")
121
(display (y-point p))
(display ")"))
Exercise 2.3: lmplement a iepiesentation foi iectangles in
a plane. (Hint You may want to make use of Exeicise 2.2.) ln
teims of youi constiuctois and selectois, cieate pioceduies
that compute the peiimetei and the aiea of a given iectan-
gle. Now implement a dieient iepiesentation foi iectan-
gles. Can you design youi system with suitable abstiaction
baiiieis, so that the same peiimetei and aiea pioceduies
will woik using eithei iepiesentation`
2.1.3 What Is Meant by Data?
We began the iational-numbei implementation in Section 2.1.1 by im-
plementing the iational-numbei opeiations add-rat, sub-rat, and so
on in teims of thiee unspecied pioceduies make-rat, numer, and denom.
At that point, we could think of the opeiations as being dened in teims
of data objectsnumeiatois, denominatois, and iational numbeiswhose
behavioi was specied by the lauei thiee pioceduies.
But exactly what is meant by Joo` lt is not enough to say whatevei
is implemented by the given selectois and constiuctois. Cleaily, not
eveiy aibitiaiy set of thiee pioceduies can seive as an appiopiiate basis
foi the iational-numbei implementation. We need to guaiantee that, if
we constiuct a iational numbei x fiom a paii of integeis n and d, then
extiacting the numer and the denom of x and dividing them should yield
the same iesult as dividing n by d. ln othei woids, make-rat, numer,
and denom must satisfy the condition that, foi any integei n and any
122
non-zeio integei d, if x is (make-rat n d), then
(numer x)
(denom x)
=
n
d
.
ln fact, this is the only condition make-rat, numer, and denom must fulll
in oidei to foim a suitable basis foi a iational-numbei iepiesentation.
ln geneial, we can think of data as dened by some collection of se-
lectois and constiuctois, togethei with specied conditions that these
pioceduies must fulll in oidei to be a valid iepiesentation.
Tis point of view can seive to dene not only high-level data ob-
jects, such as iational numbeis, but lowei-level objects as well. Considei
the notion of a paii, which we used in oidei to dene oui iational num-
beis. We nevei actually said what a paii was, only that the language
supplied pioceduies cons, car, and cdr foi opeiating on paiis. But the
only thing we need to know about these thiee opeiations is that if we
glue two objects togethei using cons we can ietiieve the objects using
car and cdr. Tat is, the opeiations satisfy the condition that, foi any
objects x and y, if z is (cons x y) then (car z) is x and (cdr z) is y.
Suipiisingly, this idea is veiy dicult to foimulate iigoiously. Teie aie two ap-
pioaches to giving such a foimulation. One, pioneeied by C. A. R. Hoaie (192), is
known as the method of o|sroc oJe|s. lt foimalizes the pioceduies plus conditions
specication as outlined in the iational-numbei example above. Note that the condi-
tion on the iational-numbei iepiesentation was stated in teims of facts about integeis
(equality and division). ln geneial, abstiact models dene new kinds of data objects
in teims of pieviously dened types of data objects. Asseitions about data objects can
theiefoie be checked by ieducing them to asseitions about pieviously dened data ob-
jects. Anothei appioach, intioduced by Zilles at xi1, by Goguen, Tatchei, Wagnei,
and Wiight at lBM (see Tatchei et al. 198), and by Guuag at Toionto (see Guuag
19), is called o|ge|ro:c sec:co:on. lt iegaids the pioceduies as elements of an ab-
stiact algebiaic system whose behavioi is specied by axioms that coiiespond to oui
conditions, and uses the techniques of abstiact algebia to check asseitions about data
objects. Both methods aie suiveyed in the papei by Liskov and Zilles (19).
123
lndeed, we mentioned that these thiee pioceduies aie included as piim-
itives in oui language. Howevei, any tiiple of pioceduies that satises
the above condition can be used as the basis foi implementing paiis.
Tis point is illustiated stiikingly by the fact that we could implement
cons, car, and cdr without using any data stiuctuies at all but only
using pioceduies. Heie aie the denitions
(define (cons x y)
(define (dispatch m)
(cond ((= m 0) x)
((= m 1) y)
(else (error "Argument not 0 or 1: CONS" m))))
dispatch)
(define (car z) (z 0))
(define (cdr z) (z 1))
Tis use of pioceduies coiiesponds to nothing like oui intuitive notion
of what data should be. Neveitheless, all we need to do to show that
this is a valid way to iepiesent paiis is to veiify that these pioceduies
satisfy the condition given above.
Te subtle point to notice is that the value ietuined by (cons x y) is
a pioceduienamely the inteinally dened pioceduie dispatch, which
takes one aigument and ietuins eithei x oi y depending on whethei the
aigument is 0 oi 1. Coiiespondingly, (car z) is dened to apply z to 0.
Hence, if z is the pioceduie foimed by (cons x y), then z applied to 0
will yield x. Tus, we have shown that (car (cons x y)) yields x, as
desiied. Similaily, (cdr (cons x y)) applies the pioceduie ietuined by
(cons x y) to 1, which ietuins y. Teiefoie, this pioceduial implemen-
tation of paiis is a valid implementation, and if we access paiis using
only cons, car, and cdr we cannot distinguish this implementation fiom
one that uses ieal data stiuctuies.
Te point of exhibiting the pioceduial iepiesentation of paiis is not
124
that oui language woiks this way (Scheme, and Lisp systems in geneial,
implement paiis diiectly, foi eciency ieasons) but that it could woik
this way. Te pioceduial iepiesentation, although obscuie, is a peifectly
adequate way to iepiesent paiis, since it fullls the only conditions that
paiis need to fulll. Tis example also demonstiates that the ability to
manipulate pioceduies as objects automatically piovides the ability to
iepiesent compound data. Tis may seem a cuiiosity now, but piocedu-
ial iepiesentations of data will play a cential iole in oui piogiamming
iepeitoiie. Tis style of piogiamming is ofen called essoge oss:ng,
and we will be using it as a basic tool in Chaptei 3 when we addiess the
issues of modeling and simulation.
Exercise 2.4: Heie is an alteinative pioceduial iepiesenta-
tion of paiis. loi this iepiesentation, veiify that (car (cons
x y)) yields x foi any objects x and y.
(define (cons x y)
(lambda (m) (m x y)))
(define (car z)
(z (lambda (p q) p)))
What is the coiiesponding denition of cdr` (Hint To vei-
ify that this woiks, make use of the substitution model of
Section 1.1..)
Exercise 2.5: Show that we can iepiesent paiis of nonneg-
ative integeis using only numbeis and aiithmetic opeia-
tions if we iepiesent the paii a and b as the integei that is
the pioduct 2
a
3
b
. Give the coiiesponding denitions of the
pioceduies cons, car, and cdr.
12
Exercise 2.6: ln case iepiesenting paiis as pioceduies wasnt
mind-boggling enough, considei that, in a language that
can manipulate pioceduies, we can get by without numbeis
(at least insofai as nonnegative integeis aie conceined) by
implementing 0 and the opeiation of adding 1 as
(define zero (lambda (f) (lambda (x) x)))
(define (add-1 n)
(lambda (f) (lambda (x) (f ((n f) x)))))
Tis iepiesentation is known as C|vrc| nvero|s, afei its
inventoi, Alonzo Chuich, the logician who invented the -
calculus.
Dene one and two diiectly (not in teims of zero and add-
1). (Hint Use substitution to evaluate (add-1 zero)). Give
a diiect denition of the addition pioceduie + (not in teims
of iepeated application of add-1).
2.1.4 Extended Exercise: Interval Arithmetic
Alyssa P. Hackei is designing a system to help people solve engineei-
ing pioblems. One featuie she wants to piovide in hei system is the
ability to manipulate inexact quantities (such as measuied paiameteis
of physical devices) with known piecision, so that when computations
aie done with such appioximate quantities the iesults will be numbeis
of known piecision.
Electiical engineeis will be using Alyssas system to compute elec-
tiical quantities. lt is sometimes necessaiy foi them to compute the
value of a paiallel equivalent iesistance R
p
of two iesistois R
1
, R
2
using
the foimula
R
p
=
1
1/R
1
1/R
2
.
12
Resistance values aie usually known only up to some toleiance guaian-
teed by the manufactuiei of the iesistoi. loi example, if you buy a iesis-
toi labeled .8 ohms with 10 toleiance you can only be suie that the
iesistoi has a iesistance between .8 0.8 = .12 and .8 0.8 = .48
ohms. Tus, if you have a .8-ohm10iesistoi in paiallel with a 4.-ohm
iesistoi, the iesistance of the combination can iange fiom about 2.8
ohms (if the two iesistois aie at the lowei bounds) to about 2.9 ohms
(if the two iesistois aie at the uppei bounds).
Alyssas idea is to implement inteival aiithmetic as a set of aiith-
metic opeiations foi combining inteivals (objects that iepiesent the
iange of possible values of an inexact quantity). Te iesult of adding,
subtiacting, multiplying, oi dividing two inteivals is itself an inteival,
iepiesenting the iange of the iesult.
Alyssa postulates the existence of an abstiact object called an in-
teival that has two endpoints a lowei bound and an uppei bound. She
also piesumes that, given the endpoints of an inteival, she can con-
stiuct the inteival using the data constiuctoi make-interval. Alyssa
ist wiites a pioceduie foi adding two inteivals. She ieasons that the
minimum value the sum could be is the sum of the two lowei bounds
and the maximum value it could be is the sum of the two uppei bounds
(define (add-interval x y)
(make-interval (+ (lower-bound x) (lower-bound y))
(+ (upper-bound x) (upper-bound y))))
Alyssa also woiks out the pioduct of two inteivals by nding the min-
imum and the maximum of the pioducts of the bounds and using them
as the bounds of the iesulting inteival. (Min and max aie piimitives that
nd the minimum oi maximum of any numbei of aiguments.)
(define (mul-interval x y)
(let ((p1 (* (lower-bound x) (lower-bound y)))
12
(p2 (* (lower-bound x) (upper-bound y)))
(p3 (* (upper-bound x) (lower-bound y)))
(p4 (* (upper-bound x) (upper-bound y))))
(make-interval (min p1 p2 p3 p4)
(max p1 p2 p3 p4))))
To divide two inteivals, Alyssa multiplies the ist by the iecipiocal of
the second. Note that the bounds of the iecipiocal inteival aie the ie-
cipiocal of the uppei bound and the iecipiocal of the lowei bound, in
that oidei.
(define (div-interval x y)
(mul-interval
x
(make-interval (/ 1.0 (upper-bound y))
(/ 1.0 (lower-bound y)))))
Exercise 2.7: Alyssas piogiam is incomplete because she
has not specied the implementation of the inteival ab-
stiaction. Heie is a denition of the inteival constiuctoi
(define (make-interval a b) (cons a b))
Dene selectois upper-bound and lower-bound to complete
the implementation.
Exercise 2.8: Using ieasoning analogous to Alyssas, de-
sciibe how the dieience of two inteivals may be com-
puted. Dene a coiiesponding subtiaction pioceduie, called
sub-interval.
Exercise 2.9: Te +:J| of an inteival is half of the diei-
ence between its uppei and lowei bounds. Te width is a
128
measuie of the unceitainty of the numbei specied by the
inteival. loi some aiithmetic opeiations the width of the
iesult of combining two inteivals is a function only of the
widths of the aigument inteivals, wheieas foi otheis the
width of the combination is not a function of the widths of
the aigument inteivals. Show that the width of the sum (oi
dieience) of two inteivals is a function only of the widths
of the inteivals being added (oi subtiacted). Give examples
to show that this is not tiue foi multiplication oi division.
Exercise 2.10: Ben Bitdiddle, an expeit systems piogiam-
mei, looks ovei Alyssas shouldei and comments that it is
not cleai what it means to divide by an inteival that spans
zeio. Modify Alyssas code to check foi this condition and
to signal an eiioi if it occuis.
Exercise 2.11: ln passing, Ben also ciyptically comments
By testing the signs of the endpoints of the inteivals, it is
possible to bieak mul-interval into nine cases, only one
of which iequiies moie than two multiplications. Rewiite
this pioceduie using Bens suggestion.
Afei debugging hei piogiam, Alyssa shows it to a poten-
tial usei, who complains that hei piogiamsolves the wiong
pioblem. He wants a piogiam that can deal with numbeis
iepiesented as a centei value and an additive toleiance, foi
example, he wants to woik with inteivals such as 3.0.1
iathei than [3.3, 3.]. Alyssa ietuins to hei desk and xes
this pioblem by supplying an alteinate constiuctoi and al-
teinate selectois
129
(define (make-center-width c w)
(make-interval (- c w) (+ c w)))
(define (center i)
(/ (+ (lower-bound i) (upper-bound i)) 2))
(define (width i)
(/ (- (upper-bound i) (lower-bound i)) 2))
Unfoitunately, most of Alyssas useis aie engineeis. Real
engineeiing situations usually involve measuiements with
only a small unceitainty, measuied as the iatio of the width
of the inteival to the midpoint of the inteival. Engineeis
usually specify peicentage toleiances on the paiameteis of
devices, as in the iesistoi specications given eailiei.
Exercise 2.12: Dene a constiuctoi make-center-percent
that takes a centei and a peicentage toleiance and pio-
duces the desiied inteival. You must also dene a selectoi
percent that pioduces the peicentage toleiance foi a given
inteival. Te center selectoi is the same as the one shown
above.
Exercise 2.13: Show that undei the assumption of small
peicentage toleiances theie is a simple foimula foi the ap-
pioximate peicentage toleiance of the pioduct of two in-
teivals in teims of the toleiances of the factois. You may
simplify the pioblemby assuming that all numbeis aie pos-
itive.
Afei consideiable woik, Alyssa P. Hackei deliveis hei n-
ished system. Seveial yeais latei, afei she has foigouen all
about it, she gets a fienzied call fiom an iiate usei, Lem E.
Tweakit. lt seems that Lem has noticed that the foimula foi
130
paiallel iesistois can be wiiuen in two algebiaically equiv-
alent ways
R
1
R
2
R
1
R
2
and
1
1/R
1
1/R
2
.
He has wiiuen the following two piogiams, each of which
computes the paiallel-iesistois foimula dieiently
(define (par1 r1 r2)
(div-interval (mul-interval r1 r2)
(add-interval r1 r2)))
(define (par2 r1 r2)
(let ((one (make-interval 1 1)))
(div-interval
one (add-interval (div-interval one r1)
(div-interval one r2)))))
Lem complains that Alyssas piogiam gives dieient an-
sweis foi the two ways of computing. Tis is a seiious com-
plaint.
Exercise 2.14: Demonstiate that Lem is iight. lnvestigate
the behavioi of the system on a vaiiety of aiithmetic ex-
piessions. Make some inteivals A and B, and use them in
computing the expiessions A/A and A/B. You will get the
most insight by using inteivals whose width is a small pei-
centage of the centei value. Examine the iesults of the com-
putation in centei-peicent foim (see Exeicise 2.12).
131
Exercise 2.15: Eva Lu Atoi, anothei usei, has also noticed
the dieient inteivals computed by dieient but algebiaically
equivalent expiessions. She says that a foimula to compute
with inteivals using Alyssas system will pioduce tightei
eiioi bounds if it can be wiiuen in such a foimthat no vaii-
able that iepiesents an unceitain numbei is iepeated. Tus,
she says, par2 is a beuei piogiam foi paiallel iesistances
than par1. ls she iight` Why`
Exercise 2.16: Explain, in geneial, why equivalent alge-
biaic expiessions may lead to dieient answeis. Can you
devise an inteival-aiithmetic package that does not have
this shoitcoming, oi is this task impossible` (Waining Tis
pioblem is veiy dicult.)
2.2 Hierarchical Data and the Closure Property
As we have seen, paiis piovide a piimitive glue that we can use to
constiuct compound data objects. liguie 2.2 shows a standaid way to
visualize a paiiin this case, the paii foimed by (cons 1 2). ln this
iepiesentation, which is called |o:onJo:ner noo:on, each object is
shown as a o:ner to a box. Te box foi a piimitive object contains a
iepiesentation of the object. loi example, the box foi a numbei contains
a numeial. Te box foi a paii is actually a double box, the lef pait con-
taining (a pointei to) the car of the paii and the iight pait containing
the cdr.
We have alieady seen that cons can be used to combine not only
numbeis but paiis as well. (You made use of this fact, oi should have,
in doing Exeicise 2.2 and Exeicise 2.3.) As a consequence, paiis pio-
vide a univeisal building block fiom which we can constiuct all soits of
132
2
1
Figure 2.2: Box-and-pointei iepiesentation of (cons 1 2).
1
4
2 3
3 4
1 2
(cons (cons 1 2) (cons (cons 1
(cons 3 4)) (cons 2 3))
4)
Figure 2.3: Two ways to combine 1, 2, 3, and 4 using paiis.
data stiuctuies. liguie 2.3 shows two ways to use paiis to combine the
numbeis 1, 2, 3, and 4.
Te ability to cieate paiis whose elements aie paiis is the essence
of list stiuctuies impoitance as a iepiesentational tool. We iefei to this
ability as the c|osvre roery of cons. ln geneial, an opeiation foi com-
bining data objects satises the closuie piopeity if the iesults of com-
bining things with that opeiation can themselves be combined using the
same opeiation.
Te use of the woid closuie heie comes fiom abstiact algebia, wheie a set of
133
tion because it peimits us to cieate |:erorc|:co| stiuctuiesstiuctuies
made up of paits, which themselves aie made up of paits, and so on.
liom the outset of Chaptei 1, weve made essential use of closuie
in dealing with pioceduies, because all but the veiy simplest piogiams
iely on the fact that the elements of a combination can themselves be
combinations. ln this section, we take up the consequences of closuie
foi compound data. We desciibe some conventional techniques foi us-
ing paiis to iepiesent sequences and tiees, and we exhibit a giaphics
language that illustiates closuie in a vivid way.
(cons a
2
(cons . . .
(cons a
n
nil). . .)))
Lisp systems conventionally piint lists by piinting the sequence of el-
ements, enclosed in paientheses. Tus, the data object in liguie 2.4 is
piinted as (1 2 3 4)
(define one-through-four (list 1 2 3 4))
one-through-four
(1 2 3 4)
Be caieful not to confuse the expiession (list 1 2 3 4) with the list
(1 2 3 4), which is the iesult obtained when the expiession is evalu-
ated. Auempting to evaluate the expiession (1 2 3 4) will signal an
eiioi when the inteipietei tiies to apply the pioceduie 1 to aiguments
2, 3, and 4.
We can think of car as selecting the ist item in the list, and of
cdr as selecting the sublist consisting of all but the ist item. Nested
applications of car and cdr can be used to extiact the second, thiid,
and subsequent items in the list.
9
Te constiuctoi cons makes a list like
the oiiginal one, but with an additional item at the beginning.
9
Since nested applications of car and cdr aie cumbeisome to wiite, Lisp dialects
piovide abbieviations foi themfoi instance,
(cadr ar) = (car (cdr ar))
Te names of all such pioceduies stait with c and end with r. Each a between them
stands foi a car opeiation and each d foi a cdr opeiation, to be applied in the same
oidei in which they appeai in the name. Te names car and cdr peisist because simple
combinations like cadr aie pionounceable.
13
(car one-through-four)
1
(cdr one-through-four)
(2 3 4)
(car (cdr one-through-four))
2
(cons 10 one-through-four)
(10 1 2 3 4)
(cons 5 one-through-four)
(5 1 2 3 4)
Te value of nil, used to teiminate the chain of paiis, can be thought of
as a sequence of no elements, the ey |:s. Te woid n:| is a contiaction
of the Latin woid n:|:|, which means nothing.
10
List operations
Te use of paiis to iepiesent sequences of elements as lists is accompa-
nied by conventional piogiamming techniques foi manipulating lists by
successively cdring down the lists. loi example, the pioceduie list-
ref takes as aiguments a list and a numbei n and ietuins the n
th
item
of the list. lt is customaiy to numbei the elements of the list beginning
with 0. Te method foi computing list-ref is the following
10
lts iemaikable how much eneigy in the standaidization of Lisp dialects has been
dissipated in aiguments that aie liteially ovei nothing Should nil be an oidinaiy
name` Should the value of nil be a symbol` Should it be a list` Should it be a paii`
ln Scheme, nil is an oidinaiy name, which we use in this section as a vaiiable whose
value is the end-of-list maikei (just as true is an oidinaiy vaiiable that has a tiue value).
Othei dialects of Lisp, including Common Lisp, tieat nil as a special symbol. Te au-
thois of this book, who have enduied too many language standaidization biawls, would
like to avoid the entiie issue. Once we have intioduced quotation in Section 2.3, we will
denote the empty list as '() and dispense with the vaiiable nil entiiely.
13
loi n = 0, list-ref should ietuin the car of the list.
Otheiwise, list-ref should ietuin the (n 1)-st item of the cdr
of the list.
(define (list-ref items n)
(if (= n 0)
(car items)
(list-ref (cdr items) (- n 1))))
(define squares (list 1 4 9 16 25))
(list-ref squares 3)
16
Ofen we cdr down the whole list. To aid in this, Scheme includes a
piimitive piedicate null?, which tests whethei its aigument is the empty
list. Te pioceduie length, which ietuins the numbei of items in a list,
illustiates this typical pauein of use
(define (length items)
(if (null? items)
0
(+ 1 (length (cdr items)))))
(define odds (list 1 3 5 7))
(length odds)
4
Te length pioceduie implements a simple iecuisive plan. Te ieduc-
tion step is
Te length of any list is 1 plus the length of the cdr of the list.
Tis is applied successively until we ieach the base case
Te length of the empty list is 0.
138
We could also compute length in an iteiative style
(define (length items)
(define (length-iter a count)
(if (null? a)
count
(length-iter (cdr a) (+ 1 count))))
(length-iter items 0))
Anothei conventional piogiamming technique is to cons up an an-
swei list while cdring down a list, as in the pioceduie append, which
takes two lists as aiguments and combines theii elements to make a new
list
(append squares odds)
(1 4 9 16 25 1 3 5 7)
(append odds squares)
(1 3 5 7 1 4 9 16 25)
Append is also implemented using a iecuisive plan. To append lists list1
and list2, do the following
lf list1 is the empty list, then the iesult is just list2.
Otheiwise, append the cdr of list1 and list2, and cons the car
of list1 onto the iesult
(define (append list1 list2)
(if (null? list1)
list2
(cons (car list1) (append (cdr list1) list2))))
Exercise 2.17: Dene a pioceduie last-pair that ietuins
the list that contains only the last element of a given (nonempty)
list
139
(last-pair (list 23 72 149 34))
(34)
Exercise 2.18: Dene a pioceduie reverse that takes a list
as aigument and ietuins a list of the same elements in ie-
veise oidei
(reverse (list 1 4 9 16 25))
(25 16 9 4 1)
Exercise 2.19: Considei the change-counting piogiam of
Section 1.2.2. lt would be nice to be able to easily change the
cuiiency used by the piogiam, so that we could compute
the numbei of ways to change a Biitish pound, foi example.
As the piogiamis wiiuen, the knowledge of the cuiiency is
distiibuted paitly into the pioceduie first-denomination
and paitly into the pioceduie count-change (which knows
that theie aie ve kinds of U.S. coins). lt would be nicei
to be able to supply a list of coins to be used foi making
change.
We want to iewiite the pioceduie cc so that its second ai-
gument is a list of the values of the coins to use iathei than
an integei specifying which coins to use. We could then
have lists that dened each kind of cuiiency
(define us-coins (list 50 25 10 5 1))
(define uk-coins (list 100 50 20 10 5 2 1 0.5))
We could then call cc as follows
(cc 100 us-coins)
292
140
To do this will iequiie changing the piogiam cc somewhat.
lt will still have the same foim, but it will access its second
aigument dieiently, as follows
(define (cc amount coin-values)
(cond ((= amount 0) 1)
((or (< amount 0) (no-more? coin-values)) 0)
(else
(+ (cc amount
(except-first-denomination
coin-values))
(cc (- amount
(first-denomination
coin-values))
coin-values)))))
Dene the pioceduies first-denomination, except-first-
de- nomination, and no-more? in teims of piimitive op-
eiations on list stiuctuies. Does the oidei of the list coin-
values aect the answei pioduced by cc` Why oi why not`
Exercise 2.20: Te pioceduies +, *, and list take aibitiaiy
numbeis of aiguments. One way to dene such pioceduies
is to use define with JoueJo:| noo:on. ln a pioceduie
denition, a paiametei list that has a dot befoie the last pa-
iametei name indicates that, when the pioceduie is called,
the initial paiameteis (if any) will have as values the initial
aiguments, as usual, but the nal paiameteis value will be
a |:s of any iemaining aiguments. loi instance, given the
denition
(define (f x y . z) body)
141
the pioceduie f can be called with two oi moie aiguments.
lf we evaluate
(f 1 2 3 4 5 6)
then in the body of f, x will be 1, y will be 2, and z will be
the list (3 4 5 6). Given the denition
(define (g . w) body)
the pioceduie g can be called with zeio oi moie aiguments.
lf we evaluate
(g 1 2 3 4 5 6)
then in the body of g, w will be the list (1 2 3 4 5 6).
11
Use this notation to wiite a pioceduie same-parity that
takes one oi moie integeis and ietuins a list of all the ai-
guments that have the same even-odd paiity as the ist
aigument. loi example,
(same-parity 1 2 3 4 5 6 7)
(1 3 5 7)
(same-parity 2 3 4 5 6 7)
(2 4 6)
11
To dene f and g using lambda we would wiite
(define f (lambda (x y . z) body))
(define g (lambda w body))
142
Mapping over lists
One extiemely useful opeiation is to apply some tiansfoimation to each
element in a list and geneiate the list of iesults. loi instance, the follow-
ing pioceduie scales each numbei in a list by a given factoi
(define (scale-list items factor)
(if (null? items)
nil
(cons (* (car items) factor)
(scale-list (cdr items)
factor))))
(scale-list (list 1 2 3 4 5) 10)
(10 20 30 40 50)
We can abstiact this geneial idea and captuie it as a common pauein
expiessed as a highei-oidei pioceduie, just as in Section 1.3. Te highei-
oidei pioceduie heie is called map. Map takes as aiguments a pioceduie
of one aigument and a list, and ietuins a list of the iesults pioduced by
applying the pioceduie to each element in the list
12
(define (map proc items)
(if (null? items)
12
Scheme standaidly piovides a map pioceduie that is moie geneial than the one
desciibed heie. Tis moie geneial map takes a pioceduie of n aiguments, togethei with
n lists, and applies the pioceduie to all the ist elements of the lists, all the second
elements of the lists, and so on, ietuining a list of the iesults. loi example
(map + (list 1 2 3) (list 40 50 60) (list 700 800 900))
(741 852 963)
(map (lambda (x y) (+ x (* 2 y)))
(list 1 2 3)
(list 4 5 6))
(9 12 15)
143
nil
(cons (proc (car items))
(map proc (cdr items)))))
(map abs (list -10 2.5 -11.6 17))
(10 2.5 11.6 17)
(map (lambda (x) (* x x)) (list 1 2 3 4))
(1 4 9 16)
Now we can give a new denition of scale-list in teims of map
(define (scale-list items factor)
(map (lambda (x) (* x factor))
items))
Map is an impoitant constiuct, not only because it captuies a common
pauein, but because it establishes a highei level of abstiaction in dealing
with lists. ln the oiiginal denition of scale-list, the iecuisive stiuc-
tuie of the piogiamdiaws auention to the element-by-element piocess-
ing of the list. Dening scale-list in teims of map suppiesses that level
of detail and emphasizes that scaling tiansfoims a list of elements to a
list of iesults. Te dieience between the two denitions is not that the
computei is peifoiming a dieient piocess (it isnt) but that we think
about the piocess dieiently. ln eect, map helps establish an abstiac-
tion baiiiei that isolates the implementation of pioceduies that tians-
foim lists fiom the details of how the elements of the list aie extiacted
and combined. Like the baiiieis shown in liguie 2.1, this abstiaction
gives us the exibility to change the low-level details of how sequences
aie implemented, while pieseiving the conceptual fiamewoik of opei-
ations that tiansfoim sequences to sequences. Section 2.2.3 expands on
this use of sequences as a fiamewoik foi oiganizing piogiams.
Exercise 2.21: Te pioceduie square-list takes a list of
numbeis as aigument and ietuins a list of the squaies of
144
those numbeis.
(square-list (list 1 2 3 4))
(1 4 9 16)
Heie aie two dieient denitions of square-list. Com-
plete both of them by lling in the missing expiessions
(define (square-list items)
(if (null? items)
nil
(cons ?? ??)))
(define (square-list items)
(map ?? ??))
Exercise 2.22: Louis Reasonei tiies to iewiite the ist square-
list pioceduie of Exeicise 2.21 so that it evolves an iteia-
tive piocess
(define (square-list items)
(define (iter things answer)
(if (null? things)
answer
(iter (cdr things)
(cons (square (car things))
answer))))
(iter items nil))
Unfoitunately, dening square-list this way pioduces the
answei list in the ieveise oidei of the one desiied. Why`
Louis then tiies to x his bug by inteichanging the aigu-
ments to cons
(define (square-list items)
14
(define (iter things answer)
(if (null? things)
answer
(iter (cdr things)
(cons answer
(square (car things))))))
(iter items nil))
Tis doesnt woik eithei. Explain.
Exercise 2.23: Te pioceduie for-each is similai to map. lt
takes as aiguments a pioceduie and a list of elements. How-
evei, iathei than foiming a list of the iesults, for-each just
applies the pioceduie to each of the elements in tuin, fiom
lef to iight. Te values ietuined by applying the pioceduie
to the elements aie not used at allfor-each is used with
pioceduies that peifoiman action, such as piinting. loi ex-
ample,
(for-each (lambda (x)
(newline)
(display x))
(list 57 321 88))
57
321
88
Te value ietuined by the call to for-each (not illustiated
above) can be something aibitiaiy, such as tiue. Give an
implementation of for-each.
14
(1 2)
4
1 2
3
(3 4)
((1 2) 3 4)
Figure 2.5: Stiuctuie foimed by (cons (list 1 2) (list
3 4)).
2.2.2 Hierarchical Structures
Te iepiesentation of sequences in teims of lists geneializes natuially
to iepiesent sequences whose elements may themselves be sequences.
loi example, we can iegaid the object ((1 2) 3 4) constiucted by
(cons (list 1 2) (list 3 4))
as a list of thiee items, the ist of which is itself a list, (1 2). lndeed, this
is suggested by the foimin which the iesult is piinted by the inteipietei.
liguie 2. shows the iepiesentation of this stiuctuie in teims of paiis.
Anothei way to think of sequences whose elements aie sequences
is as rees. Te elements of the sequence aie the bianches of the tiee, and
elements that aie themselves sequences aie subtiees. liguie 2. shows
the stiuctuie in liguie 2. viewed as a tiee.
Recuision is a natuial tool foi dealing with tiee stiuctuies, since we
can ofen ieduce opeiations on tiees to opeiations on theii bianches,
which ieduce in tuin to opeiations on the bianches of the bianches, and
so on, until we ieach the leaves of the tiee. As an example, compaie the
14
((1 2) 3 4)
(1 2)
3 4
1 2
Figure 2.6: Te list stiuctuie in liguie 2. viewed as a tiee.
length pioceduie of Section 2.2.1 with the count-leaves pioceduie,
which ietuins the total numbei of leaves of a tiee
(define x (cons (list 1 2) (list 3 4)))
(length x)
3
(count-leaves x)
4
(list x x)
(((1 2) 3 4) ((1 2) 3 4))
(length (list x x))
2
(count-leaves (list x x))
8
To implement count-leaves, iecall the iecuisive plan foi computing
length
Length of a list x is 1 plus length of the cdr of x.
Length of the empty list is 0.
Count-leaves is similai. Te value foi the empty list is the same
Count-leaves of the empty list is 0.
148
But in the ieduction step, wheie we stiip o the car of the list, we must
take into account that the car may itself be a tiee whose leaves we need
to count. Tus, the appiopiiate ieduction step is
Count-leaves of a tiee x is count-leaves of the car of x plus
count-leaves of the cdr of x.
linally, by taking cars we ieach actual leaves, so we need anothei base
case
Count-leaves of a leaf is 1.
To aid in wiiting iecuisive pioceduies on tiees, Scheme piovides the
piimitive piedicate pair?, which tests whethei its aigument is a paii.
Heie is the complete pioceduie
13
(define (count-leaves x)
(cond ((null? x) 0)
((not (pair? x)) 1)
(else (+ (count-leaves (car x))
(count-leaves (cdr x))))))
Exercise 2.24: Suppose we evaluate the expiession (list
1 (list 2 (list 3 4))). Give the iesult piinted by the
inteipietei, the coiiesponding box-and-pointei stiuctuie,
and the inteipietation of this as a tiee (as in liguie 2.).
Exercise 2.25: Give combinations of cars and cdrs that
will pick fiom each of the following lists
13
Te oidei of the ist two clauses in the cond maueis, since the empty list satises
null? and also is not a paii.
149
(1 3 (5 7) 9)
((7))
(1 (2 (3 (4 (5 (6 7))))))
Exercise 2.26: Suppose we dene x and y to be two lists
(define x (list 1 2 3))
(define y (list 4 5 6))
What iesult is piinted by the inteipietei in iesponse to eval-
uating each of the following expiessions
(append x y)
(cons x y)
(list x y)
Exercise 2.27: Modify youi reverse pioceduie of Exeicise
2.18 to pioduce a deep-reverse pioceduie that takes a list
as aigument and ietuins as its value the list with its ele-
ments ieveised and with all sublists deep-ieveised as well.
loi example,
(define x (list (list 1 2) (list 3 4)))
x
((1 2) (3 4))
(reverse x)
((3 4) (1 2))
(deep-reverse x)
((4 3) (2 1))
Exercise 2.28: Wiite a pioceduie fringe that takes as aigu-
ment a tiee (iepiesented as a list) and ietuins a list whose
elements aie all the leaves of the tiee aiianged in lef-to-
iight oidei. loi example,
10
(define x (list (list 1 2) (list 3 4)))
(fringe x)
(1 2 3 4)
(fringe (list x x))
(1 2 3 4 1 2 3 4)
Exercise 2.29: A binaiy mobile consists of two bianches,
a lef bianch and a iight bianch. Each bianch is a iod of
a ceitain length, fiom which hangs eithei a weight oi an-
othei binaiy mobile. We can iepiesent a binaiy mobile us-
ing compound data by constiucting it fiom two bianches
(foi example, using list)
(define (make-mobile left right)
(list left right))
A bianch is constiucted fiom a length (which must be a
numbei) togethei with a structure, which may be eithei a
numbei (iepiesenting a simple weight) oi anothei mobile
(define (make-branch length structure)
(list length structure))
a. Wiite the coiiesponding selectois left-branch and
right-branch, which ietuin the bianches of a mobile,
and branch-length and branch-structure, which ie-
tuin the components of a bianch.
b. Using youi selectois, dene a pioceduie total-weight
that ietuins the total weight of a mobile.
c. Amobile is said to be |o|onceJ if the toique applied by
its top-lef bianch is equal to that applied by its top-
11
iight bianch (that is, if the length of the lef iod mul-
tiplied by the weight hanging fiom that iod is equal
to the coiiesponding pioduct foi the iight side) and if
each of the submobiles hanging o its bianches is bal-
anced. Design a piedicate that tests whethei a binaiy
mobile is balanced.
d. Suppose we change the iepiesentation of mobiles so
that the constiuctois aie
(define (make-mobile left right) (cons left right))
(define (make-branch length structure)
(cons length structure))
How much do you need to change youi piogiams to
conveit to the new iepiesentation`
Mapping over trees
Just as map is a poweiful abstiaction foi dealing with sequences, map
togethei with iecuision is a poweiful abstiaction foi dealing with tiees.
loi instance, the scale-tree pioceduie, analogous to scale-list of
Section 2.2.1, takes as aiguments a numeiic factoi and a tiee whose
leaves aie numbeis. lt ietuins a tiee of the same shape, wheie each
numbei is multiplied by the factoi. Te iecuisive plan foi scale-tree
is similai to the one foi count-leaves
(define (scale-tree tree factor)
(cond ((null? tree) nil)
((not (pair? tree)) (* tree factor))
(else (cons (scale-tree (car tree) factor)
(scale-tree (cdr tree) factor)))))
(scale-tree (list 1 (list 2 (list 3 4) 5) (list 6 7)) 10)
(10 (20 (30 40) 50) (60 70))
12
Anothei way to implement scale-tree is to iegaid the tiee as a se-
quence of sub-tiees and use map. We map ovei the sequence, scaling
each sub-tiee in tuin, and ietuin the list of iesults. ln the base case,
wheie the tiee is a leaf, we simply multiply by the factoi
(define (scale-tree tree factor)
(map (lambda (sub-tree)
(if (pair? sub-tree)
(scale-tree sub-tree factor)
(* sub-tree factor)))
tree))
Many tiee opeiations can be implemented by similai combinations of
sequence opeiations and iecuision.
Exercise 2.30: Dene a pioceduie square-tree analogous
to the square-list pioceduie of Exeicise 2.21. Tat is, square-
tree should behave as follows
(square-tree
(list 1
(list 2 (list 3 4) 5)
(list 6 7)))
(1 (4 (9 16) 25) (36 49))
Dene square-tree both diiectly (i.e., without using any
highei-oidei pioceduies) and also by using map and iecui-
sion.
Exercise 2.31: Abstiact youi answei to Exeicise 2.30 to
pioduce a pioceduie tree-map with the piopeity that square-
tree could be dened as
(define (square-tree tree) (tree-map square tree))
13
Exercise 2.32: We can iepiesent a set as a list of distinct
elements, and we can iepiesent the set of all subsets of the
set as a list of lists. loi example, if the set is (1 2 3), then
the set of all subsets is (() (3) (2) (2 3) (1) (1 3)
(1 2) (1 2 3)). Complete the following denition of a
pioceduie that geneiates the set of subsets of a set and give
a cleai explanation of why it woiks
(define (subsets s)
(if (null? s)
(list nil)
(let ((rest (subsets (cdr s))))
(append rest (map ?? rest)))))
2.2.3 Sequences as Conventional Interfaces
ln woiking with compound data, weve stiessed how data abstiaction
peimits us to design piogiams without becoming enmeshed in the de-
tails of data iepiesentations, and how abstiaction pieseives foi us the
exibility to expeiiment with alteinative iepiesentations. ln this sec-
tion, we intioduce anothei poweiful design piinciple foi woiking with
data stiuctuiesthe use of con+en:ono| :ner[oces.
ln Section 1.3 we saw how piogiam abstiactions, implemented as
highei-oidei pioceduies, can captuie common paueins in piogiams
that deal with numeiical data. Oui ability to foimulate analogous opei-
ations foi woiking with compound data depends ciucially on the style
in which we manipulate oui data stiuctuies. Considei, foi example, the
following pioceduie, analogous to the count-leaves pioceduie of Sec-
tion 2.2.2, which takes a tiee as aigument and computes the sum of the
squaies of the leaves that aie odd
14
(define (sum-odd-squares tree)
(cond ((null? tree) 0)
((not (pair? tree))
(if (odd? tree) (square tree) 0))
(else (+ (sum-odd-squares (car tree))
(sum-odd-squares (cdr tree))))))
On the suiface, this pioceduie is veiy dieient fiom the following one,
which constiucts a list of all the even libonacci numbeis lib(k), wheie
k is less than oi equal to a given integei n
(define (even-fibs n)
(define (next k)
(if (> k n)
nil
(let ((f (fib k)))
(if (even? f)
(cons f (next (+ k 1)))
(next (+ k 1))))))
(next 0))
Despite the fact that these two pioceduies aie stiuctuially veiy diei-
ent, a moie abstiact desciiption of the two computations ieveals a gieat
deal of similaiity. Te ist piogiam
enumeiates the leaves of a tiee,
lteis them, selecting the odd ones,
squaies each of the selected ones, and
accumulates the iesults using +, staiting with 0.
Te second piogiam
1
enumerate:
tree leaves
filter:
odd?
map:
square
accumulate:
+, 0
enumerate:
integers
map:
fib
filter:
even?
accumulate:
cons, ()
Figure 2.7: Te signal-ow plans foi the pioceduies sum-
odd-squares (top) and even-fibs (bouom) ieveal the com-
monality between the two piogiams.
enumeiates the integeis fiom 0 to n,
computes the libonacci numbei foi each integei,
lteis them, selecting the even ones, and
accumulates the iesults using cons, staiting with the empty list.
Asignal-piocessing engineei would nd it natuial to conceptualize these
piocesses in teims of signals owing thiough a cascade of stages, each
of which implements pait of the piogiam plan, as shown in liguie 2..
ln sum-odd-squares, we begin with an enveroor, which geneiates a
signal consisting of the leaves of a given tiee. Tis signal is passed
thiough a |er, which eliminates all but the odd elements. Te iesult-
ing signal is in tuin passed thiough a o, which is a tiansducei that
applies the square pioceduie to each element. Te output of the map
is then fed to an occvv|oor, which combines the elements using +,
staiting fiom an initial 0. Te plan foi even-fibs is analogous.
Unfoitunately, the two pioceduie denitions above fail to exhibit
this signal-ow stiuctuie. loi instance, if we examine the sum-odd-
1
squares pioceduie, we nd that the enumeiation is implemented paitly
by the null? and pair? tests and paitly by the tiee-iecuisive stiuctuie
of the pioceduie. Similaily, the accumulation is found paitly in the tests
and paitly in the addition used in the iecuision. ln geneial, theie aie no
distinct paits of eithei pioceduie that coiiespond to the elements in the
signal-ow desciiption. Oui two pioceduies decompose the computa-
tions in a dieient way, spieading the enumeiation ovei the piogiam
and mingling it with the map, the ltei, and the accumulation. lf we
could oiganize oui piogiams to make the signal-owstiuctuie manifest
in the pioceduies we wiite, this would inciease the conceptual claiity
of the iesulting code.
Sequence Operations
Te key to oiganizing piogiams so as to moie cleaily ieect the signal-
owstiuctuie is to concentiate on the signals that owfiomone stage
in the piocess to the next. lf we iepiesent these signals as lists, then we
can use list opeiations to implement the piocessing at each of the stages.
loi instance, we can implement the mapping stages of the signal-ow
diagiams using the map pioceduie fiom Section 2.2.1
(map square (list 1 2 3 4 5))
(1 4 9 16 25)
lilteiing a sequence to select only those elements that satisfy a given
piedicate is accomplished by
(define (filter predicate sequence)
(cond ((null? sequence) nil)
((predicate (car sequence))
(cons (car sequence)
(filter predicate (cdr sequence))))
(else (filter predicate (cdr sequence)))))
1
loi example,
(filter odd? (list 1 2 3 4 5))
(1 3 5)
Accumulations can be implemented by
(define (accumulate op initial sequence)
(if (null? sequence)
initial
(op (car sequence)
(accumulate op initial (cdr sequence)))))
(accumulate + 0 (list 1 2 3 4 5))
15
(accumulate * 1 (list 1 2 3 4 5))
120
(accumulate cons nil (list 1 2 3 4 5))
(1 2 3 4 5)
All that iemains to implement signal-ow diagiams is to enumeiate the
sequence of elements to be piocessed. loi even-fibs, we need to gen-
eiate the sequence of integeis in a given iange, which we can do as
follows
(define (enumerate-interval low high)
(if (> low high)
nil
(cons low (enumerate-interval (+ low 1) high))))
(enumerate-interval 2 7)
(2 3 4 5 6 7)
To enumeiate the leaves of a tiee, we can use
14
14
Tis is, in fact, piecisely the fringe pioceduie fiom Exeicise 2.28. Heie weve ie-
named it to emphasize that it is pait of a family of geneial sequence-manipulation
pioceduies.
18
(define (enumerate-tree tree)
(cond ((null? tree) nil)
((not (pair? tree)) (list tree))
(else (append (enumerate-tree (car tree))
(enumerate-tree (cdr tree))))))
(enumerate-tree (list 1 (list 2 (list 3 4)) 5))
(1 2 3 4 5)
Now we can iefoimulate sum-odd-squares and even-fibs as in the
signal-owdiagiams. loi sum-odd-squares, we enumeiate the sequence
of leaves of the tiee, ltei this to keep only the odd numbeis in the se-
quence, squaie each element, and sum the iesults
(define (sum-odd-squares tree)
(accumulate
+ 0 (map square (filter odd? (enumerate-tree tree)))))
loi even-fibs, we enumeiate the integeis fiom 0 to n, geneiate the li-
bonacci numbei foi each of these integeis, ltei the iesulting sequence
to keep only the even elements, and accumulate the iesults into a list
(define (even-fibs n)
(accumulate
cons
nil
(filter even? (map fib (enumerate-interval 0 n)))))
Te value of expiessing piogiams as sequence opeiations is that this
helps us make piogiam designs that aie modulai, that is, designs that
aie constiucted by combining ielatively independent pieces. We can en-
couiage modulai design by pioviding a libiaiy of standaid components
togethei with a conventional inteiface foi connecting the components
in exible ways.
19
Modulai constiuction is a poweiful stiategy foi contiolling com-
plexity in engineeiing design. ln ieal signal-piocessing applications, foi
example, designeis iegulaily build systems by cascading elements se-
lected fiom standaidized families of lteis and tiansduceis. Similaily,
sequence opeiations piovide a libiaiy of standaid piogiam elements
that we can mix and match. loi instance, we can ieuse pieces fiom the
sum-odd-squares and even-fibs pioceduies in a piogiam that con-
stiucts a list of the squaies of the ist n 1 libonacci numbeis
(define (list-fib-squares n)
(accumulate
cons
nil
(map square (map fib (enumerate-interval 0 n)))))
(list-fib-squares 10)
(0 1 1 4 9 25 64 169 441 1156 3025)
We can ieaiiange the pieces and use them in computing the pioduct of
the squaies of the odd integeis in a sequence
(define (product-of-squares-of-odd-elements sequence)
(accumulate * 1 (map square (filter odd? sequence))))
(product-of-squares-of-odd-elements (list 1 2 3 4 5))
225
We can also foimulate conventional data-piocessing applications in teims
of sequence opeiations. Suppose we have a sequence of peisonnel iecoids
and we want to nd the salaiy of the highest-paid piogiammei. Assume
that we have a selectoi salary that ietuins the salaiy of a iecoid, and a
piedicate programmer? that tests if a iecoid is foi a piogiammei. Ten
we can wiite
(define (salary-of-highest-paid-programmer records)
(accumulate max 0 (map salary (filter programmer? records))))
10
Tese examples give just a hint of the vast iange of opeiations that can
be expiessed as sequence opeiations.
1
Sequences, implemented heie as lists, seive as a conventional in-
teiface that peimits us to combine piocessing modules. Additionally,
when we unifoimly iepiesent stiuctuies as sequences, we have local-
ized the data-stiuctuie dependencies in oui piogiams to a small numbei
of sequence opeiations. By changing these, we can expeiiment with al-
teinative iepiesentations of sequences, while leaving the oveiall design
of oui piogiams intact. We will exploit this capability in Section 3.,
when we geneialize the sequence-piocessing paiadigm to admit in-
nite sequences.
Exercise 2.33: lill in the missing expiessions to complete
the following denitions of some basic list-manipulation
opeiations as accumulations
(define (map p sequence)
(accumulate (lambda (x y) ??) nil sequence))
(define (append seq1 seq2)
(accumulate cons ?? ??))
(define (length sequence)
(accumulate ?? 0 sequence))
1
Richaid Wateis (199) developed a piogiamthat automatically analyzes tiaditional
loitian piogiams, viewing them in teims of maps, lteis, and accumulations. He found
that fully 90 peicent of the code in the loitian Scientic Subioutine Package ts neatly
into this paiadigm. One of the ieasons foi the success of Lisp as a piogiamming lan-
guage is that lists piovide a standaid medium foi expiessing oideied collections so that
they can be manipulated using highei-oidei opeiations. Te piogiamming language
APL owes much of its powei and appeal to a similai choice. ln APL all data aie iepie-
sented as aiiays, and theie is a univeisal and convenient set of geneiic opeiatois foi all
soits of aiiay opeiations.
11
Exercise 2.34: Evaluating a polynomial inx at a given value
of x can be foimulated as an accumulation. We evaluate the
polynomial
a
n
x
n
a
n1
x
n1
a
1
x a
0
using a well-known algoiithm called Horners rv|e, which
stiuctuies the computation as
(. . . (a
n
x a
n1
)x a
1
)x a
0
.
ln othei woids, we stait with a
n
, multiply by x, add a
n1
,
multiply by x, and so on, until we ieach a
0
.
1
lill in the following template to pioduce a pioceduie that
evaluates a polynomial using Hoineis iule. Assume that
the coecients of the polynomial aie aiianged in a sequence,
fiom a
0
thiough a
n
.
(define (horner-eval x coefficient-sequence)
(accumulate (lambda (this-coeff higher-terms) ??)
0
coefficient-sequence))
1
Accoiding to Knuth 1981, this iule was foimulated by W. G. Hoinei eaily in the
nineteenth centuiy, but the method was actually used by Newton ovei a hundied yeais
eailiei. Hoineis iule evaluates the polynomial using fewei additions and multipli-
cations than does the stiaightfoiwaid method of ist computing a
n
x
n
, then adding
a
n1
x
n1
, and so on. ln fact, it is possible to piove that any algoiithm foi evaluating
aibitiaiy polynomials must use at least as many additions and multiplications as does
Hoineis iule, and thus Hoineis iule is an optimal algoiithmfoi polynomial evaluation.
Tis was pioved (foi the numbei of additions) by A. M. Ostiowski in a 194 papei that
essentially founded the modein study of optimal algoiithms. Te analogous statement
foi multiplications was pioved by V. Y. Pan in 19. Te book by Boiodin and Munio
(19) piovides an oveiview of these and othei iesults about optimal algoiithms.
12
loi example, to compute 13x x
3
x
at x = 2 you would
evaluate
(horner-eval 2 (list 1 3 0 5 0 1))
Exercise 2.35: Redene count-leaves fiom Section 2.2.2
as an accumulation
(define (count-leaves t)
(accumulate ?? ?? (map ?? ??)))
Exercise 2.36: Te pioceduie accumulate-n is similai to
accumu-late except that it takes as its thiid aigument a
sequence of sequences, which aie all assumed to have the
same numbei of elements. lt applies the designated accu-
mulation pioceduie to combine all the ist elements of the
sequences, all the second elements of the sequences, and so
on, and ietuins a sequence of the iesults. loi instance, if s
is a sequence containing foui sequences, ((1 2 3) (4 5 6)
(7 8 9) (10 11 12)), then the value of (accumulate-n +
0 s) should be the sequence (22 26 30). lill in the missing
expiessions in the following denition of accumulate-n
(define (accumulate-n op init seqs)
(if (null? (car seqs))
nil
(cons (accumulate op init ??)
(accumulate-n op init ??))))
Exercise 2.37: Suppose we iepiesent vectois v = (v
i
) as se-
quences of numbeis, and matiices m = (m
i j
) as sequences
13
of vectois (the iows of the matiix). loi example, the matiix
_
_
1 2 3 4
4
8 9
_
_
is iepiesented as the sequence ((1 2 3 4) (4 5 6 6)
(6 7 8 9)). With this iepiesentation, we can use sequence
opeiations to concisely expiess the basic matiix and vectoi
opeiations. Tese opeiations (which aie desciibed in any
book on matiix algebia) aie the following
(dot-product v w) ietuins the sum
i
v
i
w
i
,
(matrix-*-vector m v) ietuins the vectoi t,
wheie t
i
=
j
m
i j
v
j
,
(matrix-*-matrix m n) ietuins the matiix p,
wheie p
i j
=
k
m
ik
n
kj
,
(transpose m) ietuins the matiix n,
wheie n
i j
= m
ji
.
We can dene the dot pioduct as
1
(define (dot-product v w)
(accumulate + 0 (map * v w)))
lill in the missing expiessions in the following pioceduies
foi computing the othei matiix opeiations. (Te pioceduie
accumulate-n is dened in Exeicise 2.3.)
(define (matrix-*-vector m v)
(map ?? m))
1
Tis denition uses the extended veision of map desciibed in lootnote 12.
14
(define (transpose mat)
(accumulate-n ?? ?? mat))
(define (matrix-*-matrix m n)
(let ((cols (transpose n)))
(map ?? m)))
Exercise 2.38: Te accumulate pioceduie is also known as
fold-right, because it combines the ist element of the se-
quence with the iesult of combining all the elements to the
iight. Teie is also a fold-left, which is similai to fold-
right, except that it combines elements woiking in the op-
posite diiection
(define (fold-left op initial sequence)
(define (iter result rest)
(if (null? rest)
result
(iter (op result (car rest))
(cdr rest))))
(iter initial sequence))
What aie the values of
(fold-right / 1 (list 1 2 3))
(fold-left / 1 (list 1 2 3))
(fold-right list nil (list 1 2 3))
(fold-left list nil (list 1 2 3))
Give a piopeity that op should satisfy to guaiantee that
fold-right and fold-left will pioduce the same values
foi any sequence.
1
Exercise 2.39: Complete the following denitions of reverse
(Exeicise 2.18) in teims of fold-right and fold-left fiom
Exeicise 2.38
(define (reverse sequence)
(fold-right (lambda (x y) ??) nil sequence))
(define (reverse sequence)
(fold-left (lambda (x y) ??) nil sequence))
Nested Mappings
We can extend the sequence paiadigm to include many computations
that aie commonly expiessed using nested loops.
18
Considei this piob-
lem Given a positive integei n, nd all oideied paiis of distinct positive
integeis i and j, wheie 1 j < i n, such that i j is piime. loi
example, if n is , then the paiis aie the following
i 2 3 4 4
j 1 2 1 3 2 1
i j 3 11
A natuial way to oiganize this computation is to geneiate the sequence
of all oideied paiis of positive integeis less than oi equal to n, ltei to
select those paiis whose sum is piime, and then, foi each paii (i , j) that
passes thiough the ltei, pioduce the tiiple (i , j, i j).
Heie is a way to geneiate the sequence of paiis loi each integei
i n, enumeiate the integeis j < i, and foi each such i and j genei-
ate the paii (i , j). ln teims of sequence opeiations, we map along the
18
Tis appioach to nested mappings was shown to us by David Tuinei, whose lan-
guages KRC and Miianda piovide elegant foimalisms foi dealing with these constiucts.
Te examples in this section (see also Exeicise 2.42) aie adapted fiom Tuinei 1981. ln
Section 3..3, well see how this appioach geneializes to innite sequences.
1
sequence (enumerate-interval 1 n). loi each i in this sequence, we
map along the sequence (enumerate-interval 1 (- i 1)). loi each
j in this lauei sequence, we geneiate the paii (list i j). Tis gives
us a sequence of paiis foi each i. Combining all the sequences foi all
the i (by accumulating with append) pioduces the iequiied sequence of
paiis
19
(accumulate
append nil (map (lambda (i)
(map (lambda (j) (list i j))
(enumerate-interval 1 (- i 1))))
(enumerate-interval 1 n)))
Te combination of mapping and accumulating with append is so com-
mon in this soit of piogiam that we will isolate it as a sepaiate pioce-
duie
(define (flatmap proc seq)
(accumulate append nil (map proc seq)))
Now ltei this sequence of paiis to nd those whose sum is piime. Te
ltei piedicate is called foi each element of the sequence, its aigument is
a paii and it must extiact the integeis fiom the paii. Tus, the piedicate
to apply to each element in the sequence is
(define (prime-sum? pair)
(prime? (+ (car pair) (cadr pair))))
linally, geneiate the sequence of iesults by mapping ovei the lteied
paiis using the following pioceduie, which constiucts a tiiple consisting
of the two elements of the paii along with theii sum
19
Weie iepiesenting a paii heie as a list of two elements iathei than as a Lisp paii.
Tus, the paii (i , j) is iepiesented as (list i j), not (cons i j).
1
(define (make-pair-sum pair)
(list (car pair) (cadr pair) (+ (car pair) (cadr pair))))
Combining all these steps yields the complete pioceduie
(define (prime-sum-pairs n)
(map make-pair-sum
(filter prime-sum? (flatmap
(lambda (i)
(map (lambda (j) (list i j))
(enumerate-interval 1 (- i 1))))
(enumerate-interval 1 n)))))
Nested mappings aie also useful foi sequences othei than those that
enumeiate inteivals. Suppose we wish to geneiate all the peimutations
of a set S, that is, all the ways of oideiing the items in the set. loi in-
stance, the peimutations of {1, 2, 3} aie {1, 2, 3}, {1, 3, 2}, {2, 1, 3}, {2, 3, 1},
{3, 1, 2}, and {3, 2, 1}. Heie is a plan foi geneiating the peimutations of S
loi each itemx in S, iecuisively geneiate the sequence of peimutations
of S x,
20
and adjoin x to the fiont of each one. Tis yields, foi each x
in S, the sequence of peimutations of S that begin with x. Combining
these sequences foi all x gives all the peimutations of S
21
(define (permutations s)
(if (null? s) , empty set`
(list nil) , sequence containing empty set
(flatmap (lambda (x)
(map (lambda (p) (cons x p))
(permutations (remove x s))))
s)))
20
Te set S x is the set of all elements of S, excluding x.
21
Semicolons in Scheme code aie used to intioduce coens. Eveiything fiom the
semicolon to the end of the line is ignoied by the inteipietei. ln this book we dont use
many comments, we tiy to make oui piogiams self-documenting by using desciiptive
names.
18
Notice how this stiategy ieduces the pioblem of geneiating peimuta-
tions of S to the pioblem of geneiating the peimutations of sets with
fewei elements than S. ln the teiminal case, we woik oui way down to
the empty list, which iepiesents a set of no elements. loi this, we gen-
eiate (list nil), which is a sequence with one item, namely the set
with no elements. Te remove pioceduie used in permutations ietuins
all the items in a given sequence except foi a given item. Tis can be
expiessed as a simple ltei
(define (remove item sequence)
(filter (lambda (x) (not (= x item)))
sequence))
Exercise 2.40: Dene a pioceduie unique-pairs that, given
an integei n, geneiates the sequence of paiis (i , j) with 1
j < i n. Use unique-pairs to simplify the denition of
prime-sum-pairs given above.
Exercise 2.41: Wiite a pioceduie to nd all oideied tiiples
of distinct positive integeis i, j, and k less than oi equal to
a given integei n that sum to a given integei s.
Exercise 2.42: Te eight-queens puzzle asks howto place
eight queens on a chessboaid so that no queen is in check
fiomany othei (i.e., no two queens aie in the same iow, col-
umn, oi diagonal). One possible solution is shown in liguie
2.8. One way to solve the puzzle is to woik acioss the boaid,
placing a queen in each column. Once we have placed k 1
queens, we must place the k
th
queen in a position wheie it
does not check any of the queens alieady on the boaid. We
can foimulate this appioach iecuisively Assume that we
19
Figure 2.8: A solution to the eight-queens puzzle.
have alieady geneiated the sequence of all possible ways
to place k 1 queens in the ist k 1 columns of the boaid.
loi each of these ways, geneiate an extended set of posi-
tions by placing a queen in each iow of the k
th
column.
Now ltei these, keeping only the positions foi which the
queen in the k
th
column is safe with iespect to the othei
queens. Tis pioduces the sequence of all ways to place k
queens in the ist k columns. By continuing this piocess,
we will pioduce not only one solution, but all solutions to
the puzzle.
We implement this solution as a pioceduie queens, which
ietuins a sequence of all solutions to the pioblem of plac-
ing n queens on an n n chessboaid. Queens has an intei-
nal pioceduie queen-cols that ietuins the sequence of all
ways to place queens in the ist k columns of the boaid.
10
(define (queens board-size)
(define (queen-cols k)
(if (= k 0)
(list empty-board)
(filter
(lambda (positions) (safe? k positions))
(flatmap
(lambda (rest-of-queens)
(map (lambda (new-row)
(adjoin-position
new-row k rest-of-queens))
(enumerate-interval 1 board-size)))
(queen-cols (- k 1))))))
(queen-cols board-size))
ln this pioceduie rest-of-queens is a way to place k 1
queens in the ist k 1 columns, and new-row is a pioposed
iow in which to place the queen foi the k
th
column. Com-
plete the piogiam by implementing the iepiesentation foi
sets of boaid positions, including the pioceduie adjoin-
position, which adjoins a new iow-column position to a
set of positions, and empty-board, which iepiesents an empty
set of positions. You must also wiite the pioceduie safe?,
which deteimines foi a set of positions, whethei the queen
in the k
th
column is safe with iespect to the otheis. (Note
that we need only check whethei the new queen is safe
the othei queens aie alieady guaianteed safe with iespect
to each othei.)
Exercise 2.43: Louis Reasonei is having a teiiible time do-
ing Exeicise 2.42. His queens pioceduie seems to woik, but
it iuns extiemely slowly. (Louis nevei does manage to wait
11
long enough foi it to solve even the case.) When Louis
asks Eva Lu Atoi foi help, she points out that he has intei-
changed the oidei of the nested mappings in the flatmap,
wiiting it as
(flatmap
(lambda (new-row)
(map (lambda (rest-of-queens)
(adjoin-position new-row k rest-of-queens))
(queen-cols (- k 1))))
(enumerate-interval 1 board-size))
Explain why this inteichange makes the piogiamiun slowly.
Estimate how long it will take Louiss piogiam to solve the
eight-queens puzzle, assuming that the piogiamin Exeicise
2.42 solves the puzzle in time T.
2.2.4 Example: A Picture Language
Tis section piesents a simple language foi diawing pictuies that il-
lustiates the powei of data abstiaction and closuie, and also exploits
highei-oidei pioceduies in an essential way. Te language is designed
to make it easy to expeiiment with paueins such as the ones in lig-
uie 2.9, which aie composed of iepeated elements that aie shifed and
scaled.
22
ln this language, the data objects being combined aie iepie-
sented as pioceduies iathei than as list stiuctuie. Just as cons, which
satises the closuie piopeity, allowed us to easily build aibitiaiily com-
plicated list stiuctuie, the opeiations in this language, which also sat-
22
Te pictuie language is based on the language Petei Hendeison cieated to constiuct
images like M.C. Escheis Squaie Limit woodcut (see Hendeison 1982). Te woodcut
incoipoiates a iepeated scaled pauein, similai to the aiiangements diawn using the
square-limit pioceduie in this section.
12
Figure 2.9: Designs geneiated with the pictuie language.
isfy the closuie piopeity, allowus to easily build aibitiaiily complicated
paueins.
The picture language
When we began oui study of piogiamming in Section 1.1, we empha-
sized the impoitance of desciibing a language by focusing on the lan-
guages piimitives, its means of combination, and its means of abstiac-
tion. Well follow that fiamewoik heie.
Pait of the elegance of this pictuie language is that theie is only one
kind of element, called a o:ner. Apaintei diaws an image that is shifed
and scaled to t within a designated paiallelogiam-shaped fiame. loi
example, theies a piimitive paintei well call wave that makes a ciude
line diawing, as shown in liguie 2.10. Te actual shape of the diawing
depends on the fiameall foui images in guie 2.10 aie pioduced by the
13
Figure 2.10: lmages pioduced by the wave paintei, with
iespect to foui dieient fiames. Te fiames, shown with
doued lines, aie not pait of the images.
same wave paintei, but with iespect to foui dieient fiames. Painteis
can be moie elaboiate than this Te piimitive paintei called rogers
paints a pictuie of xi1s foundei, William Baiton Rogeis, as shown in
liguie 2.11.
23
Te foui images in guie 2.11 aie diawn with iespect to
23
William Baiton Rogeis (1804-1882) was the foundei and ist piesident of xi1.
A geologist and talented teachei, he taught at William and Maiy College and at the
Univeisity of Viiginia. ln 189 he moved to Boston, wheie he had moie time foi ie-
seaich, woiked on a plan foi establishing a polytechnic institute, and seived as Mas-
sachuseuss ist State lnspectoi of Gas Meteis.
When xi1 was established in 181, Rogeis was elected its ist piesident. Rogeis
espoused an ideal of useful leaining that was dieient fiom the univeisity education
of the time, with its oveiemphasis on the classics, which, as he wiote, stand in the way
of the bioadei, highei and moie piactical instiuction and discipline of the natuial and
social sciences. Tis education was likewise to be dieient fiom naiiow tiade-school
14
the same foui fiames as the wave images in guie 2.10.
To combine images, we use vaiious opeiations that constiuct new
painteis fiom given painteis. loi example, the beside opeiation takes
two painteis and pioduces a new, compound paintei that diaws the ist
education. ln Rogeiss woids
Te woild-enfoiced distinction between the piactical and the scientic
woikei is uueily futile, and the whole expeiience of modein times has
demonstiated its uuei woithlessness.
Rogeis seived as piesident of xi1 until 180, when he iesigned due to ill health.
ln 188 the second piesident of xi1, John Runkle, iesigned undei the piessuie of a
nancial ciisis biought on by the Panic of 183 and stiain of ghting o auempts by
Haivaid to take ovei xi1. Rogeis ietuined to hold the oce of piesident until 1881.
Rogeis collapsed and died while addiessing xi1s giaduating class at the commence-
ment exeicises of 1882. Runkle quoted Rogeiss last woids in a memoiial addiess de-
liveied that same yeai
As l stand heie today and see what the lnstitute is, . . . l call to mind
the beginnings of science. l iemembei one hundied and fy yeais ago
Stephen Hales published a pamphlet on the subject of illuminating gas,
in which he stated that his ieseaiches had demonstiated that 128 giains
of bituminous coal Bituminous coal, these weie his last woids on
eaith. Heie he bent foiwaid, as if consulting some notes on the table
befoie him, then slowly iegaining an eiect position, thiew up his hands,
and was tianslated fiom the scene of his eaithly labois and tiiumphs
to the tomoiiow of death, wheie the mysteiies of life aie solved, and
the disembodied spiiit nds unending satisfaction in contemplating the
new and still unfathomable mysteiies of the innite futuie.
ln the woids of liancis A. Walkei (xi1s thiid piesident)
All his life he had boine himself most faithfully and heioically, and he
died as so good a knight would suiely have wished, in hainess, at his
post, and in the veiy pait and act of public duty.
1
Figure 2.11: lmages of WilliamBaiton Rogeis, foundei and
ist piesident of xi1, painted with iespect to the same foui
fiames as in liguie 2.10 (oiiginal image fiom Wikimedia
Commons).
painteis image in the lef half of the fiame and the second painteis im-
age in the iight half of the fiame. Similaily, below takes two painteis and
pioduces a compound paintei that diaws the ist painteis image below
the second painteis image. Some opeiations tiansfoim a single paintei
to pioduce a new paintei. loi example, flip-vert takes a paintei and
pioduces a paintei that diaws its image upside-down, and flip-horiz
pioduces a paintei that diaws the oiiginal painteis image lef-to-iight
ieveised.
liguie 2.12 shows the diawing of a paintei called wave4 that is built
up in two stages staiting fiom wave
(define wave2 (beside wave (flip-vert wave)))
(define wave4 (below wave2 wave2))
1
Figure 2.12: Cieating a complex guie, staiting fiom the
wave paintei of liguie 2.10.
ln building up a complex image in this mannei we aie exploiting the
fact that painteis aie closed undei the languages means of combination.
Te beside oi below of two painteis is itself a paintei, theiefoie, we can
use it as an element in making moie complex painteis. As with building
up list stiuctuie using cons, the closuie of oui data undei the means of
combination is ciucial to the ability to cieate complex stiuctuies while
using only a few opeiations.
Once we can combine painteis, we would like to be able to abstiact
typical paueins of combining painteis. We will implement the paintei
opeiations as Scheme pioceduies. Tis means that we dont need a spe-
cial abstiaction mechanism in the pictuie language Since the means of
combination aie oidinaiy Scheme pioceduies, we automatically have
the capability to do anything with paintei opeiations that we can do
with pioceduies. loi example, we can abstiact the pauein in wave4 as
(define (flipped-pairs painter)
(let ((painter2 (beside painter (flip-vert painter))))
(below painter2 painter2)))
and dene wave4 as an instance of this pauein
(define wave4 (flipped-pairs wave))
1
right-split
identity
right-split
right-split n
right-split
corner-split
up-
split
n--1
up-
split
right-split
identity
n--1 n--1
n--1
n--1
corner-split n
n--1
n--1
Figure 2.13: Recuisive plans foi right-split and corner-split.
We can also dene iecuisive opeiations. Heies one that makes painteis
split and bianch towaids the iight as shown in liguie 2.13 and liguie
2.14
(define (right-split painter n)
(if (= n 0)
painter
(let ((smaller (right-split painter (- n 1))))
(beside painter (below smaller smaller)))))
We can pioduce balanced paueins by bianching upwaids as well as
towaids the iight (see exeicise Exeicise 2.44 and guies liguie 2.13 and
liguie 2.14)
(define (corner-split painter n)
(if (= n 0)
painter
18
(let ((up (up-split painter (- n 1)))
(right (right-split painter (- n 1))))
(let ((top-left (beside up up))
(bottom-right (below right right))
(corner (corner-split painter (- n 1))))
(beside (below painter top-left)
(below bottom-right corner))))))
By placing foui copies of a corner-split appiopiiately, we obtain a
pauein called square-limit, whose application to wave and rogers is
shown in liguie 2.9
(define (square-limit painter n)
(let ((quarter (corner-split painter n)))
(let ((half (beside (flip-horiz quarter) quarter)))
(below (flip-vert half) half))))
Exercise 2.44: Dene the pioceduie up-split used by corner-
split. lt is similai to right-split, except that it switches
the ioles of below and beside.
Higher-order operations
ln addition to abstiacting paueins of combining painteis, we can woik
at a highei level, abstiacting paueins of combining paintei opeiations.
Tat is, we can view the paintei opeiations as elements to manipulate
and can wiite means of combination foi these elementspioceduies
that take paintei opeiations as aiguments and cieate new paintei opei-
ations.
loi example, flipped-pairs and square-limit each aiiange foui
copies of a painteis image in a squaie pauein, they diei only in how
19
(right-split wave 4) (right-split rogers 4)
(corner-split wave 4) (corner-split rogers 4)
Figure 2.14: Te iecuisive opeiations right-split and
corner-split applied to the painteis wave and rogers.
Combining foui corner-split guies pioduces symmet-
iic square-limit designs as shown in liguie 2.9.
180
they oiient the copies. One way to abstiact this pauein of paintei com-
bination is with the following pioceduie, which takes foui one-aigument
paintei opeiations and pioduces a paintei opeiation that tiansfoims a
given paintei with those foui opeiations and aiianges the iesults in a
squaie. Tl, tr, bl, and br aie the tiansfoimations to apply to the top
lef copy, the top iight copy, the bouom lef copy, and the bouom iight
copy, iespectively.
(define (square-of-four tl tr bl br)
(lambda (painter)
(let ((top (beside (tl painter) (tr painter)))
(bottom (beside (bl painter) (br painter))))
(below bottom top))))
Ten flipped-pairs can be dened in teims of square-of-four as
follows
24
(define (flipped-pairs painter)
(let ((combine4 (square-of-four identity flip-vert
identity flip-vert)))
(combine4 painter)))
and square-limit can be expiessed as
2
(define (square-limit painter n)
(let ((combine4 (square-of-four flip-horiz identity
rotate180 flip-vert)))
24
Equivalently, we could wiite
(define flipped-pairs
(square-of-four identity flip-vert identity flip-vert))
2
Rotate180 iotates a paintei by 180 degiees (see Exeicise 2.0). lnstead of ro-
tate180 we could say (compose flip-vert flip-horiz), using the compose pio-
ceduie fiom Exeicise 1.42.
181
(combine4 (corner-split painter n))))
Exercise 2.45: Right-split and up-split can be expiessed
as instances of a geneial spliuing opeiation. Dene a pio-
ceduie split with the piopeity that evaluating
(define right-split (split beside below))
(define up-split (split below beside))
pioduces pioceduies right-split and up-split with the
same behaviois as the ones alieady dened.
Frames
Befoie we can showhowto implement painteis and theii means of com-
bination, we must ist considei fiames. A fiame can be desciibed by
thiee vectoisan oiigin vectoi and two edge vectois. Te oiigin vectoi
species the oset of the fiames oiigin fiom some absolute oiigin in
the plane, and the edge vectois specify the osets of the fiames coi-
neis fiom its oiigin. lf the edges aie peipendiculai, the fiame will be
iectangulai. Otheiwise the fiame will be a moie geneial paiallelogiam.
liguie 2.1 shows a fiame and its associated vectois. ln accoidance
with data abstiaction, we need not be specic yet about how fiames aie
iepiesented, othei than to say that theie is a constiuctoi make-frame,
which takes thiee vectois and pioduces a fiame, and thiee coiiespond-
ing selectois origin-frame, edge1-frame, and edge2-frame (see Exei-
cise 2.4).
We will use cooidinates in the unit squaie (0 x, y 1) to specify
images. With each fiame, we associate a [roe coorJ:noe o, which
will be used to shif and scale images to t the fiame. Te map tians-
foims the unit squaie into the fiame by mapping the vectoi v = (x, y)
182
frame
edge1
vector
frame
edge2
vector
frame
origin
vector
(0, 0) point on
display screen
Figure 2.15: A fiame is desciibed by thiee vectois an
oiigin and two edges.
to the vectoi sum
Oiigin(liame) x Edge
1
(liame) y Edge
2
(liame).
loi example, (0, 0) is mapped to the oiigin of the fiame, (1, 1) to the
veitex diagonally opposite the oiigin, and (0., 0.) to the centei of the
fiame. We can cieate a fiames cooidinate map with the following pio-
ceduie
2
(define (frame-coord-map frame)
(lambda (v)
(add-vect
(origin-frame frame)
2
Frame-coord-map uses the vectoi opeiations desciibed in Exeicise 2.4 below,
which we assume have been implemented using some iepiesentation foi vectois. Be-
cause of data abstiaction, it doesnt mauei what this vectoi iepiesentation is, so long
as the vectoi opeiations behave coiiectly.
183
(add-vect (scale-vect (xcor-vect v) (edge1-frame frame))
(scale-vect (ycor-vect v) (edge2-frame frame))))))
Obseive that applying frame-coord-map to a fiame ietuins a pioceduie
that, given a vectoi, ietuins a vectoi. lf the aigument vectoi is in the unit
squaie, the iesult vectoi will be in the fiame. loi example,
((frame-coord-map a-frame) (make-vect 0 0))
ietuins the same vectoi as
(origin-frame a-frame)
Exercise 2.46: A two-dimensional vectoi v iunning fiom
the oiigin to a point can be iepiesented as a paii consisting
of an x-cooidinate and a y-cooidinate. lmplement a data
abstiaction foi vectois by giving a constiuctoi make-vect
and coiiesponding selectois xcor-vect and ycor-vect. ln
teims of youi selectois and constiuctoi, implement pioce-
duies add-vect, sub-vect, and scale-vect that peifoim
the opeiations vectoi addition, vectoi subtiaction, and mul-
tiplying a vectoi by a scalai
(x
1
, y
1
) (x
2
, y
2
) = (x
1
x
2
, y
1
y
2
),
(x
1
, y
1
) (x
2
, y
2
) = (x
1
x
2
, y
1
y
2
),
s (x, y) = (sx, sy).
Exercise 2.47: Heie aie two possible constiuctois foi fiames
(define (make-frame origin edge1 edge2)
(list origin edge1 edge2))
(define (make-frame origin edge1 edge2)
(cons origin (cons edge1 edge2)))
loi each constiuctoi supply the appiopiiate selectois to
pioduce an implementation foi fiames.
184
Painters
Apaintei is iepiesented as a pioceduie that, given a fiame as aigument,
diaws a paiticulai image shifed and scaled to t the fiame. Tat is to
say, if p is a paintei and f is a fiame, then we pioduce ps image in f by
calling p with f as aigument.
Te details of how piimitive painteis aie implemented depend on
the paiticulai chaiacteiistics of the giaphics system and the type of im-
age to be diawn. loi instance, suppose we have a pioceduie draw-line
that diaws a line on the scieen between two specied points. Ten we
can cieate painteis foi line diawings, such as the wave paintei in liguie
2.10, fiom lists of line segments as follows
2
(define (segments->painter segment-list)
(lambda (frame)
(for-each
(lambda (segment)
(draw-line
((frame-coord-map frame)
(start-segment segment))
((frame-coord-map frame)
(end-segment segment))))
segment-list)))
Te segments aie given using cooidinates with iespect to the unit squaie.
loi each segment in the list, the paintei tiansfoims the segment end-
points with the fiame cooidinate map and diaws a line between the
tiansfoimed points.
Repiesenting painteis as pioceduies eiects a poweiful abstiaction
baiiiei in the pictuie language. We can cieate and inteimix all soits of
2
Segments->painter uses the iepiesentation foi line segments desciibed in Exei-
cise 2.48 below. lt also uses the for-each pioceduie desciibed in Exeicise 2.23.
18
piimitive painteis, based on a vaiiety of giaphics capabilities. Te de-
tails of theii implementation do not mauei. Any pioceduie can seive as
a paintei, piovided that it takes a fiame as aigument and diaws some-
thing scaled to t the fiame.
28
Exercise 2.48: A diiected line segment in the plane can be
iepiesented as a paii of vectoisthe vectoi iunning fiom
the oiigin to the stait-point of the segment, and the vectoi
iunning fiom the oiigin to the end-point of the segment.
Use youi vectoi iepiesentation fiom Exeicise 2.4 to de-
ne a iepiesentation foi segments with a constiuctoi make-
segment and selectois start-segment and end-segment.
Exercise 2.49: Use segments->painter to dene the fol-
lowing piimitive painteis
a. Te paintei that diaws the outline of the designated
fiame.
b. Te paintei that diaws an X by connecting opposite
coineis of the fiame.
c. Te paintei that diaws a diamond shape by connect-
ing the midpoints of the sides of the fiame.
d. Te wave paintei.
28
loi example, the rogers paintei of liguie 2.11 was constiucted fiom a giay-level
image. loi each point in a given fiame, the rogers paintei deteimines the point in the
image that is mapped to it undei the fiame cooidinate map, and shades it accoidingly.
By allowing dieient types of painteis, we aie capitalizing on the abstiact data idea
discussed in Section 2.1.3, wheie we aigued that a iational-numbei iepiesentation could
be anything at all that satises an appiopiiate condition. Heie weie using the fact
that a paintei can be implemented in any way at all, so long as it diaws something
in the designated fiame. Section 2.1.3 also showed how paiis could be implemented as
pioceduies. Painteis aie oui second example of a pioceduial iepiesentation foi data.
18
Transforming and combining painters
An opeiation on painteis (such as flip-vert oi beside) woiks by cie-
ating a paintei that invokes the oiiginal painteis with iespect to fiames
deiived fiomthe aigument fiame. Tus, foi example, flip-vert doesnt
have to know how a paintei woiks in oidei to ip itit just has to know
how to tuin a fiame upside down Te ipped paintei just uses the oiig-
inal paintei, but in the inveited fiame.
Paintei opeiations aie based on the pioceduie transform-painter,
which takes as aiguments a paintei and infoimation on how to tians-
foim a fiame and pioduces a new paintei. Te tiansfoimed paintei,
when called on a fiame, tiansfoims the fiame and calls the oiiginal
paintei on the tiansfoimed fiame. Te aiguments to transform-painter
aie points (iepiesented as vectois) that specify the coineis of the new
fiame When mapped into the fiame, the ist point species the new
fiames oiigin and the othei two specify the ends of its edge vectois.
Tus, aiguments within the unit squaie specify a fiame contained within
the oiiginal fiame.
(define (transform-painter painter origin corner1 corner2)
(lambda (frame)
(let ((m (frame-coord-map frame)))
(let ((new-origin (m origin)))
(painter (make-frame
new-origin
(sub-vect (m corner1) new-origin)
(sub-vect (m corner2) new-origin)))))))
Heies how to ip paintei images veitically
(define (flip-vert painter)
(transform-painter painter
(make-vect 0.0 1.0) , new origin
18
(make-vect 1.0 1.0) , new end of edge1
(make-vect 0.0 0.0))) , new end of edge2
Using transform-painter, we can easily dene new tiansfoimations.
loi example, we can dene a paintei that shiinks its image to the uppei-
iight quaitei of the fiame it is given
(define (shrink-to-upper-right painter)
(transform-painter
painter (make-vect 0.5 0.5)
(make-vect 1.0 0.5) (make-vect 0.5 1.0)))
Othei tiansfoimations iotate images counteiclockwise by 90 degiees
29
(define (rotate90 painter)
(transform-painter painter
(make-vect 1.0 0.0)
(make-vect 1.0 1.0)
(make-vect 0.0 0.0)))
oi squash images towaids the centei of the fiame
30
(define (squash-inwards painter)
(transform-painter painter
(make-vect 0.0 0.0)
(make-vect 0.65 0.35)
(make-vect 0.35 0.65)))
liame tiansfoimation is also the key to dening means of combining
two oi moie painteis. Te beside pioceduie, foi example, takes two
painteis, tiansfoims them to paint in the lef and iight halves of an
aigument fiame iespectively, and pioduces a new, compound paintei.
29
Rotate90 is a puie iotation only foi squaie fiames, because it also stietches and
shiinks the image to t into the iotated fiame.
30
Te diamond-shaped images in liguie 2.10 and liguie 2.11 weie cieated with
squash-inwards applied to wave and rogers.
188
When the compound paintei is given a fiame, it calls the ist tians-
foimed paintei to paint in the lef half of the fiame and calls the second
tiansfoimed paintei to paint in the iight half of the fiame
(define (beside painter1 painter2)
(let ((split-point (make-vect 0.5 0.0)))
(let ((paint-left
(transform-painter
painter1
(make-vect 0.0 0.0)
split-point
(make-vect 0.0 1.0)))
(paint-right
(transform-painter
painter2
split-point
(make-vect 1.0 0.0)
(make-vect 0.5 1.0))))
(lambda (frame)
(paint-left frame)
(paint-right frame)))))
Obseive how the paintei data abstiaction, and in paiticulai the iepie-
sentation of painteis as pioceduies, makes beside easy to implement.
Te beside pioceduie need not know anything about the details of the
component painteis othei than that each paintei will diaw something
in its designated fiame.
Exercise 2.50: Dene the tiansfoimation flip-horiz, which
ips painteis hoiizontally, and tiansfoimations that iotate
painteis counteiclockwise by 180 degiees and 20 degiees.
Exercise 2.51: Dene the below opeiation foi painteis. Below
takes two painteis as aiguments. Te iesulting paintei, given
189
a fiame, diaws with the ist paintei in the bouom of the
fiame and with the second paintei in the top. Dene below
in two dieient waysist by wiiting a pioceduie that is
analogous to the beside pioceduie given above, and again
in teims of beside and suitable iotation opeiations (fiom
Exeicise 2.0).
Levels of language for robust design
Te pictuie language exeicises some of the ciitical ideas weve intio-
duced about abstiaction with pioceduies and data. Te fundamental
data abstiactions, painteis, aie implemented using pioceduial iepiesen-
tations, which enables the language to handle dieient basic diawing
capabilities in a unifoim way. Te means of combination satisfy the
closuie piopeity, which peimits us to easily build up complex designs.
linally, all the tools foi abstiacting pioceduies aie available to us foi
abstiacting means of combination foi painteis.
We have also obtained a glimpse of anothei ciucial idea about lan-
guages and piogiam design. Tis is the appioach of sro:eJ Jes:gn,
the notion that a complex system should be stiuctuied as a sequence
of levels that aie desciibed using a sequence of languages. Each level is
constiucted by combining paits that aie iegaided as piimitive at that
level, and the paits constiucted at each level aie used as piimitives at
the next level. Te language used at each level of a stiatied design has
piimitives, means of combination, and means of abstiaction appiopiiate
to that level of detail.
Stiatied design peivades the engineeiing of complex systems. loi
example, in computei engineeiing, iesistois and tiansistois aie com-
bined (and desciibed using a language of analog ciicuits) to pioduce
paits such as and-gates and oi-gates, which foim the piimitives of a
190
language foi digital-ciicuit design.
31
Tese paits aie combined to build
piocessois, bus stiuctuies, and memoiy systems, which aie in tuin com-
bined to foim computeis, using languages appiopiiate to computei ai-
chitectuie. Computeis aie combined to foim distiibuted systems, using
languages appiopiiate foi desciibing netwoik inteiconnections, and so
on.
As a tiny example of stiatication, oui pictuie language uses piim-
itive elements (piimitive painteis) that aie cieated using a language
that species points and lines to piovide the lists of line segments foi
segments->painter, oi the shading details foi a paintei like rogers.
Te bulk of oui desciiption of the pictuie language focused on com-
bining these piimitives, using geometiic combineis such as beside and
below. We also woiked at a highei level, iegaiding beside and below
as piimitives to be manipulated in a language whose opeiations, such
as square-of-four, captuie common paueins of combining geometiic
combineis.
Stiatied design helps make piogiams ro|vs, that is, it makes it
likely that small changes in a specication will iequiie coiiespondingly
small changes in the piogiam. loi instance, suppose we wanted to change
the image based on wave shown in liguie 2.9. We could woik at the
lowest level to change the detailed appeaiance of the wave element,
we could woik at the middle level to change the way corner-split
ieplicates the wave, we could woik at the highest level to change how
square-limit aiianges the foui copies of the coinei. ln geneial, each
level of a stiatied design piovides a dieient vocabulaiy foi expiess-
ing the chaiacteiistics of the system, and a dieient kind of ability to
change it.
31
Section 3.3.4 desciibes one such language.
191
Exercise 2.52: Make changes to the squaie limit of wave
shown in liguie 2.9 by woiking at each of the levels de-
sciibed above. ln paiticulai
a. Add some segments to the piimitive wave paintei of
Exeicise 2.49 (to add a smile, foi example).
b. Change the pauein constiucted by corner-split (foi
example, by using only one copy of the up-split and
right-split images instead of two).
c. Modify the veision of square-limit that uses square-
of-four so as to assemble the coineis in a dieient
pauein. (loi example, you might make the big Mi.
Rogeis look outwaid fiom each coinei of the squaie.)
2.3 Symbolic Data
All the compound data objects we have used so fai weie constiucted ul-
timately fiom numbeis. ln this section we extend the iepiesentational
capability of oui language by intioducing the ability to woik with aibi-
tiaiy symbols as data.
2.3.1 otation
lf we can foim compound data using symbols, we can have lists such as
(a b c d)
(23 45 17)
((Norah 12) (Molly 9) (Anna 7) (Lauren 6) (Charlotte 4))
Lists containing symbols can look just like the expiessions of oui lan-
guage
192
(* (+ 23 45)
(+ x 9))
(define (fact n)
(if (= n 1) 1 (* n (fact (- n 1)))))
ln oidei to manipulate symbols we need a newelement in oui language
the ability to qvoe a data object. Suppose we want to constiuct the list
(a b). We cant accomplish this with (list a b), because this expies-
sion constiucts a list of the +o|ves of a and b iathei than the symbols
themselves. Tis issue is well known in the context of natuial languages,
wheie woids and sentences may be iegaided eithei as semantic entities
oi as chaiactei stiings (syntactic entities). Te common piactice in nat-
uial languages is to use quotation maiks to indicate that a woid oi a
sentence is to be tieated liteially as a stiing of chaiacteis. loi instance,
the ist leuei of John is cleaily J. lf we tell somebody say youi
name aloud, we expect to heai that peisons name. Howevei, if we tell
somebody say youi name aloud, we expect to heai the woids youi
name. Note that we aie foiced to nest quotation maiks to desciibe what
somebody else might say.
32
We can follow this same piactice to identify lists and symbols that
aie to be tieated as data objects iathei than as expiessions to be evalu-
32
Allowing quotation in a language wieaks havoc with the ability to ieason about
the language in simple teims, because it destioys the notion that equals can be sub-
stituted foi equals. loi example, thiee is one plus two, but the woid thiee is not the
phiase one plus two. Qotation is poweiful because it gives us a way to build expies-
sions that manipulate othei expiessions (as we will see when we wiite an inteipietei in
Chaptei 4). But allowing statements in a language that talk about othei statements in
that language makes it veiy dicult to maintain any coheient piinciple of what equals
can be substituted foi equals should mean. loi example, if we know that the evening
stai is the moining stai, then fiom the statement the evening stai is Venus we can
deduce the moining stai is Venus. Howevei, given that John knows that the evening
stai is Venus we cannot infei that John knows that the moining stai is Venus.
193
ated. Howevei, oui foimat foi quoting dieis fiom that of natuial lan-
guages in that we place a quotation maik (tiaditionally, the single quote
symbol ') only at the beginning of the object to be quoted. We can get
away with this in Scheme syntax because we iely on blanks and paien-
theses to delimit objects. Tus, the meaning of the single quote chaiactei
is to quote the next object.
33
Now we can distinguish between symbols and theii values
(define a 1)
(define b 2)
(list a b)
(1 2)
(list 'a 'b)
(a b)
(list 'a b)
(a 2)
Qotation also allows us to type in compound objects, using the con-
ventional piinted iepiesentation foi lists
34
33
Te single quote is dieient fiom the double quote we have been using to enclose
chaiactei stiings to be piinted. Wheieas the single quote can be used to denote lists oi
symbols, the double quote is used only with chaiactei stiings. ln this book, the only
use foi chaiactei stiings is as items to be piinted.
34
Stiictly, oui use of the quotation maik violates the geneial iule that all compound
expiessions in oui language should be delimited by paientheses and look like lists. We
can iecovei this consistency by intioducing a special foim quote, which seives the
same puipose as the quotation maik. Tus, we would type (quote a) instead of 'a,
and we would type (quote (a b c)) instead of '(a b c). Tis is piecisely how
the inteipietei woiks. Te quotation maik is just a single-chaiactei abbieviation foi
wiapping the next complete expiession with quote to foim (quote expression).
Tis is impoitant because it maintains the piinciple that any expiession seen by the
inteipietei can be manipulated as a data object. loi instance, we could constiuct the
expiession (car '(a b c)), which is the same as (car (quote (a b c))), by evaluating
(list 'car (list 'quote '(a b c))).
194
(car '(a b c))
a
(cdr '(a b c))
(b c)
ln keeping with this, we can obtain the empty list by evaluating '(),
and thus dispense with the vaiiable nil.
One additional piimitive used in manipulating symbols is eq?, which
takes two symbols as aiguments and tests whethei they aie the same.
3
Using eq?, we can implement a useful pioceduie called memq. Tis takes
two aiguments, a symbol and a list. lf the symbol is not contained in the
list (i.e., is not eq? to any itemin the list), then memq ietuins false. Othei-
wise, it ietuins the sublist of the list beginning with the ist occuiience
of the symbol
(define (memq item x)
(cond ((null? x) false)
((eq? item (car x)) x)
(else (memq item (cdr x)))))
loi example, the value of
(memq 'apple '(pear banana prune))
is false, wheieas the value of
(memq 'apple '(x (apple sauce) y apple pear))
is (apple pear).
Exercise 2.53: What would the inteipietei piint in iesponse
to evaluating each of the following expiessions`
3
We can considei two symbols to be the same if they consist of the same chaiacteis
in the same oidei. Such a denition skiits a deep issue that we aie not yet ieady to
addiess the meaning of sameness in a piogiamming language. We will ietuin to this
in Chaptei 3 (Section 3.1.3).
19
(list 'a 'b 'c)
(list (list 'george))
(cdr '((x1 x2) (y1 y2)))
(cadr '((x1 x2) (y1 y2)))
(pair? (car '(a short list)))
(memq 'red '((red shoes) (blue socks)))
(memq 'red '(red shoes blue socks))
Exercise 2.54: Two lists aie said to be equal? if they con-
tain equal elements aiianged in the same oidei. loi exam-
ple,
(equal? '(this is a list) '(this is a list))
is tiue, but
(equal? '(this is a list) '(this (is a) list))
is false. To be moie piecise, we can dene equal? iecui-
sively in teims of the basic eq? equality of symbols by say-
ing that a and b aie equal? if they aie both symbols and
the symbols aie eq?, oi if they aie both lists such that (car
a) is equal? to (car b) and (cdr a) is equal? to (cdr b).
Using this idea, implement equal? as a pioceduie.
3
Exercise 2.55: Eva Lu Atoi types to the inteipietei the ex-
piession
3
ln piactice, piogiammeis use equal? to compaie lists that contain numbeis as
well as symbols. Numbeis aie not consideied to be symbols. Te question of whethei
two numeiically equal numbeis (as tested by =) aie also eq? is highly implementation-
dependent. A beuei denition of equal? (such as the one that comes as a piimitive in
Scheme) would also stipulate that if a and b aie both numbeis, then a and b aie equal?
if they aie numeiically equal.
19
(car ''abracadabra)
To hei suipiise, the inteipietei piints back quote. Explain.
2.3.2 Example: Symbolic Dierentiation
As an illustiation of symbol manipulation and a fuithei illustiation of
data abstiaction, considei the design of a pioceduie that peifoims sym-
bolic dieientiation of algebiaic expiessions. We would like the pioce-
duie to take as aiguments an algebiaic expiession and a vaiiable and to
ietuin the deiivative of the expiession with iespect to the vaiiable. loi
example, if the aiguments to the pioceduie aie ax
2
bx c and x, the
pioceduie should ietuin 2ax b. Symbolic dieientiation is of special
histoiical signicance in Lisp. lt was one of the motivating examples
behind the development of a computei language foi symbol manipula-
tion. luitheimoie, it maiked the beginning of the line of ieseaich that
led to the development of poweiful systems foi symbolic mathematical
woik, which aie cuiiently being used by a giowing numbei of applied
mathematicians and physicists.
ln developing the symbolic-dieientiation piogiam, we will follow
the same stiategy of data abstiaction that we followed in developing
the iational-numbei system of Section 2.1.1. Tat is, we will ist de-
ne a dieientiation algoiithm that opeiates on abstiact objects such as
sums, pioducts, and vaiiables without woiiying about how these
aie to be iepiesented. Only afeiwaid will we addiess the iepiesentation
pioblem.
The dierentiation program with abstract data
ln oidei to keep things simple, we will considei a veiy simple symbolic-
dieientiation piogiam that handles expiessions that aie built up using
19
only the opeiations of addition and multiplication with two aiguments.
Dieientiation of any such expiession can be caiiied out by applying
the following ieduction iules
Jc
J:
= 0, foi c a constant oi a vaiiable diffeient fiom x,
J:
J:
= 1,
J (v + )
J:
=
Jv
J:
J+
J:
,
J (v+ )
J:
= u
J+
J:
v
Jv
J:
.
Obseive that the lauei two iules aie iecuisive in natuie. Tat is, to ob-
tain the deiivative of a sumwe ist nd the deiivatives of the teims and
add them. Each of the teims may in tuin be an expiession that needs
to be decomposed. Decomposing into smallei and smallei pieces will
eventually pioduce pieces that aie eithei constants oi vaiiables, whose
deiivatives will be eithei 0 oi 1.
To embody these iules in a pioceduie we indulge in a liule wishful
thinking, as we did in designing the iational-numbei implementation.
lf we had a means foi iepiesenting algebiaic expiessions, we should
be able to tell whethei an expiession is a sum, a pioduct, a constant,
oi a vaiiable. We should be able to extiact the paits of an expiession.
loi a sum, foi example we want to be able to extiact the addend (ist
teim) and the augend (second teim). We should also be able to constiuct
expiessions fiom paits. Let us assume that we alieady have pioceduies
to implement the following selectois, constiuctois, and piedicates
(variable? e) ls e a vaiiable`
(same-variable? v1 v2) Aie v1 and v2 the same vaiiable`
198
(sum? e) ls e a sum`
(addend e) Addend of the sum e.
(augend e) Augend of the sum e.
(make-sum a1 a2) Constiuct the sum of a1 and a2.
(product? e) ls e a pioduct`
(multiplier e) Multipliei of the pioduct e.
(multiplicand e) Multiplicand of the pioduct e.
(make-product m1 m2) Constiuct the pioduct of m1 and m2.
Using these, and the piimitive piedicate number?, which identies num-
beis, we can expiess the dieientiation iules as the following pioceduie
(define (deriv exp var)
(cond ((number? exp) 0)
((variable? exp) (if (same-variable? exp var) 1 0))
((sum? exp) (make-sum (deriv (addend exp) var)
(deriv (augend exp) var)))
((product? exp)
(make-sum
(make-product (multiplier exp)
(deriv (multiplicand exp) var))
(make-product (deriv (multiplier exp) var)
(multiplicand exp))))
(else
(error "unknown expression type: DERIV" exp))))
Tis deriv pioceduie incoipoiates the complete dieientiation algo-
iithm. Since it is expiessed in teims of abstiact data, it will woik no
mauei how we choose to iepiesent algebiaic expiessions, as long as we
design a piopei set of selectois and constiuctois. Tis is the issue we
must addiess next.
199
Representing algebraic expressions
We can imagine many ways to use list stiuctuie to iepiesent algebiaic
expiessions. loi example, we could use lists of symbols that miiioi
the usual algebiaic notation, iepiesenting ax b as the list (a * x +
b). Howevei, one especially stiaightfoiwaid choice is to use the same
paienthesized piex notation that Lisp uses foi combinations, that is,
to iepiesent ax b as (+ (* a x) b). Ten oui data iepiesentation foi
the dieientiation pioblem is as follows
Te vaiiables aie symbols. Tey aie identied by the piimitive
piedicate symbol?
(define (variable? x) (symbol? x))
Two vaiiables aie the same if the symbols iepiesenting them aie
eq?
(define (same-variable? v1 v2)
(and (variable? v1) (variable? v2) (eq? v1 v2)))
Sums and pioducts aie constiucted as lists
(define (make-sum a1 a2) (list '+ a1 a2))
(define (make-product m1 m2) (list '* m1 m2))
A sum is a list whose ist element is the symbol +
(define (sum? x) (and (pair? x) (eq? (car x) '+)))
Te addend is the second item of the sum list
(define (addend s) (cadr s))
200
Te augend is the thiid item of the sum list
(define (augend s) (caddr s))
A pioduct is a list whose ist element is the symbol *
(define (product? x) (and (pair? x) (eq? (car x) '*)))
Te multipliei is the second item of the pioduct list
(define (multiplier p) (cadr p))
Te multiplicand is the thiid item of the pioduct list
(define (multiplicand p) (caddr p))
Tus, we need only combine these with the algoiithm as embodied by
deriv in oidei to have a woiking symbolic-dieientiation piogiam. Let
us look at some examples of its behavioi
(deriv '(+ x 3) 'x)
(+ 1 0)
(deriv '(* x y) 'x)
(+ (* x 0) (* 1 y))
(deriv '(* (* x y) (+ x 3)) 'x)
(+ (* (* x y) (+ 1 0))
(* (+ (* x 0) (* 1 y))
(+ x 3)))
Te piogiam pioduces answeis that aie coiiect, howevei, they aie un-
simplied. lt is tiue that
J (:y )
J:
= x 0 1 y,
201
but we would like the piogiam to know that x 0 = 0, 1 y = y, and
0 y = y. Te answei foi the second example should have been simply
y. As the thiid example shows, this becomes a seiious issue when the
expiessions aie complex.
Oui diculty is much like the one we encounteied with the iational-
numbei implementation we havent ieduced answeis to simplest foim.
To accomplish the iational-numbei ieduction, we needed to change only
the constiuctois and the selectois of the implementation. We can adopt
a similai stiategy heie. We wont change deriv at all. lnstead, we will
change make-sum so that if both summands aie numbeis, make-sum will
add them and ietuin theii sum. Also, if one of the summands is 0, then
make-sum will ietuin the othei summand.
(define (make-sum a1 a2)
(cond ((=number? a1 0) a2)
((=number? a2 0) a1)
((and (number? a1) (number? a2))
(+ a1 a2))
(else (list '+ a1 a2))))
Tis uses the pioceduie =number?, which checks whethei an expiession
is equal to a given numbei
(define (=number? exp num) (and (number? exp) (= exp num)))
Similaily, we will change make-product to build in the iules that 0 times
anything is 0 and 1 times anything is the thing itself
(define (make-product m1 m2)
(cond ((or (=number? m1 0) (=number? m2 0)) 0)
((=number? m1 1) m2)
((=number? m2 1) m1)
((and (number? m1) (number? m2)) (* m1 m2))
(else (list '* m1 m2))))
202
Heie is how this veision woiks on oui thiee examples
(deriv '(+ x 3) 'x)
1
(deriv '(* x y) 'x)
y
(deriv '(* (* x y) (+ x 3)) 'x)
(+ (* x y) (* y (+ x 3)))
Although this is quite an impiovement, the thiid example shows that
theie is still a long way to go befoie we get a piogiam that puts ex-
piessions into a foim that we might agiee is simplest. Te pioblem
of algebiaic simplication is complex because, among othei ieasons, a
foim that may be simplest foi one puipose may not be foi anothei.
Exercise 2.56: Show how to extend the basic dieientiatoi
to handle moie kinds of expiessions. loi instance, imple-
ment the dieientiation iule
J (v
n
)
J:
= nu
n1
Jv
J:
by adding a new clause to the deriv piogiam and dening
appiopiiate pioceduies exponentiation?, base, exponent,
and make-exponentiation. (You may use the symbol **
to denote exponentiation.) Build in the iules that anything
iaised to the powei 0 is 1 and anything iaised to the powei
1 is the thing itself.
Exercise 2.57: Extend the dieientiation piogiam to han-
dle sums and pioducts of aibitiaiy numbeis of (two oi moie)
teims. Ten the last example above could be expiessed as
(deriv '(* x y (+ x 3)) 'x)
203
Tiy to do this by changing only the iepiesentation foi sums
and pioducts, without changing the deriv pioceduie at all.
loi example, the addend of a sum would be the ist teim,
and the augend would be the sum of the iest of the teims.
Exercise 2.58: Suppose we want to modify the dieientia-
tion piogiam so that it woiks with oidinaiy mathematical
notation, in which + and * aie inx iathei than piex opeia-
tois. Since the dieientiation piogiamis dened in teims of
abstiact data, we can modify it to woik with dieient iepie-
sentations of expiessions solely by changing the piedicates,
selectois, and constiuctois that dene the iepiesentation of
the algebiaic expiessions on which the dieientiatoi is to
opeiate.
a. Showhowto do this in oidei to dieientiate algebiaic
expiessions piesented in inx foim, such as (x + (3
* (x + (y + 2)))). To simplify the task, assume that
+ and * always take two aiguments and that expies-
sions aie fully paienthesized.
b. Te pioblembecomes substantially haidei if we allow
standaid algebiaic notation, such as (x + 3 * (x +
y + 2)), which diops unnecessaiy paientheses and
assumes that multiplication is done befoie addition.
Can you design appiopiiate piedicates, selectois, and
constiuctois foi this notation such that oui deiivative
piogiam still woiks`
204
2.3.3 Example: Representing Sets
ln the pievious examples we built iepiesentations foi two kinds of com-
pound data objects iational numbeis and algebiaic expiessions. ln one
of these examples we had the choice of simplifying (ieducing) the ex-
piessions at eithei constiuction time oi selection time, but othei than
that the choice of a iepiesentation foi these stiuctuies in teims of lists
was stiaightfoiwaid. When we tuin to the iepiesentation of sets, the
choice of a iepiesentation is not so obvious. lndeed, theie aie a num-
bei of possible iepiesentations, and they diei signicantly fiom one
anothei in seveial ways.
lnfoimally, a set is simply a collection of distinct objects. To give
a moie piecise denition we can employ the method of data abstiac-
tion. Tat is, we dene set by specifying the opeiations that aie to be
used on sets. Tese aie union-set, intersection-set, element-of-
set?, and adjoin-set. Element-of-set? is a piedicate that deteimines
whethei a given element is a membei of a set. Adjoin-set takes an ob-
ject and a set as aiguments and ietuins a set that contains the elements
of the oiiginal set and also the adjoined element. Union-set computes
the union of two sets, which is the set containing each element that
appeais in eithei aigument. Intersection-set computes the inteisec-
tion of two sets, which is the set containing only elements that appeai
in both aiguments. liom the viewpoint of data abstiaction, we aie fiee
to design any iepiesentation that implements these opeiations in a way
consistent with the inteipietations given above.
3
3
lf we want to be moie foimal, we can specify consistent with the inteipietations
given above to mean that the opeiations satisfy a collection of iules such as these
loi any set S and any object x, (element-of-set? x (adjoin-set x S)) is tiue
(infoimally Adjoining an object to a set pioduces a set that contains the object).
loi any sets S and T and any object x, (element-of-set? x (union-set S T)) is
20
Sets as unordered lists
One way to iepiesent a set is as a list of its elements in which no el-
ement appeais moie than once. Te empty set is iepiesented by the
empty list. ln this iepiesentation, element-of-set? is similai to the
pioceduie memq of Section 2.3.1. lt uses equal? instead of eq? so that
the set elements need not be symbols
(define (element-of-set? x set)
(cond ((null? set) false)
((equal? x (car set)) true)
(else (element-of-set? x (cdr set)))))
Using this, we can wiite adjoin-set. lf the object to be adjoined is al-
ieady in the set, we just ietuin the set. Otheiwise, we use cons to add
the object to the list that iepiesents the set
(define (adjoin-set x set)
(if (element-of-set? x set)
set
(cons x set)))
loi intersection-set we can use a iecuisive stiategy. lf we knowhow
to foim the inteisection of set2 and the cdr of set1, we only need to
decide whethei to include the car of set1 in this. But this depends on
whethei (car set1) is also in set2. Heie is the iesulting pioceduie
(define (intersection-set set1 set2)
(cond ((or (null? set1) (null? set2)) '())
((element-of-set? (car set1) set2)
(cons (car set1) (intersection-set (cdr set1) set2)))
(else (intersection-set (cdr set1) set2))))
equal to (or (element-of-set? x S) (element-of-set? x T)) (infoimally Te
elements of (union S T) aie the elements that aie in S oi in T).
loi any object x, (element-of-set? x '()) is false (infoimally No object is an
element of the empty set).
20
ln designing a iepiesentation, one of the issues we should be conceined
with is eciency. Considei the numbei of steps iequiied by oui set
opeiations. Since they all use element-of-set?, the speed of this opei-
ation has a majoi impact on the eciency of the set implementation as
a whole. Now, in oidei to check whethei an object is a membei of a set,
element-of-set? may have to scan the entiie set. (ln the woist case,
the object tuins out not to be in the set.) Hence, if the set has n elements,
element-of-set? might take up to n steps. Tus, the numbei of steps
iequiied giows as (n). Te numbei of steps iequiied by adjoin-set,
which uses this opeiation, also giows as (n). loi intersection-set,
which does an element-of-set? check foi each element of set1, the
numbei of steps iequiied giows as the pioduct of the sizes of the sets
involved, oi (n
2
) foi two sets of size n. Te same will be tiue of union-
set.
Exercise 2.59: lmplement the union-set opeiation foi the
unoideied-list iepiesentation of sets.
Exercise 2.60: We specied that a set would be iepiesented
as a list with no duplicates. Now suppose we allow dupli-
cates. loi instance, the set {1, 2, 3} could be iepiesented as
the list (2 3 2 1 3 2 2). Design pioceduies element-
of-set?, adjoin-set, union-set, and intersection-set
that opeiate on this iepiesentation. Howdoes the eciency
of each compaie with the coiiesponding pioceduie foi the
non-duplicate iepiesentation` Aie theie applications foi which
you would use this iepiesentation in piefeience to the non-
duplicate one`
20
Sets as ordered lists
One way to speed up oui set opeiations is to change the iepiesentation
so that the set elements aie listed in incieasing oidei. To do this, we
need some way to compaie two objects so that we can say which is
biggei. loi example, we could compaie symbols lexicogiaphically, oi
we could agiee on some method foi assigning a unique numbei to an
object and then compaie the elements by compaiing the coiiesponding
numbeis. To keep oui discussion simple, we will considei only the case
wheie the set elements aie numbeis, so that we can compaie elements
using > and <. We will iepiesent a set of numbeis by listing its elements
in incieasing oidei. Wheieas oui ist iepiesentation above allowed us
to iepiesent the set {1, 3, , 10} by listing the elements in any oidei, oui
new iepiesentation allows only the list (1 3 6 10).
One advantage of oideiing shows up in element-of-set? ln check-
ing foi the piesence of an item, we no longei have to scan the entiie set.
lf we ieach a set element that is laigei than the item we aie looking foi,
then we know that the item is not in the set
(define (element-of-set? x set)
(cond ((null? set) false)
((= x (car set)) true)
((< x (car set)) false)
(else (element-of-set? x (cdr set)))))
How many steps does this save` ln the woist case, the item we aie
looking foi may be the laigest one in the set, so the numbei of steps
is the same as foi the unoideied iepiesentation. On the othei hand, if
we seaich foi items of many dieient sizes we can expect that some-
times we will be able to stop seaiching at a point neai the beginning of
the list and that othei times we will still need to examine most of the
list. On the aveiage we should expect to have to examine about half of
208
the items in the set. Tus, the aveiage numbei of steps iequiied will be
about n/2. Tis is still (n) giowth, but it does save us, on the aveiage,
a factoi of 2 in numbei of steps ovei the pievious implementation.
We obtain a moie impiessive speedup with intersection-set. ln
the unoideied iepiesentation this opeiation iequiied (n
2
) steps, be-
cause we peifoimed a complete scan of set2 foi each element of set1.
But with the oideied iepiesentation, we can use a moie clevei method.
Begin by compaiing the initial elements, x1 and x2, of the two sets. lf
x1 equals x2, then that gives an element of the inteisection, and the iest
of the inteisection is the inteisection of the cdr-s of the two sets. Sup-
pose, howevei, that x1 is less than x2. Since x2 is the smallest element
in set2, we can immediately conclude that x1 cannot appeai anywheie
in set2 and hence is not in the inteisection. Hence, the inteisection is
equal to the inteisection of set2 with the cdr of set1. Similaily, if x2
is less than x1, then the inteisection is given by the inteisection of set1
with the cdr of set2. Heie is the pioceduie
(define (intersection-set set1 set2)
(if (or (null? set1) (null? set2))
'()
(let ((x1 (car set1)) (x2 (car set2)))
(cond ((= x1 x2)
(cons x1 (intersection-set (cdr set1)
(cdr set2))))
((< x1 x2)
(intersection-set (cdr set1) set2))
((< x2 x1)
(intersection-set set1 (cdr set2)))))))
To estimate the numbei of steps iequiied by this piocess, obseive that
at each step we ieduce the inteisection pioblem to computing intei-
sections of smallei setsiemoving the ist element fiom set1 oi set2
209
oi both. Tus, the numbei of steps iequiied is at most the sum of the
sizes of set1 and set2, iathei than the pioduct of the sizes as with the
unoideied iepiesentation. Tis is (n) giowth iathei than (n
2
)a con-
sideiable speedup, even foi sets of modeiate size.
Exercise 2.61: Give an implementation of adjoin-set us-
ing the oideied iepiesentation. By analogy with element-
of-set? show how to take advantage of the oideiing to
pioduce a pioceduie that iequiies on the aveiage about half
as many steps as with the unoideied iepiesentation.
Exercise 2.62: Give a (n) implementation of union-set
foi sets iepiesented as oideied lists.
Sets as binary trees
We can do beuei than the oideied-list iepiesentation by aiianging the
set elements in the foim of a tiee. Each node of the tiee holds one ele-
ment of the set, called the entiy at that node, and a link to each of two
othei (possibly empty) nodes. Te lef link points to elements smallei
than the one at the node, and the iight link to elements gieatei than
the one at the node. liguie 2.1 shows some tiees that iepiesent the set
{1, 3, , , 9, 11}. Te same set may be iepiesented by a tiee in a numbei
of dieient ways. Te only thing we iequiie foi a valid iepiesentation
is that all elements in the lef subtiee be smallei than the node entiy
and that all elements in the iight subtiee be laigei.
Te advantage of the tiee iepiesentation is this Suppose we want to
check whethei a numbei x is contained in a set. We begin by compaiing
x with the entiy in the top node. lf x is less than this, we know that we
need only seaich the lef subtiee, if x is gieatei, we need only seaich
the iight subtiee. Now, if the tiee is balanced, each of these subtiees
210
7 3 5
3 9 1 7 9 3
1 11 5 9 1 7 11
11
5
Figure 2.16: Vaiious binaiy tiees that iepiesent the set
{1, 3, , , 9, 11}.
will be about half the size of the oiiginal. Tus, in one step we have
ieduced the pioblem of seaiching a tiee of size n to seaiching a tiee
of size n/2. Since the size of the tiee is halved at each step, we should
expect that the numbei of steps needed to seaich a tiee of size n giows
as (logn).
38
loi laige sets, this will be a signicant speedup ovei the
pievious iepiesentations.
We can iepiesent tiees by using lists. Each node will be a list of thiee
items the entiy at the node, the lef subtiee, and the iight subtiee. Alef
oi a iight subtiee of the empty list will indicate that theie is no subtiee
connected theie. We can desciibe this iepiesentation by the following
pioceduies
39
38
Halving the size of the pioblem at each step is the distinguishing chaiacteiistic of
logaiithmic giowth, as we saw with the fast-exponentiation algoiithm of Section 1.2.4
and the half-inteival seaich method of Section 1.3.3.
39
We aie iepiesenting sets in teims of tiees, and tiees in teims of listsin eect, a
data abstiaction built upon a data abstiaction. We can iegaid the pioceduies entry,
left-branch, right-branch, and make-tree as a way of isolating the abstiaction of a
binaiy tiee fiom the paiticulai way we might wish to iepiesent such a tiee in teims
of list stiuctuie.
211
(define (entry tree) (car tree))
(define (left-branch tree) (cadr tree))
(define (right-branch tree) (caddr tree))
(define (make-tree entry left right)
(list entry left right))
Now we can wiite the element-of-set? pioceduie using the stiategy
desciibed above
(define (element-of-set? x set)
(cond ((null? set) false)
((= x (entry set)) true)
((< x (entry set))
(element-of-set? x (left-branch set)))
((> x (entry set))
(element-of-set? x (right-branch set)))))
Adjoining an item to a set is implemented similaily and also iequiies
(logn) steps. To adjoin an item x, we compaie x with the node en-
tiy to deteimine whethei x should be added to the iight oi to the lef
bianch, and having adjoined x to the appiopiiate bianch we piece this
newly constiucted bianch togethei with the oiiginal entiy and the othei
bianch. lf x is equal to the entiy, we just ietuin the node. lf we aie asked
to adjoin x to an empty tiee, we geneiate a tiee that has x as the entiy
and empty iight and lef bianches. Heie is the pioceduie
(define (adjoin-set x set)
(cond ((null? set) (make-tree x '() '()))
((= x (entry set)) set)
((< x (entry set))
(make-tree (entry set)
(adjoin-set x (left-branch set))
(right-branch set)))
((> x (entry set))
212
(make-tree (entry set) (left-branch set)
(adjoin-set x (right-branch set))))))
Te above claim that seaiching the tiee can be peifoimed in a logaiith-
mic numbei of steps iests on the assumption that the tiee is balanced,
i.e., that the lef and the iight subtiee of eveiy tiee have appioximately
the same numbei of elements, so that each subtiee contains about half
the elements of its paient. But how can we be ceitain that the tiees we
constiuct will be balanced` Even if we stait with a balanced tiee, adding
elements with adjoin-set may pioduce an unbalanced iesult. Since the
position of a newly adjoined element depends on howthe element com-
paies with the items alieady in the set, we can expect that if we add ele-
ments iandomly the tiee will tend to be balanced on the aveiage. But
this is not a guaiantee. loi example, if we stait with an empty set and
adjoin the numbeis 1 thiough in sequence we end up with the highly
unbalanced tiee shown in liguie 2.1. ln this tiee all the lef subtiees
aie empty, so it has no advantage ovei a simple oideied list. One way to
solve this pioblem is to dene an opeiation that tiansfoims an aibitiaiy
tiee into a balanced tiee with the same elements. Ten we can peifoim
this tiansfoimation afei eveiy few adjoin-set opeiations to keep oui
set in balance. Teie aie also othei ways to solve this pioblem, most of
which involve designing new data stiuctuies foi which seaiching and
inseition both can be done in (logn) steps.
40
Exercise 2.63: Each of the following two pioceduies con-
veits a binaiy tiee to a list.
(define (tree->list-1 tree)
(if (null? tree)
40
Examples of such stiuctuies include Brees and reJ||oc| rees. Teie is a laige
liteiatuie on data stiuctuies devoted to this pioblem. See Coimen et al. 1990.
213
1
2
3
4
5
6
7
Figure 2.17: Unbalanced tiee pioduced by adjoining 1
thiough in sequence.
'()
(append (tree->list-1 (left-branch tree))
(cons (entry tree)
(tree->list-1
(right-branch tree))))))
(define (tree->list-2 tree)
(define (copy-to-list tree result-list)
(if (null? tree)
result-list
(copy-to-list (left-branch tree)
(cons (entry tree)
(copy-to-list
(right-branch tree)
result-list)))))
(copy-to-list tree '()))
a. Do the two pioceduies pioduce the same iesult foi
eveiy tiee` lf not, howdo the iesults diei` What lists
214
do the two pioceduies pioduce foi the tiees in liguie
2.1`
b. Do the two pioceduies have the same oidei of giowth
in the numbei of steps iequiied to conveit a balanced
tiee with n elements to a list` lf not, which one giows
moie slowly`
Exercise 2.64: Te following pioceduie list->tree con-
veits an oideied list to a balanced binaiy tiee. Te helpei
pioceduie partial-tree takes as aiguments an integei n
and list of at least n elements and constiucts a balanced
tiee containing the ist n elements of the list. Te iesult ie-
tuined by partial-tree is a paii (foimed with cons) whose
car is the constiucted tiee and whose cdr is the list of ele-
ments not included in the tiee.
(define (list->tree elements)
(car (partial-tree elements (length elements))))
(define (partial-tree elts n)
(if (= n 0)
(cons '() elts)
(let ((left-size (quotient (- n 1) 2)))
(let ((left-result
(partial-tree elts left-size)))
(let ((left-tree (car left-result))
(non-left-elts (cdr left-result))
(right-size (- n (+ left-size 1))))
(let ((this-entry (car non-left-elts))
(right-result
(partial-tree
(cdr non-left-elts)
right-size)))
21
(let ((right-tree (car right-result))
(remaining-elts
(cdr right-result)))
(cons (make-tree this-entry
left-tree
right-tree)
remaining-elts))))))))
a. Wiite a shoit paiagiaph explaining as cleaily as you
can howpartial-tree woiks. Diawthe tiee pioduced
by list->tree foi the list (1 3 5 7 9 11).
b. What is the oidei of giowth in the numbei of steps ie-
quiied by list->tree to conveit a list of n elements`
Exercise 2.65: Use the iesults of Exeicise 2.3 and Exei-
cise 2.4 to give (n) implementations of union-set and
intersection-set foi sets implemented as (balanced) bi-
naiy tiees.
41
Sets and information retrieval
We have examined options foi using lists to iepiesent sets and have
seen how the choice of iepiesentation foi a data object can have a laige
impact on the peifoimance of the piogiams that use the data. Anothei
ieason foi concentiating on sets is that the techniques discussed heie
appeai again and again in applications involving infoimation ietiieval.
Considei a data base containing a laige numbei of individual iecoids,
such as the peisonnel les foi a company oi the tiansactions in an ac-
counting system. A typical data-management system spends a laige
41
Exeicise 2.3 thiough Exeicise 2. aie due to Paul Hilngei.
21
amount of time accessing oi modifying the data in the iecoids and
theiefoie iequiies an ecient method foi accessing iecoids. Tis is done
by identifying a pait of each iecoid to seive as an identifying |ey. A
key can be anything that uniquely identies the iecoid. loi a peisonnel
le, it might be an employees lD numbei. loi an accounting system, it
might be a tiansaction numbei. Whatevei the key is, when we dene the
iecoid as a data stiuctuie we should include a key selectoi pioceduie
that ietiieves the key associated with a given iecoid.
Now we iepiesent the data base as a set of iecoids. To locate the
iecoid with a given key we use a pioceduie lookup, which takes as
aiguments a key and a data base and which ietuins the iecoid that has
that key, oi false if theie is no such iecoid. Lookup is implemented in
almost the same way as element-of-set?. loi example, if the set of
iecoids is implemented as an unoideied list, we could use
(define (lookup given-key set-of-records)
(cond ((null? set-of-records) false)
((equal? given-key (key (car set-of-records)))
(car set-of-records))
(else (lookup given-key (cdr set-of-records)))))
Of couise, theie aie beuei ways to iepiesent laige sets than as un-
oideied lists. lnfoimation-ietiieval systems in which iecoids have to be
iandomly accessed aie typically implemented by a tiee-based method,
such as the binaiy-tiee iepiesentation discussed pieviously. ln design-
ing such a system the methodology of data abstiaction can be a gieat
help. Te designei can cieate an initial implementation using a sim-
ple, stiaightfoiwaid iepiesentation such as unoideied lists. Tis will be
unsuitable foi the eventual system, but it can be useful in pioviding a
quick and diity data base with which to test the iest of the system.
Latei on, the data iepiesentation can be modied to be moie sophisti-
21
cated. lf the data base is accessed in teims of abstiact selectois and con-
stiuctois, this change in iepiesentation will not iequiie any changes to
the iest of the system.
Exercise 2.66: lmplement the lookup pioceduie foi the case
wheie the set of iecoids is stiuctuied as a binaiy tiee, oi-
deied by the numeiical values of the keys.
2.3.4 Example: Human Encoding Trees
Tis section piovides piactice in the use of list stiuctuie and data ab-
stiaction to manipulate sets and tiees. Te application is to methods foi
iepiesenting data as sequences of ones and zeios (bits). loi example,
the ASCll standaid code used to iepiesent text in computeis encodes
each chaiactei as a sequence of seven bits. Using seven bits allows us
to distinguish 2
x
2
y
2
,
y = r sin A, A = aictan(y, x),
which ielate the ieal and imaginaiy paits (x, y) to the magnitude and the
23
angle (r, A).
44
Bens iepiesentation is theiefoie given by the following
selectois and constiuctois
(define (real-part z) (car z))
(define (imag-part z) (cdr z))
(define (magnitude z)
(sqrt (+ (square (real-part z))
(square (imag-part z)))))
(define (angle z)
(atan (imag-part z) (real-part z)))
(define (make-from-real-imag x y) (cons x y))
(define (make-from-mag-ang r a)
(cons (* r (cos a)) (* r (sin a))))
Alyssa, in contiast, chooses to iepiesent complex numbeis in polai foim.
loi hei, selecting the magnitude and angle is stiaightfoiwaid, but she
has to use the tiigonometiic ielations to obtain the ieal and imaginaiy
paits. Alyssas iepiesentation is
(define (real-part z) (* (magnitude z) (cos (angle z))))
(define (imag-part z) (* (magnitude z) (sin (angle z))))
(define (magnitude z) (car z))
(define (angle z) (cdr z))
(define (make-from-real-imag x y)
(cons (sqrt (+ (square x) (square y)))
(atan y x)))
(define (make-from-mag-ang r a) (cons r a))
Te discipline of data abstiaction ensuies that the same implementation
of add-complex, sub-complex, mul-complex, and div-complex will woik
with eithei Bens iepiesentation oi Alyssas iepiesentation.
44
Te aictangent function iefeiied to heie, computed by Schemes atan pioceduie,
is dened so as to take two aiguments y and x and to ietuin the angle whose tangent
is y/x. Te signs of the aiguments deteimine the quadiant of the angle.
23
2.4.2 Tagged data
One way to view data abstiaction is as an application of the piinci-
ple of least commitment. ln implementing the complex-numbei system
in Section 2.4.1, we can use eithei Bens iectangulai iepiesentation oi
Alyssas polai iepiesentation. Te abstiaction baiiiei foimed by the se-
lectois and constiuctois peimits us to defei to the last possible moment
the choice of a conciete iepiesentation foi oui data objects and thus
ietain maximum exibility in oui system design.
Te piinciple of least commitment can be caiiied to even fuithei
extiemes. lf we desiie, we can maintain the ambiguity of iepiesentation
even oer we have designed the selectois and constiuctois, and elect
to use both Bens iepiesentation onJ Alyssas iepiesentation. lf both
iepiesentations aie included in a single system, howevei, we will need
some way to distinguish data in polai foim fiom data in iectangulai
foim. Otheiwise, if we weie asked, foi instance, to nd the magnitude
of the paii (3, 4), we wouldnt know whethei to answei (inteipieting
the numbei in iectangulai foim) oi 3 (inteipieting the numbei in polai
foim). Astiaightfoiwaid way to accomplish this distinction is to include
a ye ogthe symbol rectangular oi polaras pait of each complex
numbei. Ten when we need to manipulate a complex numbei we can
use the tag to decide which selectoi to apply.
ln oidei to manipulate tagged data, we will assume that we have
pioceduies type-tag and contents that extiact fiom a data object the
tag and the actual contents (the polai oi iectangulai cooidinates, in the
case of a complex numbei). We will also postulate a pioceduie attach-
tag that takes a tag and contents and pioduces a tagged data object. A
stiaightfoiwaid way to implement this is to use oidinaiy list stiuctuie
(define (attach-tag type-tag contents)
(cons type-tag contents))
23
(define (type-tag datum)
(if (pair? datum)
(car datum)
(error "Bad tagged datum: TYPE-TAG" datum)))
(define (contents datum)
(if (pair? datum)
(cdr datum)
(error "Bad tagged datum: CONTENTS" datum)))
Using these pioceduies, we can dene piedicates rectangular? and
polar?, which iecognize iectangulai and polai numbeis, iespectively
(define (rectangular? z)
(eq? (type-tag z) 'rectangular))
(define (polar? z) (eq? (type-tag z) 'polar))
With type tags, Ben and Alyssa can now modify theii code so that theii
two dieient iepiesentations can coexist in the same system. When-
evei Ben constiucts a complex numbei, he tags it as iectangulai. When-
evei Alyssa constiucts a complex numbei, she tags it as polai. ln addi-
tion, Ben and Alyssa must make suie that the names of theii pioce-
duies do not conict. One way to do this is foi Ben to append the sux
rectangular to the name of each of his iepiesentation pioceduies and
foi Alyssa to append polar to the names of heis. Heie is Bens ievised
iectangulai iepiesentation fiom Section 2.4.1
(define (real-part-rectangular z) (car z))
(define (imag-part-rectangular z) (cdr z))
(define (magnitude-rectangular z)
(sqrt (+ (square (real-part-rectangular z))
(square (imag-part-rectangular z)))))
(define (angle-rectangular z)
(atan (imag-part-rectangular z)
(real-part-rectangular z)))
238
(define (make-from-real-imag-rectangular x y)
(attach-tag 'rectangular (cons x y)))
(define (make-from-mag-ang-rectangular r a)
(attach-tag 'rectangular
(cons (* r (cos a)) (* r (sin a)))))
and heie is Alyssas ievised polai iepiesentation
(define (real-part-polar z)
(* (magnitude-polar z) (cos (angle-polar z))))
(define (imag-part-polar z)
(* (magnitude-polar z) (sin (angle-polar z))))
(define (magnitude-polar z) (car z))
(define (angle-polar z) (cdr z))
(define (make-from-real-imag-polar x y)
(attach-tag 'polar
(cons (sqrt (+ (square x) (square y)))
(atan y x))))
(define (make-from-mag-ang-polar r a)
(attach-tag 'polar (cons r a)))
Each geneiic selectoi is implemented as a pioceduie that checks the tag
of its aigument and calls the appiopiiate pioceduie foi handling data
of that type. loi example, to obtain the ieal pait of a complex numbei,
real-part examines the tag to deteimine whethei to use Bens real-
part-rectangular oi Alyssas real-part-polar. ln eithei case, we use
contents to extiact the baie, untagged datum and send this to the iect-
angulai oi polai pioceduie as iequiied
(define (real-part z)
(cond ((rectangular? z)
(real-part-rectangular (contents z)))
((polar? z)
(real-part-polar (contents z)))
(else (error "Unknown type: REAL-PART" z))))
239
(define (imag-part z)
(cond ((rectangular? z)
(imag-part-rectangular (contents z)))
((polar? z)
(imag-part-polar (contents z)))
(else (error "Unknown type: IMAG-PART" z))))
(define (magnitude z)
(cond ((rectangular? z)
(magnitude-rectangular (contents z)))
((polar? z)
(magnitude-polar (contents z)))
(else (error "Unknown type: MAGNITUDE" z))))
(define (angle z)
(cond ((rectangular? z)
(angle-rectangular (contents z)))
((polar? z)
(angle-polar (contents z)))
(else (error "Unknown type: ANGLE" z))))
To implement the complex-numbei aiithmetic opeiations, we can use
the same pioceduies add-complex, sub-complex, mul-complex, and div-
complex fiom Section 2.4.1, because the selectois they call aie geneiic,
and so will woik with eithei iepiesentation. loi example, the pioceduie
add-complex is still
(define (add-complex z1 z2)
(make-from-real-imag (+ (real-part z1) (real-part z2))
(+ (imag-part z1) (imag-part z2))))
linally, we must choose whethei to constiuct complex numbeis using
Bens iepiesentation oi Alyssas iepiesentation. One ieasonable choice
is to constiuct iectangulai numbeis whenevei we have ieal and imag-
inaiy paits and to constiuct polai numbeis whenevei we have magni-
tudes and angles
240
add-complex sub-complex mul-complex div-complex
Programs that use complex numbers
Complex-arithmetic package
Rectangular
representation
Polar
representation
List structure and primitive machine arithmetic
real-part
imag-part
magnitude
angle
Figure 2.21: Stiuctuie of the geneiic complex-aiithmetic system.
(define (make-from-real-imag x y)
(make-from-real-imag-rectangular x y))
(define (make-from-mag-ang r a)
(make-from-mag-ang-polar r a))
Te iesulting complex-numbei system has the stiuctuie shown in lig-
uie 2.21. Te system has been decomposed into thiee ielatively inde-
pendent paits the complex-numbei-aiithmetic opeiations, Alyssas po-
lai implementation, and Bens iectangulai implementation. Te polai
and iectangulai implementations could have been wiiuen by Ben and
Alyssa woiking sepaiately, and both of these can be used as undeily-
ing iepiesentations by a thiid piogiammei implementing the complex-
aiithmetic pioceduies in teims of the abstiact constiuctoi/selectoi in-
teiface.
Since each data object is tagged with its type, the selectois opeiate
on the data in a geneiic mannei. Tat is, each selectoi is dened to have
a behavioi that depends upon the paiticulai type of data it is applied to.
241
Notice the geneial mechanism foi inteifacing the sepaiate iepiesenta-
tions Within a given iepiesentation implementation (say, Alyssas po-
lai package) a complex numbei is an untyped paii (magnitude, angle).
When a geneiic selectoi opeiates on a numbei of polar type, it stiips o
the tag and passes the contents on to Alyssas code. Conveisely, when
Alyssa constiucts a numbei foi geneial use, she tags it with a type so
that it can be appiopiiately iecognized by the highei-level pioceduies.
Tis discipline of stiipping o and auaching tags as data objects aie
passed fiom level to level can be an impoitant oiganizational stiategy,
as we shall see in Section 2..
2.4.3 Data-Directed Programming and Additivity
Te geneial stiategy of checking the type of a datum and calling an
appiopiiate pioceduie is called J:soc|:ng on ye. Tis is a poweiful
stiategy foi obtaining modulaiity in system design. On the othei hand,
implementing the dispatch as in Section 2.4.2 has two signicant weak-
nesses. One weakness is that the geneiic inteiface pioceduies (real-
part, imag-part, magnitude, and angle) must know about all the dif-
feient iepiesentations. loi instance, suppose we wanted to incoipoiate
a new iepiesentation foi complex numbeis into oui complex-numbei
system. We would need to identify this new iepiesentation with a type,
and then add a clause to each of the geneiic inteiface pioceduies to
check foi the new type and apply the appiopiiate selectoi foi that iep-
iesentation.
Anothei weakness of the technique is that even though the indi-
vidual iepiesentations can be designed sepaiately, we must guaiantee
that no two pioceduies in the entiie system have the same name. Tis
is why Ben and Alyssa had to change the names of theii oiiginal pioce-
duies fiom Section 2.4.1.
242
Te issue undeilying both of these weaknesses is that the technique
foi implementing geneiic inteifaces is not oJJ::+e. Te peison imple-
menting the geneiic selectoi pioceduies must modify those pioceduies
each time a new iepiesentation is installed, and the people inteifacing
the individual iepiesentations must modify theii code to avoid name
conicts. ln each of these cases, the changes that must be made to the
code aie stiaightfoiwaid, but they must be made nonetheless, and this
is a souice of inconvenience and eiioi. Tis is not much of a pioblem
foi the complex-numbei system as it stands, but suppose theie weie
not two but hundieds of dieient iepiesentations foi complex numbeis.
And suppose that theie weie many geneiic selectois to be maintained
in the abstiact-data inteiface. Suppose, in fact, that no one piogiam-
mei knew all the inteiface pioceduies oi all the iepiesentations. Te
pioblem is ieal and must be addiessed in such piogiams as laige-scale
data-base-management systems.
What we need is a means foi modulaiizing the system design even
fuithei. Tis is piovided by the piogiamming technique known as Joo
J:receJ rogro:ng. To undeistand how data-diiected piogiamming
woiks, begin with the obseivation that whenevei we deal with a set of
geneiic opeiations that aie common to a set of dieient types we aie,
in eect, dealing with a two-dimensional table that contains the possi-
ble opeiations on one axis and the possible types on the othei axis. Te
entiies in the table aie the pioceduies that implement each opeiation
foi each type of aigument piesented. ln the complex-numbei system
developed in the pievious section, the coiiespondence between opeia-
tion name, data type, and actual pioceduie was spiead out among the
vaiious conditional clauses in the geneiic inteiface pioceduies. But the
same infoimation could have been oiganized in a table, as shown in
liguie 2.22.
243
real-part
imag-part
magnitude
angle
real-part-polar
imag-part-polar
magnitude-polar
angle-polar
real-part-rectangular
imag-part-rectangular
magnitude-rectangular
angle-rectangular
Types
Polar Rectangular
O
p
e
r
a
t
i
o
n
s
Figure 2.22: Table of opeiations foi the complex-numbei system.
Data-diiected piogiamming is the technique of designing piogiams
to woik with such a table diiectly. Pieviously, we implemented the
mechanism that inteifaces the complex-aiithmetic code with the two
iepiesentation packages as a set of pioceduies that each peifoim an
explicit dispatch on type. Heie we will implement the inteiface as a sin-
gle pioceduie that looks up the combination of the opeiation name and
aigument type in the table to nd the coiiect pioceduie to apply, and
then applies it to the contents of the aigument. lf we do this, then to
add a new iepiesentation package to the system we need not change
any existing pioceduies, we need only add new entiies to the table.
To implement this plan, assume that we have two pioceduies, put
and get, foi manipulating the opeiation-and-type table
(put o ye :e) installs the :e in the table, indexed
by the o and the ye.
(get o ye) looks up the o, ye entiy in the table and
ietuins the itemfound theie. lf no itemis found, get ietuins false.
loi now, we can assume that put and get aie included in oui language.
ln Chaptei 3 (Section 3.3.3) we will see how to implement these and
244
othei opeiations foi manipulating tables.
Heie is howdata-diiected piogiamming can be used in the complex-
numbei system. Ben, who developed the iectangulai iepiesentation,
implements his code just as he did oiiginally. He denes a collection
of pioceduies, oi a oc|oge, and inteifaces these to the iest of the sys-
tem by adding entiies to the table that tell the system how to opeiate
on iectangulai numbeis. Tis is accomplished by calling the following
pioceduie
(define (install-rectangular-package)
,, inteinal pioceduies
(define (real-part z) (car z))
(define (imag-part z) (cdr z))
(define (make-from-real-imag x y) (cons x y))
(define (magnitude z)
(sqrt (+ (square (real-part z))
(square (imag-part z)))))
(define (angle z)
(atan (imag-part z) (real-part z)))
(define (make-from-mag-ang r a)
(cons (* r (cos a)) (* r (sin a))))
,, inteiface to the iest of the system
(define (tag x) (attach-tag 'rectangular x))
(put 'real-part '(rectangular) real-part)
(put 'imag-part '(rectangular) imag-part)
(put 'magnitude '(rectangular) magnitude)
(put 'angle '(rectangular) angle)
(put 'make-from-real-imag 'rectangular
(lambda (x y) (tag (make-from-real-imag x y))))
(put 'make-from-mag-ang 'rectangular
(lambda (r a) (tag (make-from-mag-ang r a))))
'done)
24
Notice that the inteinal pioceduies heie aie the same pioceduies fiom
Section 2.4.1 that Ben wiote when he was woiking in isolation. No
changes aie necessaiy in oidei to inteiface them to the iest of the sys-
tem. Moieovei, since these pioceduie denitions aie inteinal to the in-
stallation pioceduie, Ben neednt woiiy about name conicts with othei
pioceduies outside the iectangulai package. To inteiface these to the
iest of the system, Ben installs his real-part pioceduie undei the op-
eiation name real-part and the type (rectangular), and similaily foi
the othei selectois.
4
Te inteiface also denes the constiuctois to be
used by the exteinal system.
4
Tese aie identical to Bens inteinally
dened constiuctois, except that they auach the tag.
Alyssas polai package is analogous
(define (install-polar-package)
,, inteinal pioceduies
(define (magnitude z) (car z))
(define (angle z) (cdr z))
(define (make-from-mag-ang r a) (cons r a))
(define (real-part z) (* (magnitude z) (cos (angle z))))
(define (imag-part z) (* (magnitude z) (sin (angle z))))
(define (make-from-real-imag x y)
(cons (sqrt (+ (square x) (square y)))
(atan y x)))
,, inteiface to the iest of the system
(define (tag x) (attach-tag 'polar x))
(put 'real-part '(polar) real-part)
(put 'imag-part '(polar) imag-part)
(put 'magnitude '(polar) magnitude)
4
We use the list (rectangular) iathei than the symbol rectangular to allow foi
the possibility of opeiations with multiple aiguments, not all of the same type.
4
Te type the constiuctois aie installed undei neednt be a list because a constiuctoi
is always used to make an object of one paiticulai type.
24
(put 'angle '(polar) angle)
(put 'make-from-real-imag 'polar
(lambda (x y) (tag (make-from-real-imag x y))))
(put 'make-from-mag-ang 'polar
(lambda (r a) (tag (make-from-mag-ang r a))))
'done)
Even though Ben and Alyssa both still use theii oiiginal pioceduies
dened with the same names as each otheis (e.g., real-part), these
denitions aie now inteinal to dieient pioceduies (see Section 1.1.8),
so theie is no name conict.
Te complex-aiithmetic selectois access the table by means of a
geneial opeiation pioceduie called apply-generic, which applies a
geneiic opeiation to some aiguments. Apply-generic looks in the ta-
ble undei the name of the opeiation and the types of the aiguments and
applies the iesulting pioceduie if one is piesent
4
(define (apply-generic op . args)
(let ((type-tags (map type-tag args)))
(let ((proc (get op type-tags)))
(if proc
(apply proc (map contents args))
(error
4
Apply-generic uses the doued-tail notation desciibed in Exeicise 2.20, because dif-
feient geneiic opeiations may take dieient numbeis of aiguments. ln apply-generic,
op has as its value the ist aigument to apply-generic and args has as its value a list
of the iemaining aiguments.
Apply-generic also uses the piimitive pioceduie apply, which takes two aiguments,
a pioceduie and a list. Apply applies the pioceduie, using the elements in the list as
aiguments. loi example,
(apply + (list 1 2 3 4))
ietuins 10.
24
"No method for these types: APPLY-GENERIC"
(list op type-tags))))))
Using apply-generic, we can dene oui geneiic selectois as follows
(define (real-part z) (apply-generic 'real-part z))
(define (imag-part z) (apply-generic 'imag-part z))
(define (magnitude z) (apply-generic 'magnitude z))
(define (angle z) (apply-generic 'angle z))
Obseive that these do not change at all if a new iepiesentation is added
to the system.
We can also extiact fiomthe table the constiuctois to be used by the
piogiams exteinal to the packages in making complex numbeis fiom
ieal and imaginaiy paits and fiommagnitudes and angles. As in Section
2.4.2, we constiuct iectangulai numbeis whenevei we have ieal and
imaginaiy paits, and polai numbeis whenevei we have magnitudes and
angles
(define (make-from-real-imag x y)
((get 'make-from-real-imag 'rectangular) x y))
(define (make-from-mag-ang r a)
((get 'make-from-mag-ang 'polar) r a))
Exercise 2.73: Section 2.3.2 desciibed a piogiam that pei-
foims symbolic dieientiation
(define (deriv exp var)
(cond ((number? exp) 0)
((variable? exp)
(if (same-variable? exp var) 1 0))
((sum? exp)
(make-sum (deriv (addend exp) var)
(deriv (augend exp) var)))
248
((product? exp)
(make-sum (make-product
(multiplier exp)
(deriv (multiplicand exp) var))
(make-product
(deriv (multiplier exp) var)
(multiplicand exp))))
more rules can be added here
(else (error "unknown expression type:
DERIV" exp))))
We can iegaid this piogiam as peifoiming a dispatch on
the type of the expiession to be dieientiated. ln this situ-
ation the type tag of the datum is the algebiaic opeiatoi
symbol (such as +) and the opeiation being peifoimed is
deriv. We can tiansfoim this piogiam into data-diiected
style by iewiiting the basic deiivative pioceduie as
(define (deriv exp var)
(cond ((number? exp) 0)
((variable? exp) (if (same-variable? exp var) 1 0))
(else ((get 'deriv (operator exp))
(operands exp) var))))
(define (operator exp) (car exp))
(define (operands exp) (cdr exp))
a. Explain what was done above. Why cant we assim-
ilate the piedicates number? and variable? into the
data-diiected dispatch`
b. Wiite the pioceduies foi deiivatives of sums and piod-
ucts, and the auxiliaiy code iequiied to install themin
the table used by the piogiam above.
249
c. Choose any additional dieientiation iule that you
like, such as the one foi exponents (Exeicise 2.), and
install it in this data-diiected system.
d. ln this simple algebiaic manipulatoi the type of an
expiession is the algebiaic opeiatoi that binds it to-
gethei. Suppose, howevei, we indexed the pioceduies
in the opposite way, so that the dispatch line in deriv
looked like
((get (operator exp) 'deriv) (operands exp) var)
What coiiesponding changes to the deiivative system
aie iequiied`
Exercise 2.74: lnsatiable Enteipiises, lnc., is a highly de-
centialized conglomeiate company consisting of a laige num-
bei of independent divisions located all ovei the woild. Te
companys computei facilities have just been inteiconnected
by means of a clevei netwoik-inteifacing scheme that makes
the entiie netwoik appeai to any usei to be a single com-
putei. lnsatiables piesident, in hei ist auempt to exploit
the ability of the netwoik to extiact administiative infoi-
mation fiom division les, is dismayed to discovei that, al-
though all the division les have been implemented as data
stiuctuies in Scheme, the paiticulai data stiuctuie used vaiies
fiom division to division. A meeting of division manageis
is hastily called to seaich foi a stiategy to integiate the les
that will satisfy headquaiteis needs while pieseiving the
existing autonomy of the divisions.
Show how such a stiategy can be implemented with data-
diiected piogiamming. As an example, suppose that each
20
divisions peisonnel iecoids consist of a single le, which
contains a set of iecoids keyed on employees names. Te
stiuctuie of the set vaiies fiom division to division. lui-
theimoie, each employees iecoid is itself a set (stiuctuied
dieiently fiomdivision to division) that contains infoima-
tion keyed undei identieis such as address and salary.
ln paiticulai
a. lmplement foi headquaiteis a get-record pioceduie
that ietiieves a specied employees iecoid fiom a
specied peisonnel le. Te pioceduie should be ap-
plicable to any divisions le. Explain howthe individ-
ual divisions les should be stiuctuied. ln paiticulai,
what type infoimation must be supplied`
b. lmplement foi headquaiteis a get-salary pioceduie
that ietuins the salaiy infoimation fiom a given em-
ployees iecoid fiomany divisions peisonnel le. How
should the iecoid be stiuctuied in oidei to make this
opeiation woik`
c. lmplement foi headquaiteis a find-employee-record
pioceduie. Tis should seaich all the divisions les
foi the iecoid of a given employee and ietuin the iecoid.
Assume that this pioceduie takes as aiguments an
employees name and a list of all the divisions les.
d. When lnsatiable takes ovei a newcompany, what changes
must be made in oidei to incoipoiate the newpeison-
nel infoimation into the cential system`
21
Message passing
Te key idea of data-diiected piogiamming is to handle geneiic opeia-
tions in piogiams by dealing explicitly with opeiation-and-type tables,
such as the table in liguie 2.22. Te style of piogiamming we used in
Section 2.4.2 oiganized the iequiied dispatching on type by having each
opeiation take caie of its own dispatching. ln eect, this decomposes the
opeiation-and-type table into iows, with each geneiic opeiation pioce-
duie iepiesenting a iow of the table.
An alteinative implementation stiategy is to decompose the table
into columns and, instead of using intelligent opeiations that dispatch
on data types, to woik with intelligent data objects that dispatch on
opeiation names. We can do this by aiianging things so that a data
object, such as a iectangulai numbei, is iepiesented as a pioceduie that
takes as input the iequiied opeiation name and peifoims the opeiation
indicated. ln such a discipline, make-from-real-imag could be wiiuen
as
(define (make-from-real-imag x y)
(define (dispatch op)
(cond ((eq? op 'real-part) x)
((eq? op 'imag-part) y)
((eq? op 'magnitude) (sqrt (+ (square x) (square y))))
((eq? op 'angle) (atan y x))
(else (error "Unknown op: MAKE-FROM-REAL-IMAG" op))))
dispatch)
Te coiiesponding apply-generic pioceduie, which applies a geneiic
opeiation to an aigument, now simply feeds the opeiations name to
the data object and lets the object do the woik
48
48
One limitation of this oiganization is it peimits only geneiic pioceduies of one
aigument.
22
(define (apply-generic op arg) (arg op))
Note that the value ietuined by make-from-real-imag is a pioceduie
the inteinal dispatch pioceduie. Tis is the pioceduie that is invoked
when apply-generic iequests an opeiation to be peifoimed.
Tis style of piogiamming is called essoge oss:ng. Te name comes
fiomthe image that a data object is an entity that ieceives the iequested
opeiation name as a message. We have alieady seen an example of
message passing in Section 2.1.3, wheie we saw how cons, car, and cdr
could be dened with no data objects but only pioceduies. Heie we see
that message passing is not a mathematical tiick but a useful technique
foi oiganizing systems with geneiic opeiations. ln the iemaindei of this
chaptei we will continue to use data-diiected piogiamming, iathei than
message passing, to discuss geneiic aiithmetic opeiations. ln Chaptei 3
we will ietuin to message passing, and we will see that it can be a pow-
eiful tool foi stiuctuiing simulation piogiams.
Exercise 2.75: lmplement the constiuctoi make-from-mag-
ang in message-passing style. Tis pioceduie should be anal-
ogous to the make-from-real-imag pioceduie given above.
Exercise 2.76: As a laige system with geneiic opeiations
evolves, new types of data objects oi new opeiations may
be needed. loi each of the thiee stiategiesgeneiic opeia-
tions with explicit dispatch, data-diiected style, and message-
passing-styledesciibe the changes that must be made to a
systemin oidei to add newtypes oi newopeiations. Which
oiganization would be most appiopiiate foi a system in
which new types must ofen be added` Which would be
most appiopiiate foi a system in which new opeiations
must ofen be added`
23
2.5 Systems with Generic Operations
ln the pievious section, we saw how to design systems in which data
objects can be iepiesented in moie than one way. Te key idea is to
link the code that species the data opeiations to the seveial iepiesen-
tations by means of geneiic inteiface pioceduies. Now we will see how
to use this same idea not only to dene opeiations that aie geneiic ovei
dieient iepiesentations but also to dene opeiations that aie geneiic
ovei dieient kinds of aiguments. We have alieady seen seveial dif-
feient packages of aiithmetic opeiations the piimitive aiithmetic (+, -,
*, /) built into oui language, the iational-numbei aiithmetic (add-rat,
sub-rat, mul-rat, div-rat) of Section 2.1.1, and the complex-numbei
aiithmetic that we implemented in Section 2.4.3. We will now use data-
diiected techniques to constiuct a package of aiithmetic opeiations that
incoipoiates all the aiithmetic packages we have alieady constiucted.
liguie 2.23 shows the stiuctuie of the system we shall build. Notice
the abstiaction baiiieis. liom the peispective of someone using num-
beis, theie is a single pioceduie add that opeiates on whatevei num-
beis aie supplied. Add is pait of a geneiic inteiface that allows the sep-
aiate oidinaiy-aiithmetic, iational-aiithmetic, and complex-aiithmetic
packages to be accessed unifoimly by piogiams that use numbeis. Any
individual aiithmetic package (such as the complex package) may it-
self be accessed thiough geneiic pioceduies (such as add-complex) that
combine packages designed foi dieient iepiesentations (such as iect-
angulai and polai). Moieovei, the stiuctuie of the system is additive, so
that one can design the individual aiithmetic packages sepaiately and
combine them to pioduce a geneiic aiithmetic system.
24
add sub mul div
add-complex
mul-complex
sub-complex
div-complex
Programs that use numbers
Generic arithmetic package
Complex arithmetic
Rectangular Polar
sub-rat
div-rat
add-rat
mul-rat
Rational
arithmetic
Ordinary
arithmetic
List structure and primitive machine arithmetic
+ --
*
/
Figure 2.23: Geneiic aiithmetic system.
2.5.1 Generic Arithmetic Operations
Te task of designing geneiic aiithmetic opeiations is analogous to that
of designing the geneiic complex-numbei opeiations. We would like,
foi instance, to have a geneiic addition pioceduie add that acts like oi-
dinaiy piimitive addition + on oidinaiy numbeis, like add-rat on ia-
tional numbeis, and like add-complex on complex numbeis. We can
implement add, and the othei geneiic aiithmetic opeiations, by follow-
ing the same stiategy we used in Section 2.4.3 to implement the geneiic
selectois foi complex numbeis. We will auach a type tag to each kind of
numbei and cause the geneiic pioceduie to dispatch to an appiopiiate
package accoiding to the data type of its aiguments.
Te geneiic aiithmetic pioceduies aie dened as follows
(define (add x y) (apply-generic 'add x y))
2
(define (sub x y) (apply-generic 'sub x y))
(define (mul x y) (apply-generic 'mul x y))
(define (div x y) (apply-generic 'div x y))
We begin by installing a package foi handling orJ:nory numbeis, that
is, the piimitive numbeis of oui language. We will tag these with the
symbol scheme-number. Te aiithmetic opeiations in this package aie
the piimitive aiithmetic pioceduies (so theie is no need to dene extia
pioceduies to handle the untagged numbeis). Since these opeiations
each take two aiguments, they aie installed in the table keyed by the
list (scheme-number scheme-number)
(define (install-scheme-number-package)
(define (tag x) (attach-tag 'scheme-number x))
(put 'add '(scheme-number scheme-number)
(lambda (x y) (tag (+ x y))))
(put 'sub '(scheme-number scheme-number)
(lambda (x y) (tag (- x y))))
(put 'mul '(scheme-number scheme-number)
(lambda (x y) (tag (* x y))))
(put 'div '(scheme-number scheme-number)
(lambda (x y) (tag (/ x y))))
(put 'make 'scheme-number (lambda (x) (tag x)))
'done)
Useis of the Scheme-numbei package will cieate (tagged) oidinaiy num-
beis by means of the pioceduie
(define (make-scheme-number n)
((get 'make 'scheme-number) n))
Now that the fiamewoik of the geneiic aiithmetic system is in place,
we can ieadily include new kinds of numbeis. Heie is a package that
peifoims iational aiithmetic. Notice that, as a benet of additivity, we
2
can use without modication the iational-numbei code fiom Section
2.1.1 as the inteinal pioceduies in the package
(define (install-rational-package)
,, inteinal pioceduies
(define (numer x) (car x))
(define (denom x) (cdr x))
(define (make-rat n d)
(let ((g (gcd n d)))
(cons (/ n g) (/ d g))))
(define (add-rat x y)
(make-rat (+ (* (numer x) (denom y))
(* (numer y) (denom x)))
(* (denom x) (denom y))))
(define (sub-rat x y)
(make-rat (- (* (numer x) (denom y))
(* (numer y) (denom x)))
(* (denom x) (denom y))))
(define (mul-rat x y)
(make-rat (* (numer x) (numer y))
(* (denom x) (denom y))))
(define (div-rat x y)
(make-rat (* (numer x) (denom y))
(* (denom x) (numer y))))
,, inteiface to iest of the system
(define (tag x) (attach-tag 'rational x))
(put 'add '(rational rational)
(lambda (x y) (tag (add-rat x y))))
(put 'sub '(rational rational)
(lambda (x y) (tag (sub-rat x y))))
(put 'mul '(rational rational)
(lambda (x y) (tag (mul-rat x y))))
(put 'div '(rational rational)
(lambda (x y) (tag (div-rat x y))))
2
(put 'make 'rational
(lambda (n d) (tag (make-rat n d))))
'done)
(define (make-rational n d)
((get 'make 'rational) n d))
We can install a similai package to handle complex numbeis, using the
tag complex. ln cieating the package, we extiact fiom the table the op-
eiations make-from-real-imag and make-from-mag-ang that weie de-
ned by the iectangulai and polai packages. Additivity peimits us to
use, as the inteinal opeiations, the same add-complex, sub-complex,
mul-complex, and div-complex pioceduies fiom Section 2.4.1.
(define (install-complex-package)
,, impoited pioceduies fiom iectangulai and polai packages
(define (make-from-real-imag x y)
((get 'make-from-real-imag 'rectangular) x y))
(define (make-from-mag-ang r a)
((get 'make-from-mag-ang 'polar) r a))
,, inteinal pioceduies
(define (add-complex z1 z2)
(make-from-real-imag (+ (real-part z1) (real-part z2))
(+ (imag-part z1) (imag-part z2))))
(define (sub-complex z1 z2)
(make-from-real-imag (- (real-part z1) (real-part z2))
(- (imag-part z1) (imag-part z2))))
(define (mul-complex z1 z2)
(make-from-mag-ang (* (magnitude z1) (magnitude z2))
(+ (angle z1) (angle z2))))
(define (div-complex z1 z2)
(make-from-mag-ang (/ (magnitude z1) (magnitude z2))
(- (angle z1) (angle z2))))
,, inteiface to iest of the system
(define (tag z) (attach-tag 'complex z))
28
(put 'add '(complex complex)
(lambda (z1 z2) (tag (add-complex z1 z2))))
(put 'sub '(complex complex)
(lambda (z1 z2) (tag (sub-complex z1 z2))))
(put 'mul '(complex complex)
(lambda (z1 z2) (tag (mul-complex z1 z2))))
(put 'div '(complex complex)
(lambda (z1 z2) (tag (div-complex z1 z2))))
(put 'make-from-real-imag 'complex
(lambda (x y) (tag (make-from-real-imag x y))))
(put 'make-from-mag-ang 'complex
(lambda (r a) (tag (make-from-mag-ang r a))))
'done)
Piogiams outside the complex-numbei package can constiuct complex
numbeis eithei fiom ieal and imaginaiy paits oi fiom magnitudes and
angles. Notice how the undeilying pioceduies, oiiginally dened in the
iectangulai and polai packages, aie expoited to the complex package,
and expoited fiom theie to the outside woild.
(define (make-complex-from-real-imag x y)
((get 'make-from-real-imag 'complex) x y))
(define (make-complex-from-mag-ang r a)
((get 'make-from-mag-ang 'complex) r a))
What we have heie is a two-level tag system. A typical complex num-
bei, such as 3 4i in iectangulai foim, would be iepiesented as shown
in liguie 2.24. Te outei tag (complex) is used to diiect the numbei to
the complex package. Once within the complex package, the next tag
(rectangular) is used to diiect the numbei to the iectangulai package.
ln a laige and complicated system theie might be many levels, each in-
teifaced with the next by means of geneiic opeiations. As a data object
is passed downwaid, the outei tag that is used to diiect it to the ap-
29
3 4 complex rectangular
Figure 2.24: Repiesentation of 3 4i in iectangulai foim.
piopiiate package is stiipped o (by applying contents) and the next
level of tag (if any) becomes visible to be used foi fuithei dispatching.
ln the above packages, we used add-rat, add-complex, and the othei
aiithmetic pioceduies exactly as oiiginally wiiuen. Once these deni-
tions aie inteinal to dieient installation pioceduies, howevei, they no
longei need names that aie distinct fiom each othei we could simply
name them add, sub, mul, and div in both packages.
Exercise 2.77: Louis Reasonei tiies to evaluate the expies-
sion (magnitude z) wheie z is the object shown in liguie
2.24. To his suipiise, instead of the answei he gets an eiioi
message fiom apply-generic, saying theie is no method
foi the opeiation magnitude on the types (complex). He
shows this inteiaction to Alyssa P. Hackei, who says Te
pioblem is that the complex-numbei selectois weie nevei
dened foi complex numbeis, just foi polar and rectangular
numbeis. All you have to do to make this woik is add the
following to the complex package
(put 'real-part '(complex) real-part)
(put 'imag-part '(complex) imag-part)
(put 'magnitude '(complex) magnitude)
(put 'angle '(complex) angle)
20
Desciibe in detail why this woiks. As an example, tiace
thiough all the pioceduies called in evaluating the expies-
sion (magnitude z) wheie z is the object shown in liguie
2.24. ln paiticulai, how many times is apply-generic in-
voked` What pioceduie is dispatched to in each case`
Exercise 2.78: Te inteinal pioceduies in the scheme-number
package aie essentially nothing moie than calls to the piim-
itive pioceduies +, -, etc. lt was not possible to use the piim-
itives of the language diiectly because oui type-tag system
iequiies that each data object have a type auached to it. ln
fact, howevei, all Lisp implementations do have a type sys-
tem, which they use inteinally. Piimitive piedicates such
as symbol? and number? deteimine whethei data objects
have paiticulai types. Modify the denitions of type-tag,
contents, and attach-tag fiom Section 2.4.2 so that oui
geneiic system takes advantage of Schemes inteinal type
system. Tat is to say, the system should woik as befoie ex-
cept that oidinaiy numbeis should be iepiesented simply
as Scheme numbeis iathei than as paiis whose car is the
symbol scheme-number.
Exercise 2.79: Dene a geneiic equality piedicate equ? that
tests the equality of two numbeis, and install it in the geneiic
aiithmetic package. Tis opeiation should woik foi oidi-
naiy numbeis, iational numbeis, and complex numbeis.
Exercise 2.80: Dene a geneiic piedicate =zero? that tests
if its aigument is zeio, and install it in the geneiic aiith-
metic package. Tis opeiation should woik foi oidinaiy
numbeis, iational numbeis, and complex numbeis.
21
2.5.2 Combining Data of Dierent Types
We have seen how to dene a unied aiithmetic system that encom-
passes oidinaiy numbeis, complex numbeis, iational numbeis, and any
othei type of numbei we might decide to invent, but we have ignoied an
impoitant issue. Te opeiations we have dened so fai tieat the diei-
ent data types as being completely independent. Tus, theie aie sepaiate
packages foi adding, say, two oidinaiy numbeis, oi two complex num-
beis. What we have not yet consideied is the fact that it is meaningful to
dene opeiations that cioss the type boundaiies, such as the addition of
a complex numbei to an oidinaiy numbei. We have gone to gieat pains
to intioduce baiiieis between paits of oui piogiams so that they can be
developed and undeistood sepaiately. We would like to intioduce the
cioss-type opeiations in some caiefully contiolled way, so that we can
suppoit them without seiiously violating oui module boundaiies.
One way to handle cioss-type opeiations is to design a dieient pio-
ceduie foi each possible combination of types foi which the opeiation
is valid. loi example, we could extend the complex-numbei package so
that it piovides a pioceduie foi adding complex numbeis to oidinaiy
numbeis and installs this in the table using the tag (complex scheme-
number)
49
,, to be included in the complex package
(define (add-complex-to-schemenum z x)
(make-from-real-imag (+ (real-part z) x) (imag-part z)))
(put 'add '(complex scheme-number)
(lambda (z x) (tag (add-complex-to-schemenum z x))))
Tis technique woiks, but it is cumbeisome. With such a system, the
cost of intioducing a new type is not just the constiuction of the pack-
49
We also have to supply an almost identical pioceduie to handle the types (scheme-
number complex).
22
age of pioceduies foi that type but also the constiuction and installa-
tion of the pioceduies that implement the cioss-type opeiations. Tis
can easily be much moie code than is needed to dene the opeiations
on the type itself. Te method also undeimines oui ability to combine
sepaiate packages additively, oi at least to limit the extent to which the
implementois of the individual packages need to take account of othei
packages. loi instance, in the example above, it seems ieasonable that
handling mixed opeiations on complex numbeis and oidinaiy numbeis
should be the iesponsibility of the complex-numbei package. Combin-
ing iational numbeis and complex numbeis, howevei, might be done by
the complex package, by the iational package, oi by some thiid package
that uses opeiations extiacted fiom these two packages. loimulating
coheient policies on the division of iesponsibility among packages can
be an oveiwhelming task in designing systems with many packages and
many cioss-type opeiations.
Coercion
ln the geneial situation of completely unielated opeiations acting on
completely unielated types, implementing explicit cioss-type opeiations,
cumbeisome though it may be, is the best that one can hope foi. loi-
tunately, we can usually do beuei by taking advantage of additional
stiuctuie that may be latent in oui type system. Ofen the dieient data
types aie not completely independent, and theie may be ways by which
objects of one type may be viewed as being of anothei type. Tis piocess
is called coerc:on. loi example, if we aie asked to aiithmetically combine
an oidinaiy numbei with a complex numbei, we can view the oidinaiy
numbei as a complex numbei whose imaginaiy pait is zeio. Tis tians-
foims the pioblem to that of combining two complex numbeis, which
can be handled in the oidinaiy way by the complex-aiithmetic package.
23
ln geneial, we can implement this idea by designing coeicion pio-
ceduies that tiansfoim an object of one type into an equivalent object
of anothei type. Heie is a typical coeicion pioceduie, which tiansfoims
a given oidinaiy numbei to a complex numbei with that ieal pait and
zeio imaginaiy pait
(define (scheme-number->complex n)
(make-complex-from-real-imag (contents n) 0))
We install these coeicion pioceduies in a special coeicion table, indexed
undei the names of the two types
(put-coercion 'scheme-number
'complex
scheme-number->complex)
(We assume that theie aie put-coercion and get-coercion pioceduies
available foi manipulating this table.) Geneially some of the slots in the
table will be empty, because it is not geneially possible to coeice an ai-
bitiaiy data object of each type into all othei types. loi example, theie
is no way to coeice an aibitiaiy complex numbei to an oidinaiy num-
bei, so theie will be no geneial complex->scheme-number pioceduie
included in the table.
Once the coeicion table has been set up, we can handle coeicion
in a unifoim mannei by modifying the apply-generic pioceduie of
Section 2.4.3. When asked to apply an opeiation, we ist check whethei
the opeiation is dened foi the aiguments types, just as befoie. lf so,
we dispatch to the pioceduie found in the opeiation-and-type table.
Otheiwise, we tiy coeicion. loi simplicity, we considei only the case
wheie theie aie two aiguments.
0
We check the coeicion table to see
if objects of the ist type can be coeiced to the second type. lf so, we
0
See Exeicise 2.82 foi geneializations.
24
coeice the ist aigument and tiy the opeiation again. lf objects of the
ist type cannot in geneial be coeiced to the second type, we tiy the
coeicion the othei way aiound to see if theie is a way to coeice the
second aigument to the type of the ist aigument. linally, if theie is no
known way to coeice eithei type to the othei type, we give up. Heie is
the pioceduie
(define (apply-generic op . args)
(let ((type-tags (map type-tag args)))
(let ((proc (get op type-tags)))
(if proc
(apply proc (map contents args))
(if (= (length args) 2)
(let ((type1 (car type-tags))
(type2 (cadr type-tags))
(a1 (car args))
(a2 (cadr args)))
(let ((t1->t2 (get-coercion type1 type2))
(t2->t1 (get-coercion type2 type1)))
(cond (t1->t2
(apply-generic op (t1->t2 a1) a2))
(t2->t1
(apply-generic op a1 (t2->t1 a2)))
(else (error "No method for these types"
(list op type-tags))))))
(error "No method for these types"
(list op type-tags)))))))
Tis coeicion scheme has many advantages ovei the method of dening
explicit cioss-type opeiations, as outlined above. Although we still need
to wiite coeicion pioceduies to ielate the types (possibly n
2
pioceduies
foi a system with n types), we need to wiite only one pioceduie foi
each paii of types iathei than a dieient pioceduie foi each collection
2
of types and each geneiic opeiation.
1
What we aie counting on heie
is the fact that the appiopiiate tiansfoimation between types depends
only on the types themselves, not on the opeiation to be applied.
On the othei hand, theie may be applications foi which oui coei-
cion scheme is not geneial enough. Even when neithei of the objects to
be combined can be conveited to the type of the othei it may still be
possible to peifoim the opeiation by conveiting both objects to a thiid
type. ln oidei to deal with such complexity and still pieseive modulai-
ity in oui piogiams, it is usually necessaiy to build systems that take
advantage of still fuithei stiuctuie in the ielations among types, as we
discuss next.
Hierarchies of types
Te coeicion scheme piesented above ielied on the existence of natuial
ielations between paiis of types. Ofen theie is moie global stiuctuie
in how the dieient types ielate to each othei. loi instance, suppose
we aie building a geneiic aiithmetic system to handle integeis, iational
numbeis, ieal numbeis, and complex numbeis. ln such a system, it is
quite natuial to iegaid an integei as a special kind of iational numbei,
which is in tuin a special kind of ieal numbei, which is in tuin a special
kind of complex numbei. What we actually have is a so-called |:erorc|y
o[ yes, in which, foi example, integeis aie a sv|ye of iational num-
1
lf we aie clevei, we can usually get by with fewei than n
2
coeicion pioceduies.
loi instance, if we know how to conveit fiom type 1 to type 2 and fiom type 2 to
type 3, then we can use this knowledge to conveit fiom type 1 to type 3. Tis can
gieatly deciease the numbei of coeicion pioceduies we need to supply explicitly when
we add a new type to the system. lf we aie willing to build the iequiied amount of
sophistication into oui system, we can have it seaich the giaph of ielations among
types and automatically geneiate those coeicion pioceduies that can be infeiied fiom
the ones that aie supplied explicitly.
2
complex
real
rational
integer
Figure 2.25: A towei of types.
beis (i.e., any opeiation that can be applied to a iational numbei can
automatically be applied to an integei). Conveisely, we say that iatio-
nal numbeis foim a sverye of integeis. Te paiticulai hieiaichy we
have heie is of a veiy simple kind, in which each type has at most one
supeitype and at most one subtype. Such a stiuctuie, called a o+er, is
illustiated in liguie 2.2.
lf we have a towei stiuctuie, then we can gieatly simplify the piob-
lem of adding a new type to the hieiaichy, foi we need only specify
how the new type is embedded in the next supeitype above it and how
it is the supeitype of the type below it. loi example, if we want to add
an integei to a complex numbei, we need not explicitly dene a special
coeicion pioceduie integer->complex. lnstead, we dene how an inte-
gei can be tiansfoimed into a iational numbei, howa iational numbei is
tiansfoimed into a ieal numbei, and how a ieal numbei is tiansfoimed
into a complex numbei. We then allow the system to tiansfoim the in-
tegei into a complex numbei thiough these steps and then add the two
complex numbeis.
We can iedesign oui apply-generic pioceduie in the following
way loi each type, we need to supply a raise pioceduie, which iaises
2
objects of that type one level in the towei. Ten when the system is ie-
quiied to opeiate on objects of dieient types it can successively iaise
the lowei types until all the objects aie at the same level in the towei.
(Exeicise 2.83 and Exeicise 2.84 concein the details of implementing
such a stiategy.)
Anothei advantage of a towei is that we can easily implement the
notion that eveiy type inheiits all opeiations dened on a supeitype.
loi instance, if we do not supply a special pioceduie foi nding the ieal
pait of an integei, we should neveitheless expect that real-part will
be dened foi integeis by viitue of the fact that integeis aie a subtype
of complex numbeis. ln a towei, we can aiiange foi this to happen in a
unifoim way by modifying apply-generic. lf the iequiied opeiation is
not diiectly dened foi the type of the object given, we iaise the object
to its supeitype and tiy again. We thus ciawl up the towei, tiansfoiming
oui aigument as we go, until we eithei nd a level at which the desiied
opeiation can be peifoimed oi hit the top (in which case we give up).
Yet anothei advantage of a towei ovei a moie geneial hieiaichy is
that it gives us a simple way to lowei a data object to the simplest
iepiesentation. loi example, if we add 2 3i to 4 3i, it would be nice
to obtain the answei as the integei iathei than as the complex num-
bei 0i. Exeicise 2.8 discusses a way to implement such a loweiing
opeiation. (Te tiick is that we need a geneial way to distinguish those
objects that can be loweied, such as 0i, fiom those that cannot, such
as 2i.)
Inadequacies of hierarchies
lf the data types in oui system can be natuially aiianged in a towei,
this gieatly simplies the pioblems of dealing with geneiic opeiations
on dieient types, as we have seen. Unfoitunately, this is usually not the
case. liguie 2.2 illustiates a moie complex aiiangement of mixed types,
28
polygon
quadrilateral
kite
trapezoid
parallelogram
rectangle rhombus
square
triangle
isosceles
triangle
right
triangle
isosceles
right triangle
equilateral
triangle
Figure 2.26: Relations among types of geometiic guies.
this one showing ielations among dieient types of geometiic guies.
We see that, in geneial, a type may have moie than one subtype. Tii-
angles and quadiilateials, foi instance, aie both subtypes of polygons.
ln addition, a type may have moie than one supeitype. loi example,
an isosceles iight tiiangle may be iegaided eithei as an isosceles tiian-
gle oi as a iight tiiangle. Tis multiple-supeitypes issue is paiticulaily
thoiny, since it means that theie is no unique way to iaise a type in the
hieiaichy. linding the coiiect supeitype in which to apply an opeia-
tion to an object may involve consideiable seaiching thiough the entiie
type netwoik on the pait of a pioceduie such as apply-generic. Since
theie geneially aie multiple subtypes foi a type, theie is a similai piob-
lem in coeicing a value down the type hieiaichy. Dealing with laige
numbeis of inteiielated types while still pieseiving modulaiity in the
29
design of laige systems is veiy dicult, and is an aiea of much cuiient
ieseaich.
2
Exercise 2.81: Louis Reasonei has noticed that apply-generic
may tiy to coeice the aiguments to each otheis type even
if they alieady have the same type. Teiefoie, he ieasons,
we need to put pioceduies in the coeicion table to coerce
aiguments of each type to theii own type. loi example, in
addition to the scheme-number->complex coeicion shown
above, he would do
(define (scheme-number->scheme-number n) n)
(define (complex->complex z) z)
(put-coercion 'scheme-number
'scheme-number
scheme-number->scheme-number)
(put-coercion 'complex 'complex complex->complex)
2
Tis statement, which also appeais in the ist edition of this book, is just as tiue
now as it was when we wiote it twelve yeais ago. Developing a useful, geneial fiame-
woik foi expiessing the ielations among dieient types of entities (what philosopheis
call ontology) seems intiactably dicult. Te main dieience between the confu-
sion that existed ten yeais ago and the confusion that exists now is that now a va-
iiety of inadequate ontological theoiies have been embodied in a plethoia of coiie-
spondingly inadequate piogiamming languages. loi example, much of the complexity
of object-oiiented piogiamming languagesand the subtle and confusing dieiences
among contempoiaiy object-oiiented languagescenteis on the tieatment of geneiic
opeiations on inteiielated types. Oui own discussion of computational objects in Chap-
tei 3 avoids these issues entiiely. Readeis familiai with object-oiiented piogiamming
will notice that we have much to say in chaptei 3 about local state, but we do not even
mention classes oi inheiitance. ln fact, we suspect that these pioblems cannot be ad-
equately addiessed in teims of computei-language design alone, without also diawing
on woik in knowledge iepiesentation and automated ieasoning.
20
a. With Louiss coeicion pioceduies installed, what hap-
pens if apply-generic is called with two aiguments
of type scheme-number oi two aiguments of type complex
foi an opeiation that is not found in the table foi those
types` loi example, assume that weve dened a geneiic
exponentiation opeiation
(define (exp x y) (apply-generic 'exp x y))
and have put a pioceduie foi exponentiation in the
Scheme-numbei package but not in any othei pack-
age
,, following added to Scheme-numbei package
(put 'exp '(scheme-number scheme-number)
(lambda (x y) (tag (expt x y))))
, using piimitive expt
What happens if we call exp with two complex num-
beis as aiguments`
b. ls Louis coiiect that something had to be done about
coeicion with aiguments of the same type, oi does
apply-generic woik coiiectly as is`
c. Modify apply-generic so that it doesnt tiy coeicion
if the two aiguments have the same type.
Exercise 2.82: Show how to geneialize apply-generic to
handle coeicion in the geneial case of multiple aiguments.
One stiategy is to auempt to coeice all the aiguments to
the type of the ist aigument, then to the type of the sec-
ond aigument, and so on. Give an example of a situation
21
wheie this stiategy (and likewise the two-aigument vei-
sion given above) is not suciently geneial. (Hint Con-
sidei the case wheie theie aie some suitable mixed-type
opeiations piesent in the table that will not be tiied.)
Exercise 2.83: Suppose you aie designing a geneiic aiith-
metic system foi dealing with the towei of types shown in
liguie 2.2 integei, iational, ieal, complex. loi each type
(except complex), design a pioceduie that iaises objects of
that type one level in the towei. Show how to install a
geneiic raise opeiation that will woik foi each type (ex-
cept complex).
Exercise 2.84: Using the raise opeiation of Exeicise 2.83,
modify the apply-generic pioceduie so that it coeices its
aiguments to have the same type by the method of succes-
sive iaising, as discussed in this section. You will need to
devise a way to test which of two types is highei in the
towei. Do this in a mannei that is compatible with the
iest of the system and will not lead to pioblems in adding
new levels to the towei.
Exercise 2.85: Tis section mentioned a method foi sim-
plifying a data object by loweiing it in the towei of types
as fai as possible. Design a pioceduie drop that accom-
plishes this foi the towei desciibed in Exeicise 2.83. Te
key is to decide, in some geneial way, whethei an object
can be loweied. loi example, the complex numbei 1. 0i
can be loweied as fai as real, the complex numbei 1 0i
can be loweied as fai as integer, and the complex numbei
22
2 3i cannot be loweied at all. Heie is a plan foi deteimin-
ing whethei an object can be loweied Begin by dening
a geneiic opeiation project that pushes an object down
in the towei. loi example, piojecting a complex numbei
would involve thiowing away the imaginaiy pait. Ten a
numbei can be diopped if, when we project it and raise
the iesult back to the type we staited with, we end up with
something equal to what we staited with. Show how to im-
plement this idea in detail, by wiiting a drop pioceduie that
diops an object as fai as possible. You will need to design
the vaiious piojection opeiations
3
and install project as a
geneiic opeiation in the system. You will also need to make
use of a geneiic equality piedicate, such as desciibed in
Exeicise 2.9. linally, use drop to iewiite apply-generic
fiom Exeicise 2.84 so that it simplies its answeis.
Exercise 2.86: Suppose we want to handle complex num-
beis whose ieal paits, imaginaiy paits, magnitudes, and an-
gles can be eithei oidinaiy numbeis, iational numbeis, oi
othei numbeis we might wish to add to the system. De-
sciibe and implement the changes to the system needed to
accommodate this. You will have to dene opeiations such
as sine and cosine that aie geneiic ovei oidinaiy numbeis
and iational numbeis.
3
A ieal numbei can be piojected to an integei using the round piimitive, which
ietuins the closest integei to its aigument.
23
2.5.3 Example: Symbolic Algebra
Te manipulation of symbolic algebiaic expiessions is a complex pio-
cess that illustiates many of the haidest pioblems that occui in the de-
sign of laige-scale systems. An algebiaic expiession, in geneial, can
be viewed as a hieiaichical stiuctuie, a tiee of opeiatois applied to
opeiands. We can constiuct algebiaic expiessions by staiting with a
set of piimitive objects, such as constants and vaiiables, and combining
these by means of algebiaic opeiatois, such as addition and multipli-
cation. As in othei languages, we foim abstiactions that enable us to
iefei to compound objects in simple teims. Typical abstiactions in sym-
bolic algebia aie ideas such as lineai combination, polynomial, iational
function, oi tiigonometiic function. We can iegaid these as compound
types, which aie ofen useful foi diiecting the piocessing of expies-
sions. loi example, we could desciibe the expiession
x
2
sin(y
2
1) x cos 2y cos(y
3
2y
2
)
as a polynomial in x with coecients that aie tiigonometiic functions
of polynomials in y whose coecients aie integeis.
We will not auempt to develop a complete algebiaic-manipulation
systemheie. Such systems aie exceedingly complex piogiams, embody-
ing deep algebiaic knowledge and elegant algoiithms. What we will do
is look at a simple but impoitant pait of algebiaic manipulation the
aiithmetic of polynomials. We will illustiate the kinds of decisions the
designei of such a system faces, and how to apply the ideas of abstiact
data and geneiic opeiations to help oiganize this eoit.
Arithmetic on polynomials
Oui ist task in designing a system foi peifoiming aiithmetic on poly-
nomials is to decide just what a polynomial is. Polynomials aie noimally
24
dened ielative to ceitain vaiiables (the :nJeer:noes of the polyno-
mial). loi simplicity, we will iestiict ouiselves to polynomials having
just one indeteiminate (vn:+or:oe o|yno:o|s).
4
We will dene a poly-
nomial to be a sum of teims, each of which is eithei a coecient, a
powei of the indeteiminate, oi a pioduct of a coecient and a powei
of the indeteiminate. A coecient is dened as an algebiaic expiession
that is not dependent upon the indeteiminate of the polynomial. loi
example,
x
2
3x
is a simple polynomial in x, and
(y
2
1)x
3
(2y)x 1
is a polynomial in x whose coecients aie polynomials in y.
Alieady we aie skiiting some thoiny issues. ls the ist of these poly-
nomials the same as the polynomial y
2
3y , oi not` A ieasonable
answei might be yes, if we aie consideiing a polynomial puiely as a
mathematical function, but no, if we aie consideiing a polynomial to
be a syntactic foim. Te second polynomial is algebiaically equivalent
to a polynomial in y whose coecients aie polynomials in x. Should
oui system iecognize this, oi not` luitheimoie, theie aie othei ways to
iepiesent a polynomialfoi example, as a pioduct of factois, oi (foi a
univaiiate polynomial) as the set of ioots, oi as a listing of the values of
the polynomial at a specied set of points.
loi univaiiate polynomials, giving the value of a polynomial at a given set of points
can be a paiticulaily good iepiesentation. Tis makes polynomial aiithmetic extiemely
2
tions by deciding that in oui algebiaic-manipulation system a polyno-
mial will be a paiticulai syntactic foim, not its undeilying mathemat-
ical meaning.
Nowwe must considei howto go about doing aiithmetic on polyno-
mials. ln this simple system, we will considei only addition and multi-
plication. Moieovei, we will insist that two polynomials to be combined
must have the same indeteiminate.
We will appioach the design of oui systemby following the familiai
discipline of data abstiaction. We will iepiesent polynomials using a
data stiuctuie called a o|y, which consists of a vaiiable and a collection
of teims. We assume that we have selectois variable and term-list
that extiact those paits fiom a poly and a constiuctoi make-poly that
assembles a poly fioma given vaiiable and a teim list. A vaiiable will be
just a symbol, so we can use the same-variable? pioceduie of Section
2.3.2 to compaie vaiiables. Te following pioceduies dene addition and
multiplication of polys
(define (add-poly p1 p2)
(if (same-variable? (variable p1) (variable p2))
(make-poly (variable p1)
(add-terms (term-list p1) (term-list p2)))
(error "Polys not in same var: ADD-POLY" (list p1 p2))))
(define (mul-poly p1 p2)
(if (same-variable? (variable p1) (variable p2))
(make-poly (variable p1)
(mul-terms (term-list p1) (term-list p2)))
(error "Polys not in same var: MUL-POLY" (list p1 p2))))
simple. To obtain, foi example, the sum of two polynomials iepiesented in this way,
we need only add the values of the polynomials at coiiesponding points. To tiansfoim
back to a moie familiai iepiesentation, we can use the Lagiange inteipolation foimula,
which shows how to iecovei the coecients of a polynomial of degiee n given the
values of the polynomial at n 1 points.
2
To incoipoiate polynomials into oui geneiic aiithmetic system, we need
to supply them with type tags. Well use the tag polynomial, and install
appiopiiate opeiations on tagged polynomials in the opeiation table.
Well embed all oui code in an installation pioceduie foi the polynomial
package, similai to the ones in Section 2..1
(define (install-polynomial-package)
,, inteinal pioceduies
,, iepiesentation of poly
(define (make-poly variable term-list) (cons variable term-list))
(define (variable p) (car p))
(define (term-list p) (cdr p))
procedures same-variable? and variable? from section 2.3.2
,, iepiesentation of teims and teim lists
procedures adjoin-term . . . coeff from text below
(define (add-poly p1 p2) . . .)
procedures used by add-poly
(define (mul-poly p1 p2) . . .)
procedures used by mul-poly
,, inteiface to iest of the system
(define (tag p) (attach-tag 'polynomial p))
(put 'add '(polynomial polynomial)
(lambda (p1 p2) (tag (add-poly p1 p2))))
(put 'mul '(polynomial polynomial)
(lambda (p1 p2) (tag (mul-poly p1 p2))))
(put 'make 'polynomial
(lambda (var terms) (tag (make-poly var terms))))
'done)
Polynomial addition is peifoimed teimwise. Teims of the same oidei
(i.e., with the same powei of the indeteiminate) must be combined. Tis
is done by foiming a newteimof the same oidei whose coecient is the
sum of the coecients of the addends. Teims in one addend foi which
2
theie aie no teims of the same oidei in the othei addend aie simply
accumulated into the sum polynomial being constiucted.
ln oidei to manipulate teim lists, we will assume that we have a
constiuctoi the-empty-termlist that ietuins an empty teim list and
a constiuctoi adjoin-term that adjoins a new teim to a teim list. We
will also assume that we have a piedicate empty-termlist? that tells if a
given teimlist is empty, a selectoi first-term that extiacts the highest-
oidei teim fiom a teim list, and a selectoi rest-terms that ietuins all
but the highest-oidei teim. To manipulate teims, we will suppose that
we have a constiuctoi make-term that constiucts a teim with given oi-
dei and coecient, and selectois order and coeff that ietuin, iespec-
tively, the oidei and the coecient of the teim. Tese opeiations allow
us to considei both teims and teim lists as data abstiactions, whose
conciete iepiesentations we can woiiy about sepaiately.
Heie is the pioceduie that constiucts the teim list foi the sum of
two polynomials
Tis opeiation is veiy much like the oideied union-set opeiation we developed
in Exeicise 2.2. ln fact, if we think of the teims of the polynomial as a set oideied
accoiding to the powei of the indeteiminate, then the piogiam that pioduces the teim
list foi a sum is almost identical to union-set.
28
(adjoin-term
t2 (add-terms L1 (rest-terms L2))))
(else
(adjoin-term
(make-term (order t1)
(add (coeff t1) (coeff t2)))
(add-terms (rest-terms L1)
(rest-terms L2)))))))))
Te most impoitant point to note heie is that we used the geneiic ad-
dition pioceduie add to add togethei the coecients of the teims being
combined. Tis has poweiful consequences, as we will see below.
ln oidei to multiply two teim lists, we multiply each teim of the
ist list by all the teims of the othei list, iepeatedly using mul-term-
by-all-terms, which multiplies a given teim by all teims in a given
teim list. Te iesulting teim lists (one foi each teim of the ist list) aie
accumulated into a sum. Multiplying two teims foims a teim whose
oidei is the sum of the oideis of the factois and whose coecient is the
pioduct of the coecients of the factois
(define (mul-terms L1 L2)
(if (empty-termlist? L1)
(the-empty-termlist)
(add-terms (mul-term-by-all-terms (first-term L1) L2)
(mul-terms (rest-terms L1) L2))))
(define (mul-term-by-all-terms t1 L)
(if (empty-termlist? L)
(the-empty-termlist)
(let ((t2 (first-term L)))
(adjoin-term
(make-term (+ (order t1) (order t2))
(mul (coeff t1) (coeff t2)))
(mul-term-by-all-terms t1 (rest-terms L))))))
29
Tis is ieally all theie is to polynomial addition and multiplication. No-
tice that, since we opeiate on teims using the geneiic pioceduies add
and mul, oui polynomial package is automatically able to handle any
type of coecient that is known about by the geneiic aiithmetic pack-
age. lf we include a coeicion mechanism such as one of those discussed
in Section 2..2, then we also aie automatically able to handle opeiations
on polynomials of dieient coecient types, such as
[3x
2
(2 3i)x ]
_
x
4
2
3
x
2
( 3i)
_
.
Because we installed the polynomial addition and multiplication pioce-
duies add-poly and mul-poly in the geneiic aiithmetic system as the
add and mul opeiations foi type polynomial, oui system is also auto-
matically able to handle polynomial opeiations such as
_
(y 1)x
2
(y
2
1)x (y 1)
_
_
(y 2)x (y
3
)
_
.
Te ieason is that when the system tiies to combine coecients, it will
dispatch thiough add and mul. Since the coecients aie themselves
polynomials (in y), these will be combined using add-poly and mul-
poly. Te iesult is a kind of data-diiected iecuision in which, foi ex-
ample, a call to mul-poly will iesult in iecuisive calls to mul-poly in
oidei to multiply the coecients. lf the coecients of the coecients
weie themselves polynomials (as might be used to iepiesent polynomi-
als in thiee vaiiables), the data diiection would ensuie that the system
would followthiough anothei level of iecuisive calls, and so on thiough
as many levels as the stiuctuie of the data dictates.
To make this woik completely smoothly, we should also add to oui geneiic aiith-
metic system the ability to coeice a numbei to a polynomial by iegaiding it as a
280
Representing term lists
linally, we must confiont the job of implementing a good iepiesenta-
tion foi teim lists. A teim list is, in eect, a set of coecients keyed
by the oidei of the teim. Hence, any of the methods foi iepiesenting
sets, as discussed in Section 2.3.3, can be applied to this task. On the
othei hand, oui pioceduies add-terms and mul-terms always access
teim lists sequentially fiom highest to lowest oidei. Tus, we will use
some kind of oideied list iepiesentation.
How should we stiuctuie the list that iepiesents a teim list` One
consideiation is the density of the polynomials we intend to manip-
ulate. A polynomial is said to be Jense if it has nonzeio coecients in
teims of most oideis. lf it has many zeio teims it is said to be sorse. loi
example,
A x
2x
4
3x
2
2x
is a dense polynomial, wheieas
B x
100
2x
2
1
is spaise.
Te teim lists of dense polynomials aie most eciently iepiesented
as lists of the coecients. loi example, A above would be nicely iep-
iesented as (1 2 0 3 -2 -5). Te oidei of a teim in this iepiesenta-
tion is the length of the sublist beginning with that teims coecient,
polynomial of degiee zeio whose coecient is the numbei. Tis is necessaiy if we aie
going to peifoim opeiations such as
[x
2
(y 1)x ] [x
2
2x 1],
which iequiies adding the coecient y 1 to the coecient 2.
281
deciemented by 1.
8
Tis would be a teiiible iepiesentation foi a spaise
polynomial such as B Teie would be a giant list of zeios punctuated
by a few lonely nonzeio teims. A moie ieasonable iepiesentation of the
teim list of a spaise polynomial is as a list of the nonzeio teims, wheie
each teim is a list containing the oidei of the teim and the coecient
foi that oidei. ln such a scheme, polynomial B is eciently iepiesented
as ((100 1) (2 2) (0 1)). As most polynomial manipulations aie
peifoimed on spaise polynomials, we will use this method. We will as-
sume that teim lists aie iepiesented as lists of teims, aiianged fiom
highest-oidei to lowest-oidei teim. Once we have made this decision,
implementing the selectois and constiuctois foi teims and teim lists is
stiaightfoiwaid
9
(define (adjoin-term term term-list)
(if (=zero? (coeff term))
term-list
(cons term term-list)))
(define (the-empty-termlist) '())
(define (first-term term-list) (car term-list))
(define (rest-terms term-list) (cdr term-list))
(define (empty-termlist? term-list) (null? term-list))
(define (make-term order coeff) (list order coeff))
8
ln these polynomial examples, we assume that we have implemented the geneiic
aiithmetic system using the type mechanism suggested in Exeicise 2.8. Tus, coe-
cients that aie oidinaiy numbeis will be iepiesented as the numbeis themselves iathei
than as paiis whose car is the symbol scheme-number.
9
Although we aie assuming that teim lists aie oideied, we have implemented ad-
join-term to simply cons the new teim onto the existing teim list. We can get away
with this so long as we guaiantee that the pioceduies (such as add-terms) that use ad-
join-term always call it with a highei-oidei teim than appeais in the list. lf we did not
want to make such a guaiantee, we could have implemented adjoin-term to be simi-
lai to the adjoin-set constiuctoi foi the oideied-list iepiesentation of sets (Exeicise
2.1).
282
(define (order term) (car term))
(define (coeff term) (cadr term))
wheie =zero? is as dened in Exeicise 2.80. (See also Exeicise 2.8 be-
low.)
Useis of the polynomial package will cieate (tagged) polynomials
by means of the pioceduie
(define (make-polynomial var terms)
((get 'make 'polynomial) var terms))
Exercise 2.87: lnstall =zero? foi polynomials in the geneiic
aiithmetic package. Tis will allow adjoin-term to woik
foi polynomials with coecients that aie themselves poly-
nomials.
Exercise 2.88: Extend the polynomial system to include
subtiaction of polynomials. (Hint You may nd it helpful
to dene a geneiic negation opeiation.)
Exercise 2.89: Dene pioceduies that implement the teim-
list iepiesentation desciibed above as appiopiiate foi dense
polynomials.
Exercise 2.90: Suppose we want to have a polynomial sys-
tem that is ecient foi both spaise and dense polynomials.
One way to do this is to allow both kinds of teim-list iepie-
sentations in oui system. Te situation is analogous to the
complex-numbei example of Section 2.4, wheie we allowed
both iectangulai and polai iepiesentations. To do this we
must distinguish dieient types of teim lists and make the
opeiations on teim lists geneiic. Redesign the polynomial
283
system to implement this geneialization. Tis is a majoi ef-
foit, not a local change.
Exercise 2.91: A univaiiate polynomial can be divided by
anothei one to pioduce a polynomial quotient and a poly-
nomial iemaindei. loi example,
x
1
x
2
1
= x
3
x, iemaindei x 1.
Division can be peifoimed via long division. Tat is, divide
the highest-oidei teimof the dividend by the highest-oidei
teim of the divisoi. Te iesult is the ist teim of the quo-
tient. Next, multiply the iesult by the divisoi, subtiact that
fiomthe dividend, and pioduce the iest of the answei by ie-
cuisively dividing the dieience by the divisoi. Stop when
the oidei of the divisoi exceeds the oidei of the dividend
and declaie the dividend to be the iemaindei. Also, if the
dividend evei becomes zeio, ietuin zeio as both quotient
and iemaindei.
We can design a div-poly pioceduie on the model of add-
poly and mul-poly. Te pioceduie checks to see if the two
polys have the same vaiiable. lf so, div-poly stiips o the
vaiiable and passes the pioblem to div-terms, which pei-
foims the division opeiation on teim lists. Div-poly nally
ieauaches the vaiiable to the iesult supplied by div-terms.
lt is convenient to design div-terms to compute both the
quotient and the iemaindei of a division. Div-terms can
take two teim lists as aiguments and ietuin a list of the
quotient teim list and the iemaindei teim list.
284
Complete the following denition of div-terms by lling
in the missing expiessions. Use this to implement div-poly,
which takes two polys as aiguments and ietuins a list of the
quotient and iemaindei polys.
(define (div-terms L1 L2)
(if (empty-termlist? L1)
(list (the-empty-termlist) (the-empty-termlist))
(let ((t1 (first-term L1))
(t2 (first-term L2)))
(if (> (order t2) (order t1))
(list (the-empty-termlist) L1)
(let ((new-c (div (coeff t1) (coeff t2)))
(new-o (- (order t1) (order t2))))
(let ((rest-of-result
compute rest of result recursively ))
form complete result ))))))
Hierarchies of types in symbolic algebra
Oui polynomial system illustiates how objects of one type (polynomi-
als) may in fact be complex objects that have objects of many dieient
types as paits. Tis poses no ieal diculty in dening geneiic opeia-
tions. We need only install appiopiiate geneiic opeiations foi peifoim-
ing the necessaiy manipulations of the paits of the compound types.
ln fact, we saw that polynomials foim a kind of iecuisive data abstiac-
tion, in that paits of a polynomial may themselves be polynomials. Oui
geneiic opeiations and oui data-diiected piogiamming style can handle
this complication without much tiouble.
On the othei hand, polynomial algebia is a system foi which the
data types cannot be natuially aiianged in a towei. loi instance, it is
possible to have polynomials in x whose coecients aie polynomials
in y. lt is also possible to have polynomials in y whose coecients aie
28
polynomials in x. Neithei of these types is above the othei in any
natuial way, yet it is ofen necessaiy to add togethei elements fiom
each set. Teie aie seveial ways to do this. One possibility is to conveit
one polynomial to the type of the othei by expanding and ieaiiang-
ing teims so that both polynomials have the same piincipal vaiiable.
One can impose a toweilike stiuctuie on this by oideiing the vaiiables
and thus always conveiting any polynomial to a canonical foim with
the highest-piioiity vaiiable dominant and the lowei-piioiity vaiiables
buiied in the coecients. Tis stiategy woiks faiily well, except that
the conveision may expand a polynomial unnecessaiily, making it haid
to iead and peihaps less ecient to woik with. Te towei stiategy is
ceitainly not natuial foi this domain oi foi any domain wheie the usei
can invent newtypes dynamically using old types in vaiious combining
foims, such as tiigonometiic functions, powei seiies, and integials.
lt should not be suipiising that contiolling coeicion is a seiious
pioblem in the design of laige-scale algebiaic-manipulation systems.
Much of the complexity of such systems is conceined with ielationships
among diveise types. lndeed, it is faii to say that we do not yet com-
pletely undeistand coeicion. ln fact, we do not yet completely undei-
stand the concept of a data type. Neveitheless, what we know piovides
us with poweiful stiuctuiing and modulaiity piinciples to suppoit the
design of laige systems.
Exercise 2.92: By imposing an oideiing on vaiiables, ex-
tend the polynomial package so that addition and multipli-
cation of polynomials woiks foi polynomials in dieient
vaiiables. (Tis is not easy')
28
Extended exercise: Rational functions
We can extend oui geneiic aiithmetic system to include ro:ono| [vnc
:ons. Tese aie fiactions whose numeiatoi and denominatoi aie poly-
nomials, such as
x 1
x
3
1
.
Te system should be able to add, subtiact, multiply, and divide iational
functions, and to peifoim such computations as
x 1
x
3
1
x
x
2
1
=
x
3
2x
2
3x 1
x
4
x
3
x 1
.
(Heie the sum has been simplied by iemoving common factois. Oidi-
naiy cioss multiplication would have pioduced a fouith-degiee poly-
nomial ovei a fh-degiee polynomial.)
lf we modify oui iational-aiithmetic package so that it uses geneiic
opeiations, then it will do what we want, except foi the pioblem of
ieducing fiactions to lowest teims.
Exercise 2.93: Modify the iational-aiithmetic package to
use geneiic opeiations, but change make-rat so that it does
not auempt to ieduce fiactions to lowest teims. Test youi
system by calling make-rational on two polynomials to
pioduce a iational function
(define p1 (make-polynomial 'x '((2 1) (0 1))))
(define p2 (make-polynomial 'x '((3 1) (0 1))))
(define rf (make-rational p2 p1))
Now add rf to itself, using add. You will obseive that this
addition pioceduie does not ieduce fiactions to lowest teims.
28
We can ieduce polynomial fiactions to lowest teims using the same idea
we used with integeis modifying make-rat to divide both the numeia-
toi and the denominatoi by theii gieatest common divisoi. Te notion
of gieatest common divisoi makes sense foi polynomials. ln fact, we
can compute the ccu of two polynomials using essentially the same
Euclids Algoiithm that woiks foi integeis.
0
Te integei veision is
(define (gcd a b)
(if (= b 0)
a
(gcd b (remainder a b))))
Using this, we could make the obvious modication to dene a ccu
opeiation that woiks on teim lists
(define (gcd-terms a b)
(if (empty-termlist? b)
a
(gcd-terms b (remainder-terms a b))))
wheie remainder-terms picks out the iemaindei component of the list
ietuined by the teim-list division opeiation div-terms that was imple-
mented in Exeicise 2.91.
0
Te fact that Euclids Algoiithm woiks foi polynomials is foimalized in algebia
by saying that polynomials foim a kind of algebiaic domain called a Fvc|:Jeon r:ng. A
Euclidean iing is a domain that admits addition, subtiaction, and commutative mul-
tiplication, togethei with a way of assigning to each element x of the iing a positive
integei measuie m(x) with the piopeities that m(xy) m(x) foi any nonzeio x and
y and that, given any x and y, theie exists a q such that y = qx r and eithei r = 0
oi m(r ) < m(x). liom an abstiact point of view, this is what is needed to piove that
Euclids Algoiithm woiks. loi the domain of integeis, the measuie m of an integei is
the absolute value of the integei itself. loi the domain of polynomials, the measuie of
a polynomial is its degiee.
288
Exercise 2.94: Using div-terms, implement the pioceduie
remainder-terms and use this to dene gcd-terms as above.
Now wiite a pioceduie gcd-poly that computes the poly-
nomial ccu of two polys. (Te pioceduie should signal an
eiioi if the two polys aie not in the same vaiiable.) lnstall in
the system a geneiic opeiation greatest-common-divisor
that ieduces to gcd-poly foi polynomials and to oidinaiy
gcd foi oidinaiy numbeis. As a test, tiy
(define p1 (make-polynomial
'x '((4 1) (3 -1) (2 -2) (1 2))))
(define p2 (make-polynomial 'x '((3 1) (1 -1))))
(greatest-common-divisor p1 p2)
and check youi iesult by hand.
Exercise 2.95: Dene P
1
, P
2
, and P
3
to be the polynomials
P
1
x
2
2x 1,
P
2
11x
2
,
P
3
13x .
Now dene Q
1
to be the pioduct of P
1
and P
2
and Q
2
to
be the pioduct of P
1
and P
3
, and use greatest-common-
divisor (Exeicise 2.94) to compute the ccu of Q
1
and Q
2
.
Note that the answei is not the same as P
1
. Tis example in-
tioduces nonintegei opeiations into the computation, caus-
ing diculties with the ccu algoiithm.
1
To undeistand
1
ln an implementation like xi1 Scheme, this pioduces a polynomial that is indeed
a divisoi of Q
1
and Q
2
, but with iational coecients. ln many othei Scheme systems,
in which division of integeis can pioduce limited-piecision decimal numbeis, we may
fail to get a valid divisoi.
289
what is happening, tiy tiacing gcd-terms while comput-
ing the ccu oi tiy peifoiming the division by hand.
We can solve the pioblemexhibited in Exeicise 2.9 if we use the follow-
ing modication of the ccu algoiithm (which ieally woiks only in the
case of polynomials with integei coecients). Befoie peifoiming any
polynomial division in the ccu computation, we multiply the dividend
by an integei constant factoi, chosen to guaiantee that no fiactions will
aiise duiing the division piocess. Oui answei will thus diei fiom the
actual ccu by an integei constant factoi, but this does not mauei in the
case of ieducing iational functions to lowest teims, the ccu will be used
to divide both the numeiatoi and denominatoi, so the integei constant
factoi will cancel out.
Moie piecisely, if P and Q aie polynomials, let O
1
be the oidei of P
(i.e., the oidei of the laigest teim of P) and let O
2
be the oidei of Q. Let c
be the leading coecient of Q. Ten it can be shown that, if we multiply
P by the :neger:::ng [ocor c
1O
1
O
2
, the iesulting polynomial can be
divided byQ by using the div-terms algoiithmwithout intioducing any
fiactions. Te opeiation of multiplying the dividend by this constant
and then dividing is sometimes called the sevJoJ:+:s:on of P by Q. Te
iemaindei of the division is called the sevJoreo:nJer.
Exercise 2.96:
a. lmplement the pioceduie pseudoremainder-terms, which
is just like remainder-terms except that it multiplies
the dividend by the integeiizing factoi desciibed above
befoie calling div-terms. Modify gcd-terms to use
pseudoremainder-terms, and veiify that greatest-
common-divisor now pioduces an answei with inte-
gei coecients on the example in Exeicise 2.9.
290
b. Te ccu now has integei coecients, but they aie
laigei than those of P
1
. Modify gcd-terms so that it
iemoves common factois fiom the coecients of the
answei by dividing all the coecients by theii (inte-
gei) gieatest common divisoi.
Tus, heie is how to ieduce a iational function to lowest teims
Compute the ccu of the numeiatoi and denominatoi, using the
veision of gcd-terms fiom Exeicise 2.9.
When you obtain the ccu, multiply both numeiatoi and denomi-
natoi by the same integeiizing factoi befoie dividing thiough by
the ccu, so that division by the ccu will not intioduce any nonin-
tegei coecients. As the factoi you can use the leading coecient
of the ccu iaised to the powei 1 O
1
O
2
, wheie O
2
is the oidei
of the ccu and O
1
is the maximum of the oideis of the numeiatoi
and denominatoi. Tis will ensuie that dividing the numeiatoi
and denominatoi by the ccu will not intioduce any fiactions.
Te iesult of this opeiation will be a numeiatoi and denominatoi
with integei coecients. Te coecients will noimally be veiy
laige because of all of the integeiizing factois, so the last step is to
iemove the iedundant factois by computing the (integei) gieatest
common divisoi of all the coecients of the numeiatoi and the
denominatoi and dividing thiough by this factoi.
Exercise 2.97:
a. lmplement this algoiithmas a pioceduie reduce-terms
that takes two teim lists n and d as aiguments and ie-
291
tuins a list nn, dd, which aie n and d ieduced to low-
est teims via the algoiithm given above. Also wiite a
pioceduie reduce-poly, analogous to add-poly, that
checks to see if the two polys have the same vaiiable.
lf so, reduce-poly stiips o the vaiiable and passes
the pioblemto reduce-terms, then ieauaches the vaii-
able to the two teim lists supplied by reduce-terms.
b. Dene a pioceduie analogous to reduce-terms that
does what the oiiginal make-rat did foi integeis
(define (reduce-integers n d)
(let ((g (gcd n d)))
(list (/ n g) (/ d g))))
and dene reduce as a geneiic opeiation that calls
apply-generic to dispatch to eithei reduce-poly (foi
polynomial aiguments) oi reduce-integers (foi scheme-
number aiguments). You can noweasily make the iational-
aiithmetic package ieduce fiactions to lowest teims
by having make-rat call reduce befoie combining the
given numeiatoi and denominatoi to foim a iatio-
nal numbei. Te system now handles iational expies-
sions in eithei integeis oi polynomials. To test youi
piogiam, tiy the example at the beginning of this ex-
tended exeicise
(define p1 (make-polynomial 'x '((1 1) (0 1))))
(define p2 (make-polynomial 'x '((3 1) (0 -1))))
(define p3 (make-polynomial 'x '((1 1))))
(define p4 (make-polynomial 'x '((2 1) (0 -1))))
(define rf1 (make-rational p1 p2))
(define rf2 (make-rational p3 p4))
292
(add rf1 rf2)
See if you get the coiiect answei, coiiectly ieduced to
lowest teims.
Te ccu computation is at the heait of any system that does opeia-
tions on iational functions. Te algoiithm used above, although mathe-
matically stiaightfoiwaid, is extiemely slow. Te slowness is due paitly
to the laige numbei of division opeiations and paitly to the enoimous
size of the inteimediate coecients geneiated by the pseudodivisions.
One of the active aieas in the development of algebiaic-manipulation
systems is the design of beuei algoiithms foi computing polynomial
ccus.
2
2
One extiemely ecient and elegant method foi computing polynomial ccus was
discoveied by Richaid Zippel (199). Te method is a piobabilistic algoiithm, as is the
fast test foi piimality that we discussed in Chaptei 1. Zippels book (Zippel 1993) de-
sciibes this method, togethei with othei ways to compute polynomial ccus.
293
ln contiast with new-withdraw above, we do not have to use let to make balance
a local vaiiable, since foimal paiameteis aie alieady local. Tis will be cleaiei afei the
discussion of the enviionment model of evaluation in Section 3.2. (See also Exeicise
3.10.)
301
(W2 40)
"Insufficient funds"
(W1 40)
10
Obseive that W1 and W2 aie completely independent objects, each with
its own local state vaiiable balance. Withdiawals fiomone do not aect
the othei.
We can also cieate objects that handle deposits as well as with-
diawals, and thus we can iepiesent simple bank accounts. Heie is a
pioceduie that ietuins a bank-account object with a specied initial
balance
(define (make-account balance)
(define (withdraw amount)
(if (>= balance amount)
(begin (set! balance (- balance amount))
balance)
"Insufficient funds"))
(define (deposit amount)
(set! balance (+ balance amount))
balance)
(define (dispatch m)
(cond ((eq? m 'withdraw) withdraw)
((eq? m 'deposit) deposit)
(else (error "Unknown request: MAKE-ACCOUNT"
m))))
dispatch)
Each call to make-account sets up an enviionment with a local state
vaiiable balance. Within this enviionment, make-account denes pio-
ceduies deposit and withdraw that access balance and an additional
pioceduie dispatch that takes a message as input and ietuins one of
302
the two local pioceduies. Te dispatch pioceduie itself is ietuined as
the value that iepiesents the bank-account object. Tis is piecisely the
essogeoss:ng style of piogiamming that we saw in Section 2.4.3, al-
though heie we aie using it in conjunction with the ability to modify
local vaiiables.
Make-account can be used as follows
(define acc (make-account 100))
((acc 'withdraw) 50)
50
((acc 'withdraw) 60)
"Insufficient funds"
((acc 'deposit) 40)
90
((acc 'withdraw) 60)
30
Each call to acc ietuins the locally dened deposit oi withdraw pio-
ceduie, which is then applied to the specied amount. As was the case
with make-withdraw, anothei call to make-account
(define acc2 (make-account 100))
will pioduce a completely sepaiate account object, which maintains its
own local balance.
Exercise 3.1: An occvv|oor is a pioceduie that is called
iepeatedly with a single numeiic aigument and accumu-
lates its aiguments into a sum. Each time it is called, it
ietuins the cuiiently accumulated sum. Wiite a pioceduie
make-accumulator that geneiates accumulatois, each main-
taining an independent sum. Te input to make-accumulator
should specify the initial value of the sum, foi example
303
(define A (make-accumulator 5))
(A 10)
15
(A 10)
25
Exercise 3.2: ln sofwaie-testing applications, it is useful
to be able to count the numbei of times a given pioceduie
is called duiing the couise of a computation. Wiite a pio-
ceduie make-monitored that takes as input a pioceduie, f,
that itself takes one input. Te iesult ietuined by make-
monitored is a thiid pioceduie, say mf, that keeps tiack
of the numbei of times it has been called by maintaining
an inteinal countei. lf the input to mf is the special symbol
how-many-calls?, then mf ietuins the value of the countei.
lf the input is the special symbol reset-count, then mf ie-
sets the countei to zeio. loi any othei input, mf ietuins the
iesult of calling f on that input and inciements the countei.
loi instance, we could make a monitoied veision of the
sqrt pioceduie
(define s (make-monitored sqrt))
(s 100)
10
(s 'how-many-calls?)
1
Exercise 3.3: Modify the make-account pioceduie so that
it cieates passwoid-piotected accounts. Tat is, make-account
should take a symbol as an additional aigument, as in
(define acc (make-account 100 'secret-password))
304
Te iesulting account object should piocess a iequest only
if it is accompanied by the passwoid with which the ac-
count was cieated, and should otheiwise ietuin a complaint
((acc 'secret-password 'withdraw) 40)
60
((acc 'some-other-password 'deposit) 50)
"Incorrect password"
Exercise 3.4: Modify the make-account pioceduie of Ex-
eicise 3.3 by adding anothei local state vaiiable so that, if
an account is accessed moie than seven consecutive times
with an incoiiect passwoid, it invokes the pioceduie call-
the-cops.
3.1.2 The Benefits of Introducing Assignment
As we shall see, intioducing assignment into oui piogiamming lan-
guage leads us into a thicket of dicult conceptual issues. Neveitheless,
viewing systems as collections of objects with local state is a poweiful
technique foi maintaining a modulai design. As a simple example, con-
sidei the design of a pioceduie rand that, whenevei it is called, ietuins
an integei chosen at iandom.
lt is not at all cleai what is meant by chosen at iandom. What we
piesumably want is foi successive calls to rand to pioduce a sequence of
numbeis that has statistical piopeities of unifoim distiibution. We will
not discuss methods foi geneiating suitable sequences heie. Rathei, let
us assume that we have a pioceduie rand-update that has the piopeity
that if we stait with a given numbei x
1
and foim
x
2
= (rand-update x
1
)
x
3
= (rand-update x
2
)
30
then the sequence of values x
1
, x
2
, x
3
, . . . will have the desiied statistical
piopeities.
One common way to implement rand-update is to use the iule that x is updated
to ax b modulo m, wheie a, b, and m aie appiopiiately chosen integeis. Chaptei 3
of Knuth 1981 includes an extensive discussion of techniques foi geneiating sequences
of iandom numbeis and establishing theii statistical piopeities. Notice that the rand-
update pioceduie computes a mathematical function Given the same input twice, it
pioduces the same output. Teiefoie, the numbei sequence pioduced by rand-update
ceitainly is not iandom, if by iandom we insist that each numbei in the sequence
is unielated to the pieceding numbei. Te ielation between ieal iandomness and so-
called sevJoronJosequences, which aie pioduced by well-deteimined computations
and yet have suitable statistical piopeities, is a complex question involving dicult
issues in mathematics and philosophy. Kolmogoiov, Solomono, and Chaitin have made
gieat piogiess in claiifying these issues, a discussion can be found in Chaitin 19.
30
the piobabilities estimated fiom tabulating the iesults of those expeii-
ments. loi example, we can appioximate using the fact that /
2
is
the piobability that two integeis chosen at iandom will have no fac-
tois in common, that is, that theii gieatest common divisoi will be 1.
Tis theoiem is due to E. Cesio. See section 4..2 of Knuth 1981 foi a discussion
and a pioof.
30
(iter (- trials-remaining 1)
trials-passed))))
(iter trials 0))
Nowlet us tiy the same computation using rand-update diiectly iathei
than rand, the way we would be foiced to pioceed if we did not use
assignment to model local state
(define (estimate-pi trials)
(sqrt (/ 6 (random-gcd-test trials random-init))))
(define (random-gcd-test trials initial-x)
(define (iter trials-remaining trials-passed x)
(let ((x1 (rand-update x)))
(let ((x2 (rand-update x1)))
(cond ((= trials-remaining 0)
(/ trials-passed trials))
((= (gcd x1 x2) 1)
(iter (- trials-remaining 1)
(+ trials-passed 1)
x2))
(else
(iter (- trials-remaining 1)
trials-passed
x2))))))
(iter trials 0 initial-x))
While the piogiam is still simple, it betiays some painful bieaches of
modulaiity. ln oui ist veision of the piogiam, using rand, we can ex-
piess the Monte Cailo method diiectly as a geneial monte-carlo pioce-
duie that takes as an aigument an aibitiaiy experiment pioceduie. ln
oui second veision of the piogiam, with no local state foi the iandom-
numbei geneiatoi, random-gcd-test must explicitly manipulate the ian-
dom numbeis x1 and x2 and iecycle x2 thiough the iteiative loop as
the new input to rand-update. Tis explicit handling of the iandom
308
numbeis inteitwines the stiuctuie of accumulating test iesults with the
fact that oui paiticulai expeiiment uses two iandom numbeis, wheieas
othei Monte Cailo expeiiments might use one iandomnumbei oi thiee.
Even the top-level pioceduie estimate-pi has to be conceined with
supplying an initial iandom numbei. Te fact that the iandom-numbei
geneiatois insides aie leaking out into othei paits of the piogiammakes
it dicult foi us to isolate the Monte Cailo idea so that it can be applied
to othei tasks. ln the ist veision of the piogiam, assignment encapsu-
lates the state of the iandom-numbei geneiatoi within the rand pioce-
duie, so that the details of iandom-numbei geneiation iemain indepen-
dent of the iest of the piogiam.
Te geneial phenomenon illustiated by the Monte Cailo example is
this liom the point of view of one pait of a complex piocess, the othei
paits appeai to change with time. Tey have hidden time-vaiying local
state. lf we wish to wiite computei piogiams whose stiuctuie ieects
this decomposition, we make computational objects (such as bank ac-
counts and iandom-numbei geneiatois) whose behavioi changes with
time. We model state with local state vaiiables, and we model the changes
of state with assignments to those vaiiables.
lt is tempting to conclude this discussion by saying that, by intio-
ducing assignment and the technique of hiding state in local vaiiables,
we aie able to stiuctuie systems in a moie modulai fashion than if all
state had to be manipulated explicitly, by passing additional paiameteis.
Unfoitunately, as we shall see, the stoiy is not so simple.
Exercise 3.5: Mone Cor|o :negro:on is a method of esti-
mating denite integials by means of Monte Cailo simula-
tion. Considei computing the aiea of a iegion of space de-
sciibed by a piedicate P(x, y) that is tiue foi points (x, y)
in the iegion and false foi points not in the iegion. loi
309
example, the iegion contained within a ciicle of iadius 3
centeied at (, ) is desciibed by the piedicate that tests
whethei (x )
2
(y )
2
3
2
. To estimate the aiea of the
iegion desciibed by such a piedicate, begin by choosing a
iectangle that contains the iegion. loi example, a iectangle
with diagonally opposite coineis at (2, 4) and (8, 10) con-
tains the ciicle above. Te desiied integial is the aiea of
that poition of the iectangle that lies in the iegion. We can
estimate the integial by picking, at iandom, points (x, y)
that lie in the iectangle, and testing P(x, y) foi each point
to deteimine whethei the point lies in the iegion. lf we tiy
this with many points, then the fiaction of points that fall
in the iegion should give an estimate of the piopoition of
the iectangle that lies in the iegion. Hence, multiplying this
fiaction by the aiea of the entiie iectangle should pioduce
an estimate of the integial.
lmplement Monte Cailo integiation as a pioceduie estimate-
integral that takes as aiguments a piedicate P, uppei and
lowei bounds x1, x2, y1, and y2 foi the iectangle, and the
numbei of tiials to peifoim in oidei to pioduce the esti-
mate. Youi pioceduie should use the same monte-carlo
pioceduie that was used above to estimate . Use youi estimate-
integral to pioduce an estimate of by measuiing the
aiea of a unit ciicle.
You will nd it useful to have a pioceduie that ietuins a
numbei chosen at iandom fiom a given iange. Te follow-
ing random-in-range pioceduie implements this in teims
of the random pioceduie used in Section 1.2., which ie-
310
tuins a nonnegative numbei less than its input.
8
(define (random-in-range low high)
(let ((range (- high low)))
(+ low (random range))))
Exercise 3.6: lt is useful to be able to ieset a iandom-numbei
geneiatoi to pioduce a sequence staiting fioma given value.
Design a new rand pioceduie that is called with an ai-
gument that is eithei the symbol generate oi the symbol
reset and behaves as follows (rand 'generate) pioduces
a new iandom numbei, ((rand 'reset) ne++o|ve ) ie-
sets the inteinal state vaiiable to the designated ne++o|ve.
Tus, by ieseuing the state, one can geneiate iepeatable se-
quences. Tese aie veiy handy to have when testing and
debugging piogiams that use iandom numbeis.
3.1.3 The Costs of Introducing Assignment
As we have seen, the set! opeiation enables us to model objects that
have local state. Howevei, this advantage comes at a piice. Oui pio-
giamming language can no longei be inteipieted in teims of the sub-
stitution model of pioceduie application that we intioduced in Section
1.1.. Moieovei, no simple model with nice mathematical piopeities
can be an adequate fiamewoik foi dealing with objects and assignment
in piogiamming languages.
So long as we do not use assignments, two evaluations of the same
pioceduie with the same aiguments will pioduce the same iesult, so
8
xi1 Scheme piovides such a pioceduie. lf random is given an exact integei (as in
Section 1.2.) it ietuins an exact integei, but if it is given a decimal value (as in this
exeicise) it ietuins a decimal value.
311
that pioceduies can be viewed as computing mathematical functions.
Piogiamming without any use of assignments, as we did thioughout
the ist two chapteis of this book, is accoidingly known as [vnc:ono|
rogro:ng.
To undeistand howassignment complicates maueis, considei a sim-
plied veision of the make-withdraw pioceduie of Section 3.1.1 that
does not bothei to check foi an insucient amount
(define (make-simplified-withdraw balance)
(lambda (amount)
(set! balance (- balance amount))
balance))
(define W (make-simplified-withdraw 25))
(W 20)
5
(W 10)
-5
Compaie this pioceduie with the following make-decrementer pioce-
duie, which does not use set!
(define (make-decrementer balance)
(lambda (amount)
(- balance amount)))
Make-decrementer ietuins a pioceduie that subtiacts its input fiom a
designated amount balance, but theie is no accumulated eect ovei
successive calls, as with make-simplified-withdraw
(define D (make-decrementer 25))
(D 20)
5
(D 10)
15
312
We can use the substitution model to explain how make-decrementer
woiks. loi instance, let us analyze the evaluation of the expiession
((make-decrementer 25) 20)
We ist simplify the opeiatoi of the combination by substituting 2 foi
balance in the body of make-decrementer. Tis ieduces the expiession
to
((lambda (amount) (- 25 amount)) 20)
Now we apply the opeiatoi by substituting 20 foi amount in the body
of the lambda expiession
(- 25 20)
Te nal answei is .
Obseive, howevei, what happens if we auempt a similai substitution
analysis with make-simplified-withdraw
((make-simplified-withdraw 25) 20)
We ist simplify the opeiatoi by substituting 2 foi balance in the body
of make-simplified-withdraw. Tis ieduces the expiession to
9
((lambda (amount) (set! balance (- 25 amount)) 25) 20)
Now we apply the opeiatoi by substituting 20 foi amount in the body
of the lambda expiession
(set! balance (- 25 20)) 25
lf we adheied to the substitution model, we would have to say that the
meaning of the pioceduie application is to ist set balance to and then
9
We dont substitute foi the occuiience of balance in the set! expiession because
the noe in a set! is not evaluated. lf we did substitute foi it, we would get (set!
25 (- 25 amount)), which makes no sense.
313
ietuin 2 as the value of the expiession. Tis gets the wiong answei. ln
oidei to get the coiiect answei, we would have to somehow distinguish
the ist occuiience of balance (befoie the eect of the set!) fiom the
second occuiience of balance (afei the eect of the set!), and the
substitution model cannot do this.
Te tiouble heie is that substitution is based ultimately on the no-
tion that the symbols in oui language aie essentially names foi values.
But as soon as we intioduce set! and the idea that the value of a vaii-
able can change, a vaiiable can no longei be simply a name. Now a
vaiiable somehow iefeis to a place wheie a value can be stoied, and the
value stoied at this place can change. ln Section 3.2 we will see how
enviionments play this iole of place in oui computational model.
Sameness and change
Te issue suifacing heie is moie piofound than the meie bieakdown of
a paiticulai model of computation. As soon as we intioduce change into
oui computational models, many notions that weie pieviously stiaight-
foiwaid become pioblematical. Considei the concept of two things be-
ing the same.
Suppose we call make-decrementer twice with the same aigument
to cieate two pioceduies
(define D1 (make-decrementer 25))
(define D2 (make-decrementer 25))
Aie D1 and D2 the same` An acceptable answei is yes, because D1 and D2
have the same computational behavioieach is a pioceduie that sub-
tiacts its input fiom 2. ln fact, D1 could be substituted foi D2 in any
computation without changing the iesult.
Contiast this with making two calls to make-simplified-withdraw
314
(define W1 (make-simplified-withdraw 25))
(define W2 (make-simplified-withdraw 25))
Aie W1 and W2 the same` Suiely not, because calls to W1 and W2 have
distinct eects, as shown by the following sequence of inteiactions
(W1 20)
5
(W1 20)
-15
(W2 20)
5
Even though W1 and W2 aie equal in the sense that they aie both cie-
ated by evaluating the same expiession, (make-simplified-withdraw
25), it is not tiue that W1 could be substituted foi W2 in any expiession
without changing the iesult of evaluating the expiession.
Alanguage that suppoits the concept that equals can be substituted
foi equals in an expiession without changing the value of the expies-
sion is said to be re[eren:o||y ronsoren. Refeiential tianspaiency is
violated when we include set! in oui computei language. Tis makes
it tiicky to deteimine when we can simplify expiessions by substituting
equivalent expiessions. Consequently, ieasoning about piogiams that
use assignment becomes diastically moie dicult.
Once we foigo iefeiential tianspaiency, the notion of what it means
foi computational objects to be the same becomes dicult to captuie
in a foimal way. lndeed, the meaning of same in the ieal woild that oui
piogiams model is haidly cleai in itself. ln geneial, we can deteimine
that two appaiently identical objects aie indeed the same one only by
modifying one object and then obseiving whethei the othei object has
changed in the same way. But howcan we tell if an object has changed
othei than by obseiving the same object twice and seeing whethei
31
some piopeity of the object dieis fiom one obseivation to the next`
Tus, we cannot deteimine change without some o r:or: notion of
sameness, and we cannot deteimine sameness without obseiving the
eects of change.
As an example of how this issue aiises in piogiamming, considei
the situation wheie Petei and Paul have a bank account with S100 in it.
Teie is a substantial dieience between modeling this as
(define peter-acc (make-account 100))
(define paul-acc (make-account 100))
and modeling it as
(define peter-acc (make-account 100))
(define paul-acc peter-acc)
ln the ist situation, the two bank accounts aie distinct. Tiansactions
made by Petei will not aect Pauls account, and vice veisa. ln the sec-
ond situation, howevei, we have dened paul-acc to be |e soe |:ng
as peter-acc. ln eect, Petei and Paul now have a joint bank account,
and if Petei makes a withdiawal fiom peter-acc Paul will obseive less
money in paul-acc. Tese two similai but distinct situations can cause
confusion in building computational models. With the shaied account,
in paiticulai, it can be especially confusing that theie is one object (the
bank account) that has two dieient names (peter-acc and paul-acc),
if we aie seaiching foi all the places in oui piogiam wheie paul-acc
can be changed, we must iemembei to look also at things that change
peter-acc.
10
10
Te phenomenon of a single computational object being accessed by moie than one
name is known as o|:os:ng. Te joint bank account situation illustiates a veiy simple
example of an alias. ln Section 3.3 we will see much moie complex examples, such as
distinct compound data stiuctuies that shaie paits. Bugs can occui in oui piogiams
31
With iefeience to the above iemaiks on sameness and change,
obseive that if Petei and Paul could only examine theii bank balances,
and could not peifoim opeiations that changed the balance, then the is-
sue of whethei the two accounts aie distinct would be moot. ln geneial,
so long as we nevei modify data objects, we can iegaid a compound
data object to be piecisely the totality of its pieces. loi example, a iatio-
nal numbei is deteimined by giving its numeiatoi and its denominatoi.
But this view is no longei valid in the piesence of change, wheie a com-
pound data object has an identity that is something dieient fiom the
pieces of which it is composed. A bank account is still the same bank
account even if we change the balance by making a withdiawal, con-
veisely, we could have two dieient bank accounts with the same state
infoimation. Tis complication is a consequence, not of oui piogiam-
ming language, but of oui peiception of a bank account as an object. We
do not, foi example, oidinaiily iegaid a iational numbei as a change-
able object with identity, such that we could change the numeiatoi and
still have the same iational numbei.
Pitfalls of imperative programming
ln contiast to functional piogiamming, piogiamming that makes ex-
tensive use of assignment is known as :ero:+e rogro:ng. ln ad-
dition to iaising complications about computational models, piogiams
wiiuen in impeiative style aie susceptible to bugs that cannot occui in
functional piogiams. loi example, iecall the iteiative factoiial piogiam
if we foiget that a change to an object may also, as a side eect, change a dieient
object because the two dieient objects aie actually a single object appeaiing undei
dieient aliases. Tese so-called s:Jeeec |vgs aie so dicult to locate and to analyze
that some people have pioposed that piogiamming languages be designed in such a
way as to not allow side eects oi aliasing (Lampson et al. 1981, Moiiis et al. 1980).
31
fiom Section 1.2.1
(define (factorial n)
(define (iter product counter)
(if (> counter n)
product
(iter (* counter product) (+ counter 1))))
(iter 1 1))
lnstead of passing aiguments in the inteinal iteiative loop, we could
adopt a moie impeiative style by using explicit assignment to update
the values of the vaiiables product and counter
(define (factorial n)
(let ((product 1)
(counter 1))
(define (iter)
(if (> counter n)
product
(begin (set! product (* counter product))
(set! counter (+ counter 1))
(iter))))
(iter)))
Tis does not change the iesults pioduced by the piogiam, but it does
intioduce a subtle tiap. Howdo we decide the oidei of the assignments`
As it happens, the piogiam is coiiect as wiiuen. But wiiting the assign-
ments in the opposite oidei
(set! counter (+ counter 1))
(set! product (* counter product))
would have pioduced a dieient, incoiiect iesult. ln geneial, piogiam-
ming with assignment foices us to caiefully considei the ielative oideis
of the assignments to make suie that each statement is using the coiiect
318
veision of the vaiiables that have been changed. Tis issue simply does
not aiise in functional piogiams.
11
Te complexity of impeiative piogiams becomes even woise if we
considei applications in which seveial piocesses execute concuiiently.
We will ietuin to this in Section 3.4. liist, howevei, we will addiess the
issue of pioviding a computational model foi expiessions that involve
assignment, and exploie the uses of objects with local state in designing
simulations.
Exercise 3.7: Considei the bank account objects cieated by
make-account, with the passwoid modication desciibed
in Exeicise 3.3. Suppose that oui banking system iequiies
the ability to make joint accounts. Dene a pioceduie make-
joint that accomplishes this. Make-joint should take thiee
aiguments. Te ist is a passwoid-piotected account. Te
second aigument must match the passwoid with which the
account was dened in oidei foi the make-joint opeiation
to pioceed. Te thiid aigument is a new passwoid. Make-
joint is to cieate an additional access to the oiiginal ac-
count using the new passwoid. loi example, if peter-acc
is a bank account with passwoid open-sesame, then
(define paul-acc
(make-joint peter-acc 'open-sesame 'rosebud))
11
ln view of this, it is iionic that intioductoiy piogiamming is most ofen taught
in a highly impeiative style. Tis may be a vestige of a belief, common thioughout
the 190s and 190s, that piogiams that call pioceduies must inheiently be less e-
cient than piogiams that peifoim assignments. (Steele 19 debunks this aigument.)
Alteinatively it may ieect a view that step-by-step assignment is easiei foi beginneis
to visualize than pioceduie call. Whatevei the ieason, it ofen saddles beginning pio-
giammeis with should l set this vaiiable befoie oi afei that one conceins that can
complicate piogiamming and obscuie the impoitant ideas.
319
will allowone to make tiansactions on peter-acc using the
name paul-acc and the passwoid rosebud. You may wish
to modify youi solution to Exeicise 3.3 to accommodate this
new featuie.
Exercise 3.8: When we dened the evaluation model in
Section 1.1.3, we said that the ist step in evaluating an
expiession is to evaluate its subexpiessions. But we nevei
specied the oidei in which the subexpiessions should be
evaluated (e.g., lef to iight oi iight to lef). When we in-
tioduce assignment, the oidei in which the aiguments to a
pioceduie aie evaluated can make a dieience to the iesult.
Dene a simple pioceduie f such that evaluating
(+ (f 0) (f 1))
will ietuin 0 if the aiguments to + aie evaluated fiom lef to
iight but will ietuin 1 if the aiguments aie evaluated fiom
iight to lef.
3.2 The Environment Model of Evaluation
When we intioduced compound pioceduies in Chaptei 1, we used the
substitution model of evaluation (Section 1.1.) to dene what is meant
by applying a pioceduie to aiguments
To apply a compound pioceduie to aiguments, evaluate the body
of the pioceduie with each foimal paiametei ieplaced by the coi-
iesponding aigument.
Once we admit assignment into oui piogiamming language, such a def-
inition is no longei adequate. ln paiticulai, Section 3.1.3 aigued that, in
320
the piesence of assignment, a vaiiable can no longei be consideied to be
meiely a name foi a value. Rathei, a vaiiable must somehow designate
a place in which values can be stoied. ln oui new model of evaluation,
these places will be maintained in stiuctuies called en+:ronens.
An enviionment is a sequence of [roes. Each fiame is a table (pos-
sibly empty) of |:nJ:ngs, which associate vaiiable names with theii coi-
iesponding values. (A single fiame may contain at most one binding
foi any vaiiable.) Each fiame also has a pointei to its enc|os:ng en+:ron
en, unless, foi the puiposes of discussion, the fiame is consideied to
be g|o|o|. Te +o|ve o[ o +or:o||e with iespect to an enviionment is the
value given by the binding of the vaiiable in the ist fiame in the en-
viionment that contains a binding foi that vaiiable. lf no fiame in the
sequence species a binding foi the vaiiable, then the vaiiable is said to
be vn|ovnJ in the enviionment.
liguie 3.1 shows a simple enviionment stiuctuie consisting of thiee
fiames, labeled l, ll, and lll. ln the diagiam, A, B, C, and Daie pointeis to
enviionments. C and D point to the same enviionment. Te vaiiables z
and x aie bound in fiame ll, while y and x aie bound in fiame l. Te value
of x in enviionment D is 3. Te value of x with iespect to enviionment
B is also 3. Tis is deteimined as follows We examine the ist fiame in
the sequence (fiame lll) and do not nd a binding foi x, so we pioceed
to the enclosing enviionment D and nd the binding in fiame l. On the
othei hand, the value of x in enviionment A is , because the ist fiame
in the sequence (fiame ll) contains a binding of x to . With iespect to
enviionment A, the binding of x to in fiame ll is said to s|oJo+ the
binding of x to 3 in fiame l.
Te enviionment is ciucial to the evaluation piocess, because it de-
teimines the context in which an expiession should be evaluated. ln-
deed, one could say that expiessions in a piogiamming language do
321
A B
C D
I
II III
z:6
x:7
m:1
y:2
x:3
y:5
Figure 3.1: A simple enviionment stiuctuie.
not, in themselves, have any meaning. Rathei, an expiession acquiies a
meaning only with iespect to some enviionment in which it is evalu-
ated. Even the inteipietation of an expiession as stiaightfoiwaid as (+
1 1) depends on an undeistanding that one is opeiating in a context in
which + is the symbol foi addition. Tus, in oui model of evaluation we
will always speak of evaluating an expiession with iespect to some envi-
ionment. To desciibe inteiactions with the inteipietei, we will suppose
that theie is a global enviionment, consisting of a single fiame (with no
enclosing enviionment) that includes values foi the symbols associated
with the piimitive pioceduies. loi example, the idea that + is the sym-
bol foi addition is captuied by saying that the symbol + is bound in the
global enviionment to the piimitive addition pioceduie.
3.2.1 The Rules for Evaluation
Te oveiall specication of howthe inteipietei evaluates a combination
iemains the same as when we ist intioduced it in Section 1.1.3
322
To evaluate a combination
1. Evaluate the subexpiessions of the combination.
12
2. Apply the value of the opeiatoi subexpiession to the values of the
opeiand subexpiessions.
Te enviionment model of evaluation ieplaces the substitution model in
specifying what it means to apply a compound pioceduie to aiguments.
ln the enviionment model of evaluation, a pioceduie is always a paii
consisting of some code and a pointei to an enviionment. Pioceduies
aie cieated in one way only by evaluating a -expiession. Tis pioduces
a pioceduie whose code is obtained fiom the text of the -expiession
and whose enviionment is the enviionment in which the -expiession
was evaluated to pioduce the pioceduie. loi example, considei the pio-
ceduie denition
(define (square x)
(* x x))
evaluated in the global enviionment. Te pioceduie denition syntax
is just syntactic sugai foi an undeilying implicit -expiession. lt would
have been equivalent to have used
(define square
(lambda (x) (* x x)))
12
Assignment intioduces a subtlety into step 1 of the evaluation iule. As shown in
Exeicise 3.8, the piesence of assignment allows us to wiite expiessions that will pioduce
dieient values depending on the oidei in which the subexpiessions in a combination
aie evaluated. Tus, to be piecise, we should specify an evaluation oidei in step 1 (e.g.,
lef to iight oi iight to lef). Howevei, this oidei should always be consideied to be
an implementation detail, and one should nevei wiite piogiams that depend on some
paiticulai oidei. loi instance, a sophisticated compilei might optimize a piogiam by
vaiying the oidei in which subexpiessions aie evaluated.
323
other variables
square:
global
env
(define (square x)
(* x x))
parameters: x
body: (* x x)
Figure 3.2: Enviionment stiuctuie pioduced by evaluating
(define (square x) (* x x)) in the global enviionment.
which evaluates (lambda (x) (* x x)) and binds square to the ie-
sulting value, all in the global enviionment.
liguie 3.2 shows the iesult of evaluating this define expiession.
Te pioceduie object is a paii whose code species that the pioceduie
has one foimal paiametei, namely x, and a pioceduie body (* x x).
Te enviionment pait of the pioceduie is a pointei to the global envi-
ionment, since that is the enviionment in which the -expiession was
evaluated to pioduce the pioceduie. A new binding, which associates
the pioceduie object with the symbol square, has been added to the
global fiame. ln geneial, define cieates denitions by adding bindings
to fiames.
Nowthat we have seen howpioceduies aie cieated, we can desciibe
how pioceduies aie applied. Te enviionment model species To ap-
ply a pioceduie to aiguments, cieate a new enviionment containing a
fiame that binds the paiameteis to the values of the aiguments. Te en-
closing enviionment of this fiame is the enviionment specied by the
324
E1
(* x x)
parameters: x
body: (* x x)
(square 5)
global
env
other variables
square:
x:5
Figure 3.3: Enviionment cieated by evaluating (square 5)
in the global enviionment.
pioceduie. Now, within this new enviionment, evaluate the pioceduie
body.
To showhowthis iule is followed, liguie 3.3 illustiates the enviion-
ment stiuctuie cieated by evaluating the expiession (square 5) in the
global enviionment, wheie square is the pioceduie geneiated in liguie
3.2. Applying the pioceduie iesults in the cieation of a new enviion-
ment, labeled E1 in the guie, that begins with a fiame in which x, the
foimal paiametei foi the pioceduie, is bound to the aigument . Te
pointei leading upwaid fiom this fiame shows that the fiames enclos-
ing enviionment is the global enviionment. Te global enviionment is
chosen heie, because this is the enviionment that is indicated as pait
of the square pioceduie object. Within E1, we evaluate the body of the
pioceduie, (* x x). Since the value of x in E1 is , the iesult is (* 5
5), oi 2.
Te enviionment model of pioceduie application can be summa-
iized by two iules
32
A pioceduie object is applied to a set of aiguments by constiuct-
ing a fiame, binding the foimal paiameteis of the pioceduie to the
aiguments of the call, and then evaluating the body of the pioce-
duie in the context of the new enviionment constiucted. Te new
fiame has as its enclosing enviionment the enviionment pait of
the pioceduie object being applied.
A pioceduie is cieated by evaluating a -expiession ielative to a
given enviionment. Te iesulting pioceduie object is a paii con-
sisting of the text of the -expiession and a pointei to the envi-
ionment in which the pioceduie was cieated.
We also specify that dening a symbol using define cieates a binding
in the cuiient enviionment fiame and assigns to the symbol the indi-
cated value.
13
linally, we specify the behavioi of set!, the opeiation
that foiced us to intioduce the enviionment model in the ist place.
Evaluating the expiession (set! +or:o||e +o|ve) in some enviion-
ment locates the binding of the vaiiable in the enviionment and changes
that binding to indicate the new value. Tat is, one nds the ist fiame
in the enviionment that contains a binding foi the vaiiable and modi-
es that fiame. lf the vaiiable is unbound in the enviionment, then set!
signals an eiioi.
Tese evaluation iules, though consideiably moie complex than the
substitution model, aie still ieasonably stiaightfoiwaid. Moieovei, the
evaluation model, though abstiact, piovides a coiiect desciiption of
13
lf theie is alieady a binding foi the vaiiable in the cuiient fiame, then the binding is
changed. Tis is convenient because it allows iedenition of symbols, howevei, it also
means that define can be used to change values, and this biings up the issues of assign-
ment without explicitly using set!. Because of this, some people piefei iedenitions
of existing symbols to signal eiiois oi wainings.
32
how the inteipietei evaluates expiessions. ln Chaptei 4 we shall see
how this model can seive as a bluepiint foi implementing a woiking
inteipietei. Te following sections elaboiate the details of the model by
analyzing some illustiative piogiams.
3.2.2 Applying Simple Procedures
When we intioduced the substitution model in Section 1.1. we showed
how the combination (f 5) evaluates to 13, given the following pio-
ceduie denitions
(define (square x)
(* x x))
(define (sum-of-squares x y)
(+ (square x) (square y)))
(define (f a)
(sum-of-squares (+ a 1) (* a 2)))
We can analyze the same example using the enviionment model. liguie
3.4 shows the thiee pioceduie objects cieated by evaluating the deni-
tions of f, square, and sum-of-squares in the global enviionment. Each
pioceduie object consists of some code, togethei with a pointei to the
global enviionment.
ln liguie 3. we see the enviionment stiuctuie cieated by evaluat-
ing the expiession (f 5). Te call to f cieates a new enviionment E1
beginning with a fiame in which a, the foimal paiametei of f, is bound
to the aigument . ln E1, we evaluate the body of f
(sum-of-squares (+ a 1) (* a 2))
To evaluate this combination, we ist evaluate the subexpiessions. Te
ist subexpiession, sum-of-squares, has a value that is a pioceduie ob-
ject. (Notice how this value is found We ist look in the ist fiame of
32
square:
sum-of-squares:
f:
global
env
parameters: a
body: (sum-of-squares
(+ a 1)
(* a 2))
parameters: x
body: (* x x)
parameters: x,y
body: (+ (square x)
(square y))
Figure 3.4: Pioceduie objects in the global fiame.
E1, which contains no binding foi sum-of-squares. Ten we pioceed
to the enclosing enviionment, i.e. the global enviionment, and nd the
binding shown in liguie 3.4.) Te othei two subexpiessions aie evalu-
ated by applying the piimitive opeiations + and * to evaluate the two
combinations (+ a 1) and (* a 2) to obtain and 10, iespectively.
Now we apply the pioceduie object sum-of-squares to the aigu-
ments and 10. Tis iesults in a new enviionment E2 in which the
foimal paiameteis x and y aie bound to the aiguments. Within E2 we
evaluate the combination (+ (square x) (square y)). Tis leads us to
evaluate (square x), wheie square is found in the global fiame and x
is . Once again, we set up a new enviionment, E3, in which x is bound
to , and within this we evaluate the body of square, which is (* x x).
Also as pait of applying sum-of-squares, we must evaluate the subex-
piession (square y), wheie y is 10. Tis second call to square cieates
anothei enviionment, E4, in which x, the foimal paiametei of square,
328
(* x x)
x:10
E4
(* x x)
x:6
E3
(+ (square x)
(square y))
x:6
y:10
E2
(sum-of-squares
(+ a 1)
(* a 2))
a:5
E1
(f 5)
global
env
Figure 3.5: Enviionments cieated by evaluating (f 5) us-
ing the pioceduies in liguie 3.4.
is bound to 10. And within E4 we must evaluate (* x x).
Te impoitant point to obseive is that each call to square cieates a
new enviionment containing a binding foi x. We can see heie how the
dieient fiames seive to keep sepaiate the dieient local vaiiables all
named x. Notice that each fiame cieated by square points to the global
enviionment, since this is the enviionment indicated by the square pio-
ceduie object.
Afei the subexpiessions aie evaluated, the iesults aie ietuined. Te
values geneiated by the two calls to square aie added by sum-of-squares,
and this iesult is ietuined by f. Since oui focus heie is on the enviion-
ment stiuctuies, we will not dwell on how these ietuined values aie
passed fiom call to call, howevei, this is also an impoitant aspect of the
evaluation piocess, and we will ietuin to it in detail in Chaptei .
Exercise 3.9: ln Section 1.2.1 we used the substitution model
to analyze two pioceduies foi computing factoiials, a iecui-
329
sive veision
(define (factorial n)
(if (= n 1) 1 (* n (factorial (- n 1)))))
and an iteiative veision
(define (factorial n) (fact-iter 1 1 n))
(define (fact-iter product counter max-count)
(if (> counter max-count)
product
(fact-iter (* counter product)
(+ counter 1)
max-count)))
Show the enviionment stiuctuies cieated by evaluating
(factorial 6) using each veision of the factorial pio-
ceduie.
14
3.2.3 Frames as the Repository of Local State
We can tuin to the enviionment model to see how pioceduies and as-
signment can be used to iepiesent objects with local state. As an exam-
ple, considei the withdiawal piocessoi fiom Section 3.1.1 cieated by
calling the pioceduie
(define (make-withdraw balance)
(lambda (amount)
(if (>= balance amount)
(begin (set! balance (- balance amount))
14
Te enviionment model will not claiify oui claim in Section 1.2.1 that the intei-
pietei can execute a pioceduie such as fact-iter in a constant amount of space using
tail iecuision. We will discuss tail iecuision when we deal with the contiol stiuctuie
of the inteipietei in Section .4.
330
parameters: balance
body: (lambda (amount)
(if (>= balance amount)
(begin (set! balance (-- balance amount))
balance)
"insufficient funds"))
global
env
make-withdraw:
Figure 3.6: Result of dening make-withdraw in the global
enviionment.
balance)
"Insufficient funds")))
Let us desciibe the evaluation of
(define W1 (make-withdraw 100))
followed by
(W1 50)
50
liguie 3. shows the iesult of dening the make-withdraw pioceduie in
the global enviionment. Tis pioduces a pioceduie object that contains
a pointei to the global enviionment. So fai, this is no dieient fiom the
examples we have alieady seen, except that the body of the pioceduie
is itself a -expiession.
331
E1
make-withdraw:
W1:
global
env
balance: 100
parameters: balance
body: ...
parameters: amount
body: (if (>= balance amount)
(begin (set! balance (- balance amount))
balance)
"insufficient funds")
Figure 3.7: Result of evaluating (define W1 (make-withdraw 100)).
Te inteiesting pait of the computation happens when we apply the
pioceduie make-withdraw to an aigument
(define W1 (make-withdraw 100))
We begin, as usual, by seuing up an enviionment E1 in which the foimal
paiametei balance is bound to the aigument 100. Within this enviion-
ment, we evaluate the body of make-withdraw, namely the -expiession.
Tis constiucts a new pioceduie object, whose code is as specied by
the lambda and whose enviionment is E1, the enviionment in which
the lambda was evaluated to pioduce the pioceduie. Te iesulting pio-
ceduie object is the value ietuined by the call to make-withdraw. Tis
is bound to W1 in the global enviionment, since the define itself is be-
ing evaluated in the global enviionment. liguie 3. shows the iesulting
enviionment stiuctuie.
332
E1
make-withdraw: ...
W1:
global
env
balance: 100
parameters: amount
body: ...
amount: 50
Here is the balance
that will be changed
by the set!
(if (>= balance amount)
(begin (set! balance
(- balance amount))
balance)
"insufficient funds")
Figure 3.8: Enviionments cieated by applying the pioceduie object W1.
Now we can analyze what happens when W1 is applied to an aigu-
ment
(W1 50)
50
We begin by constiucting a fiame in which amount, the foimal pa-
iametei of W1, is bound to the aigument 0. Te ciucial point to ob-
seive is that this fiame has as its enclosing enviionment not the global
enviionment, but iathei the enviionment E1, because this is the envi-
ionment that is specied by the W1 pioceduie object. Within this new
enviionment, we evaluate the body of the pioceduie
(if (>= balance amount)
(begin (set! balance (- balance amount))
balance)
"Insufficient funds")
333
E1
make-withdraw: ...
W1:
global
env
balance: 50
parameters: amount
body: ...
Figure 3.9: Enviionments afei the call to W1.
Te iesulting enviionment stiuctuie is shown in liguie 3.8. Te expies-
sion being evaluated iefeiences both amount and balance. Amount will
be found in the ist fiame in the enviionment, while balance will be
found by following the enclosing-enviionment pointei to E1.
When the set! is executed, the binding of balance in E1 is changed.
At the completion of the call to W1, balance is 0, and the fiame that
contains balance is still pointed to by the pioceduie object W1. Te
fiame that binds amount (in which we executed the code that changed
balance) is no longei ielevant, since the pioceduie call that constiucted
it has teiminated, and theie aie no pointeis to that fiame fiom othei
paits of the enviionment. Te next time W1 is called, this will build a
new fiame that binds amount and whose enclosing enviionment is E1.
We see that E1 seives as the place that holds the local state vaiiable
foi the pioceduie object W1. liguie 3.9 shows the situation afei the call
to W1.
Obseive what happens when we cieate a second withdiaw object
by making anothei call to make-withdraw
334
E1
W2:
W1:
global
env
balance: 50
parameters: amount
body: ...
E2 balance: 100
make-withdraw: ...
Figure 3.10: Using (define W2 (make-withdraw 100)) to
cieate a second object.
(define W2 (make-withdraw 100))
Tis pioduces the enviionment stiuctuie of liguie 3.10, which shows
that W2 is a pioceduie object, that is, a paii with some code and an en-
viionment. Te enviionment E2 foi W2 was cieated by the call to make-
withdraw. lt contains a fiame with its own local binding foi balance.
On the othei hand, W1 and W2 have the same code the code specied
by the -expiession in the body of make-withdraw.
1
We see heie why
W1 and W2 behave as independent objects. Calls to W1 iefeience the state
vaiiable balance stoied in E1, wheieas calls to W2 iefeience the balance
stoied in E2. Tus, changes to the local state of one object do not aect
the othei object.
1
Whethei W1 and W2 shaie the same physical code stoied in the computei, oi whethei
they each keep a copy of the code, is a detail of the implementation. loi the inteipietei
we implement in Chaptei 4, the code is in fact shaied.
33
Exercise 3.10: ln the make-withdraw pioceduie, the local
vaiiable balance is cieated as a paiametei of make-withdraw.
We could also cieate the local state vaiiable explicitly, us-
ing let, as follows
(define (make-withdraw initial-amount)
(let ((balance initial-amount))
(lambda (amount)
(if (>= balance amount)
(begin (set! balance (- balance amount))
balance)
"Insufficient funds"))))
Recall fiom Section 1.3.2 that let is simply syntactic sugai
foi a pioceduie call
(let ((var exp)) body)
is inteipieted as an alteinate syntax foi
((lambda (var) body) exp)
Use the enviionment model to analyze this alteinate vei-
sion of make-withdraw, diawing guies like the ones above
to illustiate the inteiactions
(define W1 (make-withdraw 100))
(W1 50)
(define W2 (make-withdraw 100))
Show that the two veisions of make-withdraw cieate ob-
jects with the same behavioi. Howdo the enviionment stiuc-
tuies diei foi the two veisions`
33
3.2.4 Internal Definitions
Section 1.1.8 intioduced the idea that pioceduies can have inteinal def-
initions, thus leading to a block stiuctuie as in the following pioceduie
to compute squaie ioots
(define (sqrt x)
(define (good-enough? guess)
(< (abs (- (square guess) x)) 0.001))
(define (improve guess)
(average guess (/ x guess)))
(define (sqrt-iter guess)
(if (good-enough? guess)
guess
(sqrt-iter (improve guess))))
(sqrt-iter 1.0))
Now we can use the enviionment model to see why these inteinal de-
nitions behave as desiied. liguie 3.11 shows the point in the evaluation
of the expiession (sqrt 2) wheie the inteinal pioceduie good-enough?
has been called foi the ist time with guess equal to 1.
Obseive the stiuctuie of the enviionment. Sqrt is a symbol in the
global enviionment that is bound to a pioceduie object whose associ-
ated enviionment is the global enviionment. When sqrt was called, a
new enviionment E1 was foimed, suboidinate to the global enviion-
ment, in which the paiametei x is bound to 2. Te body of sqrt was
then evaluated in E1. Since the ist expiession in the body of sqrt is
(define (good-enough? guess)
(< (abs (- (square guess) x)) 0.001))
evaluating this expiession dened the pioceduie good-enough? in the
enviionment E1. To be moie piecise, the symbol good-enough? was
added to the ist fiame of E1, bound to a pioceduie object whose asso-
33
parameters: x
body: (define good-enough? ...)
(define improve ...)
(define sqrt-iter ...)
(sqrt-iter 1.0)
global
env
sqrt:
E1
x:2
good-enough?:
improve: ...
sqrt-iter: ...
parameters: guess
body: (< (abs ...)
...)
guess: 1
guess: 1
call to sqrt-iter
E2
call to good-enough?
E3
Figure 3.11: Sqrt pioceduie with inteinal denitions.
ciated enviionment is E1. Similaily, improve and sqrt-iter weie de-
ned as pioceduies in E1. loi conciseness, liguie 3.11 shows only the
pioceduie object foi good-enough?.
Afei the local pioceduies weie dened, the expiession (sqrt-iter
1.0) was evaluated, still in enviionment E1. So the pioceduie object
bound to sqrt-iter in E1 was called with 1 as an aigument. Tis cie-
ated an enviionment E2 in which guess, the paiametei of sqrt-iter,
is bound to 1. Sqrt-iter in tuin called good-enough? with the value of
guess (fiom E2) as the aigument foi good-enough?. Tis set up anothei
338
enviionment, E3, in which guess (the paiametei of good-enough?) is
bound to 1. Although sqrt-iter and good-enough? both have a pa-
iametei named guess, these aie two distinct local vaiiables located in
dieient fiames. Also, E2 and E3 both have E1 as theii enclosing en-
viionment, because the sqrt-iter and good-enough? pioceduies both
have E1 as theii enviionment pait. One consequence of this is that the
symbol x that appeais in the body of good-enough? will iefeience the
binding of x that appeais in E1, namely the value of x with which the
oiiginal sqrt pioceduie was called.
Te enviionment model thus explains the two key piopeities that
make local pioceduie denitions a useful technique foi modulaiizing
piogiams
Te names of the local pioceduies do not inteifeie with names
exteinal to the enclosing pioceduie, because the local pioceduie
names will be bound in the fiame that the pioceduie cieates when
it is iun, iathei than being bound in the global enviionment.
Te local pioceduies can access the aiguments of the enclosing
pioceduie, simply by using paiametei names as fiee vaiiables.
Tis is because the body of the local pioceduie is evaluated in an
enviionment that is suboidinate to the evaluation enviionment
foi the enclosing pioceduie.
Exercise 3.11: ln Section 3.2.3 we saw how the enviion-
ment model desciibed the behavioi of pioceduies with local
state. Now we have seen how inteinal denitions woik. A
typical message-passing pioceduie contains both of these
aspects. Considei the bank account pioceduie of Section
3.1.1
339
(define (make-account balance)
(define (withdraw amount)
(if (>= balance amount)
(begin (set! balance (- balance amount))
balance)
"Insufficient funds"))
(define (deposit amount)
(set! balance (+ balance amount))
balance)
(define (dispatch m)
(cond ((eq? m 'withdraw) withdraw)
((eq? m 'deposit) deposit)
(else
(error "Unknown request: MAKE-ACCOUNT"
m))))
dispatch)
Showthe enviionment stiuctuie geneiated by the sequence
of inteiactions
(define acc (make-account 50))
((acc 'deposit) 40)
90
((acc 'withdraw) 60)
30
Wheie is the local state foi acc kept` Suppose we dene
anothei account
(define acc2 (make-account 100))
How aie the local states foi the two accounts kept distinct`
Which paits of the enviionment stiuctuie aie shaied be-
tween acc and acc2`
340
3.3 Modeling with Mutable Data
Chaptei 2 dealt with compound data as a means foi constiucting com-
putational objects that have seveial paits, in oidei to model ieal-woild
objects that have seveial aspects. ln that chaptei we intioduced the dis-
cipline of data abstiaction, accoiding to which data stiuctuies aie spec-
ied in teims of constiuctois, which cieate data objects, and selectois,
which access the paits of compound data objects. But we now know
that theie is anothei aspect of data that Chaptei 2 did not addiess. Te
desiie to model systems composed of objects that have changing state
leads us to the need to modify compound data objects, as well as to con-
stiuct and select fiom them. ln oidei to model compound objects with
changing state, we will design data abstiactions to include, in addition
to selectois and constiuctois, opeiations called voors, which mod-
ify data objects. loi instance, modeling a banking system iequiies us to
change account balances. Tus, a data stiuctuie foi iepiesenting bank
accounts might admit an opeiation
(set-balance! account new-value)
that changes the balance of the designated account to the designated
new value. Data objects foi which mutatois aie dened aie known as
vo||e Joo o|jecs.
Chaptei 2 intioduced paiis as a geneial-puipose glue foi synthe-
sizing compound data. We begin this section by dening basic mutatois
foi paiis, so that paiis can seive as building blocks foi constiucting mu-
table data objects. Tese mutatois gieatly enhance the iepiesentational
powei of paiis, enabling us to build data stiuctuies othei than the se-
quences and tiees that we woiked with in Section 2.2. We also piesent
some examples of simulations in which complex systems aie modeled
as collections of objects with local state.
341
3.3.1 Mutable List Structure
Te basic opeiations on paiiscons, car, and cdrcan be used to con-
stiuct list stiuctuie and to select paits fiom list stiuctuie, but they aie
incapable of modifying list stiuctuie. Te same is tiue of the list opei-
ations we have used so fai, such as append and list, since these can
be dened in teims of cons, car, and cdr. To modify list stiuctuies we
need new opeiations.
Te piimitive mutatois foi paiis aie set-car! and set-cdr!. Set-
car! takes two aiguments, the ist of which must be a paii. lt modies
this paii, ieplacing the car pointei by a pointei to the second aigument
of set-car!.
1
As an example, suppose that x is bound to the list ((a b) c d) and
y to the list (e f) as illustiated in liguie 3.12. Evaluating the expiession
(set-car! x y) modies the paii to which x is bound, ieplacing its car
by the value of y. Te iesult of the opeiation is shown in liguie 3.13.
Te stiuctuie x has been modied and would now be piinted as ((e f)
c d). Te paiis iepiesenting the list (a b), identied by the pointei that
was ieplaced, aie now detached fiom the oiiginal stiuctuie.
1
Compaie liguie 3.13 with liguie 3.14, which illustiates the iesult of
executing (define z (cons y (cdr x))) with x and y bound to the
oiiginal lists of liguie 3.12. Te vaiiable z is now bound to a new paii
cieated by the cons opeiation, the list to which x is bound is unchanged.
Te set-cdr! opeiation is similai to set-car!. Te only dieience
is that the cdr pointei of the paii, iathei than the car pointei, is ieplaced.
Te eect of executing (set-cdr! x y) on the lists of liguie 3.12 is
1
Set-car! and set-cdr! ietuin implementation-dependent values. Like set!, they
should be used only foi theii eect.
1
We see fiom this that mutation opeiations on lists can cieate gaibage that is
not pait of any accessible stiuctuie. We will see in Section .3.2 that Lisp memoiy-
management systems include a gor|oge co||ecor, which identies and iecycles the mem-
oiy space used by unneeded paiis.
342
c d
y
x
e f
a b
Figure 3.12: Lists x ((a b) c d) and y (e f).
c d
y
x
e f
a b
Figure 3.13: Eect of (set-car! x y) on the lists in liguie 3.12.
343
c d
y
x
e f
a b z
Figure 3.14: Eect of (define z (cons y (cdr x))) on
the lists in liguie 3.12.
c d
y
x
e f
a b
Figure 3.15: Eect of (set-cdr! x y) on the lists in liguie 3.12.
344
shown in liguie 3.1. Heie the cdr pointei of x has been ieplaced by
the pointei to (e f). Also, the list (c d), which used to be the cdr of
x, is now detached fiom the stiuctuie.
Cons builds newlist stiuctuie by cieating newpaiis, while set-car!
and set-cdr! modify existing paiis. lndeed, we could implement cons
in teims of the two mutatois, togethei with a pioceduie get-new-pair,
which ietuins a new paii that is not pait of any existing list stiuctuie.
We obtain the new paii, set its car and cdr pointeis to the designated
objects, and ietuin the new paii as the iesult of the cons.
18
(define (cons x y)
(let ((new (get-new-pair)))
(set-car! new x)
(set-cdr! new y)
new))
Exercise 3.12: Te following pioceduie foi appending lists
was intioduced in Section 2.2.1
(define (append x y)
(if (null? x)
y
(cons (car x) (append (cdr x) y))))
Append foims a new list by successively consing the el-
ements of x onto y. Te pioceduie append! is similai to
append, but it is a mutatoi iathei than a constiuctoi. lt ap-
pends the lists by splicing them togethei, modifying the -
nal paii of x so that its cdr is now y. (lt is an eiioi to call
append! with an empty x.)
18
Get-new-pair is one of the opeiations that must be implemented as pait of the
memoiy management iequiied by a Lisp implementation. We will discuss this in Sec-
tion .3.1.
34
(define (append! x y)
(set-cdr! (last-pair x) y)
x)
Heie last-pair is a pioceduie that ietuins the last paii in
its aigument
(define (last-pair x)
(if (null? (cdr x)) x (last-pair (cdr x))))
Considei the inteiaction
(define x (list 'a 'b))
(define y (list 'c 'd))
(define z (append x y))
z
(a b c d)
(cdr x)
response
(define w (append! x y))
w
(a b c d)
(cdr x)
response
What aie the missing resonses` Diaw box-and-pointei
diagiams to explain youi answei.
Exercise 3.13: Considei the following make-cycle pioce-
duie, which uses the last-pair pioceduie dened in Exei-
cise 3.12
(define (make-cycle x)
(set-cdr! (last-pair x) x)
x)
34
Diaw a box-and-pointei diagiam that shows the stiuctuie
z cieated by
(define z (make-cycle (list 'a 'b 'c)))
What happens if we tiy to compute (last-pair z)`
Exercise 3.14: Te following pioceduie is quite useful, al-
though obscuie
(define (mystery x)
(define (loop x y)
(if (null? x)
y
(let ((temp (cdr x)))
(set-cdr! x y)
(loop temp x))))
(loop x '()))
Loop uses the tempoiaiy vaiiable temp to hold the old
value of the cdr of x, since the set-cdr! on the next line
destioys the cdr. Explain what mystery does in geneial.
Suppose v is dened by (define v (list 'a 'b 'c
'd)). Diaw the box-and-pointei diagiam that iepiesents
the list to which v is bound. Suppose that we now evalu-
ate (define w (mystery v)). Diaw box-and-pointei dia-
giams that show the stiuctuies v and w afei evaluating this
expiession. What would be piinted as the values of v and
w`
Sharing and identity
We mentioned in Section 3.1.3 the theoietical issues of sameness and
change iaised by the intioduction of assignment. Tese issues aiise in
34
piactice when individual paiis aie s|oreJ among dieient data objects.
loi example, considei the stiuctuie foimed by
(define x (list 'a 'b))
(define z1 (cons x x))
As shown in liguie 3.1, z1 is a paii whose car and cdr both point to
the same paii x. Tis shaiing of x by the car and cdr of z1 is a con-
sequence of the stiaightfoiwaid way in which cons is implemented. ln
geneial, using cons to constiuct lists will iesult in an inteilinked stiuc-
tuie of paiis in which many individual paiis aie shaied by many diei-
ent stiuctuies.
ln contiast to liguie 3.1, liguie 3.1 shows the stiuctuie cieated
by
(define z2 (cons (list 'a 'b) (list 'a 'b)))
ln this stiuctuie, the paiis in the two (a b) lists aie distinct, although
the actual symbols aie shaied.
19
When thought of as a list, z1 and z2 both iepiesent the same list,
((a b) a b). ln geneial, shaiing is completely undetectable if we opei-
ate on lists using only cons, car, and cdr. Howevei, if we allowmutatois
on list stiuctuie, shaiing becomes signicant. As an example of the dif-
feience that shaiing can make, considei the following pioceduie, which
modies the car of the stiuctuie to which it is applied
(define (set-to-wow! x) (set-car! (car x) 'wow) x)
19
Te two paiis aie distinct because each call to cons ietuins a newpaii. Te symbols
aie shaied, in Scheme theie is a unique symbol with any given name. Since Scheme
piovides no way to mutate a symbol, this shaiing is undetectable. Note also that the
shaiing is what enables us to compaie symbols using eq?, which simply checks equality
of pointeis.
348
z1
x
a b
Figure 3.16: Te list z1 foimed by (cons x x).
a b
z2
Figure 3.17: Te list z2 foimed by (cons (list 'a 'b)
(list 'a 'b)).
Even though z1 and z2 aie the same stiuctuie, applying set-to-wow!
to them yields dieient iesults. With z1, alteiing the car also changes
the cdr, because in z1 the car and the cdr aie the same paii. With z2,
the car and cdr aie distinct, so set-to-wow! modies only the car
z1
((a b) a b)
(set-to-wow! z1)
((wow b) wow b)
z2
((a b) a b)
349
(set-to-wow! z2)
((wow b) a b)
One way to detect shaiing in list stiuctuies is to use the piedicate eq?,
which we intioduced in Section 2.3.1 as a way to test whethei two sym-
bols aie equal. Moie geneially, (eq? x y) tests whethei x and y aie
the same object (that is, whethei x and y aie equal as pointeis). Tus,
with z1 and z2 as dened in liguie 3.1 and liguie 3.1, (eq? (car z1)
(cdr z1)) is tiue and (eq? (car z2) (cdr z2)) is false.
As will be seen in the following sections, we can exploit shaiing to
gieatly extend the iepeitoiie of data stiuctuies that can be iepiesented
by paiis. On the othei hand, shaiing can also be dangeious, since modi-
cations made to stiuctuies will also aect othei stiuctuies that happen
to shaie the modied paits. Te mutation opeiations set-car! and set-
cdr! should be used with caie, unless we have a good undeistanding of
how oui data objects aie shaied, mutation can have unanticipated ie-
sults.
20
Exercise 3.15: Diaw box-and-pointei diagiams to explain
the eect of set-to-wow! on the stiuctuies z1 and z2 above.
Exercise 3.16: Ben Bitdiddle decides to wiite a pioceduie
to count the numbei of paiis in any list stiuctuie. lts easy,
20
Te subtleties of dealing with shaiing of mutable data objects ieect the undeilying
issues of sameness and change that weie iaised in Section 3.1.3. We mentioned theie
that admiuing change to oui language iequiies that a compound object must have an
identity that is something dieient fiom the pieces fiom which it is composed. ln
Lisp, we considei this identity to be the quality that is tested by eq?, i.e., by equality of
pointeis. Since in most Lisp implementations a pointei is essentially a memoiy addiess,
we aie solving the pioblem of dening the identity of objects by stipulating that a data
object itself is the infoimation stoied in some paiticulai set of memoiy locations in
the computei. Tis suces foi simple Lisp piogiams, but is haidly a geneial way to
iesolve the issue of sameness in computational models.
30
he ieasons. Te numbei of paiis in any stiuctuie is the
numbei in the car plus the numbei in the cdr plus one
moie to count the cuiient paii. So Ben wiites the following
pioceduie
(define (count-pairs x)
(if (not (pair? x))
0
(+ (count-pairs (car x))
(count-pairs (cdr x))
1)))
Show that this pioceduie is not coiiect. ln paiticulai, diaw
box-and-pointei diagiams iepiesenting list stiuctuies made
up of exactly thiee paiis foi which Bens pioceduie would
ietuin 3, ietuin 4, ietuin , nevei ietuin at all.
Exercise 3.17: Devise a coiiect veision of the count-pairs
pioceduie of Exeicise 3.1 that ietuins the numbei of dis-
tinct paiis in any stiuctuie. (Hint Tiaveise the stiuctuie,
maintaining an auxiliaiy data stiuctuie that is used to keep
tiack of which paiis have alieady been counted.)
Exercise 3.18: Wiite a pioceduie that examines a list and
deteimines whethei it contains a cycle, that is, whethei a
piogiam that tiied to nd the end of the list by taking suc-
cessive cdrs would go into an innite loop. Exeicise 3.13
constiucted such lists.
Exercise 3.19: Redo Exeicise 3.18 using an algoiithm that
takes only a constant amount of space. (Tis iequiies a veiy
clevei idea.)
31
Mutation is just assignment
When we intioduced compound data, we obseived in Section 2.1.3 that
paiis can be iepiesented puiely in teims of pioceduies
(define (cons x y)
(define (dispatch m)
(cond ((eq? m 'car) x)
((eq? m 'cdr) y)
(else (error "Undefined operation: CONS" m))))
dispatch)
(define (car z) (z 'car))
(define (cdr z) (z 'cdr))
Te same obseivation is tiue foi mutable data. We can implement mu-
table data objects as pioceduies using assignment and local state. loi
instance, we can extend the above paii implementation to handle set-
car! and set-cdr! in a mannei analogous to the way we implemented
bank accounts using make-account in Section 3.1.1
(define (cons x y)
(define (set-x! v) (set! x v))
(define (set-y! v) (set! y v))
(define (dispatch m)
(cond ((eq? m 'car) x)
((eq? m 'cdr) y)
((eq? m 'set-car!) set-x!)
((eq? m 'set-cdr!) set-y!)
(else
(error "Undefined operation: CONS" m))))
dispatch)
(define (car z) (z 'car))
(define (cdr z) (z 'cdr))
(define (set-car! z new-value)
((z 'set-car!) new-value) z)
32
(define (set-cdr! z new-value)
((z 'set-cdr!) new-value) z)
Assignment is all that is needed, theoietically, to account foi the behav-
ioi of mutable data. As soon as we admit set! to oui language, we iaise
all the issues, not only of assignment, but of mutable data in geneial.
21
Exercise 3.20: Diaw enviionment diagiams to illustiate
the evaluation of the sequence of expiessions
(define x (cons 1 2))
(define z (cons x x))
(set-car! (cdr z) 17)
(car x)
17
using the pioceduial implementation of paiis given above.
(Compaie Exeicise 3.11.)
3.3.2 Representing eues
Te mutatois set-car! and set-cdr! enable us to use paiis to constiuct
data stiuctuies that cannot be built with cons, car, and cdr alone. Tis
section shows how to use paiis to iepiesent a data stiuctuie called a
queue. Section 3.3.3 will show how to iepiesent data stiuctuies called
tables.
A qveve is a sequence in which items aie inseited at one end (called
the reor of the queue) and deleted fiom the othei end (the [ron). lig-
uie 3.18 shows an initially empty queue in which the items a and b aie
21
On the othei hand, fiom the viewpoint of implementation, assignment iequiies us
to modify the enviionment, which is itself a mutable data stiuctuie. Tus, assignment
and mutation aie equipotent Each can be implemented in teims of the othei.
33
Operation Resulting Queue
(define q (make-queue))
(insert-queue! q 'a) a
(insert-queue! q 'b) a b
(delete-queue! q) b
(insert-queue! q 'c) b c
(insert-queue! q 'd) b c d
(delete-queue! q) c d
Figure 3.18: Qeue opeiations.
inseited. Ten a is iemoved, c and d aie inseited, and b is iemoved. Be-
cause items aie always iemoved in the oidei in which they aie inseited,
a queue is sometimes called a FIFO (ist in, ist out) buei.
ln teims of data abstiaction, we can iegaid a queue as dened by
the following set of opeiations
a constiuctoi (make-queue) ietuins an empty queue (a queue
containing no items).
two selectois
(empty-queue? queue) tests if the queue is empty.
(front-queue queue) ietuins the object at the fiont of the
queue, signaling an eiioi if the queue is empty, it does not modify
the queue.
two mutatois
(insert-queue! queue item) inseits the item at the ieai of
the queue and ietuins the modied queue as its value.
34
(delete-queue! queue) iemoves the item at the fiont of the
queue and ietuins the modied queue as its value, signaling an
eiioi if the queue is empty befoie the deletion.
Because a queue is a sequence of items, we could ceitainly iepiesent it
as an oidinaiy list, the fiont of the queue would be the car of the list,
inseiting an item in the queue would amount to appending a new ele-
ment at the end of the list, and deleting an item fiom the queue would
just be taking the cdr of the list. Howevei, this iepiesentation is ine-
cient, because in oidei to inseit an item we must scan the list until we
ieach the end. Since the only method we have foi scanning a list is by
successive cdr opeiations, this scanning iequiies (n) steps foi a list of
n items. A simple modication to the list iepiesentation oveicomes this
disadvantage by allowing the queue opeiations to be implemented so
that they iequiie (1) steps, that is, so that the numbei of steps needed
is independent of the length of the queue.
Te diculty with the list iepiesentation aiises fiom the need to
scan to nd the end of the list. Te ieason we need to scan is that, al-
though the standaid way of iepiesenting a list as a chain of paiis iead-
ily piovides us with a pointei to the beginning of the list, it gives us
no easily accessible pointei to the end. Te modication that avoids the
diawback is to iepiesent the queue as a list, togethei with an additional
pointei that indicates the nal paii in the list. Tat way, when we go to
inseit an item, we can consult the ieai pointei and so avoid scanning
the list.
A queue is iepiesented, then, as a paii of pointeis, front-ptr and
rear-ptr, which indicate, iespectively, the ist and last paiis in an oi-
dinaiy list. Since we would like the queue to be an identiable object, we
can use cons to combine the two pointeis. Tus, the queue itself will be
the cons of the two pointeis. liguie 3.19 illustiates this iepiesentation.
3
c
front-ptr
q
a b
rear-ptr
Figure 3.19: lmplementation of a queue as a list with fiont
and ieai pointeis.
To dene the queue opeiations we use the following pioceduies,
which enable us to select and to modify the fiont and ieai pointeis of a
queue
(define (front-ptr queue) (car queue))
(define (rear-ptr queue) (cdr queue))
(define (set-front-ptr! queue item)
(set-car! queue item))
(define (set-rear-ptr! queue item)
(set-cdr! queue item))
Now we can implement the actual queue opeiations. We will considei
a queue to be empty if its fiont pointei is the empty list
(define (empty-queue? queue)
(null? (front-ptr queue)))
Te make-queue constiuctoi ietuins, as an initially empty queue, a paii
whose car and cdr aie both the empty list
(define (make-queue) (cons '() '()))
3
front-ptr
q
a b
rear-ptr
c d
Figure 3.20: Result of using (insert-queue! q 'd) on the
queue of liguie 3.19.
To select the item at the fiont of the queue, we ietuin the car of the paii
indicated by the fiont pointei
(define (front-queue queue)
(if (empty-queue? queue)
(error "FRONT called with an empty queue" queue)
(car (front-ptr queue))))
To inseit an item in a queue, we follow the method whose iesult is in-
dicated in liguie 3.20. We ist cieate a new paii whose car is the item
to be inseited and whose cdr is the empty list. lf the queue was initially
empty, we set the fiont and ieai pointeis of the queue to this new paii.
Otheiwise, we modify the nal paii in the queue to point to the new
paii, and also set the ieai pointei to the new paii.
(define (insert-queue! queue item)
(let ((new-pair (cons item '())))
(cond ((empty-queue? queue)
(set-front-ptr! queue new-pair)
(set-rear-ptr! queue new-pair)
queue)
3
front-ptr
q
a b
rear-ptr
c d
Figure 3.21: Result of using (delete-queue! q) on the
queue of liguie 3.20.
(else
(set-cdr! (rear-ptr queue) new-pair)
(set-rear-ptr! queue new-pair)
queue))))
To delete the item at the fiont of the queue, we meiely modify the fiont
pointei so that it now points at the second item in the queue, which
can be found by following the cdr pointei of the ist item (see liguie
3.21)
22
(define (delete-queue! queue)
(cond ((empty-queue? queue)
(error "DELETE! called with an empty queue" queue))
(else (set-front-ptr! queue (cdr (front-ptr queue)))
queue)))
22
lf the ist item is the nal item in the queue, the fiont pointei will be the empty
list afei the deletion, which will maik the queue as empty, we neednt woiiy about
updating the ieai pointei, which will still point to the deleted item, because empty-
queue? looks only at the fiont pointei.
38
Exercise 3.21: Ben Bitdiddle decides to test the queue im-
plementation desciibed above. He types in the pioceduies
to the Lisp inteipietei and pioceeds to tiy them out
(define q1 (make-queue))
(insert-queue! q1 'a)
((a) a)
(insert-queue! q1 'b)
((a b) b)
(delete-queue! q1)
((b) b)
(delete-queue! q1)
(() b)
lts all wiong' he complains. Te inteipieteis iesponse
shows that the last item is inseited into the queue twice.
And when l delete both items, the second b is still theie,
so the queue isnt empty, even though its supposed to be.
Eva Lu Atoi suggests that Ben has misundeistood what is
happening. lts not that the items aie going into the queue
twice, she explains. lts just that the standaid Lisp piintei
doesnt know how to make sense of the queue iepiesenta-
tion. lf you want to see the queue piinted coiiectly, youll
have to dene youi own piint pioceduie foi queues. Ex-
plain what Eva Lu is talking about. ln paiticulai, show why
Bens examples pioduce the piinted iesults that they do.
Dene a pioceduie print-queue that takes a queue as in-
put and piints the sequence of items in the queue.
Exercise 3.22: lnstead of iepiesenting a queue as a paii of
pointeis, we can build a queue as a pioceduie with local
state. Te local state will consist of pointeis to the begin-
39
ning and the end of an oidinaiy list. Tus, the make-queue
pioceduie will have the foim
(define (make-queue)
(let ((front-ptr . . . )
(rear-ptr . . . ))
definitions of internal procedures
(define (dispatch m) . . .)
dispatch))
Complete the denition of make-queue and piovide imple-
mentations of the queue opeiations using this iepiesenta-
tion.
Exercise 3.23: AJeqve (double-ended queue) is a sequence
in which items can be inseited and deleted at eithei the
fiont oi the ieai. Opeiations on deques aie the constiuctoi
make-deque, the piedicate empty-deque?, selectois front-
deque and rear-deque, mutatois front-insert-deque!,
rear-insert-deque!, front-delete-deque!, and rear-delete-
deque!. Show how to iepiesent deques using paiis, and
give implementations of the opeiations.
23
All opeiations
should be accomplished in (1) steps.
3.3.3 Representing Tables
When we studied vaiious ways of iepiesenting sets in Chaptei 2, we
mentioned in Section 2.3.3 the task of maintaining a table of iecoids in-
dexed by identifying keys. ln the implementation of data-diiected pio-
giamming in Section 2.4.3, we made extensive use of two-dimensional
23
Be caieful not to make the inteipietei tiy to piint a stiuctuie that contains cycles.
(See Exeicise 3.13.)
30
a b c 1 2 3
*table*
table
Figure 3.22: A table iepiesented as a headed list.
tables, in which infoimation is stoied and ietiieved using two keys. Heie
we see how to build tables as mutable list stiuctuies.
We ist considei a one-dimensional table, in which each value is
stoied undei a single key. We implement the table as a list of iecoids,
each of which is implemented as a paii consisting of a key and the as-
sociated value. Te iecoids aie glued togethei to foim a list by paiis
whose cars point to successive iecoids. Tese gluing paiis aie called
the |oc||one of the table. ln oidei to have a place that we can change
when we add a newiecoid to the table, we build the table as a |eoJeJ |:s.
A headed list has a special backbone paii at the beginning, which holds
a dummy iecoidin this case the aibitiaiily chosen symbol *table*.
liguie 3.22 shows the box-and-pointei diagiam foi the table
a: 1
b: 2
c: 3
To extiact infoimation fioma table we use the lookup pioceduie, which
takes a key as aigument and ietuins the associated value (oi false if
31
theie is no value stoied undei that key). Lookup is dened in teims of the
assoc opeiation, which expects a key and a list of iecoids as aiguments.
Note that assoc nevei sees the dummy iecoid. Assoc ietuins the iecoid
that has the given key as its car.
24
Lookup then checks to see that the
iesulting iecoid ietuined by assoc is not false, and ietuins the value
(the cdr) of the iecoid.
(define (lookup key table)
(let ((record (assoc key (cdr table))))
(if record
(cdr record)
false)))
(define (assoc key records)
(cond ((null? records) false)
((equal? key (caar records)) (car records))
(else (assoc key (cdr records)))))
To inseit a value in a table undei a specied key, we ist use assoc
to see if theie is alieady a iecoid in the table with this key. lf not, we
foim a new iecoid by consing the key with the value, and inseit this at
the head of the tables list of iecoids, afei the dummy iecoid. lf theie
alieady is a iecoid with this key, we set the cdr of this iecoid to the
designated new value. Te headei of the table piovides us with a xed
location to modify in oidei to inseit the new iecoid.
2
(define (insert! key value table)
(let ((record (assoc key (cdr table))))
24
Because assoc uses equal?, it can iecognize keys that aie symbols, numbeis, oi
list stiuctuie.
2
Tus, the ist backbone paii is the object that iepiesents the table itself , that is,
a pointei to the table is a pointei to this paii. Tis same backbone paii always staits
the table. lf we did not aiiange things in this way, insert! would have to ietuin a new
value foi the stait of the table when it added a new iecoid.
32
(if record
(set-cdr! record value)
(set-cdr! table
(cons (cons key value)
(cdr table)))))
'ok)
To constiuct a new table, we simply cieate a list containing the symbol
*table*
(define (make-table)
(list '*table*))
Two-dimensional tables
ln a two-dimensional table, each value is indexed by two keys. We can
constiuct such a table as a one-dimensional table in which each key
identies a subtable. liguie 3.23 shows the box-and-pointei diagiam
foi the table
math: +: 43 letters: a: 97
-: 45 b: 98
*: 42
which has two subtables. (Te subtables dont need a special headei
symbol, since the key that identies the subtable seives this puipose.)
When we look up an item, we use the ist key to identify the coiiect
subtable. Ten we use the second key to identify the iecoid within the
subtable.
(define (lookup key-1 key-2 table)
(let ((subtable
(assoc key-1 (cdr table))))
33
+ - * 43 45 42
*table*
a b 97 98
letters
math
table
Figure 3.23: A two-dimensional table.
(if subtable
(let ((record
(assoc key-2 (cdr subtable))))
(if record
(cdr record)
false))
false)))
34
To inseit a new item undei a paii of keys, we use assoc to see if
theie is a subtable stoied undei the ist key. lf not, we build a new
subtable containing the single iecoid (key-2, value) and inseit it into
the table undei the ist key. lf a subtable alieady exists foi the ist key,
we inseit the new iecoid into this subtable, using the inseition method
foi one-dimensional tables desciibed above
(define (insert! key-1 key-2 value table)
(let ((subtable (assoc key-1 (cdr table))))
(if subtable
(let ((record (assoc key-2 (cdr subtable))))
(if record
(set-cdr! record value)
(set-cdr! subtable
(cons (cons key-2 value)
(cdr subtable)))))
(set-cdr! table
(cons (list key-1
(cons key-2 value))
(cdr table)))))
'ok)
Creating local tables
Te lookup and insert! opeiations dened above take the table as an
aigument. Tis enables us to use piogiams that access moie than one ta-
ble. Anothei way to deal with multiple tables is to have sepaiate lookup
and insert! pioceduies foi each table. We can do this by iepiesenting
a table pioceduially, as an object that maintains an inteinal table as pait
of its local state. When sent an appiopiiate message, this table object
supplies the pioceduie with which to opeiate on the inteinal table. Heie
is a geneiatoi foi two-dimensional tables iepiesented in this fashion
3
(define (make-table)
(let ((local-table (list '*table*)))
(define (lookup key-1 key-2)
(let ((subtable
(assoc key-1 (cdr local-table))))
(if subtable
(let ((record
(assoc key-2 (cdr subtable))))
(if record (cdr record) false))
false)))
(define (insert! key-1 key-2 value)
(let ((subtable
(assoc key-1 (cdr local-table))))
(if subtable
(let ((record
(assoc key-2 (cdr subtable))))
(if record
(set-cdr! record value)
(set-cdr! subtable
(cons (cons key-2 value)
(cdr subtable)))))
(set-cdr! local-table
(cons (list key-1 (cons key-2 value))
(cdr local-table)))))
'ok)
(define (dispatch m)
(cond ((eq? m 'lookup-proc) lookup)
((eq? m 'insert-proc!) insert!)
(else (error "Unknown operation: TABLE" m))))
dispatch))
Using make-table, we could implement the get and put opeiations
used in Section 2.4.3 foi data-diiected piogiamming, as follows
3
(define operation-table (make-table))
(define get (operation-table 'lookup-proc))
(define put (operation-table 'insert-proc!))
Get takes as aiguments two keys, and put takes as aiguments two keys
and a value. Both opeiations access the same local table, which is en-
capsulated within the object cieated by the call to make-table.
Exercise 3.24: ln the table implementations above, the keys
aie tested foi equality using equal? (called by assoc). Tis
is not always the appiopiiate test. loi instance, we might
have a table with numeiic keys in which we dont need an
exact match to the numbei weie looking up, but only a
numbei within some toleiance of it. Design a table con-
stiuctoi make-table that takes as an aigument a same-key?
pioceduie that will be used to test equality of keys. Make-
table should ietuin a dispatch pioceduie that can be used
to access appiopiiate lookup and insert! pioceduies foi a
local table.
Exercise 3.25: Geneializing one- and two-dimensional ta-
bles, show how to implement a table in which values aie
stoied undei an aibitiaiy numbei of keys and dieient val-
ues may be stoied undei dieient numbeis of keys. Te
lookup and insert! pioce- duies should take as input a
list of keys used to access the table.
Exercise 3.26: To seaich a table as implemented above, one
needs to scan thiough the list of iecoids. Tis is basically
the unoideied list iepiesentation of Section 2.3.3. loi laige
tables, it may be moie ecient to stiuctuie the table in a dif-
feient mannei. Desciibe a table implementation wheie the
3
(key, value) iecoids aie oiganized using a binaiy tiee, as-
suming that keys can be oideied in some way (e.g., numei-
ically oi alphabetically). (Compaie Exeicise 2. of Chaptei
2.)
Exercise 3.27: Meo::o:on (also called o|v|o:on) is a tech-
nique that enables a pioceduie to iecoid, in a local table,
values that have pieviously been computed. Tis technique
can make a vast dieience in the peifoimance of a piogiam.
A memoized pioceduie maintains a table in which values
of pievious calls aie stoied using as keys the aiguments
that pioduced the values. When the memoized pioceduie
is asked to compute a value, it ist checks the table to see
if the value is alieady theie and, if so, just ietuins that value.
Otheiwise, it computes the new value in the oidinaiy way
and stoies this in the table. As an example of memoization,
iecall fiom Section 1.2.2 the exponential piocess foi com-
puting libonacci numbeis
(define (fib n)
(cond ((= n 0) 0)
((= n 1) 1)
(else (+ (fib (- n 1)) (fib (- n 2))))))
Te memoized veision of the same pioceduie is
(define memo-fib
(memoize
(lambda (n)
(cond ((= n 0) 0)
((= n 1) 1)
(else (+ (memo-fib (- n 1))
(memo-fib (- n 2))))))))
38
wheie the memoizei is dened as
(define (memoize f)
(let ((table (make-table)))
(lambda (x)
(let ((previously-computed-result
(lookup x table)))
(or previously-computed-result
(let ((result (f x)))
(insert! x result table)
result))))))
Diaw an enviionment diagiam to analyze the computation
of (memo-fib 3). Explain why memo-fib computes the n
th
libonacci numbei in a numbei of steps piopoitional to n.
Would the scheme still woik if we had simply dened memo-
fib to be (memoize fib)`
3.3.4 A Simulator for Digital Circuits
Designing complex digital systems, such as computeis, is an impoitant
engineeiing activity. Digital systems aie constiucted by inteiconnect-
ing simple elements. Although the behavioi of these individual elements
is simple, netwoiks of them can have veiy complex behavioi. Computei
simulation of pioposed ciicuit designs is an impoitant tool used by digi-
tal systems engineeis. ln this section we design a systemfoi peifoiming
digital logic simulations. Tis system typies a kind of piogiam called
an e+enJr:+en s:v|o:on, in which actions (events) tiiggei fuithei
events that happen at a latei time, which in tuin tiiggei moie events,
and so on.
Oui computational model of a ciicuit will be composed of objects
that coiiespond to the elementaiy components fiom which the ciicuit
39
Inverter And-gate Or-gate
Figure 3.24: Piimitive functions in the digital logic simulatoi.
is constiucted. Teie aie +:res, which caiiy J:g:o| s:gno|s. A digital sig-
nal may at any moment have only one of two possible values, 0 and
1. Teie aie also vaiious types of digital [vnc:on |o:es, which connect
wiies caiiying input signals to othei output wiies. Such boxes pioduce
output signals computed fiom theii input signals. Te output signal is
delayed by a time that depends on the type of the function box. loi
example, an :n+erer is a piimitive function box that inveits its input.
lf the input signal to an inveitei changes to 0, then one inveitei-delay
latei the inveitei will change its output signal to 1. lf the input signal to
an inveitei changes to 1, then one inveitei-delay latei the inveitei will
change its output signal to 0. We diaw an inveitei symbolically as in
liguie 3.24. An onJgoe, also shown in liguie 3.24, is a piimitive func-
tion box with two inputs and one output. lt diives its output signal to
a value that is the |og:co| onJ of the inputs. Tat is, if both of its input
signals become 1, then one and-gate-delay time latei the and-gate will
foice its output signal to be 1, otheiwise the output will be 0. An orgoe
is a similai two-input piimitive function box that diives its output sig-
nal to a value that is the |og:co| or of the inputs. Tat is, the output will
become 1 if at least one of the input signals is 1, otheiwise the output
will become 0.
We can connect piimitive functions togethei to constiuct moie com-
plex functions. To accomplish this we wiie the outputs of some function
boxes to the inputs of othei function boxes. loi example, the |o|[oJJer
30
D
E
A
B
S
C
Figure 3.25: A half-addei ciicuit.
ciicuit shown in liguie 3.2 consists of an oi-gate, two and-gates, and
an inveitei. lt takes two input signals, A and B, and has two output sig-
nals, S and C. S will become 1 whenevei piecisely one of A and B is 1,
and C will become 1 whenevei A and B aie both 1. We can see fiom the
guie that, because of the delays involved, the outputs may be genei-
ated at dieient times. Many of the diculties in the design of digital
ciicuits aiise fiom this fact.
We will now build a piogiam foi modeling the digital logic ciicuits
we wish to study. Te piogiam will constiuct computational objects
modeling the wiies, which will hold the signals. lunction boxes will
be modeled by pioceduies that enfoice the coiiect ielationships among
the signals.
One basic element of oui simulation will be a pioceduie make-wire,
which constiucts wiies. loi example, we can constiuct six wiies as fol-
lows
(define a (make-wire))
(define b (make-wire))
(define c (make-wire))
(define d (make-wire))
(define e (make-wire))
(define s (make-wire))
31
We auach a function box to a set of wiies by calling a pioceduie that
constiucts that kind of box. Te aiguments to the constiuctoi pioceduie
aie the wiies to be auached to the box. loi example, given that we can
constiuct and-gates, oi-gates, and inveiteis, we can wiie togethei the
half-addei shown in liguie 3.2
(or-gate a b d)
ok
(and-gate a b c)
ok
(inverter c e)
ok
(and-gate d e s)
ok
Beuei yet, we can explicitly name this opeiation by dening a pioceduie
half-adder that constiucts this ciicuit, given the foui exteinal wiies to
be auached to the half-addei
(define (half-adder a b s c)
(let ((d (make-wire)) (e (make-wire)))
(or-gate a b d)
(and-gate a b c)
(inverter c e)
(and-gate d e s)
'ok))
Te advantage of making this denition is that we can use half-adder
itself as a building block in cieating moie complex ciicuits. liguie 3.2,
foi example, shows a [v||oJJer composed of two half-addeis and an
oi-gate.
2
We can constiuct a full-addei as follows
2
A full-addei is a basic ciicuit element used in adding two binaiy numbeis. Heie
A and B aie the bits at coiiesponding positions in the two numbeis to be added, and
32
half-
adder
half-
adder
A
B
C
in
SUM
C
out
or
Figure 3.26: A full-addei ciicuit.
(define (full-adder a b c-in sum c-out)
(let ((s (make-wire)) (c1 (make-wire)) (c2 (make-wire)))
(half-adder b c-in s c1)
(half-adder a s sum c2)
(or-gate c1 c2 c-out)
'ok))
Having dened full-adder as a pioceduie, we can nowuse it as a build-
ing block foi cieating still moie complex ciicuits. (loi example, see Ex-
eicise 3.30.)
ln essence, oui simulatoi piovides us with the tools to constiuct a
language of ciicuits. lf we adopt the geneial peispective on languages
with which we appioached the study of Lisp in Section 1.1, we can say
that the piimitive function boxes foim the piimitive elements of the
language, that wiiing boxes togethei piovides a means of combination,
and that specifying wiiing paueins as pioceduies seives as a means of
abstiaction.
C
in
is the caiiy bit fiom the addition one place to the iight. Te ciicuit geneiates SUM,
which is the sum bit in the coiiesponding position, and C
out
, which is the caiiy bit to
be piopagated to the lef.
33
Primitive function boxes
Te piimitive function boxes implement the foices by which a change
in the signal on one wiie inuences the signals on othei wiies. To build
function boxes, we use the following opeiations on wiies
(get-signal wire)
ietuins the cuiient value of the signal on the wiie.
(set-signal! wire new value)
changes the value of the signal on the wiie to the new value.
(add-action! wire procedure of no arguments)
asseits that the designated pioceduie should be iun whenevei
the signal on the wiie changes value. Such pioceduies aie the
vehicles by which changes in the signal value on the wiie aie
communicated to othei wiies.
ln addition, we will make use of a pioceduie after-delay that takes a
time delay and a pioceduie to be iun and executes the given pioceduie
afei the given delay.
Using these pioceduies, we can dene the piimitive digital logic
functions. To connect an input to an output thiough an inveitei, we use
add-action! to associate with the input wiie a pioceduie that will be
iun whenevei the signal on the input wiie changes value. Te pioce-
duie computes the logical-not of the input signal, and then, afei one
inverter-delay, sets the output signal to be this new value
(define (inverter input output)
(define (invert-input)
(let ((new-value (logical-not (get-signal input))))
34
(after-delay inverter-delay
(lambda () (set-signal! output new-value)))))
(add-action! input invert-input) 'ok)
(define (logical-not s)
(cond ((= s 0) 1)
((= s 1) 0)
(else (error "Invalid signal" s))))
An and-gate is a liule moie complex. Te action pioceduie must be iun
if eithei of the inputs to the gate changes. lt computes the logical-
and (using a pioceduie analogous to logical-not) of the values of the
signals on the input wiies and sets up a change to the new value to
occui on the output wiie afei one and-gate-delay.
(define (and-gate a1 a2 output)
(define (and-action-procedure)
(let ((new-value
(logical-and (get-signal a1) (get-signal a2))))
(after-delay
and-gate-delay
(lambda () (set-signal! output new-value)))))
(add-action! a1 and-action-procedure)
(add-action! a2 and-action-procedure)
'ok)
Exercise 3.28: Dene an oi-gate as a piimitive function
box. Youi or-gate constiuctoi should be similai to and-
gate.
Exercise 3.29: Anothei way to constiuct an oi-gate is as
a compound digital logic device, built fiom and-gates and
inveiteis. Dene a pioceduie or-gate that accomplishes
3
A
1
B
1
C
1
A
2
B
2
C
2
A
3
B
3
C
3
A
n
B
n
C
n
= 0
S
1
C
S
2
S
3
S
n
C
n-1
FA FA FA FA
Figure 3.27: A iipple-caiiy addei foi n-bit numbeis.
this. What is the delay time of the oi-gate in teims of and-
gate-delay and inverter-delay`
Exercise 3.30: liguie 3.2 shows a r:|ecorry oJJer foimed
by stiinging togethei n full-addeis. Tis is the simplest foim
of paiallel addei foi adding two n-bit binaiy numbeis. Te
inputs A
1
, A
2
, A
3
, . . ., A
n
and B
1
, B
2
, B
3
, . . ., B
n
aie the
two binaiy numbeis to be added (each A
k
and B
k
is a 0 oi
a 1). Te ciicuit geneiates S
1
, S
2
, S
3
, . . ., S
n
, the n bits of
the sum, and C, the caiiy fiom the addition. Wiite a pioce-
duie ripple-carry-adder that geneiates this ciicuit. Te
pioceduie should take as aiguments thiee lists of n wiies
eachthe A
k
, the B
k
, and the S
k
and also anothei wiie C.
Te majoi diawback of the iipple-caiiy addei is the need
to wait foi the caiiy signals to piopagate. What is the delay
needed to obtain the complete output fiom an n-bit iipple-
caiiy addei, expiessed in teims of the delays foi and-gates,
oi-gates, and inveiteis`
3
Representing wires
A wiie in oui simulation will be a computational object with two local
state vaiiables a signal-value (initially taken to be 0) and a collec-
tion of action-procedures to be iun when the signal changes value.
We implement the wiie, using message-passing style, as a collection of
local pioceduies togethei with a dispatch pioceduie that selects the ap-
piopiiate local opeiation, just as we did with the simple bank-account
object in Section 3.1.1
(define (make-wire)
(let ((signal-value 0) (action-procedures '()))
(define (set-my-signal! new-value)
(if (not (= signal-value new-value))
(begin (set! signal-value new-value)
(call-each action-procedures))
'done))
(define (accept-action-procedure! proc)
(set! action-procedures
(cons proc action-procedures))
(proc))
(define (dispatch m)
(cond ((eq? m 'get-signal) signal-value)
((eq? m 'set-signal!) set-my-signal!)
((eq? m 'add-action!) accept-action-procedure!)
(else (error "Unknown operation: WIRE" m))))
dispatch))
Te local pioceduie set-my-signal! tests whethei the newsignal value
changes the signal on the wiie. lf so, it iuns each of the action pioce-
duies, using the following pioceduie call-each, which calls each of the
items in a list of no-aigument pioceduies
(define (call-each procedures)
3
(if (null? procedures)
'done
(begin ((car procedures))
(call-each (cdr procedures)))))
Te local pioceduie accept-action-procedure! adds the given pioce-
duie to the list of pioceduies to be iun, and then iuns the newpioceduie
once. (See Exeicise 3.31.)
With the local dispatch pioceduie set up as specied, we can pio-
vide the following pioceduies to access the local opeiations on wiies
2
(define (get-signal wire) (wire 'get-signal))
(define (set-signal! wire new-value)
((wire 'set-signal!) new-value))
(define (add-action! wire action-procedure)
((wire 'add-action!) action-procedure))
Wiies, which have time-vaiying signals and may be inciementally at-
tached to devices, aie typical of mutable objects. We have modeled them
as pioceduies with local state vaiiables that aie modied by assignment.
When a new wiie is cieated, a new set of state vaiiables is allocated
(by the let expiession in make-wire) and a new dispatch pioceduie
is constiucted and ietuined, captuiing the enviionment with the new
state vaiiables.
2
Tese pioceduies aie simply syntactic sugai that allow us to use oidinaiy pio-
ceduial syntax to access the local pioceduies of objects. lt is stiiking that we can in-
teichange the iole of pioceduies and data in such a simple way. loi example, if we
wiite (wire 'get-signal) we think of wire as a pioceduie that is called with the mes-
sage get-signal as input. Alteinatively, wiiting (get-signal wire) encouiages us to
think of wire as a data object that is the input to a pioceduie get-signal. Te tiuth of
the mauei is that, in a language in which we can deal with pioceduies as objects, theie
is no fundamental dieience between pioceduies and data, and we can choose oui
syntactic sugai to allow us to piogiam in whatevei style we choose.
38
Te wiies aie shaied among the vaiious devices that have been con-
nected to them. Tus, a change made by an inteiaction with one device
will aect all the othei devices auached to the wiie. Te wiie communi-
cates the change to its neighbois by calling the action pioceduies pio-
vided to it when the connections weie established.
The agenda
Te only thing needed to complete the simulatoi is after-delay. Te
idea heie is that we maintain a data stiuctuie, called an ogenJo, that
contains a schedule of things to do. Te following opeiations aie dened
foi agendas
(make-agenda) ietuins a new empty agenda.
(empty-agenda? agenda) is tiue if the specied agenda is
empty.
(first-agenda-item agenda) ietuins the ist item on the
agenda.
(remove-first-agenda-item! agenda) modies the agenda
by iemoving the ist item.
(add-to-agenda! time action agenda) modies the
agenda by adding the given action pioceduie to be iun at the spec-
ied time.
(current-time agenda) ietuins the cuiient simulation time.
Te paiticulai agenda that we use is denoted by the-agenda. Te pio-
ceduie after-delay adds new elements to the-agenda
39
(define (after-delay delay action)
(add-to-agenda! (+ delay (current-time the-agenda))
action
the-agenda))
Te simulation is diiven by the pioceduie propagate, which opeiates
on the-agenda, executing each pioceduie on the agenda in sequence. ln
geneial, as the simulation iuns, new items will be added to the agenda,
and propagate will continue the simulation as long as theie aie items
on the agenda
(define (propagate)
(if (empty-agenda? the-agenda)
'done
(let ((first-item (first-agenda-item the-agenda)))
(first-item)
(remove-first-agenda-item! the-agenda)
(propagate))))
A sample simulation
Te following pioceduie, which places a piobe on a wiie, shows the
simulatoi in action. Te piobe tells the wiie that, whenevei its signal
changes value, it should piint the new signal value, togethei with the
cuiient time and a name that identies the wiie
(define (probe name wire)
(add-action! wire
(lambda ()
(newline)
(display name) (display " ")
(display (current-time the-agenda))
(display " New-value = ")
(display (get-signal wire)))))
380
We begin by initializing the agenda and specifying delays foi the piim-
itive function boxes
(define the-agenda (make-agenda))
(define inverter-delay 2)
(define and-gate-delay 3)
(define or-gate-delay 5)
Now we dene foui wiies, placing piobes on two of them
(define input-1 (make-wire))
(define input-2 (make-wire))
(define sum (make-wire))
(define carry (make-wire))
(probe 'sum sum)
sum 0 New-value = 0
(probe 'carry carry)
carry 0 New-value = 0
Next we connect the wiies in a half-addei ciicuit (as in liguie 3.2), set
the signal on input-1 to 1, and iun the simulation
(half-adder input-1 input-2 sum carry)
ok
(set-signal! input-1 1)
done
(propagate)
sum 8 New-value = 1
done
Te sum signal changes to 1 at time 8. We aie now eight time units fiom
the beginning of the simulation. At this point, we can set the signal on
input-2 to 1 and allow the values to piopagate
381
(set-signal! input-2 1)
done
(propagate)
carry 11 New-value = 1
sum 16 New-value = 0
done
Te carry changes to 1 at time 11 and the sum changes to 0 at time 1.
Exercise 3.31: Te inteinal pioceduie accept-action-procedure!
dened in make-wire species that when a new action pio-
ceduie is added to a wiie, the pioceduie is immediately
iun. Explain why this initialization is necessaiy. ln paiticu-
lai, tiace thiough the half-addei example in the paiagiaphs
above and say howthe systems iesponse would diei if we
had dened accept-action-procedure! as
(define (accept-action-procedure! proc)
(set! action-procedures
(cons proc action-procedures)))
Implementing the agenda
linally, we give details of the agenda data stiuctuie, which holds the
pioceduies that aie scheduled foi futuie execution.
Te agenda is made up of :e segens. Each time segment is a
paii consisting of a numbei (the time) and a queue (see Exeicise 3.32)
that holds the pioceduies that aie scheduled to be iun duiing that time
segment.
(define (make-time-segment time queue)
(cons time queue))
382
(define (segment-time s) (car s))
(define (segment-queue s) (cdr s))
We will opeiate on the time-segment queues using the queue opeiations
desciibed in Section 3.3.2.
Te agenda itself is a one-dimensional table of time segments. lt
dieis fiomthe tables desciibed in Section 3.3.3 in that the segments will
be soited in oidei of incieasing time. ln addition, we stoie the cvrren
:e (i.e., the time of the last action that was piocessed) at the head of
the agenda. A newly constiucted agenda has no time segments and has
a cuiient time of 0
28
(define (make-agenda) (list 0))
(define (current-time agenda) (car agenda))
(define (set-current-time! agenda time)
(set-car! agenda time))
(define (segments agenda) (cdr agenda))
(define (set-segments! agenda segments)
(set-cdr! agenda segments))
(define (first-segment agenda) (car (segments agenda)))
(define (rest-segments agenda) (cdr (segments agenda)))
An agenda is empty if it has no time segments
(define (empty-agenda? agenda)
(null? (segments agenda)))
To add an action to an agenda, we ist check if the agenda is empty. lf so,
we cieate a time segment foi the action and install this in the agenda.
Otheiwise, we scan the agenda, examining the time of each segment.
lf we nd a segment foi oui appointed time, we add the action to the
28
Te agenda is a headed list, like the tables in Section 3.3.3, but since the list is
headed by the time, we do not need an additional dummy headei (such as the *table*
symbol used with tables).
383
associated queue. lf we ieach a time latei than the one to which we aie
appointed, we inseit a new time segment into the agenda just befoie it.
lf we ieach the end of the agenda, we must cieate a new time segment
at the end.
(define (add-to-agenda! time action agenda)
(define (belongs-before? segments)
(or (null? segments)
(< time (segment-time (car segments)))))
(define (make-new-time-segment time action)
(let ((q (make-queue)))
(insert-queue! q action)
(make-time-segment time q)))
(define (add-to-segments! segments)
(if (= (segment-time (car segments)) time)
(insert-queue! (segment-queue (car segments))
action)
(let ((rest (cdr segments)))
(if (belongs-before? rest)
(set-cdr!
segments
(cons (make-new-time-segment time action)
(cdr segments)))
(add-to-segments! rest)))))
(let ((segments (segments agenda)))
(if (belongs-before? segments)
(set-segments!
agenda
(cons (make-new-time-segment time action)
segments))
(add-to-segments! segments))))
Te pioceduie that iemoves the ist item fiom the agenda deletes the
item at the fiont of the queue in the ist time segment. lf this deletion
384
makes the time segment empty, we iemove it fiomthe list of segments
29
(define (remove-first-agenda-item! agenda)
(let ((q (segment-queue (first-segment agenda))))
(delete-queue! q)
(if (empty-queue? q)
(set-segments! agenda (rest-segments agenda)))))
Te ist agenda item is found at the head of the queue in the ist
time segment. Whenevei we extiact an item, we also update the cui-
ient time
30
(define (first-agenda-item agenda)
(if (empty-agenda? agenda)
(error "Agenda is empty: FIRST-AGENDA-ITEM")
(let ((first-seg (first-segment agenda)))
(set-current-time! agenda
(segment-time first-seg))
(front-queue (segment-queue first-seg)))))
Exercise 3.32: Te pioceduies to be iun duiing each time
segment of the agenda aie kept in a queue. Tus, the pio-
ceduies foi each segment aie called in the oidei in which
they weie added to the agenda (ist in, ist out). Explain
why this oidei must be used. ln paiticulai, tiace the behav-
ioi of an and-gate whose inputs change fiom 0, 1 to 1, 0
29
Obseive that the if expiession in this pioceduie has no o|erno:+e expiession.
Such a one-aimed if statement is used to decide whethei to do something, iathei
than to select between two expiessions. An if expiession ietuins an unspecied value
if the piedicate is false and theie is no o|erno:+e.
30
ln this way, the cuiient time will always be the time of the action most iecently
piocessed. Stoiing this time at the head of the agenda ensuies that it will still be avail-
able even if the associated time segment has been deleted.
38
in the same segment and say how the behavioi would dif-
fei if we stoied a segments pioceduies in an oidinaiy list,
adding and iemoving pioceduies only at the fiont (last in,
ist out).
3.3.5 Propagation of Constraints
Computei piogiams aie tiaditionally oiganized as one-diiectional com-
putations, which peifoim opeiations on piespecied aiguments to pio-
duce desiied outputs. On the othei hand, we ofen model systems in
teims of ielations among quantities. loi example, a mathematical model
of a mechanical stiuctuie might include the infoimation that the deec-
tion d of a metal iod is ielated to the foice F on the iod, the length L
of the iod, the cioss-sectional aiea A, and the elastic modulus E via the
equation
dAE = F L.
Such an equation is not one-diiectional. Given any foui of the quanti-
ties, we can use it to compute the fh. Yet tianslating the equation into
a tiaditional computei language would foice us to choose one of the
quantities to be computed in teims of the othei foui. Tus, a pioceduie
foi computing the aiea A could not be used to compute the deection
d, even though the computations of A and d aiise fiom the same equa-
tion.
31
31
Constiaint piopagation ist appeaied in the inciedibly foiwaid-looking sxr1cu
v~u systemof lvan Sutheiland (193). Abeautiful constiaint-piopagation systembased
on the Smalltalk language was developed by Alan Boining (19) at Xeiox Palo Alto
Reseaich Centei. Sussman, Stallman, and Steele applied constiaint piopagation to elec-
tiical ciicuit analysis (Sussman and Stallman 19, Sussman and Steele 1980). TK'Solvei
(Konopasek and Jayaiaman 1984) is an extensive modeling enviionment based on
constiaints.
38
ln this section, we sketch the design of a language that enables us
to woik in teims of ielations themselves. Te piimitive elements of the
language aie r:::+e consro:ns, which state that ceitain ielations hold
between quantities. loi example, (adder a b c) species that the quan-
tities a, b, and c must be ielated by the equation a b = c, (multiplier
x y z) expiesses the constiaint xy = z, and (constant 3.14 x) says
that the value of x must be 3.14.
Oui language piovides a means of combining piimitive constiaints
in oidei to expiess moie complex ielations. We combine constiaints
by constiucting consro:n ne+or|s, in which constiaints aie joined by
connecors. A connectoi is an object that holds a value that may pai-
ticipate in one oi moie constiaints. loi example, we know that the ie-
lationship between lahienheit and Celsius tempeiatuies is
9C = (F 32).
Such a constiaint can be thought of as a netwoik consisting of piimitive
addei, multipliei, and constant constiaints (liguie 3.28). ln the guie,
we see on the lef a multipliei box with thiee teiminals, labeled m1,
m2, and p. Tese connect the multipliei to the iest of the netwoik as
follows Te m1 teiminal is linked to a connectoi C, which will hold the
Celsius tempeiatuie. Te m2 teiminal is linked to a connectoi w, which
is also linked to a constant box that holds 9. Te p teiminal, which the
multipliei box constiains to be the pioduct of m1 and m2, is linked to
the p teiminal of anothei multipliei box, whose m2 is connected to a
constant and whose m1 is connected to one of the teims in a sum.
Computation by such a netwoik pioceeds as follows When a con-
nectoi is given a value (by the usei oi by a constiaint box to which
it is linked), it awakens all of its associated constiaints (except foi the
constiaint that just awakened it) to infoim them that it has a value.
38
m1
m2
p
*
p
m1
m2
*
u
v
32 5 9
a1
a2
s + F
C
w x y
Figure 3.28: Te ielation 9C = (F 32) expiessed as a
constiaint netwoik.
Each awakened constiaint box then polls its connectois to see if theie
is enough infoimation to deteimine a value foi a connectoi. lf so, the
box sets that connectoi, which then awakens all of its associated con-
stiaints, and so on. loi instance, in conveision between Celsius and
lahienheit, w, x, and y aie immediately set by the constant boxes to 9,
, and 32, iespectively. Te connectois awaken the multiplieis and the
addei, which deteimine that theie is not enough infoimation to pio-
ceed. lf the usei (oi some othei pait of the netwoik) sets C to a value
(say 2), the lefmost multipliei will be awakened, and it will set u to
2 9 = 22. Ten u awakens the second multipliei, which sets v to 4,
and v awakens the addei, which sets f to .
Using the constraint system
To use the constiaint system to caiiy out the tempeiatuie computation
outlined above, we ist cieate two connectois, C and F, by calling the
constiuctoi make-connector, and link C and F in an appiopiiate net-
woik
(define C (make-connector))
(define F (make-connector))
388
(celsius-fahrenheit-converter C F)
ok
Te pioceduie that cieates the netwoik is dened as follows
(define (celsius-fahrenheit-converter c f)
(let ((u (make-connector))
(v (make-connector))
(w (make-connector))
(x (make-connector))
(y (make-connector)))
(multiplier c w u)
(multiplier v x u)
(adder v y f)
(constant 9 w)
(constant 5 x)
(constant 32 y)
'ok))
Tis pioceduie cieates the inteinal connectois u, v, w, x, and y, and links
them as shown in liguie 3.28 using the piimitive constiaint constiuc-
tois adder, multiplier, and constant. Just as with the digital-ciicuit
simulatoi of Section 3.3.4, expiessing these combinations of piimitive
elements in teims of pioceduies automatically piovides oui language
with a means of abstiaction foi compound objects.
To watch the netwoik in action, we can place piobes on the con-
nectois C and F, using a probe pioceduie similai to the one we used to
monitoi wiies in Section 3.3.4. Placing a piobe on a connectoi will cause
a message to be piinted whenevei the connectoi is given a value
(probe "Celsius temp" C)
(probe "Fahrenheit temp" F)
Next we set the value of C to 2. (Te thiid aigument to set-value!
tells C that this diiective comes fiom the user.)
389
(set-value! C 25 'user)
Probe: Celsius temp = 25
Probe: Fahrenheit temp = 77
done
Te piobe on C awakens and iepoits the value. C also piopagates its
value thiough the netwoik as desciibed above. Tis sets F to , which
is iepoited by the piobe on F.
Now we can tiy to set F to a new value, say 212
(set-value! F 212 'user)
Error! Contradiction (77 212)
Te connectoi complains that it has sensed a contiadiction lts value is
, and someone is tiying to set it to 212. lf we ieally want to ieuse the
netwoik with new values, we can tell C to foiget its old value
(forget-value! C 'user)
Probe: Celsius temp = ?
Probe: Fahrenheit temp = ?
done
C nds that the user, who set its value oiiginally, is now ietiacting that
value, so C agiees to lose its value, as shown by the piobe, and infoims
the iest of the netwoik of this fact. Tis infoimation eventually piop-
agates to F, which now nds that it has no ieason foi continuing to
believe that its own value is . Tus, F also gives up its value, as shown
by the piobe.
Now that F has no value, we aie fiee to set it to 212
(set-value! F 212 'user)
Probe: Fahrenheit temp = 212
Probe: Celsius temp = 100
done
390
Tis new value, when piopagated thiough the netwoik, foices C to have
a value of 100, and this is iegisteied by the piobe on C. Notice that the
veiy same netwoik is being used to compute C given F and to compute
F given C. Tis nondiiectionality of computation is the distinguishing
featuie of constiaint-based systems.
Implementing the constraint system
Te constiaint system is implemented via pioceduial objects with local
state, in a mannei veiy similai to the digital-ciicuit simulatoi of Sec-
tion 3.3.4. Although the piimitive objects of the constiaint system aie
somewhat moie complex, the oveiall system is simplei, since theie is
no concein about agendas and logic delays.
Te basic opeiations on connectois aie the following
(has-value? connector) tells whethei the connectoi has a
value.
(get-value connector) ietuins the connectois cuiient value.
(set-value! connector new-value informant) indicates
that the infoimant is iequesting the connectoi to set its value to
the new value.
(forget-value! connector retractor) tells the connectoi
that the ietiactoi is iequesting it to foiget its value.
(connect connector new-constraint) tells the connectoi
to paiticipate in the new constiaint.
Te connectois communicate with the constiaints by means of the pio-
ceduies inform-about-value, which tells the given constiaint that the
391
connectoi has a value, and inform-about-no-value, which tells the
constiaint that the connectoi has lost its value.
Adder constiucts an addei constiaint among summand connectois
a1 and a2 and a sum connectoi. An addei is implemented as a pioceduie
with local state (the pioceduie me below)
(define (adder a1 a2 sum)
(define (process-new-value)
(cond ((and (has-value? a1) (has-value? a2))
(set-value! sum
(+ (get-value a1) (get-value a2))
me))
((and (has-value? a1) (has-value? sum))
(set-value! a2
(- (get-value sum) (get-value a1))
me))
((and (has-value? a2) (has-value? sum))
(set-value! a1
(- (get-value sum) (get-value a2))
me))))
(define (process-forget-value)
(forget-value! sum me)
(forget-value! a1 me)
(forget-value! a2 me)
(process-new-value))
(define (me request)
(cond ((eq? request 'I-have-a-value) (process-new-value))
((eq? request 'I-lost-my-value) (process-forget-value))
(else (error "Unknown request: ADDER" request))))
(connect a1 me)
(connect a2 me)
(connect sum me)
me)
392
Adder connects the new addei to the designated connectois and ietuins
it as its value. Te pioceduie me, which iepiesents the addei, acts as a
dispatch to the local pioceduies. Te following syntax inteifaces (see
lootnote 2 in Section 3.3.4) aie used in conjunction with the dispatch
(define (inform-about-value constraint)
(constraint 'I-have-a-value))
(define (inform-about-no-value constraint)
(constraint 'I-lost-my-value))
Te addeis local pioceduie process-new-value is called when the addei
is infoimed that one of its connectois has a value. Te addei ist checks
to see if both a1 and a2 have values. lf so, it tells sum to set its value to
the sum of the two addends. Te informant aigument to set-value! is
me, which is the addei object itself. lf a1 and a2 do not both have values,
then the addei checks to see if peihaps a1 and sum have values. lf so, it
sets a2 to the dieience of these two. linally, if a2 and sum have values,
this gives the addei enough infoimation to set a1. lf the addei is told
that one of its connectois has lost a value, it iequests that all of its con-
nectois now lose theii values. (Only those values that weie set by this
addei aie actually lost.) Ten it iuns process-new-value. Te ieason
foi this last step is that one oi moie connectois may still have a value
(that is, a connectoi may have had a value that was not oiiginally set by
the addei), and these values may need to be piopagated back thiough
the addei.
A multipliei is veiy similai to an addei. lt will set its product to 0 if
eithei of the factois is 0, even if the othei factoi is not known.
(define (multiplier m1 m2 product)
(define (process-new-value)
(cond ((or (and (has-value? m1) (= (get-value m1) 0))
(and (has-value? m2) (= (get-value m2) 0)))
393
(set-value! product 0 me))
((and (has-value? m1) (has-value? m2))
(set-value! product
(* (get-value m1) (get-value m2))
me))
((and (has-value? product) (has-value? m1))
(set-value! m2
(/ (get-value product)
(get-value m1))
me))
((and (has-value? product) (has-value? m2))
(set-value! m1
(/ (get-value product)
(get-value m2))
me))))
(define (process-forget-value)
(forget-value! product me)
(forget-value! m1 me)
(forget-value! m2 me)
(process-new-value))
(define (me request)
(cond ((eq? request 'I-have-a-value) (process-new-value))
((eq? request 'I-lost-my-value) (process-forget-value))
(else (error "Unknown request: MULTIPLIER"
request))))
(connect m1 me)
(connect m2 me)
(connect product me)
me)
A constant constiuctoi simply sets the value of the designated con-
nectoi. Any I-have-a-value oi I-lost-my-value message sent to the
constant box will pioduce an eiioi.
394
(define (constant value connector)
(define (me request)
(error "Unknown request: CONSTANT" request))
(connect connector me)
(set-value! connector value me)
me)
linally, a piobe piints a message about the seuing oi unseuing of the
designated connectoi
(define (probe name connector)
(define (print-probe value)
(newline) (display "Probe: ") (display name)
(display " = ") (display value))
(define (process-new-value)
(print-probe (get-value connector)))
(define (process-forget-value) (print-probe "?"))
(define (me request)
(cond ((eq? request 'I-have-a-value) (process-new-value))
((eq? request 'I-lost-my-value) (process-forget-value))
(else (error "Unknown request: PROBE" request))))
(connect connector me)
me)
Representing connectors
A connectoi is iepiesented as a pioceduial object with local state vaii-
ables value, the cuiient value of the connectoi, informant, the object
that set the connectois value, and constraints, a list of the constiaints
in which the connectoi paiticipates.
(define (make-connector)
(let ((value false) (informant false) (constraints '()))
(define (set-my-value newval setter)
39
(cond ((not (has-value? me))
(set! value newval)
(set! informant setter)
(for-each-except setter
inform-about-value
constraints))
((not (= value newval))
(error "Contradiction" (list value newval)))
(else 'ignored)))
(define (forget-my-value retractor)
(if (eq? retractor informant)
(begin (set! informant false)
(for-each-except retractor
inform-about-no-value
constraints))
'ignored))
(define (connect new-constraint)
(if (not (memq new-constraint constraints))
(set! constraints
(cons new-constraint constraints)))
(if (has-value? me)
(inform-about-value new-constraint))
'done)
(define (me request)
(cond ((eq? request 'has-value?)
(if informant true false))
((eq? request 'value) value)
((eq? request 'set-value!) set-my-value)
((eq? request 'forget) forget-my-value)
((eq? request 'connect) connect)
(else (error "Unknown operation: CONNECTOR"
request))))
me))
39
Te connectois local pioceduie set-my-value is called when theie is
a iequest to set the connectois value. lf the connectoi does not cui-
iently have a value, it will set its value and iemembei as informant
the constiaint that iequested the value to be set.
32
Ten the connectoi
will notify all of its paiticipating constiaints except the constiaint that
iequested the value to be set. Tis is accomplished using the follow-
ing iteiatoi, which applies a designated pioceduie to all items in a list
except a given one
(define (for-each-except exception procedure list)
(define (loop items)
(cond ((null? items) 'done)
((eq? (car items) exception) (loop (cdr items)))
(else (procedure (car items))
(loop (cdr items)))))
(loop list))
lf a connectoi is asked to foiget its value, it iuns the local pioceduie
forget-my-value, which ist checks to make suie that the iequest is
coming fiom the same object that set the value oiiginally. lf so, the con-
nectoi infoims its associated constiaints about the loss of the value.
Te local pioceduie connect adds the designated new constiaint to
the list of constiaints if it is not alieady in that list. Ten, if the connectoi
has a value, it infoims the new constiaint of this fact.
Te connectois pioceduie me seives as a dispatch to the othei in-
teinal pioceduies and also iepiesents the connectoi as an object. Te
following pioceduies piovide a syntax inteiface foi the dispatch
(define (has-value? connector)
(connector 'has-value?))
32
Te setter might not be a constiaint. ln oui tempeiatuie example, we used user
as the setter.
39
(define (get-value connector)
(connector 'value))
(define (set-value! connector new-value informant)
((connector 'set-value!) new-value informant))
(define (forget-value! connector retractor)
((connector 'forget) retractor))
(define (connect connector new-constraint)
((connector 'connect) new-constraint))
Exercise 3.33: Using piimitive multipliei, addei, and con-
stant constiaints, dene a pioceduie averager that takes
thiee connectois a, b, and c as inputs and establishes the
constiaint that the value of c is the aveiage of the values of
a and b.
Exercise 3.34: Louis Reasonei wants to build a squaiei, a
constiaint device with two teiminals such that the value
of connectoi b on the second teiminal will always be the
squaie of the value a on the ist teiminal. He pioposes the
following simple device made fiom a multipliei
(define (squarer a b)
(multiplier a a b))
Teie is a seiious aw in this idea. Explain.
Exercise 3.35: Ben Bitdiddle tells Louis that one way to
avoid the tiouble in Exeicise 3.34 is to dene a squaiei
as a new piimitive constiaint. lill in the missing poitions
in Bens outline foi a pioceduie to implement such a con-
stiaint
398
(define (squarer a b)
(define (process-new-value)
(if (has-value? b)
(if (< (get-value b) 0)
(error "square less than 0: SQUARER"
(get-value b))
alternative1)
alternative2))
(define (process-forget-value) body1)
(define (me request) body2)
rest of definition
me)
Exercise 3.36: Suppose we evaluate the following sequence
of expiessions in the global enviionment
(define a (make-connector))
(define b (make-connector))
(set-value! a 10 'user)
At some time duiing evaluation of the set-value!, the fol-
lowing expiession fiom the connectois local pioceduie is
evaluated
(for-each-except
setter inform-about-value constraints)
Diaw an enviionment diagiam showing the enviionment
in which the above expiession is evaluated.
Exercise 3.37: Te celsius-fahrenheit-converter pio-
ceduie is cumbeisome when compaied with a moie expiession-
oiiented style of denition, such as
399
(define (celsius-fahrenheit-converter x)
(c+ (c* (c/ (cv 9) (cv 5))
x)
(cv 32)))
(define C (make-connector))
(define F (celsius-fahrenheit-converter C))
Heie c+, c*, etc. aie the constiaint veisions of the aiith-
metic opeiations. loi example, c+ takes two connectois as
aiguments and ietuins a connectoi that is ielated to these
by an addei constiaint
(define (c+ x y)
(let ((z (make-connector)))
(adder x y z)
z))
Dene analogous pioceduies c-, c*, c/, and cv (constant
value) that enable us to dene compound constiaints as in
the conveitei example above.
33
33
Te expiession-oiiented foimat is convenient because it avoids the need to name
the inteimediate expiessions in a computation. Oui oiiginal foimulation of the con-
stiaint language is cumbeisome in the same way that many languages aie cumbeisome
when dealing with opeiations on compound data. loi example, if we wanted to com-
pute the pioduct (a b) (c d), wheie the vaiiables iepiesent vectois, we could woik in
impeiative style, using pioceduies that set the values of designated vectoi aiguments
but do not themselves ietuin vectois as values
(v-sum a b temp1)
(v-sum c d temp2)
(v-prod temp1 temp2 answer)
Alteinatively, we could deal with expiessions, using pioceduies that ietuin vectois as
values, and thus avoid explicitly mentioning temp1 and temp2
(define answer (v-prod (v-sum a b) (v-sum c d)))
400
3.4 Concurrency: Time Is of the Essence
Weve seen the powei of computational objects with local state as tools
foi modeling. Yet, as Section 3.1.3 wained, this powei extiacts a piice
the loss of iefeiential tianspaiency, giving iise to a thicket of questions
about sameness and change, and the need to abandon the substitution
model of evaluation in favoi of the moie intiicate enviionment model.
Te cential issue luiking beneath the complexity of state, sameness,
and change is that by intioducing assignment we aie foiced to admit
:e into oui computational models. Befoie we intioduced assignment,
all oui piogiams weie timeless, in the sense that any expiession that
has a value always has the same value. ln contiast, iecall the example of
modeling withdiawals fiom a bank account and ietuining the iesulting
balance, intioduced at the beginning of Section 3.1.1
(withdraw 25)
75
(withdraw 25)
50
Since Lisp allows us to ietuin compound objects as values of pioceduies, we can tians-
foim oui impeiative-style constiaint language into an expiession-oiiented style as
shown in this exeicise. ln languages that aie impoveiished in handling compound ob-
jects, such as Algol, Basic, and Pascal (unless one explicitly uses Pascal pointei vaii-
ables), one is usually stuck with the impeiative style when manipulating compound
objects. Given the advantage of the expiession-oiiented foimat, one might ask if theie
is any ieason to have implemented the system in impeiative style, as we did in this
section. One ieason is that the non-expiession-oiiented constiaint language piovides
a handle on constiaint objects (e.g., the value of the adder pioceduie) as well as on
connectoi objects. Tis is useful if we wish to extend the system with new opeiations
that communicate with constiaints diiectly iathei than only indiiectly via opeiations
on connectois. Although it is easy to implement the expiession-oiiented style in teims
of the impeiative implementation, it is veiy dicult to do the conveise.
401
Heie successive evaluations of the same expiession yield dieient val-
ues. Tis behavioi aiises fiom the fact that the execution of assignment
statements (in this case, assignments to the vaiiable balance) delineates
oens :n :e when values change. Te iesult of evaluating an ex-
piession depends not only on the expiession itself, but also on whethei
the evaluation occuis befoie oi afei these moments. Building models
in teims of computational objects with local state foices us to confiont
time as an essential concept in piogiamming.
We can go fuithei in stiuctuiing computational models to match oui
peiception of the physical woild. Objects in the woild do not change
one at a time in sequence. Rathei we peiceive them as acting concvr
ren|yall at once. So it is ofen natuial to model systems as collections
of computational piocesses that execute concuiiently. Just as we can
make oui piogiams modulai by oiganizing models in teims of objects
with sepaiate local state, it is ofen appiopiiate to divide computational
models into paits that evolve sepaiately and concuiiently. Even if the
piogiams aie to be executed on a sequential computei, the piactice of
wiiting piogiams as if they weie to be executed concuiiently foices
the piogiammei to avoid inessential timing constiaints and thus makes
piogiams moie modulai.
ln addition to making piogiams moie modulai, concuiient compu-
tation can piovide a speed advantage ovei sequential computation. Se-
quential computeis execute only one opeiation at a time, so the amount
of time it takes to peifoim a task is piopoitional to the total numbei
of opeiations peifoimed.
34
Howevei, if it is possible to decompose a
34
Most ieal piocessois actually execute a few opeiations at a time, following a stiat-
egy called :e|:n:ng. Although this technique gieatly impioves the eective utilization
of the haidwaie, it is used only to speed up the execution of a sequential instiuction
stieam, while ietaining the behavioi of the sequential piogiam.
402
pioblem into pieces that aie ielatively independent and need to com-
municate only iaiely, it may be possible to allocate pieces to sepaiate
computing piocessois, pioducing a speed advantage piopoitional to the
numbei of piocessois available.
Unfoitunately, the complexities intioduced by assignment become
even moie pioblematic in the piesence of concuiiency. Te fact of con-
cuiient execution, eithei because the woild opeiates in paiallel oi be-
cause oui computeis do, entails additional complexity in oui undei-
standing of time.
3.4.1 The Nature of Time in Concurrent Systems
On the suiface, time seems stiaightfoiwaid. lt is an oideiing imposed
on events.
3
loi any events A and B, eithei A occuis befoie B, A and
B aie simultaneous, oi A occuis afei B. loi instance, ietuining to the
bank account example, suppose that Petei withdiaws S10 and Paul with-
diaws S2 fiom a joint account that initially contains S100, leaving S
in the account. Depending on the oidei of the two withdiawals, the
sequence of balances in the account is eithei S100 S90 S oi
S100 S S . ln a computei implementation of the banking sys-
tem, this changing sequence of balances could be modeled by successive
assignments to a vaiiable balance.
ln complex situations, howevei, such a view can be pioblematic.
Suppose that Petei and Paul, and othei people besides, aie accessing the
same bank account thiough a netwoik of banking machines distiibuted
all ovei the woild. Te actual sequence of balances in the account will
depend ciitically on the detailed timing of the accesses and the details
of the communication among the machines.
3
To quote some giati seen on a Cambiidge building wall Time is a device that
was invented to keep eveiything fiom happening at once.
403
Tis indeteiminacy in the oidei of events can pose seiious piob-
lems in the design of concuiient systems. loi instance, suppose that the
withdiawals made by Petei and Paul aie implemented as two sepaiate
piocesses shaiing a common vaiiable balance, each piocess specied
by the pioceduie given in Section 3.1.1
(define (withdraw amount)
(if (>= balance amount)
(begin
(set! balance (- balance amount))
balance)
"Insufficient funds"))
lf the two piocesses opeiate independently, then Petei might test the
balance and auempt to withdiaw a legitimate amount. Howevei, Paul
might withdiaw some funds in between the time that Petei checks the
balance and the time Petei completes the withdiawal, thus invalidating
Peteis test.
Tings can be woise still. Considei the expiession
(set! balance (- balance amount))
executed as pait of each withdiawal piocess. Tis consists of thiee steps
(1) accessing the value of the balance vaiiable, (2) computing the new
balance, (3) seuing balance to this new value. lf Petei and Pauls with-
diawals execute this statement concuiiently, then the two withdiawals
might inteileave the oidei in which they access balance and set it to
the new value.
Te timing diagiam in liguie 3.29 depicts an oidei of events wheie
balance staits at 100, Petei withdiaws 10, Paul withdiaws 2, and yet
the nal value of balance is . As shown in the diagiam, the ieason foi
this anomaly is that Pauls assignment of to balance is made undei
the assumption that the value of balance to be deciemented is 100. Tat
404
Peter
Access balance: $100
new value: 100-10=90
set! balance to $90
time
Bank Paul
$100
$90
$75
Access balance: $100
new value: 100-25=75
set! balance to $75
Figure 3.29: Timing diagiam showing how inteileaving
the oidei of events in two banking withdiawals can lead
to an incoiiect nal balance.
40
assumption, howevei, became invalid when Petei changed balance to
90. Tis is a catastiophic failuie foi the banking system, because the
total amount of money in the system is not conseived. Befoie the tians-
actions, the total amount of money was S100. Afeiwaids, Petei has S10,
Paul has S2, and the bank has S.
3
Te geneial phenomenon illustiated heie is that seveial piocesses
may shaie a common state vaiiable. What makes this complicated is that
moie than one piocess may be tiying to manipulate the shaied state at
the same time. loi the bank account example, duiing each tiansaction,
each customei should be able to act as if the othei customeis did not
exist. When a customei changes the balance in a way that depends on
the balance, he must be able to assume that, just befoie the moment of
change, the balance is still what he thought it was.
Correct behavior of concurrent programs
Te above example typies the subtle bugs that can cieep into concui-
ient piogiams. Te ioot of this complexity lies in the assignments to
vaiiables that aie shaied among the dieient piocesses. We alieady
know that we must be caieful in wiiting piogiams that use set!, be-
cause the iesults of a computation depend on the oidei in which the
3
An even woise failuie foi this system could occui if the two set! opeiations at-
tempt to change the balance simultaneously, in which case the actual data appeaiing
in memoiy might end up being a iandom combination of the infoimation being wiit-
ten by the two piocesses. Most computeis have inteilocks on the piimitive memoiy-
wiite opeiations, which piotect against such simultaneous access. Even this seemingly
simple kind of piotection, howevei, iaises implementation challenges in the design of
multipiocessing computeis, wheie elaboiate coc|eco|erence piotocols aie iequiied to
ensuie that the vaiious piocessois will maintain a consistent view of memoiy contents,
despite the fact that data may be ieplicated (cached) among the dieient piocessois
to inciease the speed of memoiy access.
40
assignments occui.
3
With concuiient piocesses we must be especially
caieful about assignments, because we may not be able to contiol the
oidei of the assignments made by the dieient piocesses. lf seveial such
changes might be made concuiiently (as with two depositois accessing
a joint account) we need some way to ensuie that oui system behaves
coiiectly. loi example, in the case of withdiawals fiom a joint bank ac-
count, we must ensuie that money is conseived. To make concuiient
piogiams behave coiiectly, we may have to place some iestiictions on
concuiient execution.
One possible iestiiction on concuiiency would stipulate that no two
opeiations that change any shaied state vaiiables can occui at the same
time. Tis is an extiemely stiingent iequiiement. loi distiibuted bank-
ing, it would iequiie the system designei to ensuie that only one tians-
action could pioceed at a time. Tis would be both inecient and oveily
conseivative. liguie 3.30 shows Petei and Paul shaiing a bank account,
wheie Paul has a piivate account as well. Te diagiam illustiates two
withdiawals fiom the shaied account (one by Petei and one by Paul)
and a deposit to Pauls piivate account.
38
Te two withdiawals fiom
the shaied account must not be concuiient (since both access and up-
date the same account), and Pauls deposit and withdiawal must not be
concuiient (since both access and update the amount in Pauls wallet).
But theie should be no pioblem peimiuing Pauls deposit to his pii-
vate account to pioceed concuiiently with Peteis withdiawal fiom the
shaied account.
Aless stiingent iestiiction on concuiiency would ensuie that a con-
3
Te factoiial piogiamin Section 3.1.3 illustiates this foi a single sequential piocess.
38
Te columns show the contents of Peteis wallet, the joint account (in Bank1),
Pauls wallet, and Pauls piivate account (in Bank2), befoie and afei each withdiawal
(W) and deposit (D). Petei withdiaws S10 fiom Bank1, Paul deposits S in Bank2, then
withdiaws S2 fiom Bank1.
40
$100 $7 $5 $300
$0 $305
$305 $25 $65 $17
$17 $90
W
W
D
time
Peter Bank1 Paul Bank2
Figure 3.30: Concuiient deposits and withdiawals fiom a
joint account in Bank1 and a piivate account in Bank2.
cuiient system pioduces the same iesult as if the piocesses had iun
sequentially in some oidei. Teie aie two impoitant aspects to this ie-
quiiement. liist, it does not iequiie the piocesses to actually iun se-
quentially, but only to pioduce iesults that aie the same os :[ they had
iun sequentially. loi the example in liguie 3.30, the designei of the
bank account system can safely allow Pauls deposit and Peteis with-
diawal to happen concuiiently, because the net iesult will be the same
as if the two opeiations had happened sequentially. Second, theie may
be moie than one possible coiiect iesult pioduced by a concuiient
piogiam, because we iequiie only that the iesult be the same as foi
408
soe sequential oidei. loi example, suppose that Petei and Pauls joint
account staits out with S100, and Petei deposits S40 while Paul concui-
iently withdiaws half the money in the account. Ten sequential exe-
cution could iesult in the account balance being eithei S0 oi S90 (see
Exeicise 3.38).
39
Teie aie still weakei iequiiements foi coiiect execution of con-
cuiient piogiams. A piogiam foi simulating diusion (say, the ow of
heat in an object) might consist of a laige numbei of piocesses, each
one iepiesenting a small volume of space, that update theii values con-
cuiiently. Each piocess iepeatedly changes its value to the aveiage of
its own value and its neighbois values. Tis algoiithm conveiges to the
iight answei independent of the oidei in which the opeiations aie done,
theie is no need foi any iestiictions on concuiient use of the shaied val-
ues.
Exercise 3.38: Suppose that Petei, Paul, and Maiy shaie
a joint bank account that initially contains S100. Concui-
iently, Petei deposits S10, Paul withdiaws S20, and Maiy
withdiaws half the money in the account, by executing the
following commands
Peter: (set! balance (+ balance 10))
Paul: (set! balance (- balance 20))
Mary: (set! balance (- balance (/ balance 2)))
a. List all the dieient possible values foi balance afei
these thiee tiansactions have been completed, assum-
39
A moie foimal way to expiess this idea is to say that concuiient piogiams aie
inheiently nonJeer:n:s:c. Tat is, they aie desciibed not by single-valued functions,
but by functions whose iesults aie sets of possible values. ln Section 4.3 we will study
a language foi expiessing nondeteiministic computations.
409
ing that the banking systemfoices the thiee piocesses
to iun sequentially in some oidei.
b. What aie some othei values that could be pioduced
if the system allows the piocesses to be inteileaved`
Diaw timing diagiams like the one in liguie 3.29 to
explain how these values can occui.
3.4.2 Mechanisms for Controlling Concurrency
Weve seen that the diculty in dealing with concuiient piocesses is
iooted in the need to considei the inteileaving of the oidei of events in
the dieient piocesses. loi example, suppose we have two piocesses,
one with thiee oideied events (a, b, c) and one with thiee oideied events
(x, y, z). lf the two piocesses iun concuiiently, with no constiaints on
how theii execution is inteileaved, then theie aie 20 dieient possible
oideiings foi the events that aie consistent with the individual oidei-
ings foi the two piocesses
(a,b,c,x,y,z) (a,x,b,y,c,z) (x,a,b,c,y,z) (x,a,y,z,b,c)
(a,b,x,c,y,z) (a,x,b,y,z,c) (x,a,b,y,c,z) (x,y,a,b,c,z)
(a,b,x,y,c,z) (a,x,y,b,c,z) (x,a,b,y,z,c) (x,y,a,b,z,c)
(a,b,x,y,z,c) (a,x,y,b,z,c) (x,a,y,b,c,z) (x,y,a,z,b,c)
(a,x,b,c,y,z) (a,x,y,z,b,c) (x,a,y,b,z,c) (x,y,z,a,b,c)
As piogiammeis designing this system, we would have to considei the
eects of each of these 20 oideiings and check that each behavioi is
acceptable. Such an appioach iapidly becomes unwieldy as the numbeis
of piocesses and events inciease.
A moie piactical appioach to the design of concuiient systems is to
devise geneial mechanisms that allowus to constiain the inteileaving of
concuiient piocesses so that we can be suie that the piogiam behavioi
410
is coiiect. Many mechanisms have been developed foi this puipose. ln
this section, we desciibe one of them, the ser:o|::er.
Serializing access to shared state
Seiialization implements the following idea Piocesses will execute con-
cuiiently, but theie will be ceitain collections of pioceduies that cannot
be executed concuiiently. Moie piecisely, seiialization cieates distin-
guished sets of pioceduies such that only one execution of a pioceduie
in each seiialized set is peimiued to happen at a time. lf some pioceduie
in the set is being executed, then a piocess that auempts to execute any
pioceduie in the set will be foiced to wait until the ist execution has
nished.
We can use seiialization to contiol access to shaied vaiiables. loi
example, if we want to update a shaied vaiiable based on the pievi-
ous value of that vaiiable, we put the access to the pievious value of
the vaiiable and the assignment of the new value to the vaiiable in the
same pioceduie. We then ensuie that no othei pioceduie that assigns
to the vaiiable can iun concuiiently with this pioceduie by seiializing
all of these pioceduies with the same seiializei. Tis guaiantees that
the value of the vaiiable cannot be changed between an access and the
coiiesponding assignment.
Serializers in Scheme
To make the above mechanism moie conciete, suppose that we have
extended Scheme to include a pioceduie called parallel-execute
(parallel-execute p
1
p
2
. . . p
k
)
Each p must be a pioceduie of no aiguments. Parallel-execute cie-
ates a sepaiate piocess foi each p, which applies p (to no aigu-
411
ments). Tese piocesses all iun concuiiently.
40
As an example of how this is used, considei
(define x 10)
(parallel-execute
(lambda () (set! x (* x x)))
(lambda () (set! x (+ x 1))))
Tis cieates two concuiient piocessesP
1
, which sets x to x times x,
and P
2
, which inciements x. Afei execution is complete, x will be lef
with one of ve possible values, depending on the inteileaving of the
events of P
1
and P
2
101: P
1
sets x to 100 and then P
2
inciements x to 101.
121: P
2
inciements x to 11 and then P
1
sets x to x * x.
110: P
2
changes x fiom 10 to 11 between the two times that
P
1
accesses the value of x duiing the evaluation of (* x x).
11: P
2
accesses x, then P
1
sets x to 100, then P
2
sets x.
100: P
1
accesses x (twice), then P
2
sets x to 11, then P
1
sets x.
We can constiain the concuiiency by using seiialized pioceduies, which
aie cieated by ser:o|::ers. Seiializeis aie constiucted by make-serializer,
whose implementation is given below. A seiializei takes a pioceduie as
aigument and ietuins a seiialized pioceduie that behaves like the oiigi-
nal pioceduie. All calls to a given seiializei ietuin seiialized pioceduies
in the same set.
Tus, in contiast to the example above, executing
(define x 10)
40
Parallel-execute is not pait of standaid Scheme, but it can be implemented in
xi1 Scheme. ln oui implementation, the new concuiient piocesses also iun concui-
iently with the oiiginal Scheme piocess. Also, in oui implementation, the value ie-
tuined by parallel-execute is a special contiol object that can be used to halt the
newly cieated piocesses.
412
(define s (make-serializer))
(parallel-execute
(s (lambda () (set! x (* x x))))
(s (lambda () (set! x (+ x 1)))))
can pioduce only two possible values foi x, 101 oi 121. Te othei pos-
sibilities aie eliminated, because the execution of P
1
and P
2
cannot be
inteileaved.
Heie is a veision of the make-account pioceduie fiom Section 3.1.1,
wheie the deposits and withdiawals have been seiialized
(define (make-account balance)
(define (withdraw amount)
(if (>= balance amount)
(begin (set! balance (- balance amount))
balance)
"Insufficient funds"))
(define (deposit amount)
(set! balance (+ balance amount))
balance)
(let ((protected (make-serializer)))
(define (dispatch m)
(cond ((eq? m 'withdraw) (protected withdraw))
((eq? m 'deposit) (protected deposit))
((eq? m 'balance) balance)
(else (error "Unknown request: MAKE-ACCOUNT"
m))))
dispatch))
With this implementation, two piocesses cannot be withdiawing fiom
oi depositing into a single account concuiiently. Tis eliminates the
souice of the eiioi illustiated in liguie 3.29, wheie Petei changes the
account balance between the times when Paul accesses the balance to
compute the new value and when Paul actually peifoims the assign-
413
ment. On the othei hand, each account has its own seiializei, so that
deposits and withdiawals foi dieient accounts can pioceed concui-
iently.
Exercise 3.39: Which of the ve possibilities in the pai-
allel execution shown above iemain if we instead seiialize
execution as follows
(define x 10)
(define s (make-serializer))
(parallel-execute
(lambda () (set! x ((s (lambda () (* x x))))))
(s (lambda () (set! x (+ x 1)))))
Exercise 3.40: Give all possible values of x that can iesult
fiom executing
(define x 10)
(parallel-execute (lambda () (set! x (* x x)))
(lambda () (set! x (* x x x))))
Which of these possibilities iemain if we instead use seii-
alized pioceduies
(define x 10)
(define s (make-serializer))
(parallel-execute (s (lambda () (set! x (* x x))))
(s (lambda () (set! x (* x x x)))))
Exercise 3.41: Ben Bitdiddle woiiies that it would be bet-
tei to implement the bank account as follows (wheie the
commented line has been changed)
414
(define (make-account balance)
(define (withdraw amount)
(if (>= balance amount)
(begin (set! balance
(- balance amount))
balance)
"Insufficient funds"))
(define (deposit amount)
(set! balance (+ balance amount))
balance)
(let ((protected (make-serializer)))
(define (dispatch m)
(cond ((eq? m 'withdraw) (protected withdraw))
((eq? m 'deposit) (protected deposit))
((eq? m 'balance)
((protected
(lambda () balance)))) , seiialized
(else
(error "Unknown request: MAKE-ACCOUNT"
m))))
dispatch))
because allowing unseiialized access to the bank balance
can iesult in anomalous behavioi. Do you agiee` ls theie
any scenaiio that demonstiates Bens concein`
Exercise 3.42: Ben Bitdiddle suggests that its a waste of
time to cieate a new seiialized pioceduie in iesponse to
eveiy withdraw and deposit message. He says that make-
account could be changed so that the calls to protected
aie done outside the dispatch pioceduie. Tat is, an ac-
count would ietuin the same seiialized pioceduie (which
41
was cieated at the same time as the account) each time it is
asked foi a withdiawal pioceduie.
(define (make-account balance)
(define (withdraw amount)
(if (>= balance amount)
(begin (set! balance (- balance amount))
balance)
"Insufficient funds"))
(define (deposit amount)
(set! balance (+ balance amount))
balance)
(let ((protected (make-serializer)))
(let ((protected-withdraw (protected withdraw))
(protected-deposit (protected deposit)))
(define (dispatch m)
(cond ((eq? m 'withdraw) protected-withdraw)
((eq? m 'deposit) protected-deposit)
((eq? m 'balance) balance)
(else
(error "Unknown request: MAKE-ACCOUNT"
m))))
dispatch)))
ls this a safe change to make` ln paiticulai, is theie any dif-
feience in what concuiiency is allowed by these two vei-
sions of make-account`
Complexity of using multiple shared resources
Seiializeis piovide a poweiful abstiaction that helps isolate the com-
plexities of concuiient piogiams so that they can be dealt with caiefully
and (hopefully) coiiectly. Howevei, while using seiializeis is ielatively
41
stiaightfoiwaid when theie is only a single shaied iesouice (such as
a single bank account), concuiient piogiamming can be tieacheiously
dicult when theie aie multiple shaied iesouices.
To illustiate one of the diculties that can aiise, suppose we wish to
swap the balances in two bank accounts. We access each account to nd
the balance, compute the dieience between the balances, withdiaw
this dieience fiom one account, and deposit it in the othei account.
We could implement this as follows
41
(define (exchange account1 account2)
(let ((difference (- (account1 'balance)
(account2 'balance))))
((account1 'withdraw) difference)
((account2 'deposit) difference)))
Tis pioceduie woiks well when only a single piocess is tiying to do
the exchange. Suppose, howevei, that Petei and Paul both have access
to accounts a1, a2, and a3, and that Petei exchanges a1 and a2 while
Paul concuiiently exchanges a1 and a3. Even with account deposits and
withdiawals seiialized foi individual accounts (as in the make-account
pioceduie shown above in this section), exchange can still pioduce in-
coiiect iesults. loi example, Petei might compute the dieience in the
balances foi a1 and a2, but then Paul might change the balance in a1
befoie Petei is able to complete the exchange.
42
loi coiiect behavioi,
we must aiiange foi the exchange pioceduie to lock out any othei con-
cuiient accesses to the accounts duiing the entiie time of the exchange.
41
We have simplied exchange by exploiting the fact that oui deposit message ac-
cepts negative amounts. (Tis is a seiious bug in oui banking system')
42
lf the account balances stait out as S10, S20, and S30, then afei any numbei of
concuiient exchanges, the balances should still be S10, S20, and S30 in some oidei.
Seiializing the deposits to individual accounts is not sucient to guaiantee this. See
Exeicise 3.43.
41
One way we can accomplish this is by using both accounts seii-
alizeis to seiialize the entiie exchange pioceduie. To do this, we will
aiiange foi access to an accounts seiializei. Note that we aie delibei-
ately bieaking the modulaiity of the bank-account object by exposing
the seiializei. Te following veision of make-account is identical to the
oiiginal veision given in Section 3.1.1, except that a seiializei is pio-
vided to piotect the balance vaiiable, and the seiializei is expoited via
message passing
(define (make-account-and-serializer balance)
(define (withdraw amount)
(if (>= balance amount)
(begin (set! balance (- balance amount))
balance)
"Insufficient funds"))
(define (deposit amount)
(set! balance (+ balance amount))
balance)
(let ((balance-serializer (make-serializer)))
(define (dispatch m)
(cond ((eq? m 'withdraw) withdraw)
((eq? m 'deposit) deposit)
((eq? m 'balance) balance)
((eq? m 'serializer) balance-serializer)
(else (error "Unknown request: MAKE-ACCOUNT" m))))
dispatch))
We can use this to do seiialized deposits and withdiawals. Howevei,
unlike oui eailiei seiialized account, it is now the iesponsibility of each
usei of bank-account objects to explicitly manage the seiialization, foi
example as follows
43
43
Exeicise 3.4 investigates why deposits and withdiawals aie no longei automati-
cally seiialized by the account.
418
(define (deposit account amount)
(let ((s (account 'serializer))
(d (account 'deposit)))
((s d) amount)))
Expoiting the seiializei in this way gives us enough exibility to imple-
ment a seiialized exchange piogiam. We simply seiialize the oiiginal
exchange pioceduie with the seiializeis foi both accounts
(define (serialized-exchange account1 account2)
(let ((serializer1 (account1 'serializer))
(serializer2 (account2 'serializer)))
((serializer1 (serializer2 exchange))
account1
account2)))
Exercise 3.43: Suppose that the balances in thiee accounts
stait out as S10, S20, and S30, and that multiple piocesses
iun, exchanging the balances in the accounts. Aigue that if
the piocesses aie iun sequentially, afei any numbei of con-
cuiient exchanges, the account balances should be S10, S20,
and S30 in some oidei. Diaw a timing diagiam like the one
in liguie 3.29 to show how this condition can be violated
if the exchanges aie implemented using the ist veision of
the account-exchange piogiamin this section. On the othei
hand, aigue that even with this exchange piogiam, the sum
of the balances in the accounts will be pieseived. Diaw a
timing diagiam to show how even this condition would be
violated if we did not seiialize the tiansactions on individ-
ual accounts.
Exercise 3.44: Considei the pioblemof tiansfeiiing an amount
fiom one account to anothei. Ben Bitdiddle claims that this
419
can be accomplished with the following pioceduie, even if
theie aie multiple people concuiiently tiansfeiiing money
among multiple accounts, using any account mechanism
that seiializes deposit and withdiawal tiansactions, foi ex-
ample, the veision of make-account in the text above.
(define (transfer from-account to-account amount)
((from-account 'withdraw) amount)
((to-account 'deposit) amount))
Louis Reasonei claims that theie is a pioblem heie, and
that we need to use a moie sophisticated method, such as
the one iequiied foi dealing with the exchange pioblem. ls
Louis iight` lf not, what is the essential dieience between
the tiansfei pioblemand the exchange pioblem` (You should
assume that the balance in from-account is at least amount.)
Exercise 3.45: Louis Reasonei thinks oui bank-account sys-
tem is unnecessaiily complex and eiioi-pione now that de-
posits and withdiawals aient automatically seiialized. He
suggests that make-account-and-serializer should have
expoited the seiializei (foi use by such pioceduies as serialized-
exchange) in addition to (iathei than instead of) using it
to seiialize accounts and deposits as make-account did. He
pioposes to iedene accounts as follows
(define (make-account-and-serializer balance)
(define (withdraw amount)
(if (>= balance amount)
(begin (set! balance (- balance amount)) balance)
"Insufficient funds"))
(define (deposit amount)
(set! balance (+ balance amount)) balance)
420
(let ((balance-serializer (make-serializer)))
(define (dispatch m)
(cond ((eq? m 'withdraw) (balance-serializer withdraw))
((eq? m 'deposit) (balance-serializer deposit))
((eq? m 'balance) balance)
((eq? m 'serializer) balance-serializer)
(else (error "Unknown request: MAKE-ACCOUNT" m))))
dispatch))
Ten deposits aie handled as with the oiiginal make-account
(define (deposit account amount)
((account 'deposit) amount))
Explain what is wiong with Louiss ieasoning. ln paiticu-
lai, considei what happens when serialized-exchange is
called.
Implementing serializers
We implement seiializeis in teims of a moie piimitive synchioniza-
tion mechanism called a ve:. A mutex is an object that suppoits two
opeiationsthe mutex can be ocqv:reJ, and the mutex can be re|eoseJ.
Once a mutex has been acquiied, no othei acquiie opeiations on that
mutex may pioceed until the mutex is ieleased.
44
ln oui implementa-
44
Te teim mutex is an abbieviation foi vvo| e:c|vs:on. Te geneial pioblem of
aiianging a mechanism that peimits concuiient piocesses to safely shaie iesouices is
called the mutual exclusion pioblem. Oui mutex is a simple vaiiant of the seo|ore
mechanism (see Exeicise 3.4), which was intioduced in the THE Multipiogiamming
System developed at the Technological Univeisity of Eindhoven and named foi the
univeisitys initials in Dutch (Dijkstia 198a). Te acquiie and ielease opeiations weie
oiiginally called P and V, fiom the Dutch woids osseren (to pass) and +r:jge+en (to
ielease), in iefeience to the semaphoies used on iailioad systems. Dijkstias classic
exposition (Dijkstia 198b) was one of the ist to cleaily piesent the issues of concui-
421
tion, each seiializei has an associated mutex. Given a pioceduie p, the
seiializei ietuins a pioceduie that acquiies the mutex, iuns p, and then
ieleases the mutex. Tis ensuies that only one of the pioceduies pio-
duced by the seiializei can be iunning at once, which is piecisely the
seiialization piopeity that we need to guaiantee.
(define (make-serializer)
(let ((mutex (make-mutex)))
(lambda (p)
(define (serialized-p . args)
(mutex 'acquire)
(let ((val (apply p args)))
(mutex 'release)
val))
serialized-p)))
Te mutex is a mutable object (heie well use a one-element list, which
well iefei to as a ce||) that can hold the value tiue oi false. When the
value is false, the mutex is available to be acquiied. When the value is
tiue, the mutex is unavailable, and any piocess that auempts to acquiie
the mutex must wait.
Oui mutex constiuctoi make-mutex begins by initializing the cell
contents to false. To acquiie the mutex, we test the cell. lf the mutex
is available, we set the cell contents to tiue and pioceed. Otheiwise,
we wait in a loop, auempting to acquiie ovei and ovei again, until we
nd that the mutex is available.
4
To ielease the mutex, we set the cell
iency contiol, and showed how to use semaphoies to handle a vaiiety of concuiiency
pioblems.
4
ln most time-shaied opeiating systems, piocesses that aie blocked by a mutex do
not waste time busy-waiting as above. lnstead, the system schedules anothei piocess
to iun while the ist is waiting, and the blocked piocess is awakened when the mutex
becomes available.
422
contents to false.
(define (make-mutex)
(let ((cell (list false)))
(define (the-mutex m)
(cond ((eq? m 'acquire)
(if (test-and-set! cell)
(the-mutex 'acquire))) , ietiy
((eq? m 'release) (clear! cell))))
the-mutex))
(define (clear! cell) (set-car! cell false))
Test-and-set! tests the cell and ietuins the iesult of the test. ln addi-
tion, if the test was false, test-and-set! sets the cell contents to tiue
befoie ietuining false. We can expiess this behavioi as the following
pioceduie
(define (test-and-set! cell)
(if (car cell) true (begin (set-car! cell true) false)))
Howevei, this implementation of test-and-set! does not suce as
it stands. Teie is a ciucial subtlety heie, which is the essential place
wheie concuiiency contiol enteis the system Te test-and-set! op-
eiation must be peifoimed oo:co||y. Tat is, we must guaiantee that,
once a piocess has tested the cell and found it to be false, the cell con-
tents will actually be set to tiue befoie any othei piocess can test the
cell. lf we do not make this guaiantee, then the mutex can fail in a way
similai to the bank-account failuie in liguie 3.29. (See Exeicise 3.4.)
Te actual implementation of test-and-set! depends on the de-
tails of how oui system iuns concuiient piocesses. loi example, we
might be executing concuiient piocesses on a sequential piocessoi us-
ing a time-slicing mechanismthat cycles thiough the piocesses, peimit-
ting each piocess to iun foi a shoit time befoie inteiiupting it and mov-
423
ing on to the next piocess. ln that case, test-and-set! can woik by dis-
abling time slicing duiing the testing and seuing.
4
Alteinatively, mul-
tipiocessing computeis piovide instiuctions that suppoit atomic opei-
ations diiectly in haidwaie.
4
Exercise 3.46: Suppose that we implement test-and-set!
using an oidinaiy pioceduie as shown in the text, without
auempting to make the opeiation atomic. Diaw a timing
4
ln xi1 Scheme foi a single piocessoi, which uses a time-slicing model, test-and-
set! can be implemented as follows
(define (test-and-set! cell)
(without-interrupts
(lambda ()
(if (car cell)
true
(begin (set-car! cell true)
false)))))
Without-interrupts disables time-slicing inteiiupts while its pioceduie aigument is
being executed.
4
Teie aie many vaiiants of such instiuctionsincluding test-and-set, test-and-
cleai, swap, compaie-and-exchange, load-ieseive, and stoie-conditionalwhose design
must be caiefully matched to the machines piocessoi-memoiy inteiface. One issue that
aiises heie is to deteimine what happens if two piocesses auempt to acquiie the same
iesouice at exactly the same time by using such an instiuction. Tis iequiies some
mechanism foi making a decision about which piocess gets contiol. Such a mechanism
is called an or|:er. Aibiteis usually boil down to some soit of haidwaie device. Un-
foitunately, it is possible to piove that one cannot physically constiuct a faii aibitei
that woiks 100 of the time unless one allows the aibitei an aibitiaiily long time to
make its decision. Te fundamental phenomenon heie was oiiginally obseived by the
fouiteenth-centuiy liench philosophei Jean Buiidan in his commentaiy on Aiistotles
De coe|o. Buiidan aigued that a peifectly iational dog placed between two equally at-
tiactive souices of food will staive to death, because it is incapable of deciding which
to go to ist.
424
diagiam like the one in liguie 3.29 to demonstiate how the
mutex implementation can fail by allowing two piocesses
to acquiie the mutex at the same time.
Exercise 3.47: Asemaphoie (of sizen) is a geneialization of
a mutex. Like a mutex, a semaphoie suppoits acquiie and
ielease opeiations, but it is moie geneial in that up to n
piocesses can acquiie it concuiiently. Additional piocesses
that auempt to acquiie the semaphoie must wait foi ielease
opeiations. Give implementations of semaphoies
a. in teims of mutexes
b. in teims of atomic test-and-set! opeiations.
Deadlock
Now that we have seen how to implement seiializeis, we can see that
account exchanging still has a pioblem, even with the serialized-
exchange pioceduie above. lmagine that Petei auempts to exchange
a1 with a2 while Paul concuiiently auempts to exchange a2 with a1.
Suppose that Peteis piocess ieaches the point wheie it has enteied a
seiialized pioceduie piotecting a1 and, just afei that, Pauls piocess en-
teis a seiialized pioceduie piotecting a2. Now Petei cannot pioceed (to
entei a seiialized pioceduie piotecting a2) until Paul exits the seiialized
pioceduie piotecting a2. Similaily, Paul cannot pioceed until Petei exits
the seiialized pioceduie piotecting a1. Each piocess is stalled foievei,
waiting foi the othei. Tis situation is called a JeoJ|oc|. Deadlock is al-
ways a dangei in systems that piovide concuiient access to multiple
shaied iesouices.
One way to avoid the deadlock in this situation is to give each ac-
count a unique identication numbei and iewiite serialized-exchange
42
so that a piocess will always auempt to entei a pioceduie piotecting the
lowest-numbeied account ist. Although this method woiks well foi
the exchange pioblem, theie aie othei situations that iequiie moie so-
phisticated deadlock-avoidance techniques, oi wheie deadlock cannot
be avoided at all. (See Exeicise 3.48 and Exeicise 3.49.)
48
Exercise 3.48: Explain in detail why the deadlock-avoidance
method desciibed above, (i.e., the accounts aie numbeied,
and each piocess auempts to acquiie the smallei-numbeied
account ist) avoids deadlock in the exchange pioblem. Re-
wiite serialized-exchange to incoipoiate this idea. (You
will also need to modify make-account so that each account
is cieated with a numbei, which can be accessed by sending
an appiopiiate message.)
Exercise 3.49: Give a scenaiio wheie the deadlock-avoid-
ance mechanism desciibed above does not woik. (Hint ln
the exchange pioblem, each piocess knows in advance which
accounts it will need to get access to. Considei a situation
wheie a piocess must get access to some shaied iesouices
befoie it can knowwhich additional shaied iesouices it will
iequiie.)
48
Te geneial technique foi avoiding deadlock by numbeiing the shaied iesouices
and acquiiing them in oidei is due to Havendei (198). Situations wheie deadlock can-
not be avoided iequiie JeoJ|oc|reco+ery methods, which entail having piocesses back
out of the deadlocked state and tiy again. Deadlock-iecoveiy mechanisms aie widely
used in database management systems, a topic that is tieated in detail in Giay and
Reutei 1993.
42
Concurrency, time, and communication
Weve seen how piogiamming concuiient systems iequiies contiolling
the oideiing of events when dieient piocesses access shaied state, and
weve seen how to achieve this contiol thiough judicious use of seiial-
izeis. But the pioblems of concuiiency lie deepei than this, because,
fiom a fundamental point of view, its not always cleai what is meant
by shaied state.
Mechanisms such as test-and-set! iequiie piocesses to examine a
global shaied ag at aibitiaiy times. Tis is pioblematic and inecient
to implement in modein high-speed piocessois, wheie due to optimiza-
tion techniques such as pipelining and cached memoiy, the contents
of memoiy may not be in a consistent state at eveiy instant. ln con-
tempoiaiy multipiocessing systems, theiefoie, the seiializei paiadigm
is being supplanted by new appioaches to concuiiency contiol.
49
Te pioblematic aspects of shaied state also aiise in laige, distiibuted
systems. loi instance, imagine a distiibuted banking systemwheie indi-
vidual bianch banks maintain local values foi bank balances and peiiod-
ically compaie these with values maintained by othei bianches. ln such
a system the value of the account balance would be undeteimined,
except iight afei synchionization. lf Petei deposits money in an ac-
count he holds jointly with Paul, when should we say that the account
balance has changedwhen the balance in the local bianch changes, oi
not until afei the synchionization` And if Paul accesses the account
49
One such alteinative to seiialization is called |orr:er sync|ron::o:on. Te piogiam-
mei peimits concuiient piocesses to execute as they please, but establishes ceitain
synchionization points (baiiieis) thiough which no piocess can pioceed until all the
piocesses have ieached the baiiiei. Modein piocessois piovide machine instiuctions
that peimit piogiammeis to establish synchionization points at places wheie consis-
tency is iequiied. Te vovrvvc, foi example, includes foi this puipose two instiuctions
called svNc and ririo (Enfoiced ln-oidei Execution of lnput/Output).
42
fiom a dieient bianch, what aie the ieasonable constiaints to place on
the banking system such that the behavioi is coiiect` Te only thing
that might mauei foi coiiectness is the behavioi obseived by Petei and
Paul individually and the state of the account immediately afei syn-
chionization. Qestions about the ieal account balance oi the oidei of
events between synchionizations may be iiielevant oi meaningless.
0
Te basic phenomenon heie is that synchionizing dieient pio-
cesses, establishing shaied state, oi imposing an oidei on events ie-
quiies communication among the piocesses. ln essence, any notion of
time in concuiiency contiol must be intimately tied to communica-
tion.
1
lt is intiiguing that a similai connection between time and com-
munication also aiises in the Teoiy of Relativity, wheie the speed of
light (the fastest signal that can be used to synchionize events) is a
fundamental constant ielating time and space. Te complexities we en-
countei in dealing with time and state in oui computational models may
in fact miiioi a fundamental complexity of the physical univeise.
3.5 Streams
Weve gained a good undeistanding of assignment as a tool in modeling,
as well as an appieciation of the complex pioblems that assignment
iaises. lt is time to ask whethei we could have gone about things in a
dieient way, so as to avoid some of these pioblems. ln this section,
0
Tis may seem like a stiange point of view, but theie aie systems that woik this
way. lnteinational chaiges to ciedit-caid accounts, foi example, aie noimally cleaied
on a pei-countiy basis, and the chaiges made in dieient countiies aie peiiodically
ieconciled. Tus the account balance may be dieient in dieient countiies.
1
loi distiibuted systems, this peispective was puisued by Lampoit (198), who
showed how to use communication to establish global clocks that can be used to
establish oideiings on events in distiibuted systems.
428
we exploie an alteinative appioach to modeling state, based on data
stiuctuies called sreos. As we shall see, stieams can mitigate some of
the complexity of modeling state.
Lets step back and ieview wheie this complexity comes fiom. ln
an auempt to model ieal-woild phenomena, we made some appaiently
ieasonable decisions We modeled ieal-woild objects with local state by
computational objects with local vaiiables. We identied time vaiiation
in the ieal woild with time vaiiation in the computei. We implemented
the time vaiiation of the states of the model objects in the computei
with assignments to the local vaiiables of the model objects.
ls theie anothei appioach` Can we avoid identifying time in the
computei with time in the modeled woild` Must we make the model
change with time in oidei to model phenomena in a changing woild`
Tink about the issue in teims of mathematical functions. We can de-
sciibe the time-vaiying behavioi of a quantity x as a function of time
x(t). lf we concentiate on x instant by instant, we think of it as a chang-
ing quantity. Yet if we concentiate on the entiie time histoiy of values,
we do not emphasize changethe function itself does not change.
2
lf time is measuied in disciete steps, then we can model a time func-
tion as a (possibly innite) sequence. ln this section, we will see how to
model change in teims of sequences that iepiesent the time histoiies
of the systems being modeled. To accomplish this, we intioduce new
data stiuctuies called sreos. liom an abstiact point of view, a stieam
is simply a sequence. Howevei, we will nd that the stiaightfoiwaid
implementation of stieams as lists (as in Section 2.2.1) doesnt fully ie-
2
Physicists sometimes adopt this view by intioducing the woild lines of paiticles
as a device foi ieasoning about motion. Weve also alieady mentioned (Section 2.2.3)
that this is the natuial way to think about signal-piocessing systems. We will exploie
applications of stieams to signal piocessing in Section 3..3.
429
veal the powei of stieam piocessing. As an alteinative, we intioduce
the technique of Je|oyeJ e+o|vo:on, which enables us to iepiesent veiy
laige (even innite) sequences as stieams.
Stieam piocessing lets us model systems that have state without
evei using assignment oi mutable data. Tis has impoitant implications,
both theoietical and piactical, because we can build models that avoid
the diawbacks inheient in intioducing assignment. On the othei hand,
the stieam fiamewoik iaises diculties of its own, and the question
of which modeling technique leads to moie modulai and moie easily
maintained systems iemains open.
3.5.1 Streams Are Delayed Lists
As we saw in Section 2.2.3, sequences can seive as standaid inteifaces
foi combining piogiam modules. We foimulated poweiful abstiactions
foi manipulating sequences, such as map, filter, and accumulate, that
captuie a wide vaiiety of opeiations in a mannei that is both succinct
and elegant.
Unfoitunately, if we iepiesent sequences as lists, this elegance is
bought at the piice of seveie ineciency with iespect to both the time
and space iequiied by oui computations. When we iepiesent manip-
ulations on sequences as tiansfoimations of lists, oui piogiams must
constiuct and copy data stiuctuies (which may be huge) at eveiy step
of a piocess.
To see why this is tiue, let us compaie two piogiams foi computing
the sum of all the piime numbeis in an inteival. Te ist piogiam is
wiiuen in standaid iteiative style
3
3
Assume that we have a piedicate prime? (e.g., as in Section 1.2.) that tests foi
piimality.
430
(define (sum-primes a b)
(define (iter count accum)
(cond ((> count b) accum)
((prime? count)
(iter (+ count 1) (+ count accum)))
(else (iter (+ count 1) accum))))
(iter a 0))
Te second piogiampeifoims the same computation using the sequence
opeiations of Section 2.2.3
(define (sum-primes a b)
(accumulate +
0
(filter prime?
(enumerate-interval a b))))
ln caiiying out the computation, the ist piogiam needs to stoie only
the sum being accumulated. ln contiast, the ltei in the second pio-
giam cannot do any testing until enumerate-interval has constiucted
a complete list of the numbeis in the inteival. Te ltei geneiates an-
othei list, which in tuin is passed to accumulate befoie being collapsed
to foim a sum. Such laige inteimediate stoiage is not needed by the ist
piogiam, which we can think of as enumeiating the inteival inciemen-
tally, adding each piime to the sum as it is geneiated.
Te ineciency in using lists becomes painfully appaient if we use
the sequence paiadigm to compute the second piime in the inteival
fiom 10,000 to 1,000,000 by evaluating the expiession
(car (cdr (filter prime?
(enumerate-interval 10000 1000000))))
Tis expiession does nd the second piime, but the computational ovei-
head is outiageous. We constiuct a list of almost a million integeis, ltei
431
this list by testing each element foi piimality, and then ignoie almost
all of the iesult. ln a moie tiaditional piogiamming style, we would in-
teileave the enumeiation and the lteiing, and stop when we ieached
the second piime.
Stieams aie a clevei idea that allows one to use sequence manipu-
lations without incuiiing the costs of manipulating sequences as lists.
With stieams we can achieve the best of both woilds We can foimu-
late piogiams elegantly as sequence manipulations, while auaining the
eciency of inciemental computation. Te basic idea is to aiiange to
constiuct a stieam only paitially, and to pass the paitial constiuction
to the piogiam that consumes the stieam. lf the consumei auempts to
access a pait of the stieam that has not yet been constiucted, the stieam
will automatically constiuct just enough moie of itself to pioduce the
iequiied pait, thus pieseiving the illusion that the entiie stieam exists.
ln othei woids, although we will wiite piogiams as if we weie piocess-
ing complete sequences, we design oui stieam implementation to au-
tomatically and tianspaiently inteileave the constiuction of the stieam
with its use.
On the suiface, stieams aie just lists with dieient names foi the
pioceduies that manipulate them. Teie is a constiuctoi, cons-stream,
and two selectois, stream-car and stream-cdr, which satisfy the con-
stiaints
(stream-car (cons-stream x y)) = x
(stream-cdr (cons-stream x y)) = y
Teie is a distinguishable object, the-empty-stream, which cannot be
the iesult of any cons-stream opeiation, and which can be identied
with the piedicate stream-null?.
4
Tus we can make and use stieams,
4
ln the xi1 implementation, the-empty-stream is the same as the empty list '(),
and stream-null? is the same as null?.
432
in just the same way as we can make and use lists, to iepiesent aggiegate
data aiianged in a sequence. ln paiticulai, we can build stieam analogs
of the list opeiations fiom Chaptei 2, such as list-ref, map, and for-
each
(define (stream-ref s n)
(if (= n 0)
(stream-car s)
(stream-ref (stream-cdr s) (- n 1))))
(define (stream-map proc s)
(if (stream-null? s)
the-empty-stream
(cons-stream (proc (stream-car s))
(stream-map proc (stream-cdr s)))))
(define (stream-for-each proc s)
(if (stream-null? s)
'done
(begin (proc (stream-car s))
(stream-for-each proc (stream-cdr s)))))
Stream-for-each is useful foi viewing stieams
(define (display-stream s)
(stream-for-each display-line s))
(define (display-line x) (newline) (display x))
To make the stieam implementation automatically and tianspaiently
inteileave the constiuction of a stieam with its use, we will aiiange foi
Tis should bothei you. Te fact that we aie dening such similai pioceduies foi
stieams and lists indicates that we aie missing some undeilying abstiaction. Unfoi-
tunately, in oidei to exploit this abstiaction, we will need to exeit nei contiol ovei
the piocess of evaluation than we can at piesent. We will discuss this point fuithei at
the end of Section 3..4. ln Section 4.2, well develop a fiamewoik that unies lists and
stieams.
433
the cdr of a stieam to be evaluated when it is accessed by the stream-
cdr pioceduie iathei than when the stieam is constiucted by cons-
stream. Tis implementation choice is ieminiscent of oui discussion of
iational numbeis in Section 2.1.2, wheie we saw that we can choose
to implement iational numbeis so that the ieduction of numeiatoi and
denominatoi to lowest teims is peifoimed eithei at constiuction time
oi at selection time. Te two iational-numbei implementations pioduce
the same data abstiaction, but the choice has an eect on eciency.
Teie is a similai ielationship between stieams and oidinaiy lists. As a
data abstiaction, stieams aie the same as lists. Te dieience is the time
at which the elements aie evaluated. With oidinaiy lists, both the car
and the cdr aie evaluated at constiuction time. With stieams, the cdr
is evaluated at selection time.
Oui implementation of stieams will be based on a special foimcalled
delay. Evaluating (delay exp) does not evaluate the expiession e:,
but iathei ietuins a so-called Je|oyeJ o|jec, which we can think of as
a piomise to evaluate e: at some futuie time. As a companion to
delay, theie is a pioceduie called force that takes a delayed object as
aigument and peifoims the evaluationin eect, foicing the delay to
fulll its piomise. We will see below how delay and force can be im-
plemented, but ist let us use these to constiuct stieams.
Cons-stream is a special foim dened so that
(cons-stream a b)
is equivalent to
(cons a (delay b))
What this means is that we will constiuct stieams using paiis. How-
evei, iathei than placing the value of the iest of the stieam into the cdr
of the paii we will put theie a piomise to compute the iest if it is evei
434
iequested. Stream-car and stream-cdr can now be dened as pioce-
duies
(define (stream-car stream) (car stream))
(define (stream-cdr stream) (force (cdr stream)))
Stream-car selects the car of the paii, stream-cdr selects the cdr of
the paii and evaluates the delayed expiession found theie to obtain the
iest of the stieam.
(cons 10000
(delay (stream-enumerate-interval 10001 1000000)))
Tat is, stream-enumerate-interval ietuins a stieam iepiesented as a
paii whose car is 10,000 and whose cdr is a piomise to enumeiate moie
of the inteival if so iequested. Tis stieam is now lteied foi piimes,
using the stieam analog of the filter pioceduie (Section 2.2.3)
(define (stream-filter pred stream)
(cond ((stream-null? stream) the-empty-stream)
((pred (stream-car stream))
(cons-stream (stream-car stream)
(stream-filter
pred
(stream-cdr stream))))
(else (stream-filter pred (stream-cdr stream)))))
Stream-filter tests the stream-car of the stieam (the car of the paii,
which is 10,000). Since this is not piime, stream-filter examines the
stream-cdr of its input stieam. Te call to stream-cdr foices evaluation
of the delayed stream-enumerate-interval, which now ietuins
(cons 10001
(delay (stream-enumerate-interval 10002 1000000)))
Te numbeis shown heie do not ieally appeai in the delayed expiession. What
actually appeais is the oiiginal expiession, in an enviionment in which the vaiiables
aie bound to the appiopiiate numbeis. loi example, (+ low 1) with low bound to
10,000 actually appeais wheie 10001 is shown.
43
Stream-filter now looks at the stream-car of this stieam, 10,001,
sees that this is not piime eithei, foices anothei stream-cdr, and so on,
until stream-enumerate-interval yields the piime 10,00, wheieupon
stream-filter, accoiding to its denition, ietuins
(cons-stream (stream-car stream)
(stream-filter pred (stream-cdr stream)))
which in this case is
(cons 10007
(delay (stream-filter
prime?
(cons 10008
(delay (stream-enumerate-interval
10009
1000000))))))
Tis iesult is now passed to stream-cdr in oui oiiginal expiession. Tis
foices the delayed stream-filter, which in tuin keeps foicing the de-
layed stream-enumerate-interval until it nds the next piime, which
is 10,009. linally, the iesult passed to stream-car in oui oiiginal ex-
piession is
(cons 10009
(delay (stream-filter
prime?
(cons 10010
(delay (stream-enumerate-interval
10011
1000000))))))
Stream-car ietuins 10,009, and the computation is complete. Only as
many integeis weie tested foi piimality as weie necessaiy to nd the
43
second piime, and the inteival was enumeiated only as fai as was nec-
essaiy to feed the piime ltei.
ln geneial, we can think of delayed evaluation as demand-diiven
piogiamming, wheieby each stage in the stieam piocess is activated
only enough to satisfy the next stage. What we have done is to decouple
the actual oidei of events in the computation fiom the appaient stiuc-
tuie of oui pioceduies. We wiite pioceduies as if the stieams existed all
at once when, in ieality, the computation is peifoimed inciementally,
as in tiaditional piogiamming styles.
Implementing delay and force
Although delay and force may seem like mysteiious opeiations, theii
implementation is ieally quite stiaightfoiwaid. Delay must package an
expiession so that it can be evaluated latei on demand, and we can ac-
complish this simply by tieating the expiession as the body of a pioce-
duie. Delay can be a special foim such that
(delay exp)
is syntactic sugai foi
(lambda () exp)
Force simply calls the pioceduie (of no aiguments) pioduced by delay,
so we can implement force as a pioceduie
(define (force delayed-object) (delayed-object))
Tis implementation suces foi delay and force to woik as adveitised,
but theie is an impoitant optimization that we can include. ln many ap-
plications, we end up foicing the same delayed object many times. Tis
can lead to seiious ineciency in iecuisive piogiams involving stieams.
(See Exeicise 3..) Te solution is to build delayed objects so that the
438
ist time they aie foiced, they stoie the value that is computed. Subse-
quent foicings will simply ietuin the stoied value without iepeating the
computation. ln othei woids, we implement delay as a special-puipose
memoized pioceduie similai to the one desciibed in Exeicise 3.2. One
way to accomplish this is to use the following pioceduie, which takes as
aigument a pioceduie (of no aiguments) and ietuins a memoized vei-
sion of the pioceduie. Te ist time the memoized pioceduie is iun, it
saves the computed iesult. On subsequent evaluations, it simply ietuins
the iesult.
(define (memo-proc proc)
(let ((already-run? false) (result false))
(lambda ()
(if (not already-run?)
(begin (set! result (proc))
(set! already-run? true)
result)
result))))
Delay is then dened so that (delay exp) is equivalent to
(memo-proc (lambda () exp))
and force is as dened pieviously.
8
8
Teie aie many possible implementations of stieams othei than the one desciibed
in this section. Delayed evaluation, which is the key to making stieams piactical, was
inheient in Algol 0s co|||ynoe paiametei-passing method. Te use of this mech-
anism to implement stieams was ist desciibed by Landin (19). Delayed evaluation
foi stieams was intioduced into Lisp by liiedman and Wise (19). ln theii implemen-
tation, cons always delays evaluating its aiguments, so that lists automatically behave
as stieams. Te memoizing optimization is also known as co|||yneeJ. Te Algol com-
munity would iefei to oui oiiginal delayed objects as co|||ynoe |vn|s and to the
optimized veisions as co|||yneeJ |vn|s.
439
Exercise 3.50: Complete the following denition, which
geneializes stream-map to allow pioceduies that take mul-
tiple aiguments, analogous to map in Section 2.2.1, lootnote
12.
(define (stream-map proc . argstreams)
(if (?? (car argstreams))
the-empty-stream
(??
(apply proc (map ?? argstreams))
(apply stream-map
(cons proc (map ?? argstreams))))))
Exercise 3.51: ln oidei to take a closei look at delayed eval-
uation, we will use the following pioceduie, which simply
ietuins its aigument afei piinting it
(define (show x)
(display-line x)
x)
What does the inteipietei piint in iesponse to evaluating
each expiession in the following sequence`
9
(define x
9
Exeicises such as Exeicise 3.1 and Exeicise 3.2 aie valuable foi testing oui un-
deistanding of how delay woiks. On the othei hand, inteimixing delayed evaluation
with piintingand, even woise, with assignmentis extiemely confusing, and instiuc-
tois of couises on computei languages have tiaditionally toimented theii students with
examination questions such as the ones in this section. Needless to say, wiiting pio-
giams that depend on such subtleties is odious piogiamming style. Pait of the powei
of stieam piocessing is that it lets us ignoie the oidei in which events actually happen
in oui piogiams. Unfoitunately, this is piecisely what we cannot aoid to do in the
piesence of assignment, which foices us to be conceined with time and change.
440
(stream-map show
(stream-enumerate-interval 0 10)))
(stream-ref x 5)
(stream-ref x 7)
Exercise 3.52: Considei the sequence of expiessions
(define sum 0)
(define (accum x) (set! sum (+ x sum)) sum)
(define seq
(stream-map accum
(stream-enumerate-interval 1 20)))
(define y (stream-filter even? seq))
(define z
(stream-filter (lambda (x) (= (remainder x 5) 0))
seq))
(stream-ref y 7)
(display-stream z)
What is the value of sum afei each of the above expiessions
is evaluated` What is the piinted iesponse to evaluating
the stream-ref and display-stream expiessions` Would
these iesponses diei if we had implemented (delay exp)
simply as (lambda () exp) without using the optimiza-
tion piovided by memo-proc` Explain
3.5.2 Infinite Streams
We have seen how to suppoit the illusion of manipulating stieams as
complete entities even though, in actuality, we compute only as much
of the stieamas we need to access. We can exploit this technique to iep-
iesent sequences eciently as stieams, even if the sequences aie veiy
441
long. What is moie stiiking, we can use stieams to iepiesent sequences
that aie innitely long. loi instance, considei the following denition
of the stieam of positive integeis
(define (integers-starting-from n)
(cons-stream n (integers-starting-from (+ n 1))))
(define integers (integers-starting-from 1))
Tis makes sense because integers will be a paii whose car is 1 and
whose cdr is a piomise to pioduce the integeis beginning with 2. Tis
is an innitely long stieam, but in any given time we can examine only
a nite poition of it. Tus, oui piogiams will nevei know that the entiie
innite stieam is not theie.
Using integers we can dene othei innite stieams, such as the
stieam of integeis that aie not divisible by
(define (divisible? x y) (= (remainder x y) 0))
(define no-sevens
(stream-filter (lambda (x) (not (divisible? x 7)))
integers))
Ten we can nd integeis not divisible by simply by accessing ele-
ments of this stieam
(stream-ref no-sevens 100)
117
ln analogy with integers, we can dene the innite stieamof libonacci
numbeis
(define (fibgen a b) (cons-stream a (fibgen b (+ a b))))
(define fibs (fibgen 0 1))
Fibs is a paii whose car is 0 and whose cdr is a piomise to evaluate
(fibgen 1 1). When we evaluate this delayed (fibgen 1 1), it will
442
pioduce a paii whose car is 1 and whose cdr is a piomise to evaluate
(fibgen 1 2), and so on.
loi a look at a moie exciting innite stieam, we can geneialize the
no-sevens example to constiuct the innite stieam of piime numbeis,
using a method known as the s:e+e o[ Froos|enes.
0
We stait with the
integeis beginning with 2, which is the ist piime. To get the iest of
the piimes, we stait by lteiing the multiples of 2 fiom the iest of the
integeis. Tis leaves a stieambeginning with 3, which is the next piime.
Now we ltei the multiples of 3 fiom the iest of this stieam. Tis leaves
a stieam beginning with , which is the next piime, and so on. ln othei
woids, we constiuct the piimes by a sieving piocess, desciibed as fol-
lows To sieve a stieam S, foim a stieam whose ist element is the ist
element of S and the iest of which is obtained by lteiing all multiples
of the ist element of S out of the iest of S and sieving the iesult. Tis
piocess is ieadily desciibed in teims of stieam opeiations
(define (sieve stream)
(cons-stream
(stream-car stream)
(sieve (stream-filter
(lambda (x)
(not (divisible? x (stream-car stream))))
(stream-cdr stream)))))
(define primes (sieve (integers-starting-from 2)))
0
Eiatosthenes, a thiid-centuiy n.c. Alexandiian Gieek philosophei, is famous foi
giving the ist accuiate estimate of the ciicumfeience of the Eaith, which he computed
by obseiving shadows cast at noon on the day of the summei solstice. Eiatostheness
sieve method, although ancient, has foimed the basis foi special-puipose haidwaie
sieves that, until iecently, weie the most poweiful tools in existence foi locating laige
piimes. Since the 0s, howevei, these methods have been supeiseded by outgiowths of
the piobabilistic techniques discussed in Section 1.2..
443
filter:
not
divisible?
sieve
sieve
car
cdr
cons
Figure 3.31: Te piime sieve viewed as a signal-piocessing
system.
Now to nd a paiticulai piime we need only ask foi it
(stream-ref primes 50)
233
lt is inteiesting to contemplate the signal-piocessing system set up by
sieve, shown in the Hendeison diagiam in liguie 3.31.
1
Te input
stieam feeds into an unconsei that sepaiates the ist element of the
stieam fiom the iest of the stieam. Te ist element is used to constiuct
a divisibility ltei, thiough which the iest is passed, and the output of
the ltei is fed to anothei sieve box. Ten the oiiginal ist element is
consed onto the output of the inteinal sieve to foim the output stieam.
Tus, not only is the stieam innite, but the signal piocessoi is also
innite, because the sieve contains a sieve within it.
1
We have named these guies afei Petei Hendeison, who was the ist peison to
show us diagiams of this soit as a way of thinking about stieam piocessing. Each solid
line iepiesents a stieam of values being tiansmiued. Te dashed line fiom the car to
the cons and the filter indicates that this is a single value iathei than a stieam.
444
Defining streams implicitly
Te integers and fibs stieams above weie dened by specifying gen-
eiating pioceduies that explicitly compute the stieam elements one by
one. An alteinative way to specify stieams is to take advantage of de-
layed evaluation to dene stieams implicitly. loi example, the following
expiession denes the stieam ones to be an innite stieam of ones
(define ones (cons-stream 1 ones))
Tis woiks much like the denition of a iecuisive pioceduie ones is
a paii whose car is 1 and whose cdr is a piomise to evaluate ones.
Evaluating the cdr gives us again a 1 and a piomise to evaluate ones,
and so on.
We can do moie inteiesting things by manipulating stieams with
opeiations such as add-streams, which pioduces the elementwise sum
of two given stieams
2
(define (add-streams s1 s2) (stream-map + s1 s2))
Now we can dene the integeis as follows
(define integers
(cons-stream 1 (add-streams ones integers)))
Tis denes integers to be a stieamwhose ist element is 1 and the iest
of which is the sum of ones and integers. Tus, the second element of
integers is 1 plus the ist element of integers, oi 2, the thiid element
of integers is 1 plus the second element of integers, oi 3, and so on.
Tis denition woiks because, at any point, enough of the integers
stieamhas been geneiated so that we can feed it back into the denition
to pioduce the next integei.
We can dene the libonacci numbeis in the same style
2
Tis uses the geneialized veision of stream-map fiom Exeicise 3.0.
44
(define fibs
(cons-stream
0
(cons-stream 1 (add-streams (stream-cdr fibs) fibs))))
Tis denition says that fibs is a stieam beginning with 0 and 1, such
that the iest of the stieam can be geneiated by adding fibs to itself
shifed by one place
1 1 2 3 5 8 13 21 . . . = (stream-cdr fibs)
0 1 1 2 3 5 8 13 . . . = fibs
0 1 1 2 3 5 8 13 21 34 . . . = fibs
Scale-stream is anothei useful pioceduie in foimulating such stieam
denitions. Tis multiplies each item in a stieam by a given constant
(define (scale-stream stream factor)
(stream-map (lambda (x) (* x factor))
stream))
loi example,
(define double (cons-stream 1 (scale-stream double 2)))
pioduces the stieam of poweis of 2 1, 2, 4, 8, 1, 32, . . ..
An alteinate denition of the stieamof piimes can be given by stait-
ing with the integeis and lteiing them by testing foi piimality. We will
need the ist piime, 2, to get staited
(define primes
(cons-stream
2
(stream-filter prime? (integers-starting-from 3))))
Tis denition is not so stiaightfoiwaid as it appeais, because we will
test whethei a numbei n is piime by checking whethei n is divisible by
a piime (not by just any integei) less than oi equal to
n
44
(define (prime? n)
(define (iter ps)
(cond ((> (square (stream-car ps)) n) true)
((divisible? n (stream-car ps)) false)
(else (iter (stream-cdr ps)))))
(iter primes))
Tis is a iecuisive denition, since primes is dened in teims of the
prime? piedicate, which itself uses the primes stieam. Te ieason this
pioceduie woiks is that, at any point, enough of the primes stieam has
been geneiated to test the piimality of the numbeis we need to check
next. Tat is, foi eveiy n we test foi piimality, eithei n is not piime (in
which case theie is a piime alieady geneiated that divides it) oi n is
piime (in which case theie is a piime alieady geneiatedi.e., a piime
less than nthat is gieatei than
n).
3
Exercise 3.53: Without iunning the piogiam, desciibe the
elements of the stieam dened by
(define s (cons-stream 1 (add-streams s s)))
Exercise 3.54: Dene a pioceduie mul-streams, analogous
to add-streams, that pioduces the elementwise pioduct of
its two input stieams. Use this togethei with the stieam of
integers to complete the following denition of the stieam
whose n
th
element (counting fiom 0) is n 1 factoiial
3
Tis last point is veiy subtle and ielies on the fact that p
n1
p
2
n
. (Heie, p
k
denotes
the k
th
piime.) Estimates such as these aie veiy dicult to establish. Te ancient pioof
by Euclid that theie aie an innite numbei of piimes shows that p
n1
p
1
p
2
p
n
1,
and no substantially beuei iesult was pioved until 181, when the Russian mathemati-
cian P. L. Chebyshev established that p
n1
2p
n
foi all n. Tis iesult, oiiginally con-
jectuied in 184, is known as BerronJs |yo|es:s. A pioof can be found in section 22.3
of Haidy and Wiight 190.
44
(define factorials
(cons-stream 1 (mul-streams ?? ??)))
Exercise 3.55: Dene a pioceduie partial-sums that takes
as aigument a stieam S and ietuins the stieam whose ele-
ments aie S
0
, S
0
S
1
, S
0
S
1
S
2
, . . .. loi example, (partial-
sums integers) should be the stieam 1, 3, , 10, 1, . . ..
Exercise 3.56: A famous pioblem, ist iaised by R. Ham-
ming, is to enumeiate, in ascending oidei with no iepeti-
tions, all positive integeis with no piime factois othei than
2, 3, oi . One obvious way to do this is to simply test each
integei in tuin to see whethei it has any factois othei than
2, 3, and . But this is veiy inecient, since, as the integeis
get laigei, fewei and fewei of them t the iequiiement. As
an alteinative, let us call the iequiied stieam of numbeis S
and notice the following facts about it.
S begins with 1.
Te elements of (scale-stream S 2) aie also ele-
ments of S.
Te same is tiue foi (scale-stream S 3) and (scale-
stream 5 S).
Tese aie all the elements of S.
Nowall we have to do is combine elements fiomthese souices.
loi this we dene a pioceduie merge that combines two oi-
deied stieams into one oideied iesult stieam, eliminating
iepetitions
448
(define (merge s1 s2)
(cond ((stream-null? s1) s2)
((stream-null? s2) s1)
(else
(let ((s1car (stream-car s1))
(s2car (stream-car s2)))
(cond ((< s1car s2car)
(cons-stream
s1car
(merge (stream-cdr s1) s2)))
((> s1car s2car)
(cons-stream
s2car
(merge s1 (stream-cdr s2))))
(else
(cons-stream
s1car
(merge (stream-cdr s1)
(stream-cdr s2)))))))))
Ten the iequiied stieam may be constiucted with merge,
as follows
(define S (cons-stream 1 (merge ?? ??)))
lill in the missing expiessions in the places maiked
above.
Exercise 3.57: How many additions aie peifoimed when
we compute the n
th
libonacci numbei using the denition
of fibs based on the add-streams pioceduie` Show that
the numbei of additions would be exponentially gieatei
if we had implemented (delay exp) simply as (lambda
449
() exp), without using the optimization piovided by the
memo-proc pioceduie desciibed in Section 3..1.
4
Exercise 3.58: Give an inteipietation of the stieam com-
puted by the following pioceduie
(define (expand num den radix)
(cons-stream
(quotient (* num radix) den)
(expand (remainder (* num radix) den) den radix)))
(Quotient is a piimitive that ietuins the integei quotient of
two integeis.) What aie the successive elements pioduced
by (expand 1 7 10)` What is pioduced by (expand 3 8
10)`
Exercise 3.59: ln Section 2..3 we saw how to implement
a polynomial aiithmetic system iepiesenting polynomials
as lists of teims. ln a similai way, we can woik with o+er
ser:es, such as
e
x
= 1 x
x
2
2
x
3
3 2
x
4
4 3 2
. . . ,
cos x = 1
x
2
2
x
4
4 3 2
. . . ,
sinx = x
x
3
3 2
x
4 3 2
. . .
4
Tis exeicise shows howcall-by-need is closely ielated to oidinaiy memoization as
desciibed in Exeicise 3.2. ln that exeicise, we used assignment to explicitly constiuct
a local table. Oui call-by-need stieam optimization eectively constiucts such a table
automatically, stoiing values in the pieviously foiced paits of the stieam.
40
iepiesented as innite stieams. We will iepiesent the seiies
a
0
a
1
x a
2
x
2
a
3
x
3
. . . as the stieam whose elements
aie the coecients a
0
, a
1
, a
2
, a
3
, . . ..
a. Te integial of the seiies a
0
a
1
x a
2
x
2
a
3
x
3
. . .
is the seiies
c a
0
x
1
2
a
1
x
2
1
3
a
2
x
3
1
4
a
3
x
4
. . . ,
wheiec is any constant. Dene a pioceduie integrate-
series that takes as input a stieama
0
, a
1
, a
2
, . . . iep-
iesenting a powei seiies and ietuins the stieam a
0
,
1
2
a
1
,
1
3
a
2
, . . . of coecients of the non-constant teims
of the integial of the seiies. (Since the iesult has no
constant teim, it doesnt iepiesent a powei seiies, when
we use integrate-series, we will cons on the ap-
piopiiate constant.)
b. Te function x e
x
is its own deiivative. Tis im-
plies that e
x
and the integial of e
x
aie the same se-
iies, except foi the constant teim, which is e
0
= 1.
Accoidingly, we can geneiate the seiies foi e
x
as
(define exp-series
(cons-stream 1 (integrate-series exp-series)))
Show how to geneiate the seiies foi sine and cosine,
staiting fiom the facts that the deiivative of sine is
cosine and the deiivative of cosine is the negative of
sine
(define cosine-series (cons-stream 1 ??))
(define sine-series (cons-stream 0 ??))
41
Exercise 3.60: With powei seiies iepiesented as stieams
of coecients as in Exeicise 3.9, adding seiies is imple-
mented by add-streams. Complete the denition of the fol-
lowing pioceduie foi multiplying seiies
(define (mul-series s1 s2)
(cons-stream ?? (add-streams ?? ??)))
You can test youi pioceduie by veiifying that sin
2
x cos
2
x = 1,
using the seiies fiom Exeicise 3.9.
Exercise 3.61: Let S be a powei seiies (Exeicise 3.9) whose
constant teim is 1. Suppose we want to nd the powei se-
iies 1/S, that is, the seiies X such that SX = 1. Wiite
S = 1 S
R
wheie S
R
is the pait of S afei the constant
teim. Ten we can solve foi X as follows
S X = 1,
(1 S
R
) X = 1,
X S
R
X = 1,
X = 1 S
R
X.
ln othei woids, X is the powei seiies whose constant teim
is 1 and whose highei-oidei teims aie given by the negative
of S
R
times X. Use this idea to wiite a pioceduie invert-
unit-series that computes 1/S foi a powei seiies S with
constant teim 1. You will need to use mul-series fiom Ex-
eicise 3.0.
Exercise 3.62: Use the iesults of Exeicise 3.0 and Exei-
cise 3.1 to dene a pioceduie div-series that divides two
powei seiies. Div-series should woik foi any two seiies,
42
piovided that the denominatoi seiies begins with a nonzeio
constant teim. (lf the denominatoi has a zeio constant teim,
then div-series should signal an eiioi.) Show how to use
div-series togethei with the iesult of Exeicise 3.9 to gen-
eiate the powei seiies foi tangent.
3.5.3 Exploiting the Stream Paradigm
Stieams with delayed evaluation can be a poweiful modeling tool, pio-
viding many of the benets of local state and assignment. Moieovei,
they avoid some of the theoietical tangles that accompany the intio-
duction of assignment into a piogiamming language.
Te stieam appioach can be illuminating because it allows us to
build systems with dieient module boundaiies than systems oiganized
aiound assignment to state vaiiables. loi example, we can think of an
entiie time seiies (oi signal) as a focus of inteiest, iathei than the values
of the state vaiiables at individual moments. Tis makes it convenient
to combine and compaie components of state fiom dieient moments.
Formulating iterations as stream processes
ln Section 1.2.1, we intioduced iteiative piocesses, which pioceed by
updating state vaiiables. We know now that we can iepiesent state as
a timeless stieam of values iathei than as a set of vaiiables to be up-
dated. Lets adopt this peispective in ievisiting the squaie-ioot pioce-
duie fiom Section 1.1.. Recall that the idea is to geneiate a sequence of
beuei and beuei guesses foi the squaie ioot of x by applying ovei and
ovei again the pioceduie that impioves guesses
(define (sqrt-improve guess x)
(average guess (/ x guess)))
43
ln oui oiiginal sqrt pioceduie, we made these guesses be the successive
values of a state vaiiable. lnstead we can geneiate the innite stieam of
guesses, staiting with an initial guess of 1
(define (sqrt-stream x)
(define guesses
(cons-stream
1.0
(stream-map (lambda (guess) (sqrt-improve guess x))
guesses)))
guesses)
(display-stream (sqrt-stream 2))
1.
1.5
1.4166666666666665
1.4142156862745097
1.4142135623746899
. . .
We can geneiate moie and moie teims of the stieam to get beuei and
beuei guesses. lf we like, we can wiite a pioceduie that keeps geneiating
teims until the answei is good enough. (See Exeicise 3.4.)
Anothei iteiation that we can tieat in the same way is to geneiate
an appioximation to , based upon the alteinating seiies that we saw
in Section 1.3.1
4
= 1
1
3
1
. . . .
We ist geneiate the stieam of summands of the seiies (the iecipiocals
of the odd integeis, with alteinating signs). Ten we take the stieam of
We cant use let to bind the local vaiiable guesses, because the value of guesses
depends on guesses itself. Exeicise 3.3 addiesses why we want a local vaiiable heie.
44
sums of moie and moie teims (using the partial-sums pioceduie of
Exeicise 3.) and scale the iesult by 4
(define (pi-summands n)
(cons-stream (/ 1.0 n)
(stream-map - (pi-summands (+ n 2)))))
(define pi-stream
(scale-stream (partial-sums (pi-summands 1)) 4))
(display-stream pi-stream)
4.
2.666666666666667
3.466666666666667
2.8952380952380956
3.3396825396825403
2.9760461760461765
3.2837384837384844
3.017071817071818
. . .
Tis gives us a stieamof beuei and beuei appioximations to , although
the appioximations conveige iathei slowly. Eight teims of the sequence
bound the value of between 3.284 and 3.01.
So fai, oui use of the stieam of states appioach is not much dieient
fiom updating state vaiiables. But stieams give us an oppoitunity to do
some inteiesting tiicks. loi example, we can tiansfoim a stieam with
a seqvence occe|eroor that conveits a sequence of appioximations to a
new sequence that conveiges to the same value as the oiiginal, only
fastei.
One such acceleiatoi, due to the eighteenth-centuiy Swiss math-
ematician Leonhaid Eulei, woiks well with sequences that aie paitial
sums of alteinating seiies (seiies of teims with alteinating signs). ln Eu-
leis technique, if S
n
is the n
th
teim of the oiiginal sum sequence, then
4
the acceleiated sequence has teims
S
n1
(S
n1
S
n
)
2
S
n1
2S
n
S
n1
.
Tus, if the oiiginal sequence is iepiesented as a stieam of values, the
tiansfoimed sequence is given by
(define (euler-transform s)
(let ((s0 (stream-ref s 0)) , S
n1
(s1 (stream-ref s 1)) , S
n
(s2 (stream-ref s 2))) , S
n1
(cons-stream (- s2 (/ (square (- s2 s1))
(+ s0 (* -2 s1) s2)))
(euler-transform (stream-cdr s)))))
We can demonstiate Eulei acceleiation with oui sequence of appioxi-
mations to
(display-stream (euler-transform pi-stream))
3.166666666666667
3.1333333333333337
3.1452380952380956
3.13968253968254
3.1427128427128435
3.1408813408813416
3.142071817071818
3.1412548236077655
. . .
Even beuei, we can acceleiate the acceleiated sequence, and iecuisively
acceleiate that, and so on. Namely, we cieate a stieam of stieams (a
stiuctuie well call a o||eov) in which each stieam is the tiansfoim of
the pieceding one
4
(define (make-tableau transform s)
(cons-stream s (make-tableau transform (transform s))))
Te tableau has the foim
s
00
s
01
s
02
s
03
s
04
. . .
s
10
s
11
s
12
s
13
. . .
s
20
s
21
s
22
. . .
. . .
linally, we foim a sequence by taking the ist teim in each iow of the
tableau
(define (accelerated-sequence transform s)
(stream-map stream-car (make-tableau transform s)))
We can demonstiate this kind of supei-acceleiation of the sequence
(display-stream
(accelerated-sequence euler-transform pi-stream))
4.
3.166666666666667
3.142105263157895
3.141599357319005
3.1415927140337785
3.1415926539752927
3.1415926535911765
3.141592653589778
. . .
Te iesult is impiessive. Taking eight teims of the sequence yields the
coiiect value of to 14 decimal places. lf we had used only the oiiginal
sequence, we would need to compute on the oidei of 10
13
teims (i.e.,
expanding the seiies fai enough so that the individual teims aie less
than 10
13
) to get that much accuiacy'
4
We could have implemented these acceleiation techniques without
using stieams. But the stieam foimulation is paiticulaily elegant and
convenient because the entiie sequence of states is available to us as a
data stiuctuie that can be manipulated with a unifoimset of opeiations.
Exercise 3.63: Louis Reasonei asks why the sqrt-stream
pioceduie was not wiiuen in the following moie stiaight-
foiwaid way, without the local vaiiable guesses
(define (sqrt-stream x)
(cons-stream 1.0 (stream-map
(lambda (guess)
(sqrt-improve guess x))
(sqrt-stream x))))
Alyssa P. Hackei ieplies that this veision of the pioceduie
is consideiably less ecient because it peifoims iedundant
computation. Explain Alyssas answei. Would the two vei-
sions still diei in eciency if oui implementation of delay
used only (lambda () exp) without using the optimiza-
tion piovided by memo-proc (Section 3..1)`
Exercise 3.64: Wiite a pioceduie stream-limit that takes
as aiguments a stieamand a numbei (the toleiance). lt should
examine the stieam until it nds two successive elements
that diei in absolute value by less than the toleiance, and
ietuin the second of the two elements. Using this, we could
compute squaie ioots up to a given toleiance by
(define (sqrt x tolerance)
(stream-limit (sqrt-stream x) tolerance))
48
Exercise 3.65: Use the seiies
ln 2 = 1
1
2
1
3
1
4
. . .
to compute thiee sequences of appioximations to the nat-
uial logaiithm of 2, in the same way we did above foi .
How iapidly do these sequences conveige`
Infinite streams of pairs
ln Section 2.2.3, we saw how the sequence paiadigm handles tiaditional
nested loops as piocesses dened on sequences of paiis. lf we geneialize
this technique to innite stieams, then we can wiite piogiams that aie
not easily iepiesented as loops, because the looping must iange ovei
an innite set.
loi example, suppose we want to geneialize the prime-sum-pairs
pioceduie of Section 2.2.3 to pioduce the stieam of paiis of o|| integeis
(i , j) with i j such that i j is piime. lf int-pairs is the sequence of
all paiis of integeis (i , j) withi j, then oui iequiied stieam is simply
(stream-filter
(lambda (pair) (prime? (+ (car pair) (cadr pair))))
int-pairs)
Oui pioblem, then, is to pioduce the stieam int-pairs. Moie geneially,
suppose we have two stieams S = (S
i
) and T = (T
j
), and imagine the
innite iectangulai aiiay
(S
0
, T
0
) (S
0
, T
1
) (S
0
, T
2
) . . .
(S
1
, T
0
) (S
1
, T
1
) (S
1
, T
2
) . . .
(S
2
, T
0
) (S
2
, T
1
) (S
2
, T
2
) . . .
. . .
As in Section 2.2.3, we iepiesent a paii of integeis as a list iathei than a Lisp paii.
49
We wish to geneiate a stieam that contains all the paiis in the aiiay
that lie on oi above the diagonal, i.e., the paiis
(S
0
, T
0
) (S
0
, T
1
) (S
0
, T
2
) . . .
(S
1
, T
1
) (S
1
, T
2
) . . .
(S
2
, T
2
) . . .
. . .
(lf we take both S and T to be the stieam of integeis, then this will be
oui desiied stieam int-pairs.)
Call the geneial stieam of paiis (pairs S T), and considei it to be
composed of thiee paits the paii (S
0
, T
0
), the iest of the paiis in the ist
iow, and the iemaining paiis
(S
0
, T
0
) (S
0
, T
1
) (S
0
, T
2
) . . .
(S
1
, T
1
) (S
1
, T
2
) . . .
(S
2
, T
2
) . . .
. . .
Obseive that the thiid piece in this decomposition (paiis that aie not
in the ist iow) is (iecuisively) the paiis foimed fiom (stream-cdr S)
and (stream-cdr T). Also note that the second piece (the iest of the
ist iow) is
(stream-map (lambda (x) (list (stream-car s) x))
(stream-cdr t))
Tus we can foim oui stieam of paiis as follows
(define (pairs s t)
(cons-stream
(list (stream-car s) (stream-car t))
See Exeicise 3.8 foi some insight into why we chose this decomposition.
40
(combine-in-some-way
(stream-map (lambda (x) (list (stream-car s) x))
(stream-cdr t))
(pairs (stream-cdr s) (stream-cdr t)))))
ln oidei to complete the pioceduie, we must choose some way to com-
bine the two innei stieams. One idea is to use the stieam analog of the
append pioceduie fiom Section 2.2.1
(define (stream-append s1 s2)
(if (stream-null? s1)
s2
(cons-stream (stream-car s1)
(stream-append (stream-cdr s1) s2))))
Tis is unsuitable foi innite stieams, howevei, because it takes all the
elements fiom the ist stieam befoie incoipoiating the second stieam.
ln paiticulai, if we tiy to geneiate all paiis of positive integeis using
(pairs integers integers)
oui stieam of iesults will ist tiy to iun thiough all paiis with the ist
integei equal to 1, and hence will nevei pioduce paiis with any othei
value of the ist integei.
To handle innite stieams, we need to devise an oidei of combina-
tion that ensuies that eveiy element will eventually be ieached if we
let oui piogiam iun long enough. An elegant way to accomplish this is
with the following interleave pioceduie
8
8
Te piecise statement of the iequiied piopeity on the oidei of combination is as
follows Teie should be a function f of two aiguments such that the paii coiiespond-
ing to element i of the ist stieam and element j of the second stieam will appeai as
element numbei f (i , j) of the output stieam. Te tiick of using interleave to accom-
plish this was shown to us by David Tuinei, who employed it in the language KRC
(Tuinei 1981).
41
(define (interleave s1 s2)
(if (stream-null? s1)
s2
(cons-stream (stream-car s1)
(interleave s2 (stream-cdr s1)))))
Since interleave takes elements alteinately fiom the two stieams, ev-
eiy element of the second stieam will eventually nd its way into the
inteileaved stieam, even if the ist stieam is innite.
We can thus geneiate the iequiied stieam of paiis as
(define (pairs s t)
(cons-stream
(list (stream-car s) (stream-car t))
(interleave
(stream-map (lambda (x) (list (stream-car s) x))
(stream-cdr t))
(pairs (stream-cdr s) (stream-cdr t)))))
Exercise 3.66: Examine the stieam(pairs integers integers).
Can you make any geneial comments about the oidei in
which the paiis aie placed into the stieam` loi example,
appioximately how many paiis piecede the paii (1, 100)`
the paii (99, 100)` the paii (100, 100)` (lf you can make pie-
cise mathematical statements heie, all the beuei. But feel
fiee to give moie qualitative answeis if you nd youiself
geuing bogged down.)
Exercise 3.67: Modify the pairs pioceduie so that (pairs
integers integers) will pioduce the stieamof o|| paiis of
integeis (i , j) (without the condition i j). Hint You will
need to mix in an additional stieam.
42
Exercise 3.68: Louis Reasonei thinks that building a stieam
of paiis fiom thiee paits is unnecessaiily complicated. ln-
stead of sepaiating the paii (S
0
, T
0
) fiomthe iest of the paiis
in the ist iow, he pioposes to woik with the whole ist
iow, as follows
(define (pairs s t)
(interleave
(stream-map (lambda (x) (list (stream-car s) x))
t)
(pairs (stream-cdr s) (stream-cdr t))))
Does this woik` Considei what happens if we evaluate (pairs
integers integers) using Louiss denition of pairs.
Exercise 3.69: Wiite a pioceduie triples that takes thiee
innite stieams, S, T, and U, and pioduces the stieam of
tiiples (S
i
, T
j
, U
k
) such that i j k. Use triples to gen-
eiate the stieam of all Pythagoiean tiiples of positive inte-
geis, i.e., the tiiples (i , j, k) such that i j and i
2
j
2
= k
2
.
Exercise 3.70: lt would be nice to be able to geneiate stieams
in which the paiis appeai in some useful oidei, iathei than
in the oidei that iesults fiom an oJ |oc inteileaving pio-
cess. We can use a technique similai to the merge pioceduie
of Exeicise 3., if we dene a way to say that one paii of
integeis is less than anothei. One way to do this is to de-
ne a weighting function W(i , j) and stipulate that (i
1
, j
1
)
is less than (i
2
, j
2
) if W(i
1
, j
1
) < W(i
2
, j
2
). Wiite a pioce-
duie merge-weighted that is like merge, except that merge-
weighted takes an additional aigument weight, which is a
pioceduie that computes the weight of a paii, and is used
43
to deteimine the oidei in which elements should appeai in
the iesulting meiged stieam.
9
Using this, geneialize pairs
to a pioceduie weighted-pairs that takes two stieams, to-
gethei with a pioceduie that computes a weighting func-
tion, and geneiates the stieam of paiis, oideied accoiding
to weight. Use youi pioceduie to geneiate
a. the stieam of all paiis of positive integeis (i , j) with
i j oideied accoiding to the sumi j,
b. the stieam of all paiis of positive integeis (i , j) with
i j, wheie neithei i noi j is divisible by 2, 3, oi , and
the paiis aie oideied accoiding to the sum 2i 3j ij.
Exercise 3.71: Numbeis that can be expiessed as the sumof
two cubes in moie than one way aie sometimes called Ro
onvjon nv|ers, in honoi of the mathematician Siinivasa
Ramanujan.
0
Oideied stieams of paiis piovide an elegant
solution to the pioblem of computing these numbeis. To
nd a numbei that can be wiiuen as the sum of two cubes
in two dieient ways, we need only geneiate the stieam of
paiis of integeis (i , j) weighted accoiding to the sumi
3
j
3
9
We will iequiie that the weighting function be such that the weight of a paii in-
cieases as we move out along a iow oi down along a column of the aiiay of paiis.
0
To quote fiom G. H. Haidys obituaiy of Ramanujan (Haidy 1921) lt was Mi.
Liulewood (l believe) who iemaiked that eveiy positive integei was one of his fiiends.
l iemembei once going to see him when he was lying ill at Putney. l had iidden in taxi-
cab No. 129, and iemaiked that the numbei seemed to me a iathei dull one, and that l
hoped it was not an unfavoiable omen. No, he ieplied, it is a veiy inteiesting numbei,
it is the smallest numbei expiessible as the sum of two cubes in two dieient ways.
Te tiick of using weighted paiis to geneiate the Ramanujan numbeis was shown to
us by Chailes Leiseison.
44
(see Exeicise 3.0), then seaich the stieam foi two consecu-
tive paiis with the same weight. Wiite a pioceduie to genei-
ate the Ramanujan numbeis. Te ist such numbei is 1,29.
What aie the next ve`
Exercise 3.72: ln a similai way to Exeicise 3.1 geneiate a
stieam of all numbeis that can be wiiuen as the sum of two
squaies in thiee dieient ways (showing how they can be
so wiiuen).
Streams as signals
We began oui discussion of stieams by desciibing them as computa-
tional analogs of the signals in signal-piocessing systems. ln fact, we
can use stieams to model signal-piocessing systems in a veiy diiect
way, iepiesenting the values of a signal at successive time inteivals as
consecutive elements of a stieam. loi instance, we can implement an
:negroor oi sver that, foi an input stieam x = (x
i
), an initial value
C, and a small inciement dt, accumulates the sum
S
i
= C
i
j =1
x
j
dt
and ietuins the stieam of values S = (S
i
). Te following integral pio-
ceduie is ieminiscent of the implicit style denition of the stieam of
integeis (Section 3..2)
(define (integral integrand initial-value dt)
(define int
(cons-stream initial-value
(add-streams (scale-stream integrand dt)
int)))
int)
4
add
cons
initial-value
integral input
scale: dt
Figure 3.32: Te integral pioceduie viewed as a signal-
piocessing system.
liguie 3.32 is a pictuie of a signal-piocessing system that coiiesponds
to the integral pioceduie. Te input stieam is scaled by dt and passed
thiough an addei, whose output is passed back thiough the same addei.
Te self-iefeience in the denition of int is ieected in the guie by
the feedback loop that connects the output of the addei to one of the
inputs.
Exercise 3.73: We can model electiical ciicuits using stieams
to iepiesent the values of cuiients oi voltages at a sequence
of times. loi instance, suppose we have an RC c:rcv: con-
sisting of a iesistoi of iesistance R and a capacitoi of capac-
itance C in seiies. Te voltage iesponse v of the ciicuit to
an injected cuiient i is deteimined by the foimula in lig-
uie 3.33, whose stiuctuie is shown by the accompanying
signal-ow diagiam.
Wiite a pioceduie RC that models this ciicuit. RC should
take as inputs the values of R, C, and dt and should ietuin
a pioceduie that takes as inputs a stieam iepiesenting the
cuiient i and an initial value foi the capacitoi voltage v
0
4
v
v
0
i
R
C
i
+ -- v
scale: R
integral
add
scale:
1
C
v = v
0
+
1
i dt + Ri
0
t
C
Z
Figure 3.33: An RC ciicuit and the associated signal-ow diagiam.
and pioduces as output the stieam of voltages v. loi ex-
ample, you should be able to use RC to model an RC ciicuit
with R ohms, C 1 faiad, and a 0.-second time step by
evaluating (define RC1 (RC 5 1 0.5)). Tis denes RC1
as a pioceduie that takes a stieam iepiesenting the time
sequence of cuiients and an initial capacitoi voltage and
pioduces the output stieam of voltages.
Exercise 3.74: Alyssa P. Hackei is designing a system to
piocess signals coming fiom physical sensois. One impoi-
tant featuie she wishes to pioduce is a signal that desciibes
the :ero cross:ngs of the input signal. Tat is, the iesulting
signal should be 1 whenevei the input signal changes fiom
negative to positive, 1 whenevei the input signal changes
fiom positive to negative, and 0 otheiwise. (Assume that
the sign of a 0 input is positive.) loi example, a typical in-
4
put signal with its associated zeio-ciossing signal would be
. . . 1 2 1.5 1 0.5 -0.1 -2 -3 -2 -0.5 0.2 3 4 . . .
. . . 0 0 0 0 0 -1 0 0 0 0 1 0 0 . . .
ln Alyssas system, the signal fiomthe sensoi is iepiesented
as a stieam sense-data and the stieam zero-crossings
is the coiiesponding stieam of zeio ciossings. Alyssa ist
wiites a pioceduie sign-change-detector that takes two
values as aiguments and compaies the signs of the values
to pioduce an appiopiiate 0, 1, oi - 1. She then constiucts
hei zeio-ciossing stieam as follows
(define (make-zero-crossings input-stream last-value)
(cons-stream
(sign-change-detector
(stream-car input-stream)
last-value)
(make-zero-crossings
(stream-cdr input-stream)
(stream-car input-stream))))
(define zero-crossings
(make-zero-crossings sense-data 0))
Alyssas boss, Eva Lu Atoi, walks by and suggests that this
piogiam is appioximately equivalent to the following one,
which uses the geneialized veision of stream-map fiomEx-
eicise 3.0
(define zero-crossings
(stream-map sign-change-detector
sense-data
expression))
48
Complete the piogiamby supplying the indicated e:ress:on.
Exercise 3.75: Unfoitunately, Alyssas zeio-ciossing de-
tectoi in Exeicise 3.4 pioves to be insucient, because the
noisy signal fiom the sensoi leads to spuiious zeio cioss-
ings. Lem E. Tweakit, a haidwaie specialist, suggests that
Alyssa smooth the signal to ltei out the noise befoie ex-
tiacting the zeio ciossings. Alyssa takes his advice and de-
cides to extiact the zeio ciossings fiomthe signal constiucted
by aveiaging each value of the sense data with the pievious
value. She explains the pioblem to hei assistant, Louis Rea-
sonei, who auempts to implement the idea, alteiing Alyssas
piogiam as follows
(define (make-zero-crossings input-stream last-value)
(let ((avpt (/ (+ (stream-car input-stream)
last-value)
2)))
(cons-stream
(sign-change-detector avpt last-value)
(make-zero-crossings
(stream-cdr input-stream) avpt))))
Tis does not coiiectly implement Alyssas plan. lind the
bug that Louis has installed and x it without changing the
stiuctuie of the piogiam. (Hint You will need to inciease
the numbei of aiguments to make-zero-crossings.)
Exercise 3.76: Eva Lu Atoi has a ciiticism of Louiss ap-
pioach in Exeicise 3.. Te piogiam he wiote is not mod-
ulai, because it inteimixes the opeiation of smoothing with
the zeio-ciossing extiaction. loi example, the extiactoi should
49
not have to be changed if Alyssa nds a beuei way to con-
dition hei input signal. Help Louis by wiiting a pioceduie
smooth that takes a stieam as input and pioduces a stieam
in which each element is the aveiage of two successive in-
put stieam elements. Ten use smooth as a component to
implement the zeio-ciossing detectoi in a moie modulai
style.
3.5.4 Streams and Delayed Evaluation
Te integral pioceduie at the end of the pieceding section shows how
we can use stieams to model signal-piocessing systems that contain
feedback loops. Te feedback loop foi the addei shown in liguie 3.32
is modeled by the fact that integrals inteinal stieam int is dened in
teims of itself
(define int
(cons-stream
initial-value
(add-streams (scale-stream integrand dt)
int)))
Te inteipieteis ability to deal with such an implicit denition depends
on the delay that is incoipoiated into cons-stream. Without this delay,
the inteipietei could not constiuct int befoie evaluating both aigu-
ments to cons-stream, which would iequiie that int alieady be dened.
ln geneial, delay is ciucial foi using stieams to model signal-piocessing
systems that contain loops. Without delay, oui models would have to
be foimulated so that the inputs to any signal-piocessing component
would be fully evaluated befoie the output could be pioduced. Tis
would outlaw loops.
40
y
0
dy y
integral map: f
Figure 3.34: An analog computei ciicuit that solves the
equation dy/dt = f (y).
Unfoitunately, stieam models of systems with loops may iequiie
uses of delay beyond the hidden delay supplied by cons-stream. loi
instance, liguie 3.34 shows a signal-piocessing system foi solving the
dieiential equation dy/dt = f (y) wheie f is a given function. Te g-
uie shows a mapping component, which applies f to its input signal,
linked in a feedback loop to an integiatoi in a mannei veiy similai to
that of the analog computei ciicuits that aie actually used to solve such
equations.
Assuming we aie given an initial value y
0
foi y, we could tiy to
model this system using the pioceduie
(define (solve f y0 dt)
(define y (integral dy y0 dt))
(define dy (stream-map f y))
y)
Tis pioceduie does not woik, because in the ist line of solve the
call to integral iequiies that the input dy be dened, which does not
happen until the second line of solve.
On the othei hand, the intent of oui denition does make sense,
because we can, in piinciple, begin to geneiate the y stieam without
41
knowing dy. lndeed, integral and many othei stieam opeiations have
piopeities similai to those of cons-stream, in that we can geneiate
pait of the answei given only paitial infoimation about the aiguments.
loi integral, the ist element of the output stieam is the specied
initial-value. Tus, we can geneiate the ist element of the output
stieam without evaluating the integiand dy. Once we know the ist
element of y, the stream-map in the second line of solve can begin
woiking to geneiate the ist element of dy, which will pioduce the
next element of y, and so on.
To take advantage of this idea, we will iedene integral to expect
the integiand stieam to be a Je|oyeJ orgven. Integral will force the
integiand to be evaluated only when it is iequiied to geneiate moie than
the ist element of the output stieam
(define (integral delayed-integrand initial-value dt)
(define int
(cons-stream
initial-value
(let ((integrand (force delayed-integrand)))
(add-streams (scale-stream integrand dt) int))))
int)
Nowwe can implement oui solve pioceduie by delaying the evaluation
of dy in the denition of y
1
(define (solve f y0 dt)
(define y (integral (delay dy) y0 dt))
(define dy (stream-map f y))
y)
1
Tis pioceduie is not guaianteed to woik in all Scheme implementations, although
foi any implementation theie is a simple vaiiation that will woik. Te pioblem has to
do with subtle dieiences in the ways that Scheme implementations handle inteinal
denitions. (See Section 4.1..)
42
ln geneial, eveiy callei of integral must now delay the integiand ai-
gument. We can demonstiate that the solve pioceduie woiks by ap-
pioximating e 2.18 by computing the value at y = 1 of the solution
to the dieiential equation dy/dt = y with initial condition y(0) = 1
(stream-ref (solve (lambda (y) y)
1
0.001)
1000)
2.716924
Exercise 3.77: Te integral pioceduie used above was
analogous to the implicit denition of the innite stieam
of integeis in Section 3..2. Alteinatively, we can give a def-
inition of integral that is moie like integers-starting-
from (also in Section 3..2)
(define (integral integrand initial-value dt)
(cons-stream
initial-value
(if (stream-null? integrand)
the-empty-stream
(integral (stream-cdr integrand)
(+ (* dt (stream-car integrand))
initial-value)
dt))))
When used in systems with loops, this pioceduie has the
same pioblem as does oui oiiginal veision of integral.
Modify the pioceduie so that it expects the integrand as
a delayed aigument and hence can be used in the solve
pioceduie shown above.
43
ddy
y
0
dy y
scale: b
integral integral
scale: a
add
dy
0
Figure 3.35: Signal-ow diagiam foi the solution to a
second-oidei lineai dieiential equation.
Exercise 3.78: Considei the pioblem of designing a signal-
piocessing system to study the homogeneous second-oidei
lineai dieiential equation
d
2
y
dt
2
a
dy
dt
by = 0.
Te output stieam, modeling y, is geneiated by a netwoik
that contains a loop. Tis is because the value of d
2
y/dt
2
de-
pends upon the values of y and dy/dt and both of these aie
deteimined by integiating d
2
y/dt
2
. Te diagiam we would
like to encode is shown in liguie 3.3. Wiite a pioceduie
solve-2nd that takes as aiguments the constants a, b, and
dt and the initial values y
0
and dy
0
foi y and dy/dt and gen-
44
+ -- v
R
R
i
R
L
v
L
+
--
i
L
C
i
C
v
C
+
--
Figure 3.36: A seiies RLC ciicuit.
eiates the stieam of successive values of y.
Exercise 3.79: Geneialize the solve-2nd pioceduie of Ex-
eicise 3.8 so that it can be used to solve geneial second-
oidei dieiential equations d
2
y/dt
2
= f (dy/dt , y).
Exercise 3.80: A ser:es RLC c:rcv: consists of a iesistoi, a
capacitoi, and an inductoi connected in seiies, as shown
in liguie 3.3. lf R, L, and C aie the iesistance, inductance,
and capacitance, then the ielations between voltage (v) and
cuiient (i) foi the thiee components aie desciibed by the
equations
v
R
= i
R
R, v
L
= L
di
L
dt
, i
C
= C
dv
C
dt
,
and the ciicuit connections dictate the ielations
i
R
= i
L
= i
C
, v
C
= v
L
v
R
.
Combining these equations shows that the state of the cii-
cuit (summaiized by v
C
, the voltage acioss the capacitoi,
4
di
L
v
C
0
i
L
v
C
dv
C
i
L
0
scale: 1/L
integral
scale:-1/C
integral
scale:-R/L
add
Figure 3.37: A signal-ow diagiam foi the solution to a
seiies RLC ciicuit.
and i
L
, the cuiient in the inductoi) is desciibed by the paii
of dieiential equations
dv
C
dt
=
i
L
C
,
di
L
dt
=
1
L
v
C
R
L
i
L
.
Te signal-ow diagiam iepiesenting this system of diei-
ential equations is shown in liguie 3.3.
Wiite a pioceduie RLC that takes as aiguments the paiam-
eteis R, L, and C of the ciicuit and the time inciement dt.
4
ln a mannei similai to that of the RC pioceduie of Exeicise
3.3, RLC should pioduce a pioceduie that takes the initial
values of the state vaiiables, v
C
0
and i
L
0
, and pioduces a
paii (using cons) of the stieams of states v
C
and i
L
. Using
RLC, geneiate the paii of stieams that models the behavioi
of a seiies RLC ciicuit with R 1 ohm, C 0.2 faiad, L 1
heniy, dt 0.1 second, and initial values i
L
0
0 amps and
v
C
0
10 volts.
Normal-order evaluation
Te examples in this section illustiate how the explicit use of delay
and force piovides gieat piogiamming exibility, but the same exam-
ples also showhowthis can make oui piogiams moie complex. Oui new
integral pioceduie, foi instance, gives us the powei to model systems
with loops, but we must now iemembei that integral should be called
with a delayed integiand, and eveiy pioceduie that uses integral must
be awaie of this. ln eect, we have cieated two classes of pioceduies
oidinaiy pioceduies and pioceduies that take delayed aiguments. ln
geneial, cieating sepaiate classes of pioceduies foices us to cieate sep-
aiate classes of highei-oidei pioceduies as well.
2
2
Tis is a small ieection, in Lisp, of the diculties that conventional stiongly typed
languages such as Pascal have in coping with highei-oidei pioceduies. ln such lan-
guages, the piogiammei must specify the data types of the aiguments and the iesult
of each pioceduie numbei, logical value, sequence, and so on. Consequently, we could
not expiess an abstiaction such as map a given pioceduie proc ovei all the elements in
a sequence by a single highei-oidei pioceduie such as stream-map. Rathei, we would
need a dieient mapping pioceduie foi each dieient combination of aigument and
iesult data types that might be specied foi a proc. Maintaining a piactical notion of
data type in the piesence of highei-oidei pioceduies iaises many dicult issues. One
way of dealing with this pioblem is illustiated by the language ML (Goidon et al. 199),
4
One way to avoid the need foi two dieient classes of pioceduies is
to make all pioceduies take delayed aiguments. We could adopt a model
of evaluation in which all aiguments to pioceduies aie automatically
delayed and aiguments aie foiced only when they aie actually needed
(foi example, when they aie iequiied by a piimitive opeiation). Tis
would tiansfoim oui language to use noimal-oidei evaluation, which
we ist desciibed when we intioduced the substitution model foi evalu-
ation in Section 1.1.. Conveiting to noimal-oidei evaluation piovides a
unifoim and elegant way to simplify the use of delayed evaluation, and
this would be a natuial stiategy to adopt if we weie conceined only
with stieam piocessing. ln Section 4.2, afei we have studied the eval-
uatoi, we will see how to tiansfoim oui language in just this way. Un-
foitunately, including delays in pioceduie calls wieaks havoc with oui
ability to design piogiams that depend on the oidei of events, such as
piogiams that use assignment, mutate data, oi peifoim input oi output.
Even the single delay in cons-stream can cause gieat confusion, as
illustiated by Exeicise 3.1 and Exeicise 3.2. As fai as anyone knows,
mutability and delayed evaluation do not mix well in piogiamming lan-
guages, and devising ways to deal with both of these at once is an active
aiea of ieseaich.
whose polymoiphic data types include templates foi highei-oidei tiansfoimations
between data types. Moieovei, data types foi most pioceduies in ML aie nevei explic-
itly declaied by the piogiammei. lnstead, ML includes a ye:n[erenc:ng mechanism
that uses infoimation in the enviionment to deduce the data types foi newly dened
pioceduies.
48
3.5.5 Modularity of Functional Programs
and Modularity of Objects
As we saw in Section 3.1.2, one of the majoi benets of intioducing
assignment is that we can inciease the modulaiity of oui systems by
encapsulating, oi hiding, paits of the state of a laige system within
local vaiiables. Stieam models can piovide an equivalent modulaiity
without the use of assignment. As an illustiation, we can ieimplement
the Monte Cailo estimation of , which we examined in Section 3.1.2,
fiom a stieam-piocessing point of view.
Te key modulaiity issue was that we wished to hide the inteinal
state of a iandom-numbei geneiatoi fiom piogiams that used iandom
numbeis. We began with a pioceduie rand-update, whose successive
values fuinished oui supply of iandom numbeis, and used this to pio-
duce a iandom-numbei geneiatoi
(define rand
(let ((x random-init))
(lambda ()
(set! x (rand-update x))
x)))
ln the stieam foimulation theie is no iandom-numbei geneiatoi er se,
just a stieam of iandom numbeis pioduced by successive calls to rand-
update
(define random-numbers
(cons-stream
random-init
(stream-map rand-update random-numbers)))
We use this to constiuct the stieam of outcomes of the Cesio expeii-
ment peifoimed on consecutive paiis in the random-numbers stieam
49
(define cesaro-stream
(map-successive-pairs
(lambda (r1 r2) (= (gcd r1 r2) 1))
random-numbers))
(define (map-successive-pairs f s)
(cons-stream
(f (stream-car s) (stream-car (stream-cdr s)))
(map-successive-pairs f (stream-cdr (stream-cdr s)))))
Te cesaro-stream is now fed to a monte-carlo pioceduie, which pio-
duces a stieam of estimates of piobabilities. Te iesults aie then con-
veited into a stieam of estimates of . Tis veision of the piogiam
doesnt need a paiametei telling howmany tiials to peifoim. Beuei esti-
mates of (fiompeifoiming moie expeiiments) aie obtained by looking
faithei into the pi stieam
(define (monte-carlo experiment-stream passed failed)
(define (next passed failed)
(cons-stream
(/ passed (+ passed failed))
(monte-carlo
(stream-cdr experiment-stream) passed failed)))
(if (stream-car experiment-stream)
(next (+ passed 1) failed)
(next passed (+ failed 1))))
(define pi
(stream-map
(lambda (p) (sqrt (/ 6 p)))
(monte-carlo cesaro-stream 0 0)))
Teie is consideiable modulaiity in this appioach, because we still can
foimulate a geneial monte-carlo pioceduie that can deal with aibitiaiy
expeiiments. Yet theie is no assignment oi local state.
480
Exercise 3.81: Exeicise 3. discussed geneializing the iandom-
numbei geneiatoi to allowone to ieset the iandom-numbei
sequence so as to pioduce iepeatable sequences of ian-
dom numbeis. Pioduce a stieam foimulation of this same
geneiatoi that opeiates on an input stieam of iequests to
generate a new iandom numbei oi to reset the sequence
to a specied value and that pioduces the desiied stieam of
iandom numbeis. Dont use assignment in youi solution.
Exercise 3.82: Redo Exeicise 3. on Monte Cailo integia-
tion in teims of stieams. Te stieam veision of estimate-
integral will not have an aigument telling how many tii-
als to peifoim. lnstead, it will pioduce a stieamof estimates
based on successively moie tiials.
A functional-programming view of time
Let us now ietuin to the issues of objects and state that weie iaised at
the beginning of this chaptei and examine them in a new light. We in-
tioduced assignment and mutable objects to piovide a mechanism foi
modulai constiuction of piogiams that model systems with state. We
constiucted computational objects with local state vaiiables and used
assignment to modify these vaiiables. We modeled the tempoial behav-
ioi of the objects in the woild by the tempoial behavioi of the coiie-
sponding computational objects.
Nowwe have seen that stieams piovide an alteinative way to model
objects with local state. We can model a changing quantity, such as the
local state of some object, using a stieam that iepiesents the time his-
toiy of successive states. ln essence, we iepiesent time explicitly, using
stieams, so that we decouple time in oui simulated woild fiom the se-
481
quence of events that take place duiing evaluation. lndeed, because of
the piesence of delay theie may be liule ielation between simulated
time in the model and the oidei of events duiing the evaluation.
ln oidei to contiast these two appioaches to modeling, let us iecon-
sidei the implementation of a withdiawal piocessoi that monitois the
balance in a bank account. ln Section 3.1.3 we implemented a simplied
veision of such a piocessoi
(define (make-simplified-withdraw balance)
(lambda (amount)
(set! balance (- balance amount))
balance))
Calls to make-simplified-withdraw pioduce computational objects,
each with a local state vaiiable balance that is deciemented by suc-
cessive calls to the object. Te object takes an amount as an aigument
and ietuins the new balance. We can imagine the usei of a bank ac-
count typing a sequence of inputs to such an object and obseiving the
sequence of ietuined values shown on a display scieen.
Alteinatively, we can model a withdiawal piocessoi as a pioceduie
that takes as input a balance and a stieam of amounts to withdiaw and
pioduces the stieam of successive balances in the account
(define (stream-withdraw balance amount-stream)
(cons-stream
balance
(stream-withdraw (- balance (stream-car amount-stream))
(stream-cdr amount-stream))))
Stream-withdraw implements a well-dened mathematical function whose
output is fully deteimined by its input. Suppose, howevei, that the in-
put amount-stream is the stieam of successive values typed by the usei
and that the iesulting stieam of balances is displayed. Ten, fiom the
482
peispective of the usei who is typing values and watching iesults, the
stieam piocess has the same behavioi as the object cieated by make-
simplified-withdraw. Howevei, with the stieam veision, theie is no
assignment, no local state vaiiable, and consequently none of the theo-
ietical diculties that we encounteied in Section 3.1.3. Yet the system
has state'
Tis is ieally iemaikable. Even though stream-withdraw implements
a well-dened mathematical function whose behavioi does not change,
the useis peiception heie is one of inteiacting with a system that has a
changing state. One way to iesolve this paiadox is to iealize that it is the
useis tempoial existence that imposes state on the system. lf the usei
could step back fiom the inteiaction and think in teims of stieams of
balances iathei than individual tiansactions, the system would appeai
stateless.
3
liom the point of view of one pait of a complex piocess, the othei
paits appeai to change with time. Tey have hidden time-vaiying lo-
cal state. lf we wish to wiite piogiams that model this kind of natuial
decomposition in oui woild (as we see it fiom oui viewpoint as a pait
of that woild) with stiuctuies in oui computei, we make computational
objects that aie not functionalthey must change with time. We model
state with local state vaiiables, and we model the changes of state with
assignments to those vaiiables. By doing this we make the time of ex-
ecution of a computation model time in the woild that we aie pait of,
and thus we get objects in oui computei.
Modeling with objects is poweiful and intuitive, laigely because this
matches the peiception of inteiacting with a woild of which we aie
3
Similaily in physics, when we obseive a moving paiticle, we say that the position
(state) of the paiticle is changing. Howevei, fiom the peispective of the paiticles woild
line in space-time theie is no change involved.
483
pait. Howevei, as weve seen iepeatedly thioughout this chaptei, these
models iaise thoiny pioblems of constiaining the oidei of events and
of synchionizing multiple piocesses. Te possibility of avoiding these
pioblems has stimulated the development of [vnc:ono| rogro:ng
|ongvoges, which do not include any piovision foi assignment oi mu-
table data. ln such a language, all pioceduies implement well-dened
mathematical functions of theii aiguments, whose behavioi does not
change. Te functional appioach is extiemely auiactive foi dealing with
concuiient systems.
4
On the othei hand, if we look closely, we can see time-ielated piob-
lems cieeping into functional models as well. One paiticulaily tiou-
blesome aiea aiises when we wish to design inteiactive systems, es-
pecially ones that model inteiactions between independent entities. loi
instance, considei once moie the implementation a banking systemthat
peimits joint bank accounts. ln a conventional systemusing assignment
and objects, we would model the fact that Petei and Paul shaie an ac-
count by having both Petei and Paul send theii tiansaction iequests
to the same bank-account object, as we saw in Section 3.1.3. liom the
stieam point of view, wheie theie aie no objects er se, we have al-
ieady indicated that a bank account can be modeled as a piocess that
opeiates on a stieam of tiansaction iequests to pioduce a stieam of
iesponses. Accoidingly, we could model the fact that Petei and Paul
have a joint bank account by meiging Peteis stieam of tiansaction ie-
quests with Pauls stieamof iequests and feeding the iesult to the bank-
account stieam piocess, as shown in liguie 3.38.
4
John Backus, the inventoi of loitian, gave high visibility to functional piogiam-
ming when he was awaided the ~cx Tuiing awaid in 198. His acceptance speech
(Backus 198) stiongly advocated the functional appioach. A good oveiview of func-
tional piogiamming is given in Hendeison 1980 and in Dailington et al. 1982.
484
merge
bank
account
Paul's requests
Peter's requests
Figure 3.38: A joint bank account, modeled by meiging
two stieams of tiansaction iequests.
Te tiouble with this foimulation is in the notion of erge. lt will
not do to meige the two stieams by simply taking alteinately one ie-
quest fiom Petei and one iequest fiom Paul. Suppose Paul accesses the
account only veiy iaiely. We could haidly foice Petei to wait foi Paul to
access the account befoie he could issue a second tiansaction. Howevei
such a meige is implemented, it must inteileave the two tiansaction
stieams in some way that is constiained by ieal time as peiceived
by Petei and Paul, in the sense that, if Petei and Paul meet, they can
agiee that ceitain tiansactions weie piocessed befoie the meeting, and
othei tiansactions weie piocessed afei the meeting.
Tis is piecisely
the same constiaint that we had to deal with in Section 3.4.1, wheie
we found the need to intioduce explicit synchionization to ensuie a
coiiect oidei of events in concuiient piocessing of objects with state.
Tus, in an auempt to suppoit the functional style, the need to meige
inputs fiom dieient agents ieintioduces the same pioblems that the
functional style was meant to eliminate.
Obseive that, foi any two stieams, theie is in geneial moie than one acceptable oi-
dei of inteileaving. Tus, technically, meige is a ielation iathei than a functionthe
answei is not a deteiministic function of the inputs. We alieady mentioned (lootnote
39) that nondeteiminism is essential when dealing with concuiiency. Te meige iela-
tion illustiates the same essential nondeteiminism, fiom the functional peispective. ln
Section 4.3, we will look at nondeteiminism fiom yet anothei point of view.
48
We began this chaptei with the goal of building computational mod-
els whose stiuctuie matches oui peiception of the ieal woild we aie
tiying to model. We can model the woild as a collection of sepaiate,
time-bound, inteiacting objects with state, oi we can model the woild
as a single, timeless, stateless unity. Each viewhas poweiful advantages,
but neithei view alone is completely satisfactoiy. A giand unication
has yet to emeige.
Metalinguistic Abstraction
. . . lts in woids that the magic isAbiacadabia, Open Sesame,
and the iestbut the magic woids in one stoiy aient magi-
cal in the next. Te ieal magic is to undeistand which woids
woik, and when, and foi what, the tiick is to leain the tiick.
. . . And those woids aie made fiomthe leueis of oui alpha-
bet a couple-dozen squiggles we can diaw with the pen.
Tis is the key' And the tieasuie, too, if we can only get
oui hands on it' lts as ifas if the key to the tieasuie :s the
tieasuie'
John Baith, C|:ero
I
N oUv s1Uuv oi vvocv~x ursicN, we have seen that expeit piogiam-
meis contiol the complexity of theii designs with the same geneial
techniques used by designeis of all complex systems. Tey combine
piimitive elements to foim compound objects, they abstiact compound
48
objects to foim highei-level building blocks, and they pieseive modu-
laiity by adopting appiopiiate laige-scale views of system stiuctuie. ln
illustiating these techniques, we have used Lisp as a language foi de-
sciibing piocesses and foi constiucting computational data objects and
piocesses to model complex phenomena in the ieal woild. Howevei, as
we confiont incieasingly complex pioblems, we will nd that Lisp, oi
indeed any xed piogiamming language, is not sucient foi oui needs.
We must constantly tuin to new languages in oidei to expiess oui ideas
moie eectively. Establishing new languages is a poweiful stiategy foi
contiolling complexity in engineeiing design, we can ofen enhance oui
ability to deal with a complex pioblem by adopting a new language that
enables us to desciibe (and hence to think about) the pioblem in a dif-
feient way, using piimitives, means of combination, and means of ab-
stiaction that aie paiticulaily well suited to the pioblem at hand.
1
Piogiamming is endowed with a multitude of languages. Teie aie
1
Te same idea is peivasive thioughout all of engineeiing. loi example, electii-
cal engineeis use many dieient languages foi desciibing ciicuits. Two of these aie
the language of electiical ne+or|s and the language of electiical syses. Te netwoik
language emphasizes the physical modeling of devices in teims of disciete electiical el-
ements. Te piimitive objects of the netwoik language aie piimitive electiical compo-
nents such as iesistois, capacitois, inductois, and tiansistois, which aie chaiacteiized
in teims of physical vaiiables called voltage and cuiient. When desciibing ciicuits in
the netwoik language, the engineei is conceined with the physical chaiacteiistics of a
design. ln contiast, the piimitive objects of the system language aie signal-piocessing
modules such as lteis and amplieis. Only the functional behavioi of the modules is
ielevant, and signals aie manipulated without concein foi theii physical iealization as
voltages and cuiients. Te system language is eiected on the netwoik language, in the
sense that the elements of signal-piocessing systems aie constiucted fiom electiical
netwoiks. Heie, howevei, the conceins aie with the laige-scale oiganization of elec-
tiical devices to solve a given application pioblem, the physical feasibility of the paits
is assumed. Tis layeied collection of languages is anothei example of the stiatied
design technique illustiated by the pictuie language of Section 2.2.4.
488
physical languages, such as the machine languages foi paiticulai com-
puteis. Tese languages aie conceined with the iepiesentation of data
and contiol in teims of individual bits of stoiage and piimitive machine
instiuctions. Te machine-language piogiammei is conceined with us-
ing the given haidwaie to eiect systems and utilities foi the ecient im-
plementation of iesouice-limited computations. High-level languages,
eiected on a machine-language substiate, hide conceins about the iep-
iesentation of data as collections of bits and the iepiesentation of pio-
giams as sequences of piimitive instiuctions. Tese languages have means
of combination and abstiaction, such as pioceduie denition, that aie
appiopiiate to the laigei-scale oiganization of systems.
Meo|:ngv:s:c o|sroc:onestablishing newlanguagesplays an im-
poitant iole in all bianches of engineeiing design. lt is paiticulaily im-
poitant to computei piogiamming, because in piogiamming not only
can we foimulate new languages but we can also implement these lan-
guages by constiucting evaluatois. An e+o|voor (oi :nerreer) foi a
piogiamming language is a pioceduie that, when applied to an expies-
sion of the language, peifoims the actions iequiied to evaluate that ex-
piession.
lt is no exaggeiation to iegaid this as the most fundamental idea in
piogiamming
Te evaluatoi, which deteimines the meaning of expies-
sions in a piogiamming language, is just anothei piogiam.
To appieciate this point is to change oui images of ouiselves as pio-
giammeis. We come to see ouiselves as designeis of languages, iathei
than only useis of languages designed by otheis.
ln fact, we can iegaid almost any piogiam as the evaluatoi foi some
language. loi instance, the polynomial manipulation system of Section
489
2..3 embodies the iules of polynomial aiithmetic and implements them
in teims of opeiations on list-stiuctuied data. lf we augment this system
with pioceduies to iead and piint polynomial expiessions, we have the
coie of a special-puipose language foi dealing with pioblems in sym-
bolic mathematics. Te digital-logic simulatoi of Section 3.3.4 and the
constiaint piopagatoi of Section 3.3. aie legitimate languages in theii
own iight, each with its own piimitives, means of combination, and
means of abstiaction. Seen fiom this peispective, the technology foi
coping with laige-scale computei systems meiges with the technology
foi building new computei languages, and computei science itself be-
comes no moie (and no less) than the discipline of constiucting appio-
piiate desciiptive languages.
We nowembaik on a toui of the technology by which languages aie
established in teims of othei languages. ln this chaptei we shall use Lisp
as a base, implementing evaluatois as Lisp pioceduies. Lisp is paiticu-
laily well suited to this task, because of its ability to iepiesent and ma-
nipulate symbolic expiessions. We will take the ist step in undeistand-
ing how languages aie implemented by building an evaluatoi foi Lisp
itself. Te language implemented by oui evaluatoi will be a subset of the
Scheme dialect of Lisp that we use in this book. Although the evaluatoi
desciibed in this chaptei is wiiuen foi a paiticulai dialect of Lisp, it con-
tains the essential stiuctuie of an evaluatoi foi any expiession-oiiented
language designed foi wiiting piogiams foi a sequential machine. (ln
fact, most language piocessois contain, deep within them, a liule Lisp
evaluatoi.) Te evaluatoi has been simplied foi the puiposes of illus-
tiation and discussion, and some featuies have been lef out that would
be impoitant to include in a pioduction-quality Lisp system. Neveithe-
less, this simple evaluatoi is adequate to execute most of the piogiams
490
in this book.
2
An impoitant advantage of making the evaluatoi accessible as a
Lisp piogiam is that we can implement alteinative evaluation iules by
desciibing these as modications to the evaluatoi piogiam. One place
wheie we can use this powei to good eect is to gain extia contiol ovei
the ways in which computational models embody the notion of time,
which was so cential to the discussion in Chaptei 3. Teie, we mitigated
some of the complexities of state and assignment by using stieams to
decouple the iepiesentation of time in the woild fiom time in the com-
putei. Oui stieam piogiams, howevei, weie sometimes cumbeisome,
because they weie constiained by the applicative-oidei evaluation of
Scheme. ln Section 4.2, well change the undeilying language to piovide
foi a moie elegant appioach, by modifying the evaluatoi to piovide foi
noro|orJer e+o|vo:on.
Section 4.3 implements a moie ambitious linguistic change, wheieby
expiessions have many values, iathei than just a single value. ln this
language of nonJeer:n:s:c cov:ng, it is natuial to expiess piocesses
that geneiate all possible values foi expiessions and then seaich foi
those values that satisfy ceitain constiaints. ln teims of models of com-
putation and time, this is like having time bianch into a set of possible
futuies and then seaiching foi appiopiiate time lines. With oui nonde-
teiministic evaluatoi, keeping tiack of multiple values and peifoiming
seaiches aie handled automatically by the undeilying mechanismof the
language.
ln Section 4.4 we implement a |og:crogro:ng language in which
2
Te most impoitant featuies that oui evaluatoi leaves out aie mechanisms foi han-
dling eiiois and suppoiting debugging. loi a moie extensive discussion of evaluatois,
see liiedman et al. 1992, which gives an exposition of piogiamming languages that
pioceeds via a sequence of evaluatois wiiuen in Scheme.
491
knowledge is expiessed in teims of ielations, iathei than in teims of
computations with inputs and outputs. Even though this makes the lan-
guage diastically dieient fiom Lisp, oi indeed fiom any conventional
language, we will see that the logic-piogiamming evaluatoi shaies the
essential stiuctuie of the Lisp evaluatoi.
4.1 The Metacircular Evaluator
Oui evaluatoi foi Lisp will be implemented as a Lisp piogiam. lt may
seem ciiculai to think about evaluating Lisp piogiams using an evalua-
toi that is itself implemented in Lisp. Howevei, evaluation is a piocess,
so it is appiopiiate to desciibe the evaluation piocess using Lisp, which,
afei all, is oui tool foi desciibing piocesses.
3
An evaluatoi that is wiit-
ten in the same language that it evaluates is said to be eoc:rcv|or.
Te metaciiculai evaluatoi is essentially a Scheme foimulation of
the enviionment model of evaluation desciibed in Section 3.2. Recall
that the model has two basic paits
1. To evaluate a combination (a compound expiession othei than
a special foim), evaluate the subexpiessions and then apply the
value of the opeiatoi subexpiession to the values of the opeiand
subexpiessions.
2. To apply a compound pioceduie to a set of aiguments, evaluate
the body of the pioceduie in a new enviionment. To constiuct
3
Even so, theie will iemain impoitant aspects of the evaluation piocess that aie not
elucidated by oui evaluatoi. Te most impoitant of these aie the detailed mechanisms
by which pioceduies call othei pioceduies and ietuin values to theii calleis. We will
addiess these issues in Chaptei , wheie we take a closei look at the evaluation piocess
by implementing the evaluatoi as a simple iegistei machine.
492
this enviionment, extend the enviionment pait of the pioceduie
object by a fiame in which the foimal paiameteis of the pioceduie
aie bound to the aiguments to which the pioceduie is applied.
Tese two iules desciibe the essence of the evaluation piocess, a basic
cycle in which expiessions to be evaluated in enviionments aie ieduced
to pioceduies to be applied to aiguments, which in tuin aie ieduced to
new expiessions to be evaluated in new enviionments, and so on, un-
til we get down to symbols, whose values aie looked up in the envi-
ionment, and to piimitive pioceduies, which aie applied diiectly (see
liguie 4.1).
4
Tis evaluation cycle will be embodied by the inteiplay
between the two ciitical pioceduies in the evaluatoi, eval and apply,
which aie desciibed in Section 4.1.1 (see liguie 4.1).
Te implementation of the evaluatoi will depend upon pioceduies
4
lf we giant ouiselves the ability to apply piimitives, then what iemains foi us to
implement in the evaluatoi` Te job of the evaluatoi is not to specify the piimitives of
the language, but iathei to piovide the connective tissuethe means of combination
and the means of abstiactionthat binds a collection of piimitives to foim a language.
Specically
Te evaluatoi enables us to deal with nested expiessions. loi example, although
simply applying piimitives would suce foi evaluating the expiession (+ 1 6), it is not
adequate foi handling (+ 1 (* 2 3)). As fai as the piimitive pioceduie + is conceined,
its aiguments must be numbeis, and it would choke if we passed it the expiession (*
2 3) as an aigument. One impoitant iole of the evaluatoi is to choieogiaph pioceduie
composition so that (* 2 3) is ieduced to befoie being passed as an aigument to +.
Te evaluatoi allows us to use vaiiables. loi example, the piimitive pioceduie foi
addition has no way to deal with expiessions such as (+ x 1). We need an evaluatoi to
keep tiack of vaiiables and obtain theii values befoie invoking the piimitive pioceduies.
Te evaluatoi allows us to dene compound pioceduies. Tis involves keeping
tiack of pioceduie denitions, knowing how to use these denitions in evaluating ex-
piessions, and pioviding a mechanism that enables pioceduies to accept aiguments.
Te evaluatoi piovides the special foims, which must be evaluated dieiently fiom
pioceduie calls.
493
Eval Apply
Procedure,
Arguments
Expression,
Environment
Figure 4.1: Te eval-apply cycle exposes the essence of a
computei language.
that dene the syno: of the expiessions to be evaluated. We will use
data abstiaction to make the evaluatoi independent of the iepiesenta-
tion of the language. loi example, iathei than commiuing to a choice
that an assignment is to be iepiesented by a list beginning with the
symbol set! we use an abstiact piedicate assignment? to test foi an
assignment, and we use abstiact selectois assignment-variable and
assignment-value to access the paits of an assignment. lmplementa-
tion of expiessions will be desciibed in detail in Section 4.1.2. Teie aie
also opeiations, desciibed in Section 4.1.3, that specify the iepiesen-
tation of pioceduies and enviionments. loi example, make-procedure
constiucts compound pioceduies, lookup-variable-value accesses the
values of vaiiables, and apply-primitive-procedure applies a piimi-
tive pioceduie to a given list of aiguments.
494
4.1.1 The Core of the Evaluator
Te evaluation piocess can be desciibed as the inteiplay between two
pioceduies eval and apply.
Eval
Eval takes as aiguments an expiession and an enviionment. lt classi-
es the expiession and diiects its evaluation. Eval is stiuctuied as a case
analysis of the syntactic type of the expiession to be evaluated. ln oi-
dei to keep the pioceduie geneial, we expiess the deteimination of the
type of an expiession abstiactly, making no commitment to any paitic-
ulai iepiesentation foi the vaiious types of expiessions. Each type of
expiession has a piedicate that tests foi it and an abstiact means foi
selecting its paits. Tis o|sroc syno: makes it easy to see how we can
change the syntax of the language by using the same evaluatoi, but with
a dieient collection of syntax pioceduies.
Primitive expressions
loi self-evaluating expiessions, such as numbeis, eval ietuins
the expiession itself.
Eval must look up vaiiables in the enviionment to nd theii val-
ues.
Special forms
loi quoted expiessions, eval ietuins the expiession that was quoted.
An assignment to (oi a denition of) a vaiiable must iecuisively
call eval to compute the newvalue to be associated with the vaii-
able. Te enviionment must be modied to change (oi cieate) the
binding of the vaiiable.
49
An if expiession iequiies special piocessing of its paits, so as to
evaluate the consequent if the piedicate is tiue, and otheiwise to
evaluate the alteinative.
A lambda expiession must be tiansfoimed into an applicable pio-
ceduie by packaging togethei the paiameteis and body specied
by the lambda expiession with the enviionment of the evaluation.
A begin expiession iequiies evaluating its sequence of expies-
sions in the oidei in which they appeai.
Acase analysis (cond) is tiansfoimed into a nest of if expiessions
and then evaluated.
Combinations
loi a pioceduie application, eval must iecuisively evaluate the
opeiatoi pait and the opeiands of the combination. Te iesulting
pioceduie and aiguments aie passed to apply, which handles the
actual pioceduie application.
Heie is the denition of eval
(define (eval exp env)
(cond ((self-evaluating? exp) exp)
((variable? exp) (lookup-variable-value exp env))
((quoted? exp) (text-of-quotation exp))
((assignment? exp) (eval-assignment exp env))
((definition? exp) (eval-definition exp env))
((if? exp) (eval-if exp env))
((lambda? exp) (make-procedure (lambda-parameters exp)
(lambda-body exp)
env))
49
((begin? exp)
(eval-sequence (begin-actions exp) env))
((cond? exp) (eval (cond->if exp) env))
((application? exp)
(apply (eval (operator exp) env)
(list-of-values (operands exp) env)))
(else
(error "Unknown expression type: EVAL" exp))))
loi claiity, eval has been implemented as a case analysis using cond.
Te disadvantage of this is that oui pioceduie handles only a fewdistin-
guishable types of expiessions, and no new ones can be dened without
editing the denition of eval. ln most Lisp implementations, dispatch-
ing on the type of an expiession is done in a data-diiected style. Tis
allows a usei to add new types of expiessions that eval can distinguish,
without modifying the denition of eval itself. (See Exeicise 4.3.)
Apply
Apply takes two aiguments, a pioceduie and a list of aiguments to
which the pioceduie should be applied. Apply classies pioceduies into
two kinds lt calls apply-primitive-procedure to apply piimitives, it
applies compound pioceduies by sequentially evaluating the expies-
sions that make up the body of the pioceduie. Te enviionment foi
the evaluation of the body of a compound pioceduie is constiucted by
extending the base enviionment caiiied by the pioceduie to include a
fiame that binds the paiameteis of the pioceduie to the aiguments to
which the pioceduie is to be applied. Heie is the denition of apply
(define (apply procedure arguments)
(cond ((primitive-procedure? procedure)
(apply-primitive-procedure procedure arguments))
49
((compound-procedure? procedure)
(eval-sequence
(procedure-body procedure)
(extend-environment
(procedure-parameters procedure)
arguments
(procedure-environment procedure))))
(else
(error
"Unknown procedure type: APPLY" procedure))))
Procedure arguments
When eval piocesses a pioceduie application, it uses list-of-values
to pioduce the list of aiguments to which the pioceduie is to be applied.
List-of-values takes as an aigument the opeiands of the combina-
tion. lt evaluates each opeiand and ietuins a list of the coiiesponding
values
We could have simplied the application? clause in eval by using map (and stip-
ulating that operands ietuins a list) iathei than wiiting an explicit list-of-values
pioceduie. We chose not to use map heie to emphasize the fact that the evaluatoi can
be implemented without any use of highei-oidei pioceduies (and thus could be wiit-
ten in a language that doesnt have highei-oidei pioceduies), even though the language
that it suppoits will include highei-oidei pioceduies.
498
Conditionals
Eval-if evaluates the piedicate pait of an if expiession in the given
enviionment. lf the iesult is tiue, eval-if evaluates the consequent,
otheiwise it evaluates the alteinative
(define (eval-if exp env)
(if (true? (eval (if-predicate exp) env))
(eval (if-consequent exp) env)
(eval (if-alternative exp) env)))
Te use of true? in eval-if highlights the issue of the connection be-
tween an implemented language and an implementation language. Te
if-predicate is evaluated in the language being implemented and thus
yields a value in that language. Te inteipietei piedicate true? tians-
lates that value into a value that can be tested by the if in the imple-
mentation language Te metaciiculai iepiesentation of tiuth might not
be the same as that of the undeilying Scheme.
Sequences
Eval-sequence is used by apply to evaluate the sequence of expiessions
in a pioceduie body and by eval to evaluate the sequence of expiessions
in a begin expiession. lt takes as aiguments a sequence of expiessions
and an enviionment, and evaluates the expiessions in the oidei in which
they occui. Te value ietuined is the value of the nal expiession.
(define (eval-sequence exps env)
(cond ((last-exp? exps)
(eval (first-exp exps) env))
ln this case, the language being implemented and the implementation language aie
the same. Contemplation of the meaning of true? heie yields expansion of conscious-
ness without the abuse of substance.
499
(else
(eval (first-exp exps) env)
(eval-sequence (rest-exps exps) env))))
Assignments and definitions
Te following pioceduie handles assignments to vaiiables. lt calls eval
to nd the value to be assigned and tiansmits the vaiiable and the ie-
sulting value to set-variable-value! to be installed in the designated
enviionment.
(define (eval-assignment exp env)
(set-variable-value! (assignment-variable exp)
(eval (assignment-value exp) env)
env)
'ok)
Denitions of vaiiables aie handled in a similai mannei.
. . .
exp
n
)
09
lmplement a syntactic tiansfoimation let->combination
that ieduces evaluating let expiessions to evaluating com-
binations of the type shown above, and add the appiopiiate
clause to eval to handle let expiessions.
Exercise 4.7: Let* is similai to let, except that the bind-
ings of the let* vaiiables aie peifoimed sequentially fiom
lef to iight, and each binding is made in an enviionment in
which all of the pieceding bindings aie visible. loi example
(let* ((x 3) (y (+ x 2)) (z (+ x y 5)))
(* x z))
ietuins 39. Explain how a let* expiession can be iewiiuen
as a set of nested let expiessions, and wiite a pioceduie
let*->nested-lets that peifoims this tiansfoimation. lf
we have alieady implemented let (Exeicise 4.) and we
want to extend the evaluatoi to handle let*, is it sucient
to add a clause to eval whose action is
(eval (let*->nested-lets exp) env)
oi must we explicitly expand let* in teims of non-deiived
expiessions`
Exercise 4.8: Named let is a vaiiant of let that has the
foim
(let var bindings body)
Te |:nJ:ngs and |oJy aie just as in oidinaiy let, ex-
cept that +or is bound within |oJy to a pioceduie whose
body is |oJy and whose paiameteis aie the vaiiables in
10
the |:nJ:ngs. Tus, one can iepeatedly execute the |oJy
by invoking the pioceduie named +or . loi example, the
iteiative libonacci pioceduie (Section 1.2.2) can be iewiit-
ten using named let as follows
(define (fib n)
(let fib-iter ((a 1)
(b 0)
(count n))
(if (= count 0)
b
(fib-iter (+ a b) a (- count 1)))))
Modify let->combination of Exeicise 4. to also suppoit
named let.
Exercise 4.9: Many languages suppoit a vaiiety of iteiation
constiucts, such as do, for, while, and until. ln Scheme,
iteiative piocesses can be expiessed in teims of oidinaiy
pioceduie calls, so special iteiation constiucts piovide no
essential gain in computational powei. On the othei hand,
such constiucts aie ofen convenient. Design some iteia-
tion constiucts, give examples of theii use, and show how
to implement them as deiived expiessions.
Exercise 4.10: By using data abstiaction, we weie able to
wiite an eval pioceduie that is independent of the paiticu-
lai syntax of the language to be evaluated. To illustiate this,
design and implement a new syntax foi Scheme by modify-
ing the pioceduies in this section, without changing eval
oi apply.
11
4.1.3 Evaluator Data Structures
ln addition to dening the exteinal syntax of expiessions, the evaluatoi
implementation must also dene the data stiuctuies that the evaluatoi
manipulates inteinally, as pait of the execution of a piogiam, such as the
iepiesentation of pioceduies and enviionments and the iepiesentation
of tiue and false.
Testing of predicates
loi conditionals, we accept anything to be tiue that is not the explicit
false object.
(define (true? x) (not (eq? x false)))
(define (false? x) (eq? x false))
Representing procedures
To handle piimitives, we assume that we have available the following
pioceduies
(apply-primitive-procedure proc args)
applies the given piimitive pioceduie to the aigument values in
the list orgs and ietuins the iesult of the application.
(primitive-procedure? proc)
tests whethei roc is a piimitive pioceduie.
Tese mechanisms foi handling piimitives aie fuithei desciibed in Sec-
tion 4.1.4.
Compound pioceduies aie constiucted fiom paiameteis, pioceduie
bodies, and enviionments using the constiuctoi make-procedure
12
(define (make-procedure parameters body env)
(list 'procedure parameters body env))
(define (compound-procedure? p)
(tagged-list? p 'procedure))
(define (procedure-parameters p) (cadr p))
(define (procedure-body p) (caddr p))
(define (procedure-environment p) (cadddr p))
Operations on Environments
Te evaluatoi needs opeiations foi manipulating enviionments. As ex-
plained in Section 3.2, an enviionment is a sequence of fiames, wheie
each fiame is a table of bindings that associate vaiiables with theii coi-
iesponding values. We use the following opeiations foi manipulating
enviionments
(lookup-variable-value var env)
ietuins the value that is bound to the symbol +or in the envi-
ionment en+, oi signals an eiioi if the vaiiable is unbound.
(extend-environment variables values base-env)
ietuins a new enviionment, consisting of a new fiame in which
the symbols in the list +or:o||es aie bound to the coiiesponding
elements in the list +o|ves, wheie the enclosing enviionment is
the enviionment |oseen+.
(define-variable! var value env)
adds to the ist fiame in the enviionment en+ a new binding
that associates the vaiiable +or with the value +o|ve.
13
(set-variable-value! var value env)
changes the binding of the vaiiable +or in the enviionment en+
so that the vaiiable is now bound to the value +o|ve, oi signals
an eiioi if the vaiiable is unbound.
To implement these opeiations we iepiesent an enviionment as a list of
fiames. Te enclosing enviionment of an enviionment is the cdr of the
list. Te empty enviionment is simply the empty list.
(define (enclosing-environment env) (cdr env))
(define (first-frame env) (car env))
(define the-empty-environment '())
Each fiame of an enviionment is iepiesented as a paii of lists a list of
the vaiiables bound in that fiame and a list of the associated values.
14
(define (make-frame variables values)
(cons variables values))
(define (frame-variables frame) (car frame))
(define (frame-values frame) (cdr frame))
(define (add-binding-to-frame! var val frame)
(set-car! frame (cons var (car frame)))
(set-cdr! frame (cons val (cdr frame))))
To extend an enviionment by a newfiame that associates vaiiables with
values, we make a fiame consisting of the list of vaiiables and the list
of values, and we adjoin this to the enviionment. We signal an eiioi if
the numbei of vaiiables does not match the numbei of values.
14
liames aie not ieally a data abstiaction in the following code Set-variable-
value! and define-variable! use set-car! to diiectly modify the values in a fiame.
Te puipose of the fiame pioceduies is to make the enviionment-manipulation pioce-
duies easy to iead.
14
(define (extend-environment vars vals base-env)
(if (= (length vars) (length vals))
(cons (make-frame vars vals) base-env)
(if (< (length vars) (length vals))
(error "Too many arguments supplied" vars vals)
(error "Too few arguments supplied" vars vals))))
To look up a vaiiable in an enviionment, we scan the list of vaiiables
in the ist fiame. lf we nd the desiied vaiiable, we ietuin the coiie-
sponding element in the list of values. lf we do not nd the vaiiable
in the cuiient fiame, we seaich the enclosing enviionment, and so on.
lf we ieach the empty enviionment, we signal an unbound vaiiable
eiioi.
(define (lookup-variable-value var env)
(define (env-loop env)
(define (scan vars vals)
(cond ((null? vars)
(env-loop (enclosing-environment env)))
((eq? var (car vars)) (car vals))
(else (scan (cdr vars) (cdr vals)))))
(if (eq? env the-empty-environment)
(error "Unbound variable" var)
(let ((frame (first-frame env)))
(scan (frame-variables frame)
(frame-values frame)))))
(env-loop env))
To set a vaiiable to a new value in a specied enviionment, we scan foi
the vaiiable, just as in lookup-variable-value, and change the coiie-
sponding value when we nd it.
(define (set-variable-value! var val env)
(define (env-loop env)
1
(define (scan vars vals)
(cond ((null? vars)
(env-loop (enclosing-environment env)))
((eq? var (car vars)) (set-car! vals val))
(else (scan (cdr vars) (cdr vals)))))
(if (eq? env the-empty-environment)
(error "Unbound variable: SET!" var)
(let ((frame (first-frame env)))
(scan (frame-variables frame)
(frame-values frame)))))
(env-loop env))
To dene a vaiiable, we seaich the ist fiame foi a binding foi the
vaiiable, and change the binding if it exists (just as in set-variable-
value!). lf no such binding exists, we adjoin one to the ist fiame.
(define (define-variable! var val env)
(let ((frame (first-frame env)))
(define (scan vars vals)
(cond ((null? vars)
(add-binding-to-frame! var val frame))
((eq? var (car vars)) (set-car! vals val))
(else (scan (cdr vars) (cdr vals)))))
(scan (frame-variables frame) (frame-values frame))))
Te method desciibed heie is only one of many plausible ways to iep-
iesent enviionments. Since we used data abstiaction to isolate the iest
of the evaluatoi fiom the detailed choice of iepiesentation, we could
change the enviionment iepiesentation if we wanted to. (See Exeicise
4.11.) ln a pioduction-quality Lisp system, the speed of the evaluatois
enviionment opeiationsespecially that of vaiiable lookuphas a ma-
joi impact on the peifoimance of the system. Te iepiesentation de-
sciibed heie, although conceptually simple, is not ecient and would
1
not oidinaiily be used in a pioduction system.
1
Exercise 4.11: lnstead of iepiesenting a fiame as a paii of
lists, we can iepiesent a fiame as a list of bindings, wheie
each binding is a name-value paii. Rewiite the enviionment
opeiations to use this alteinative iepiesentation.
Exercise 4.12: Te pioceduies set-variable-value!, define-
variable! and lookup-variable-value can be expiessed
in teims of moie abstiact pioceduies foi tiaveising the en-
viionment stiuctuie. Dene abstiactions that captuie the
common paueins and iedene the thiee pioceduies in teims
of these abstiactions.
Exercise 4.13: Scheme allows us to cieate newbindings foi
vaiiables by means of define, but piovides no way to get
iid of bindings. lmplement foi the evaluatoi a special foim
make-unbound! that iemoves the binding of a given symbol
fiomthe enviionment in which the make-unbound! expies-
sion is evaluated. Tis pioblem is not completely specied.
loi example, should we iemove only the binding in the ist
fiame of the enviionment` Complete the specication and
justify any choices you make.
1
Te diawback of this iepiesentation (as well as the vaiiant in Exeicise 4.11) is that
the evaluatoi may have to seaich thiough many fiames in oidei to nd the binding foi
a given vaiiable. (Such an appioach is iefeiied to as Jee |:nJ:ng.) One way to avoid this
ineciency is to make use of a stiategy called |e::co| oJJress:ng, which will be discussed
in Section ...
1
4.1.4 Running the Evaluator as a Program
Given the evaluatoi, we have in oui hands a desciiption (expiessed in
Lisp) of the piocess by which Lisp expiessions aie evaluated. One ad-
vantage of expiessing the evaluatoi as a piogiam is that we can iun the
piogiam. Tis gives us, iunning within Lisp, a woiking model of how
Lisp itself evaluates expiessions. Tis can seive as a fiamewoik foi ex-
peiimenting with evaluation iules, as we shall do latei in this chaptei.
Oui evaluatoi piogiam ieduces expiessions ultimately to the appli-
cation of piimitive pioceduies. Teiefoie, all that we need to iun the
evaluatoi is to cieate a mechanism that calls on the undeilying Lisp
system to model the application of piimitive pioceduies.
Teie must be a binding foi each piimitive pioceduie name, so that
when eval evaluates the opeiatoi of an application of a piimitive, it will
nd an object to pass to apply. We thus set up a global enviionment that
associates unique objects with the names of the piimitive pioceduies
that can appeai in the expiessions we will be evaluating. Te global
enviionment also includes bindings foi the symbols true and false, so
that they can be used as vaiiables in expiessions to be evaluated.
(define (setup-environment)
(let ((initial-env
(extend-environment (primitive-procedure-names)
(primitive-procedure-objects)
the-empty-environment)))
(define-variable! 'true true initial-env)
(define-variable! 'false false initial-env)
initial-env))
(define the-global-environment (setup-environment))
lt does not mauei how we iepiesent the piimitive pioceduie objects,
so long as apply can identify and apply them by using the pioceduies
18
primitive-procedure? and apply-primitive-procedure. We have cho-
sen to iepiesent a piimitive pioceduie as a list beginning with the sym-
bol primitive and containing a pioceduie in the undeilying Lisp that
implements that piimitive.
(define (primitive-procedure? proc)
(tagged-list? proc 'primitive))
(define (primitive-implementation proc) (cadr proc))
Setup-environment will get the piimitive names and implementation
pioceduies fiom a list
1
(define primitive-procedures
(list (list 'car car)
(list 'cdr cdr)
(list 'cons cons)
(list 'null? null?)
more primitives ))
(define (primitive-procedure-names)
(map car primitive-procedures))
(define (primitive-procedure-objects)
(map (lambda (proc) (list 'primitive (cadr proc)))
primitive-procedures))
To apply a piimitive pioceduie, we simply apply the implementation
pioceduie to the aiguments, using the undeilying Lisp system
1
1
Any pioceduie dened in the undeilying Lisp can be used as a piimitive foi the
metaciiculai evaluatoi. Te name of a piimitive installed in the evaluatoi need not
be the same as the name of its implementation in the undeilying Lisp, the names aie
the same heie because the metaciiculai evaluatoi implements Scheme itself. Tus, foi
example, we could put (list 'first car) oi (list 'square (lambda (x) (* x
x))) in the list of primitive-procedures.
1
Apply-in-underlying-scheme is the apply pioceduie we have used in eailiei
chapteis. Te metaciiculai evaluatois apply pioceduie (Section 4.1.1) models the
19
(define (apply-primitive-procedure proc args)
(apply-in-underlying-scheme
(primitive-implementation proc) args))
loi convenience in iunning the metaciiculai evaluatoi, we piovide a
Jr:+er |oo that models the iead-eval-piint loop of the undeilying Lisp
system. lt piints a ro, ieads an input expiession, evaluates this ex-
piession in the global enviionment, and piints the iesult. We piecede
each piinted iesult by an ovv ro so as to distinguish the value of
the expiession fiom othei output that may be piinted.
18
(define input-prompt ";;; M-Eval input:")
(define output-prompt ";;; M-Eval value:")
(define (driver-loop)
(prompt-for-input input-prompt)
(let ((input (read)))
(let ((output (eval input the-global-environment)))
(announce-output output-prompt)
(user-print output)))
(driver-loop))
woiking of this piimitive. Having two dieient things called apply leads to a tech-
nical pioblem in iunning the metaciiculai evaluatoi, because dening the metaciiculai
evaluatois apply will mask the denition of the piimitive. One way aiound this is to
iename the metaciiculai apply to avoid conict with the name of the piimitive pioce-
duie. We have assumed instead that we have saved a iefeience to the undeilying apply
by doing
(define apply-in-underlying-scheme apply)
befoie dening the metaciiculai apply. Tis allows us to access the oiiginal veision of
apply undei a dieient name.
18
Te piimitive pioceduie read waits foi input fiom the usei, and ietuins the next
complete expiession that is typed. loi example, if the usei types (+ 23 x), read ietuins
a thiee-element list containing the symbol +, the numbei 23, and the symbol x. lf the
usei types 'x, read ietuins a two-element list containing the symbol quote and the
symbol x.
20
(define (prompt-for-input string)
(newline) (newline) (display string) (newline))
(define (announce-output string)
(newline) (display string) (newline))
We use a special piinting pioceduie, user-print, to avoid piinting the
enviionment pait of a compound pioceduie, which may be a veiy long
list (oi may even contain cycles).
(define (user-print object)
(if (compound-procedure? object)
(display (list 'compound-procedure
(procedure-parameters object)
(procedure-body object)
'<procedure-env>))
(display object)))
Now all we need to do to iun the evaluatoi is to initialize the global
enviionment and stait the diivei loop. Heie is a sample inteiaction
(define the-global-environment (setup-environment))
(driver-loop)
;;; M-Eval input:
(define (append x y)
(if (null? x)
y
(cons (car x) (append (cdr x) y))))
;;; M-Eval value:
ok
;;; M-Eval input:
(append '(a b c) '(d e f))
;;; M-Eval value:
(a b c d e f)
21
Exercise 4.14: Eva Lu Atoi and Louis Reasonei aie each
expeiimenting with the metaciiculai evaluatoi. Eva types
in the denition of map, and iuns some test piogiams that
use it. Tey woik ne. Louis, in contiast, has installed the
system veision of map as a piimitive foi the metaciiculai
evaluatoi. When he tiies it, things go teiiibly wiong. Ex-
plain why Louiss map fails even though Evas woiks.
4.1.5 Data as Programs
ln thinking about a Lisp piogiam that evaluates Lisp expiessions, an
analogy might be helpful. One opeiational view of the meaning of a
piogiam is that a piogiam is a desciiption of an abstiact (peihaps in-
nitely laige) machine. loi example, considei the familiai piogiam to
compute factoiials
(define (factorial n)
(if (= n 1) 1 (* (factorial (- n 1)) n)))
We may iegaid this piogiam as the desciiption of a machine contain-
ing paits that deciement, multiply, and test foi equality, togethei with
a two-position switch and anothei factoiial machine. (Te factoiial ma-
chine is innite because it contains anothei factoiial machine within it.)
liguie 4.2 is a ow diagiam foi the factoiial machine, showing how the
paits aie wiied togethei.
ln a similai way, we can iegaid the evaluatoi as a veiy special ma-
chine that takes as input a desciiption of a machine. Given this input,
the evaluatoi conguies itself to emulate the machine desciibed. loi ex-
ample, if we feed oui evaluatoi the denition of factorial, as shown
in liguie 4.3, the evaluatoi will be able to compute factoiials.
22
=
factorial --
*
factorial
6 720
1 1
1
Figure 4.2: Te factoiial piogiam, viewed as an abstiact
machine.
liom this peispective, oui evaluatoi is seen to be a vn:+erso| o
c|:ne. lt mimics othei machines when these aie desciibed as Lisp pio-
giams.
19
Tis is stiiking. Tiy to imagine an analogous evaluatoi foi
19
Te fact that the machines aie desciibed in Lisp is inessential. lf we give oui eval-
uatoi a Lisp piogiam that behaves as an evaluatoi foi some othei language, say C,
the Lisp evaluatoi will emulate the C evaluatoi, which in tuin can emulate any ma-
chine desciibed as a C piogiam. Similaily, wiiting a Lisp evaluatoi in C pioduces a C
piogiam that can execute any Lisp piogiam. Te deep idea heie is that any evaluatoi
can emulate any othei. Tus, the notion of what can in piinciple be computed (ig-
noiing piacticalities of time and memoiy iequiied) is independent of the language oi
the computei, and instead ieects an undeilying notion of covo|:|:y. Tis was ist
demonstiated in a cleai way by Alan M. Tuiing (1912-194), whose 193 papei laid the
foundations foi theoietical computei science. ln the papei, Tuiing piesented a simple
computational modelnow known as a Tvr:ng oc|:neand aigued that any eective
piocess can be foimulated as a piogiam foi such a machine. (Tis aigument is known
23
(define (factorial n)
(if (= n 1)
1
(* (factorial (- n 1)) n)))
eval 6 720
Figure 4.3: Te evaluatoi emulating a factoiial machine.
electiical ciicuits. Tis would be a ciicuit that takes as input a signal
encoding the plans foi some othei ciicuit, such as a ltei. Given this in-
put, the ciicuit evaluatoi would then behave like a ltei with the same
desciiption. Such a univeisal electiical ciicuit is almost unimaginably
complex. lt is iemaikable that the piogiam evaluatoi is a iathei simple
piogiam.
20
as the C|vrc|Tvr:ng |es:s.) Tuiing then implemented a univeisal machine, i.e., a Tui-
ing machine that behaves as an evaluatoi foi Tuiing-machine piogiams. He used this
fiamewoik to demonstiate that theie aie well-posed pioblems that cannot be computed
by Tuiing machines (see Exeicise 4.1), and so by implication cannot be foimulated as
eective piocesses. Tuiing went on to make fundamental contiibutions to piactical
computei science as well. loi example, he invented the idea of stiuctuiing piogiams
using geneial-puipose subioutines. See Hodges 1983 foi a biogiaphy of Tuiing.
20
Some people nd it counteiintuitive that an evaluatoi, which is implemented by
a ielatively simple pioceduie, can emulate piogiams that aie moie complex than the
evaluatoi itself. Te existence of a univeisal evaluatoi machine is a deep and wondeiful
piopeity of computation. Recvrs:on |eory, a bianch of mathematical logic, is conceined
with logical limits of computation. Douglas Hofstadteis beautiful book GoJe|, Fsc|er,
Boc| exploies some of these ideas (Hofstadtei 199).
24
Anothei stiiking aspect of the evaluatoi is that it acts as a biidge
between the data objects that aie manipulated by oui piogiamming lan-
guage and the piogiamming language itself. lmagine that the evaluatoi
piogiam (implemented in Lisp) is iunning, and that a usei is typing ex-
piessions to the evaluatoi and obseiving the iesults. liom the peispec-
tive of the usei, an input expiession such as (* x x) is an expiession in
the piogiamming language, which the evaluatoi should execute. liom
the peispective of the evaluatoi, howevei, the expiession is simply a list
(in this case, a list of thiee symbols *, x, and x) that is to be manipulated
accoiding to a well-dened set of iules.
Tat the useis piogiams aie the evaluatois data need not be a
souice of confusion. ln fact, it is sometimes convenient to ignoie this
distinction, and to give the usei the ability to explicitly evaluate a data
object as a Lisp expiession, by making eval available foi use in pio-
giams. Many Lisp dialects piovide a piimitive eval pioceduie that takes
as aiguments an expiession and an enviionment and evaluates the ex-
piession ielative to the enviionment.
21
Tus,
(eval '(* 5 5) user-initial-environment)
and
(eval (cons '* (list 5 5)) user-initial-environment)
will both ietuin 2.
22
21
Waining Tis eval piimitive is not identical to the eval pioceduie we imple-
mented in Section 4.1.1, because it uses ocvo| Scheme enviionments iathei than the
sample enviionment stiuctuies we built in Section 4.1.3. Tese actual enviionments
cannot be manipulated by the usei as oidinaiy lists, they must be accessed via eval oi
othei special opeiations. Similaily, the apply piimitive we saw eailiei is not identical
to the metaciiculai apply, because it uses actual Scheme pioceduies iathei than the
pioceduie objects we constiucted in Section 4.1.3 and Section 4.1.4.
22
Te xi1 implementation of Scheme includes eval, as well as a symbol user-
initial-environment that is bound to the initial enviionment in which the useis in-
2
Exercise 4.15: Given a one-aigument pioceduie p and an
object a, p is said to halt on a if evaluating the expies-
sion (p a) ietuins a value (as opposed to teiminating with
an eiioi message oi iunning foievei). Show that it is im-
possible to wiite a pioceduie halts? that coiiectly detei-
mines whethei p halts on a foi any pioceduie p and object
a. Use the following ieasoning lf you had such a pioceduie
halts?, you could implement the following piogiam
(define (run-forever) (run-forever))
(define (try p)
(if (halts? p p) (run-forever) 'halted))
Now considei evaluating the expiession (try try) and
show that any possible outcome (eithei halting oi iunning
foievei) violates the intended behavioi of halts?.
23
4.1.6 Internal Definitions
Oui enviionment model of evaluation and oui metaciiculai evaluatoi
execute denitions in sequence, extending the enviionment fiame one
denition at a time. Tis is paiticulaily convenient foi inteiactive pio-
giam development, in which the piogiammei needs to fieely mix the
application of pioceduies with the denition of new pioceduies. How-
evei, if we think caiefully about the inteinal denitions used to im-
plement block stiuctuie (intioduced in Section 1.1.8), we will nd that
put expiessions aie evaluated.
23
Although we stipulated that halts? is given a pioceduie object, notice that this
ieasoning still applies even if halts? can gain access to the pioceduies text and its
enviionment. Tis is Tuiings celebiated Ho|:ng Teore, which gave the ist cleai
example of a noncovo||e pioblem, i.e., a well-posed task that cannot be caiiied out
as a computational pioceduie.
2
name-by-name extension of the enviionment may not be the best way
to dene local vaiiables.
Considei a pioceduie with inteinal denitions, such as
(define (f x)
(define (even? n) (if (= n 0) true (odd? (- n 1))))
(define (odd? n) (if (= n 0) false (even? (- n 1))))
rest of body of f)
Oui intention heie is that the name odd? in the body of the pioceduie
even? should iefei to the pioceduie odd? that is dened afei even?.
Te scope of the name odd? is the entiie body of f, not just the poition
of the body of f staiting at the point wheie the define foi odd? occuis.
lndeed, when we considei that odd? is itself dened in teims of even?
so that even? and odd? aie mutually iecuisive pioceduieswe see that
the only satisfactoiy inteipietation of the two defines is to iegaid them
as if the names even? and odd? weie being added to the enviionment
simultaneously. Moie geneially, in block stiuctuie, the scope of a local
name is the entiie pioceduie body in which the define is evaluated.
As it happens, oui inteipietei will evaluate calls to f coiiectly, but
foi an accidental ieason Since the denitions of the inteinal pioce-
duies come ist, no calls to these pioceduies will be evaluated until
all of them have been dened. Hence, odd? will have been dened by
the time even? is executed. ln fact, oui sequential evaluation mecha-
nism will give the same iesult as a mechanism that diiectly implements
simultaneous denition foi any pioceduie in which the inteinal deni-
tions come ist in a body and evaluation of the value expiessions foi
the dened vaiiables doesnt actually use any of the dened vaiiables.
(loi an example of a pioceduie that doesnt obey these iestiictions, so
that sequential denition isnt equivalent to simultaneous denition,
2
see Exeicise 4.19.)
24
Teie is, howevei, a simple way to tieat denitions so that intei-
nally dened names have tiuly simultaneous scopejust cieate all local
vaiiables that will be in the cuiient enviionment befoie evaluating any
of the value expiessions. One way to do this is by a syntax tiansfoi-
mation on lambda expiessions. Befoie evaluating the body of a lambda
expiession, we scan out and eliminate all the inteinal denitions in
the body. Te inteinally dened vaiiables will be cieated with a let
and then set to theii values by assignment. loi example, the pioceduie
(lambda vars
(define u e1)
(define v e2)
e3)
would be tiansfoimed into
(lambda vars
(let ((u '*unassigned*)
(v '*unassigned*))
(set! u e1)
(set! v e2)
e3))
24
Wanting piogiams to not depend on this evaluation mechanism is the ieason foi
the management is not iesponsible iemaik in lootnote 28 of Chaptei 1. By insisting
that inteinal denitions come ist and do not use each othei while the denitions aie
being evaluated, the irrr standaid foi Scheme leaves implementois some choice in
the mechanism used to evaluate these denitions. Te choice of one evaluation iule
iathei than anothei heie may seem like a small issue, aecting only the inteipietation
of badly foimed piogiams. Howevei, we will see in Section .. that moving to a
model of simultaneous scoping foi inteinal denitions avoids some nasty diculties
that would otheiwise aiise in implementing a compilei.
28
wheie *unassigned* is a special symbol that causes looking up a vaii-
able to signal an eiioi if an auempt is made to use the value of the
not-yet-assigned vaiiable.
An alteinative stiategy foi scanning out inteinal denitions is shown
in Exeicise 4.18. Unlike the tiansfoimation shown above, this enfoices
the iestiiction that the dened vaiiables values can be evaluated with-
out using any of the vaiiables values.
2
Exercise 4.16: ln this exeicise we implement the method
just desciibed foi inteipieting inteinal denitions. We as-
sume that the evaluatoi suppoits let (see Exeicise 4.).
a. Change lookup-variable-value (Section 4.1.3) to sig-
nal an eiioi if the value it nds is the symbol *unassigned*.
b. Wiite a pioceduie scan-out-defines that takes a pio-
ceduie body and ietuins an equivalent one that has
no inteinal denitions, by making the tiansfoimation
desciibed above.
c. lnstall scan-out-defines in the inteipietei, eithei in
make-procedure oi in procedure-body (see Section
4.1.3). Which place is beuei` Why`
Exercise 4.17: Diaw diagiams of the enviionment in eect
when evaluating the expiession e! in the pioceduie in the
2
Te irrr standaid foi Scheme allows foi dieient implementation stiategies by
specifying that it is up to the piogiammei to obey this iestiiction, not up to the imple-
mentation to enfoice it. Some Scheme implementations, including xi1 Scheme, use the
tiansfoimation shown above. Tus, some piogiams that dont obey this iestiiction will
in fact iun in such implementations.
29
text, compaiing how this will be stiuctuied when deni-
tions aie inteipieted sequentially with how it will be stiuc-
tuied if denitions aie scanned out as desciibed. Why is
theie an extia fiame in the tiansfoimed piogiam` Explain
why this dieience in enviionment stiuctuie can nevei make
a dieience in the behavioi of a coiiect piogiam. Design a
way to make the inteipietei implement the simultaneous
scope iule foi inteinal denitions without constiucting the
extia fiame.
Exercise 4.18: Considei an alteinative stiategy foi scan-
ning out denitions that tianslates the example in the text
to
(lambda vars
(let ((u '*unassigned*) (v '*unassigned*))
(let ((a e1) (b e2))
(set! u a)
(set! v b))
e3))
Heie a and b aie meant to iepiesent new vaiiable names,
cieated by the inteipietei, that do not appeai in the useis
piogiam. Considei the solve pioceduie fiom Section 3..4
(define (solve f y0 dt)
(define y (integral (delay dy) y0 dt))
(define dy (stream-map f y))
y)
Will this pioceduie woik if inteinal denitions aie scanned
out as shown in this exeicise` What if they aie scanned out
as shown in the text` Explain.
30
Exercise 4.19: Ben Bitdiddle, Alyssa P. Hackei, and Eva Lu
Atoi aie aiguing about the desiied iesult of evaluating the
expiession
(let ((a 1))
(define (f x)
(define b (+ a x))
(define a 5)
(+ a b))
(f 10))
Ben asseits that the iesult should be obtained using the se-
quential iule foi define b is dened to be 11, then a is de-
ned to be , so the iesult is 1. Alyssa objects that mutual
iecuision iequiies the simultaneous scope iule foi inteinal
pioceduie denitions, and that it is unieasonable to tieat
pioceduie names dieiently fiom othei names. Tus, she
aigues foi the mechanism implemented in Exeicise 4.1.
Tis would lead to a being unassigned at the time that the
value foi b is to be computed. Hence, in Alyssas view the
pioceduie should pioduce an eiioi. Eva has a thiid opinion.
She says that if the denitions of a and b aie tiuly meant
to be simultaneous, then the value foi a should be used in
evaluating b. Hence, in Evas viewa should be , b should be
1, and the iesult should be 20. Which (if any) of these view-
points do you suppoit` Can you devise a way to implement
inteinal denitions so that they behave as Eva piefeis`
2
2
Te xi1 implementois of Scheme suppoit Alyssa on the following giounds Eva is
in piinciple coiiectthe denitions should be iegaided as simultaneous. But it seems
dicult to implement a geneial, ecient mechanism that does what Eva iequiies. ln
the absence of such a mechanism, it is beuei to geneiate an eiioi in the dicult cases
of simultaneous denitions (Alyssas notion) than to pioduce an incoiiect answei (as
Ben would have it).
31
Exercise 4.20: Because inteinal denitions look sequen-
tial but aie actually simultaneous, some people piefei to
avoid them entiiely, and use the special foim letrec in-
stead. Letrec looks like let, so it is not suipiising that the
vaiiables it binds aie bound simultaneously and have the
same scope as each othei. Te sample pioceduie f above
can be wiiuen without inteinal denitions, but with ex-
actly the same meaning, as
(define (f x)
(letrec
((even? (lambda (n)
(if (= n 0) true (odd? (- n 1)))))
(odd? (lambda (n)
(if (= n 0) false (even? (- n 1))))))
rest of body of f))
Letrec expiessions, which have the foim
(letrec ((var
1
exp
1
) . . . (var
n
exp
n
))
body)
aie a vaiiation on let in which the expiessions exp
k
that
piovide the initial values foi the vaiiables var
k
aie eval-
uated in an enviionment that includes all the letrec bind-
ings. Tis peimits iecuision in the bindings, such as the
mutual iecuision of even? and odd? in the example above,
oi the evaluation of 10 factoiial with
(letrec
((fact (lambda (n)
(if (= n 1) 1 (* n (fact (- n 1)))))))
(fact 10))
32
a. lmplement letrec as a deiived expiession, by tians-
foiming a letrec expiession into a let expiession as
shown in the text above oi in Exeicise 4.18. Tat is,
the letrec vaiiables should be cieated with a let and
then be assigned theii values with set!.
b. Louis Reasonei is confused by all this fuss about intei-
nal denitions. Te way he sees it, if you dont like to
use define inside a pioceduie, you can just use let.
lllustiate what is loose about his ieasoning by diaw-
ing an enviionment diagiam that shows the enviion-
ment in which the res o[ |oJy o[ f is evaluated dui-
ing evaluation of the expiession (f 5), with f dened
as in this exeicise. Diaw an enviionment diagiam foi
the same evaluation, but with let in place of letrec
in the denition of f.
Exercise 4.21: Amazingly, Louiss intuition in Exeicise 4.20
is coiiect. lt is indeed possible to specify iecuisive pioce-
duies without using letrec (oi even define), although the
method foi accomplishing this is much moie subtle than
Louis imagined. Te following expiession computes 10 fac-
toiial by applying a iecuisive factoiial pioceduie
2
((lambda (n)
((lambda (fact) (fact fact n))
(lambda (ft k) (if (= k 1) 1 (* k (ft ft (- k 1)))))))
10)
2
Tis example illustiates a piogiamming tiick foi foimulating iecuisive pioceduies
without using define. Te most geneial tiick of this soit is the Y oeroor, which can be
used to give a puie -calculus implementation of iecuision. (See Stoy 19 foi details
on the -calculus, and Gabiiel 1988 foi an exposition of the Y opeiatoi in Scheme.)
33
a. Check (by evaluating the expiession) that this ieally
does compute factoiials. Devise an analogous expies-
sion foi computing libonacci numbeis.
b. Considei the following pioceduie, which includes mu-
tually iecuisive inteinal denitions
(define (f x)
(define (even? n)
(if (= n 0) true (odd? (- n 1))))
(define (odd? n)
(if (= n 0) false (even? (- n 1))))
(even? x))
lill in the missing expiessions to complete an alteina-
tive denition of f, which uses neithei inteinal de-
nitions noi letrec
(define (f x)
((lambda (even? odd?) (even? even? odd? x))
(lambda (ev? od? n)
(if (= n 0) true (od? ?? ?? ??)))
(lambda (ev? od? n)
(if (= n 0) false (ev? ?? ?? ??)))))
4.1.7 Separating Syntactic Analysis from Execution
Te evaluatoi implemented above is simple, but it is veiy inecient,
because the syntactic analysis of expiessions is inteileaved with theii
execution. Tus if a piogiam is executed many times, its syntax is an-
alyzed many times. Considei, foi example, evaluating (factorial 4)
using the following denition of factorial
(define (factorial n)
(if (= n 1) 1 (* (factorial (- n 1)) n)))
34
Each time factorial is called, the evaluatoi must deteimine that the
body is an if expiession and extiact the piedicate. Only then can it
evaluate the piedicate and dispatch on its value. Each time it evaluates
the expiession (* (factorial (- n 1)) n), oi the subexpiessions
(factorial (- n 1)) and (- n 1), the evaluatoi must peifoim the
case analysis in eval to deteimine that the expiession is an application,
and must extiact its opeiatoi and opeiands. Tis analysis is expensive.
Peifoiming it iepeatedly is wasteful.
We can tiansfoim the evaluatoi to be signicantly moie ecient
by aiianging things so that syntactic analysis is peifoimed only once.
28
We split eval, which takes an expiession and an enviionment, into two
paits. Te pioceduie analyze takes only the expiession. lt peifoims the
syntactic analysis and ietuins a new pioceduie, the e:ecv:on roceJvre,
that encapsulates the woik to be done in executing the analyzed expies-
sion. Te execution pioceduie takes an enviionment as its aigument
and completes the evaluation. Tis saves woik because analyze will be
called only once on an expiession, while the execution pioceduie may
be called many times.
With the sepaiation into analysis and execution, eval nowbecomes
(define (eval exp env) ((analyze exp) env))
Te iesult of calling analyze is the execution pioceduie to be applied
to the enviionment. Te analyze pioceduie is the same case analysis as
peifoimed by the oiiginal eval of Section 4.1.1, except that the pioce-
duies to which we dispatch peifoim only analysis, not full evaluation
(define (analyze exp)
28
Tis technique is an integial pait of the compilation piocess, which we shall discuss
in Chaptei . Jonathan Rees wiote a Scheme inteipietei like this in about 1982 foi the T
pioject (Rees and Adams 1982). Maic leeley (198) (see also leeley and Lapalme 198)
independently invented this technique in his masteis thesis.
3
(cond ((self-evaluating? exp) (analyze-self-evaluating exp))
((quoted? exp) (analyze-quoted exp))
((variable? exp) (analyze-variable exp))
((assignment? exp) (analyze-assignment exp))
((definition? exp) (analyze-definition exp))
((if? exp) (analyze-if exp))
((lambda? exp) (analyze-lambda exp))
((begin? exp) (analyze-sequence (begin-actions exp)))
((cond? exp) (analyze (cond->if exp)))
((application? exp) (analyze-application exp))
(else (error "Unknown expression type: ANALYZE" exp))))
Heie is the simplest syntactic analysis pioceduie, which handles self-
evaluating expiessions. lt ietuins an execution pioceduie that ignoies
its enviionment aigument and just ietuins the expiession
(define (analyze-self-evaluating exp)
(lambda (env) exp))
loi a quoted expiession, we can gain a liule eciency by extiacting the
text of the quotation only once, in the analysis phase, iathei than in the
execution phase.
(define (analyze-quoted exp)
(let ((qval (text-of-quotation exp)))
(lambda (env) qval)))
Looking up a vaiiable value must still be done in the execution phase,
since this depends upon knowing the enviionment.
29
(define (analyze-variable exp)
(lambda (env) (lookup-variable-value exp env)))
29
Teie is, howevei, an impoitant pait of the vaiiable seaich that con be done as
pait of the syntactic analysis. As we will show in Section .., one can deteimine the
position in the enviionment stiuctuie wheie the value of the vaiiable will be found, thus
obviating the need to scan the enviionment foi the entiy that matches the vaiiable.
3
Analyze-assignment also must defei actually seuing the vaiiable un-
til the execution, when the enviionment has been supplied. Howevei,
the fact that the assignment-value expiession can be analyzed (ie-
cuisively) duiing analysis is a majoi gain in eciency, because the
assignment-value expiession will nowbe analyzed only once. Te same
holds tiue foi denitions.
(define (analyze-assignment exp)
(let ((var (assignment-variable exp))
(vproc (analyze (assignment-value exp))))
(lambda (env)
(set-variable-value! var (vproc env) env)
'ok)))
(define (analyze-definition exp)
(let ((var (definition-variable exp))
(vproc (analyze (definition-value exp))))
(lambda (env)
(define-variable! var (vproc env) env)
'ok)))
loi if expiessions, we extiact and analyze the piedicate, consequent,
and alteinative at analysis time.
(define (analyze-if exp)
(let ((pproc (analyze (if-predicate exp)))
(cproc (analyze (if-consequent exp)))
(aproc (analyze (if-alternative exp))))
(lambda (env) (if (true? (pproc env))
(cproc env)
(aproc env)))))
Analyzing a lambda expiession also achieves a majoi gain in eciency
We analyze the lambda body only once, even though pioceduies iesult-
ing fiom evaluation of the lambda may be applied many times.
3
(define (analyze-lambda exp)
(let ((vars (lambda-parameters exp))
(bproc (analyze-sequence (lambda-body exp))))
(lambda (env) (make-procedure vars bproc env))))
Analysis of a sequence of expiessions (as in a begin oi the body of a
lambda expiession) is moie involved.
30
Each expiession in the sequence
is analyzed, yielding an execution pioceduie. Tese execution pioce-
duies aie combined to pioduce an execution pioceduie that takes an
enviionment as aigument and sequentially calls each individual execu-
tion pioceduie with the enviionment as aigument.
(define (analyze-sequence exps)
(define (sequentially proc1 proc2)
(lambda (env) (proc1 env) (proc2 env)))
(define (loop first-proc rest-procs)
(if (null? rest-procs)
first-proc
(loop (sequentially first-proc (car rest-procs))
(cdr rest-procs))))
(let ((procs (map analyze exps)))
(if (null? procs) (error "Empty sequence: ANALYZE"))
(loop (car procs) (cdr procs))))
To analyze an application, we analyze the opeiatoi and opeiands and
constiuct an execution pioceduie that calls the opeiatoi execution pio-
ceduie (to obtain the actual pioceduie to be applied) and the opeiand
execution pioceduies (to obtain the actual aiguments). We then pass
these to execute-application, which is the analog of apply in Section
4.1.1. Execute-application dieis fiom apply in that the pioceduie
body foi a compound pioceduie has alieady been analyzed, so theie is
30
See Exeicise 4.23 foi some insight into the piocessing of sequences.
38
no need to do fuithei analysis. lnstead, we just call the execution pio-
ceduie foi the body on the extended enviionment.
(define (analyze-application exp)
(let ((fproc (analyze (operator exp)))
(aprocs (map analyze (operands exp))))
(lambda (env)
(execute-application
(fproc env)
(map (lambda (aproc) (aproc env))
aprocs)))))
(define (execute-application proc args)
(cond ((primitive-procedure? proc)
(apply-primitive-procedure proc args))
((compound-procedure? proc)
((procedure-body proc)
(extend-environment
(procedure-parameters proc)
args
(procedure-environment proc))))
(else
(error "Unknown procedure type: EXECUTE-APPLICATION"
proc))))
Oui new evaluatoi uses the same data stiuctuies, syntax pioceduies,
and iun-time suppoit pioceduies as in sections Section 4.1.2, Section
4.1.3, and Section 4.1.4.
Exercise 4.22: Extend the evaluatoi in this section to sup-
poit the special foim let. (See Exeicise 4..)
Exercise 4.23: Alyssa P. Hackei doesnt undeistand why
analyze-sequence needs to be so complicated. All the othei
analysis pioceduies aie stiaightfoiwaid tiansfoimations of
39
the coiiesponding evaluation pioceduies (oi eval clauses)
in Section 4.1.1. She expected analyze-sequence to look
like this
(define (analyze-sequence exps)
(define (execute-sequence procs env)
(cond ((null? (cdr procs))
((car procs) env))
(else
((car procs) env)
(execute-sequence (cdr procs) env))))
(let ((procs (map analyze exps)))
(if (null? procs)
(error "Empty sequence: ANALYZE"))
(lambda (env)
(execute-sequence procs env))))
Eva Lu Atoi explains to Alyssa that the veision in the text
does moie of the woik of evaluating a sequence at analysis
time. Alyssas sequence-execution pioceduie, iathei than
having the calls to the individual execution pioceduies built
in, loops thiough the pioceduies in oidei to call them ln
eect, although the individual expiessions in the sequence
have been analyzed, the sequence itself has not been.
Compaie the two veisions of analyze-sequence. loi ex-
ample, considei the common case (typical of pioceduie bod-
ies) wheie the sequence has just one expiession. What woik
will the execution pioceduie pioduced by Alyssas piogiam
do` What about the execution pioceduie pioduced by the
piogiam in the text above` How do the two veisions com-
paie foi a sequence with two expiessions`
40
Exercise 4.24: Design and caiiy out some expeiiments to
compaie the speed of the oiiginal metaciiculai evaluatoi
with the veision in this section. Use youi iesults to esti-
mate the fiaction of time that is spent in analysis veisus
execution foi vaiious pioceduies.
4.2 Variations on a Scheme Lazy Evaluation
Now that we have an evaluatoi expiessed as a Lisp piogiam, we can
expeiiment with alteinative choices in language design simply by mod-
ifying the evaluatoi. lndeed, new languages aie ofen invented by ist
wiiting an evaluatoi that embeds the new language within an exist-
ing high-level language. loi example, if we wish to discuss some aspect
of a pioposed modication to Lisp with anothei membei of the Lisp
community, we can supply an evaluatoi that embodies the change. Te
iecipient can then expeiiment with the new evaluatoi and send back
comments as fuithei modications. Not only does the high-level imple-
mentation base make it easiei to test and debug the evaluatoi, in addi-
tion, the embedding enables the designei to snaif
31
featuies fiom the
undeilying language, just as oui embedded Lisp evaluatoi uses piimi-
tives and contiol stiuctuie fiom the undeilying Lisp. Only latei (if evei)
need the designei go to the tiouble of building a complete implemen-
tation in a low-level language oi in haidwaie. ln this section and the
next we exploie some vaiiations on Scheme that piovide signicant ad-
ditional expiessive powei.
31
Snaif To giab, especially a laige document oi le foi the puipose of using it ei-
thei with oi without the owneis peimission. Snaif down To snaif, sometimes with
the connotation of absoibing, piocessing, oi undeistanding. (Tese denitions weie
snaifed fiom Steele et al. 1983. See also Raymond 1993.)
41
4.2.1 Normal Order and Applicative Order
ln Section 1.1, wheie we began oui discussion of models of evaluation,
we noted that Scheme is an o|:co:+eorJer language, namely, that all
the aiguments to Scheme pioceduies aie evaluated when the pioceduie
is applied. ln contiast, noro|orJer languages delay evaluation of pio-
ceduie aiguments until the actual aigument values aie needed. Delay-
ing evaluation of pioceduie aiguments until the last possible moment
(e.g., until they aie iequiied by a piimitive opeiation) is called |o:y e+o|
vo:on.
32
Considei the pioceduie
(define (try a b) (if (= a 0) 1 b))
Evaluating (try 0 (/ 1 0)) geneiates an eiioi in Scheme. With lazy
evaluation, theie would be no eiioi. Evaluating the expiession would
ietuin 1, because the aigument (/ 1 0) would nevei be evaluated.
An example that exploits lazy evaluation is the denition of a pio-
ceduie unless
(define (unless condition usual-value exceptional-value)
(if condition exceptional-value usual-value))
that can be used in expiessions such as
(unless (= b 0)
(/ a b)
(begin (display "exception: returning 0") 0))
Tis wont woik in an applicative-oidei language because both the usual
value and the exceptional value will be evaluated befoie unless is called
32
Te dieience between the lazy teiminology and the noimal-oidei teiminol-
ogy is somewhat fuzzy. Geneially, lazy iefeis to the mechanisms of paiticulai eval-
uatois, while noimal-oidei iefeis to the semantics of languages, independent of any
paiticulai evaluation stiategy. But this is not a haid-and-fast distinction, and the two
teiminologies aie ofen used inteichangeably.
42
(compaie Exeicise 1.). An advantage of lazy evaluation is that some
pioceduies, such as unless, can do useful computation even if evalu-
ation of some of theii aiguments would pioduce eiiois oi would not
teiminate.
lf the body of a pioceduie is enteied befoie an aigument has been
evaluated we say that the pioceduie is nonsr:c in that aigument. lf the
aigument is evaluated befoie the body of the pioceduie is enteied we
say that the pioceduie is sr:c in that aigument.
33
ln a puiely applicative-
oidei language, all pioceduies aie stiict in each aigument. ln a puiely
noimal-oidei language, all compound pioceduies aie non-stiict in each
aigument, and piimitive pioceduies may be eithei stiict oi non-stiict.
Teie aie also languages (see Exeicise 4.31) that give piogiammeis de-
tailed contiol ovei the stiictness of the pioceduies they dene.
A stiiking example of a pioceduie that can usefully be made non-
stiict is cons (oi, in geneial, almost any constiuctoi foi data stiuctuies).
One can do useful computation, combining elements to foimdata stiuc-
tuies and opeiating on the iesulting data stiuctuies, even if the values
of the elements aie not known. lt makes peifect sense, foi instance, to
compute the length of a list without knowing the values of the indi-
vidual elements in the list. We will exploit this idea in Section 4.2.3 to
implement the stieams of Chaptei 3 as lists foimed of non-stiict cons
paiis.
Exercise 4.25: Suppose that (in oidinaiy applicative-oidei
Scheme) we dene unless as shown above and then dene
33
Te stiict veisus non-stiict teiminology means essentially the same thing as
applicative-oidei veisus noimal-oidei, except that it iefeis to individual pioceduies
and aiguments iathei than to the language as a whole. At a confeience on piogiamming
languages you might heai someone say, Te noimal-oidei language Hassle has ceitain
stiict piimitives. Othei pioceduies take theii aiguments by lazy evaluation.
43
factorial in teims of unless as
(define (factorial n)
(unless (= n 1)
(* n (factorial (- n 1)))
1))
What happens if we auempt to evaluate (factorial 5)`
Will oui denitions woik in a noimal-oidei language`
Exercise 4.26: Ben Bitdiddle and Alyssa P. Hackei disagiee
ovei the impoitance of lazy evaluation foi implementing
things such as unless. Ben points out that its possible to
implement unless in applicative oidei as a special foim.
Alyssa counteis that, if one did that, unless would be meiely
syntax, not a pioceduie that could be used in conjunction
with highei-oidei pioceduies. lill in the details on both
sides of the aigument. Show how to implement unless as
a deiived expiession (like cond oi let), and give an exam-
ple of a situation wheie it might be useful to have unless
available as a pioceduie, iathei than as a special foim.
4.2.2 An Interpreter with Lazy Evaluation
ln this section we will implement a noimal-oidei language that is the
same as Scheme except that compound pioceduies aie non-stiict in each
aigument. Piimitive pioceduies will still be stiict. lt is not dicult to
modify the evaluatoi of Section 4.1.1 so that the language it inteipiets
behaves this way. Almost all the iequiied changes centei aiound pio-
ceduie application.
Te basic idea is that, when applying a pioceduie, the inteipietei
must deteimine which aiguments aie to be evaluated and which aie to
44
be delayed. Te delayed aiguments aie not evaluated, instead, they aie
tiansfoimed into objects called |vn|s.
34
Te thunk must contain the
infoimation iequiied to pioduce the value of the aigument when it is
needed, as if it had been evaluated at the time of the application. Tus,
the thunk must contain the aigument expiession and the enviionment
in which the pioceduie application is being evaluated.
Te piocess of evaluating the expiession in a thunk is called [orc
:ng.
3
ln geneial, a thunk will be foiced only when its value is needed
when it is passed to a piimitive pioceduie that will use the value of the
thunk, when it is the value of a piedicate of a conditional, and when it is
the value of an opeiatoi that is about to be applied as a pioceduie. One
design choice we have available is whethei oi not to eo::e thunks, as
we did with delayed objects in Section 3..1. With memoization, the ist
time a thunk is foiced, it stoies the value that is computed. Subsequent
foicings simply ietuin the stoied value without iepeating the computa-
tion. Well make oui inteipietei memoize, because this is moie ecient
foi many applications. Teie aie tiicky consideiations heie, howevei.
3
34
Te woid |vn| was invented by an infoimal woiking gioup that was discussing
the implementation of call-by-name in Algol 0. Tey obseived that most of the analysis
of (thinking about) the expiession could be done at compile time, thus, at iun time,
the expiession would alieady have been thunk about (lngeiman et al. 190).
3
Tis is analogous to the use of force on the delayed objects that weie intioduced
in Chaptei 3 to iepiesent stieams. Te ciitical dieience between what we aie doing
heie and what we did in Chaptei 3 is that we aie building delaying and foicing into the
evaluatoi, and thus making this unifoim and automatic thioughout the language.
3
Lazy evaluation combined with memoization is sometimes iefeiied to as co|||y
neeJ aigument passing, in contiast to co|||ynoe aigument passing. (Call-by-name,
intioduced in Algol 0, is similai to non-memoized lazy evaluation.) As language de-
signeis, we can build oui evaluatoi to memoize, not to memoize, oi leave this an option
foi piogiammeis (Exeicise 4.31). As you might expect fiom Chaptei 3, these choices
iaise issues that become both subtle and confusing in the piesence of assignments. (See
Exeicise 4.2 and Exeicise 4.29.) An excellent aiticle by Clingei (1982) auempts to clai-
4
Modifying the evaluator
Te main dieience between the lazy evaluatoi and the one in Section
4.1 is in the handling of pioceduie applications in eval and apply.
Te application? clause of eval becomes
((application? exp)
(apply (actual-value (operator exp) env)
(operands exp)
env))
Tis is almost the same as the application? clause of eval in Sec-
tion 4.1.1. loi lazy evaluation, howevei, we call apply with the opeiand
expiessions, iathei than the aiguments pioduced by evaluating them.
Since we will need the enviionment to constiuct thunks if the aigu-
ments aie to be delayed, we must pass this as well. We still evaluate
the opeiatoi, because apply needs the actual pioceduie to be applied in
oidei to dispatch on its type (piimitive veisus compound) and apply it.
Whenevei we need the actual value of an expiession, we use
(define (actual-value exp env)
(force-it (eval exp env)))
instead of just eval, so that if the expiessions value is a thunk, it will
be foiced.
Oui new veision of apply is also almost the same as the veision
in Section 4.1.1. Te dieience is that eval has passed in unevaluated
opeiand expiessions loi piimitive pioceduies (which aie stiict), we
evaluate all the aiguments befoie applying the piimitive, foi compound
pioceduies (which aie non-stiict) we delay all the aiguments befoie ap-
plying the pioceduie.
ify the multiple dimensions of confusion that aiise heie.
4
(define (apply procedure arguments env)
(cond ((primitive-procedure? procedure)
(apply-primitive-procedure
procedure
(list-of-arg-values arguments env))) ;changed
((compound-procedure? procedure)
(eval-sequence
(procedure-body procedure)
(extend-environment
(procedure-parameters procedure)
(list-of-delayed-args arguments env) ;changed
(procedure-environment procedure))))
(else (error "Unknown procedure type: APPLY"
procedure))))
Te pioceduies that piocess the aiguments aie just like list-of-values
fiom Section 4.1.1, except that list-of-delayed-args delays the aigu-
ments instead of evaluating them, and list-of-arg-values uses actual-
value instead of eval
(define (list-of-arg-values exps env)
(if (no-operands? exps)
'()
(cons (actual-value (first-operand exps)
env)
(list-of-arg-values (rest-operands exps)
env))))
(define (list-of-delayed-args exps env)
(if (no-operands? exps)
'()
(cons (delay-it (first-operand exps)
env)
(list-of-delayed-args (rest-operands exps)
env))))
4
Te othei place we must change the evaluatoi is in the handling of if,
wheie we must use actual-value instead of eval to get the value of
the piedicate expiession befoie testing whethei it is tiue oi false
(define (eval-if exp env)
(if (true? (actual-value (if-predicate exp) env))
(eval (if-consequent exp) env)
(eval (if-alternative exp) env)))
linally, we must change the driver-loop pioceduie (Section 4.1.4) to
use actual-value instead of eval, so that if a delayed value is piop-
agated back to the iead-eval-piint loop, it will be foiced befoie being
piinted. We also change the piompts to indicate that this is the lazy
evaluatoi
(define input-prompt ";;; L-Eval input:")
(define output-prompt ";;; L-Eval value:")
(define (driver-loop)
(prompt-for-input input-prompt)
(let ((input (read)))
(let ((output
(actual-value
input the-global-environment)))
(announce-output output-prompt)
(user-print output)))
(driver-loop))
With these changes made, we can stait the evaluatoi and test it. Te
successful evaluation of the try expiession discussed in Section 4.2.1
indicates that the inteipietei is peifoiming lazy evaluation
(define the-global-environment (setup-environment))
(driver-loop)
;;; L-Eval input:
(define (try a b) (if (= a 0) 1 b))
48
;;; L-Eval value:
ok
;;; L-Eval input:
(try 0 (/ 1 0))
;;; L-Eval value:
1
Representing thunks
Oui evaluatoi must aiiange to cieate thunks when pioceduies aie ap-
plied to aiguments and to foice these thunks latei. A thunk must pack-
age an expiession togethei with the enviionment, so that the aigument
can be pioduced latei. To foice the thunk, we simply extiact the ex-
piession and enviionment fiom the thunk and evaluate the expiession
in the enviionment. We use actual-value iathei than eval so that in
case the value of the expiession is itself a thunk, we will foice that, and
so on, until we ieach something that is not a thunk
(define (force-it obj)
(if (thunk? obj)
(actual-value (thunk-exp obj) (thunk-env obj))
obj))
One easy way to package an expiession with an enviionment is to make
a list containing the expiession and the enviionment. Tus, we cieate a
thunk as follows
(define (delay-it exp env)
(list 'thunk exp env))
(define (thunk? obj)
(tagged-list? obj 'thunk))
(define (thunk-exp thunk) (cadr thunk))
(define (thunk-env thunk) (caddr thunk))
49
Actually, what we want foi oui inteipietei is not quite this, but iathei
thunks that have been memoized. When a thunk is foiced, we will tuin
it into an evaluated thunk by ieplacing the stoied expiession with its
value and changing the thunk tag so that it can be iecognized as alieady
evaluated.
3
(define (evaluated-thunk? obj)
(tagged-list? obj 'evaluated-thunk))
(define (thunk-value evaluated-thunk)
(cadr evaluated-thunk))
(define (force-it obj)
(cond ((thunk? obj)
(let ((result (actual-value (thunk-exp obj)
(thunk-env obj))))
(set-car! obj 'evaluated-thunk)
(set-car! (cdr obj)
result) ;ieplace exp with its value
(set-cdr! (cdr obj)
'()) ;foiget unneeded env
result))
((evaluated-thunk? obj) (thunk-value obj))
(else obj)))
Notice that the same delay-it pioceduie woiks both with and without
memoization.
3
Notice that we also eiase the env fiom the thunk once the expiessions value has
been computed. Tis makes no dieience in the values ietuined by the inteipietei. lt
does help save space, howevei, because iemoving the iefeience fiom the thunk to the
env once it is no longei needed allows this stiuctuie to be gor|ogeco||eceJ and its space
iecycled, as we will discuss in Section .3.
Similaily, we could have allowed unneeded enviionments in the memoized delayed
objects of Section 3..1 to be gaibage-collected, by having memo-proc do something like
(set! proc '()) to discaid the pioceduie proc (which includes the enviionment in
which the delay was evaluated) afei stoiing its value.
0
Exercise 4.27: Suppose we type in the following denitions
to the lazy evaluatoi
(define count 0)
(define (id x) (set! count (+ count 1)) x)
Give the missing values in the following sequence of intei-
actions, and explain youi answeis.
38
(define w (id (id 10)))
;;; L-Eval input:
count
;;; L-Eval value:
response
;;; L-Eval input:
w
;;; L-Eval value:
response
;;; L-Eval input:
count
;;; L-Eval value:
response
Exercise 4.28: Eval uses actual-value iathei than eval
to evaluate the opeiatoi befoie passing it to apply, in oi-
dei to foice the value of the opeiatoi. Give an example that
demonstiates the need foi this foicing.
Exercise 4.29: Exhibit a piogiam that you would expect
to iun much moie slowly without memoization than with
38
Tis exeicise demonstiates that the inteiaction between lazy evaluation and side
eects can be veiy confusing. Tis is just what you might expect fiom the discussion
in Chaptei 3.
1
memoization. Also, considei the following inteiaction, wheie
the id pioceduie is dened as in Exeicise 4.2 and count
staits at 0
(define (square x) (* x x))
;;; L-Eval input:
(square (id 10))
;;; L-Eval value:
response
;;; L-Eval input:
count
;;; L-Eval value:
response
Give the iesponses both when the evaluatoi memoizes and
when it does not.
Exercise 4.30: Cy D. lect, a iefoimed C piogiammei, is
woiiied that some side eects may nevei take place, be-
cause the lazy evaluatoi doesnt foice the expiessions in a
sequence. Since the value of an expiession in a sequence
othei than the last one is not used (the expiession is theie
only foi its eect, such as assigning to a vaiiable oi piint-
ing), theie can be no subsequent use of this value (e.g., as an
aigument to a piimitive pioceduie) that will cause it to be
foiced. Cy thus thinks that when evaluating sequences, we
must foice all expiessions in the sequence except the nal
one. He pioposes to modify eval-sequence fiom Section
4.1.1 to use actual-value iathei than eval
(define (eval-sequence exps env)
(cond ((last-exp? exps) (eval (first-exp exps) env))
(else (actual-value (first-exp exps) env)
2
(eval-sequence (rest-exps exps) env))))
a. Ben Bitdiddle thinks Cy is wiong. He shows Cy the
for-each pioceduie desciibed in Exeicise 2.23, which
gives an impoitant example of a sequence with side
eects
(define (for-each proc items)
(if (null? items)
'done
(begin (proc (car items))
(for-each proc (cdr items)))))
He claims that the evaluatoi in the text (with the oiig-
inal eval-sequence) handles this coiiectly
;;; L-Eval input:
(for-each (lambda (x) (newline) (display x))
(list 57 321 88))
57
321
88
;;; L-Eval value:
done
Explain why Ben is iight about the behavioi of for-
each.
b. Cy agiees that Ben is iight about the for-each exam-
ple, but says that thats not the kind of piogiam he
was thinking about when he pioposed his change to
eval-sequence. He denes the following two pioce-
duies in the lazy evaluatoi
3
(define (p1 x)
(set! x (cons x '(2)))
x)
(define (p2 x)
(define (p e)
e
x)
(p (set! x (cons x '(2)))))
What aie the values of (p1 1) and (p2 1) with the
oiiginal eval-sequence` What would the values be
with Cys pioposed change to eval-sequence`
c. Cy also points out that changing eval-sequence as he
pioposes does not aect the behavioi of the example
in pait a. Explain why this is tiue.
d. How do you think sequences ought to be tieated in
the lazy evaluatoi` Do you like Cys appioach, the ap-
pioach in the text, oi some othei appioach`
Exercise 4.31: Te appioach taken in this section is some-
what unpleasant, because it makes an incompatible change
to Scheme. lt might be nicei to implement lazy evaluation
as an v+orJcoo:||e e:ens:on, that is, so that oidinaiy
Scheme piogiams will woik as befoie. We can do this by
extending the syntax of pioceduie declaiations to let the
usei contiol whethei oi not aiguments aie to be delayed.
While weie at it, we may as well also give the usei the
choice between delaying with and without memoization.
loi example, the denition
(define (f a (b lazy) c (d lazy-memo))
. . .)
4
would dene f to be a pioceduie of foui aiguments, wheie
the ist and thiid aiguments aie evaluated when the pio-
ceduie is called, the second aigument is delayed, and the
fouith aigument is both delayed and memoized. Tus, oi-
dinaiy pioceduie denitions will pioduce the same behav-
ioi as oidinaiy Scheme, while adding the lazy-memo dec-
laiation to each paiametei of eveiy compound pioceduie
will pioduce the behavioi of the lazy evaluatoi dened in
this section. Design and implement the changes iequiied
to pioduce such an extension to Scheme. You will have to
implement new syntax pioceduies to handle the new syn-
tax foi define. You must also aiiange foi eval oi apply to
deteimine when aiguments aie to be delayed, and to foice
oi delay aiguments accoidingly, and you must aiiange foi
foicing to memoize oi not, as appiopiiate.
4.2.3 Streams as Lazy Lists
ln Section 3..1, we showed how to implement stieams as delayed lists.
We intioduced special foims delay and cons-stream, which allowed
us to constiuct a piomise to compute the cdr of a stieam, without
actually fullling that piomise until latei. We could use this geneial
technique of intioducing special foims whenevei we need moie contiol
ovei the evaluation piocess, but this is awkwaid. loi one thing, a spe-
cial foim is not a ist-class object like a pioceduie, so we cannot use it
togethei with highei-oidei pioceduies.
39
Additionally, we weie foiced
to cieate stieams as a newkind of data object similai but not identical to
lists, and this iequiied us to ieimplement many oidinaiy list opeiations
39
Tis is piecisely the issue with the unless pioceduie, as in Exeicise 4.2.
(3 110)
;;; Amb-Eval input:
try-again
;;; Amb-Eval value:
(8 35)
;;; Amb-Eval input:
try-again
;;; There are no more values of
(prime-sum-pair (quote (1 3 5 8)) (quote (20 35 110)))
;;; Amb-Eval input:
(prime-sum-pair '(19 27 30) '(11 36 58))
;;; Starting a new problem
;;; Amb-Eval value:
(30 11)
Exercise 4.35: Wiite a pioceduie an-integer-between that
ietuins an integei between two given bounds. Tis can be
used to implement a pioceduie that nds Pythagoiean tiiples,
i.e., tiiples of integeis (i , j, k) between the given bounds
such that i j and i
2
j
2
= k
2
, as follows
(define (a-pythagorean-triple-between low high)
(let ((i (an-integer-between low high)))
(let ((j (an-integer-between i high)))
(let ((k (an-integer-between j high)))
(require (= (+ (* i i) (* j j)) (* k k)))
(list i j k)))))
Exercise 4.36: Exeicise 3.9 discussed how to geneiate the
stieam of o|| Pythagoiean tiiples, with no uppei bound on
Logic Puzzles
Te following puzzle (taken fiom Dinesman 198) is typical of a laige
class of simple logic puzzles
Bakei, Coopei, lletchei, Millei, and Smith live on diei-
ent oois of an apaitment house that contains only ve
oois. Bakei does not live on the top ooi. Coopei does
not live on the bouom ooi. lletchei does not live on ei-
thei the top oi the bouom ooi. Millei lives on a highei
ooi than does Coopei. Smith does not live on a ooi adja-
cent to lletcheis. lletchei does not live on a ooi adjacent
to Coopeis. Wheie does eveiyone live`
We can deteimine who lives on each ooi in a stiaightfoiwaid way by
enumeiating all the possibilities and imposing the given iestiictions
48
(define (multiple-dwelling)
(let ((baker (amb 1 2 3 4 5)) (cooper (amb 1 2 3 4 5))
(fletcher (amb 1 2 3 4 5)) (miller (amb 1 2 3 4 5))
(smith (amb 1 2 3 4 5)))
(require
(distinct? (list baker cooper fletcher miller smith)))
(require (not (= baker 5)))
48
Oui piogiam uses the following pioceduie to deteimine if the elements of a list
aie distinct
(define (distinct? items)
(cond ((null? items) true)
((null? (cdr items)) true)
((member (car items) (cdr items)) false)
(else (distinct? (cdr items)))))
Member is like memq except that it uses equal? instead of eq? to test foi equality.
8
(require (not (= cooper 1)))
(require (not (= fletcher 5)))
(require (not (= fletcher 1)))
(require (> miller cooper))
(require (not (= (abs (- smith fletcher)) 1)))
(require (not (= (abs (- fletcher cooper)) 1)))
(list (list 'baker baker) (list 'cooper cooper)
(list 'fletcher fletcher) (list 'miller miller)
(list 'smith smith))))
Evaluating the expiession (multiple-dwelling) pioduces the iesult
((baker 3) (cooper 2) (fletcher 4) (miller 5) (smith 1))
Although this simple pioceduie woiks, it is veiy slow. Exeicise 4.39 and
Exeicise 4.40 discuss some possible impiovements.
Exercise 4.38: Modify the multiple-dwelling pioceduie to
omit the iequiiement that Smith and lletchei do not live
on adjacent oois. How many solutions aie theie to this
modied puzzle`
Exercise 4.39: Does the oidei of the iestiictions in the multiple-
dwelling pioceduie aect the answei` Does it aect the
time to nd an answei` lf you think it maueis, demonstiate
a fastei piogiamobtained fiomthe given one by ieoideiing
the iestiictions. lf you think it does not mauei, aigue youi
case.
Exercise 4.40: ln the multiple dwelling pioblem, howmany
sets of assignments aie theie of people to oois, both be-
foie and afei the iequiiement that ooi assignments be
distinct` lt is veiy inecient to geneiate all possible assign-
ments of people to oois and then leave it to backtiacking
9
to eliminate them. loi example, most of the iestiictions de-
pend on only one oi two of the peison-ooi vaiiables, and
can thus be imposed befoie oois have been selected foi
all the people. Wiite and demonstiate a much moie e-
cient nondeteiministic pioceduie that solves this pioblem
based upon geneiating only those possibilities that aie not
alieady iuled out by pievious iestiictions. (Hint Tis will
iequiie a nest of let expiessions.)
Exercise 4.41: Wiite an oidinaiy Scheme piogiam to solve
the multiple dwelling puzzle.
Exercise 4.42: Solve the following Liais puzzle (fiomPhillips
1934)
live schoolgiils sat foi an examination. Teii paientsso
they thoughtshowed an undue degiee of inteiest in the
iesult. Tey theiefoie agieed that, in wiiting home about
the examination, each giil should make one tiue statement
and one untiue one. Te following aie the ielevant passages
fiom theii leueis
Beuy Kiuy was second in the examination. l was
only thiid.
Ethel Youll be glad to heai that l was on top. Joan
was 2nd.
Joan l was thiid, and pooi old Ethel was bouom.
Kiuy l came out second. Maiy was only fouith.
Maiy l was fouith. Top place was taken by Beuy.
0
What in fact was the oidei in which the ve giils weie
placed`
Exercise 4.43: Use the amb evaluatoi to solve the following
puzzle
49
Maiy Ann Mooies fathei has a yacht and so has each of
his foui fiiends Colonel Downing, Mi. Hall, Sii Bainacle
Hood, and Di. Paikei. Each of the ve also has one daugh-
tei and each has named his yacht afei a daughtei of one of
the otheis. Sii Bainacles yacht is the Gabiielle, Mi. Mooie
owns the Loina, Mi. Hall the Rosalind. Te Melissa, owned
by Colonel Downing, is named afei Sii Bainacles daugh-
tei. Gabiielles fathei owns the yacht that is named afei
Di. Paikeis daughtei. Who is Loinas fathei`
Tiy to wiite the piogiam so that it iuns eciently (see Ex-
eicise 4.40). Also deteimine how many solutions theie aie
if we aie not told that Maiy Anns last name is Mooie.
Exercise 4.44: Exeicise 2.42 desciibed the eight-queens
puzzle of placing queens on a chessboaid so that no two at-
tack each othei. Wiite a nondeteiministic piogiam to solve
this puzzle.
Parsing natural language
Piogiams designed to accept natuial language as input usually stait by
auempting to orse the input, that is, to match the input against some
giammatical stiuctuie. loi example, we might tiy to iecognize simple
49
Tis is taken fiom a booklet called Pioblematical Recieations, published in the
190s by Liuon lndustiies, wheie it is auiibuted to the Konsos Soe Fng:neer.
1
sentences consisting of an aiticle followed by a noun followed by a veib,
such as Te cat eats. To accomplish such an analysis, we must be able
to identify the paits of speech of individual woids. We could stait with
some lists that classify vaiious woids
0
(define nouns '(noun student professor cat class))
(define verbs '(verb studies lectures eats sleeps))
(define articles '(article the a))
We also need a groor, that is, a set of iules desciibing how giam-
matical elements aie composed fiom simplei elements. A veiy simple
giammai might stipulate that a sentence always consists of two pieces
a noun phiase followed by a veiband that a noun phiase consists of
an aiticle followed by a noun. With this giammai, the sentence Te cat
eats is paised as follows
(sentence (noun-phrase (article the) (noun cat))
(verb eats))
We can geneiate such a paise with a simple piogiam that has sepaiate
pioceduies foi each of the giammatical iules. To paise a sentence, we
identify its two constituent pieces and ietuin a list of these two ele-
ments, tagged with the symbol sentence
(define (parse-sentence)
(list 'sentence
(parse-noun-phrase)
(parse-word verbs)))
A noun phiase, similaily, is paised by nding an aiticle followed by a
noun
0
Heie we use the convention that the ist element of each list designates the pait
of speech foi the iest of the woids in the list.
2
(define (parse-noun-phrase)
(list 'noun-phrase
(parse-word articles)
(parse-word nouns)))
At the lowest level, paising boils down to iepeatedly checking that the
next unpaised woid is a membei of the list of woids foi the iequiied pait
of speech. To implement this, we maintain a global vaiiable *unparsed*,
which is the input that has not yet been paised. Each time we check a
woid, we iequiie that *unparsed* must be non-empty and that it should
begin with a woid fiom the designated list. lf so, we iemove that woid
fiom *unparsed* and ietuin the woid togethei with its pait of speech
(which is found at the head of the list)
1
(define (parse-word word-list)
(require (not (null? *unparsed*)))
(require (memq (car *unparsed*) (cdr word-list)))
(let ((found-word (car *unparsed*)))
(set! *unparsed* (cdr *unparsed*))
(list (car word-list) found-word)))
To stait the paising, all we need to do is set *unparsed* to be the entiie
input, tiy to paise a sentence, and check that nothing is lef ovei
(define *unparsed* '())
(define (parse input)
(set! *unparsed* input)
(let ((sent (parse-sentence)))
(require (null? *unparsed*)) sent))
We can now tiy the paisei and veiify that it woiks foi oui simple test
sentence
1
Notice that parse-word uses set! to modify the unpaised input list. loi this to
woik, oui amb evaluatoi must undo the eects of set! opeiations when it backtiacks.
3
;;; Amb-Eval input:
(parse '(the cat eats))
;;; Starting a new problem
;;; Amb-Eval value:
(sentence (noun-phrase (article the) (noun cat)) (verb eats))
Te amb evaluatoi is useful heie because it is convenient to expiess
the paising constiaints with the aid of require. Automatic seaich and
backtiacking ieally pay o, howevei, when we considei moie complex
giammais wheie theie aie choices foi how the units can be decom-
posed.
Lets add to oui giammai a list of piepositions
(define prepositions '(prep for to in by with))
and dene a piepositional phiase (e.g., foi the cat) to be a pieposition
followed by a noun phiase
(define (parse-prepositional-phrase)
(list 'prep-phrase
(parse-word prepositions)
(parse-noun-phrase)))
Now we can dene a sentence to be a noun phiase followed by a veib
phiase, wheie a veib phiase can be eithei a veib oi a veib phiase ex-
tended by a piepositional phiase
2
(define (parse-sentence)
(list 'sentence (parse-noun-phrase) (parse-verb-phrase)))
(define (parse-verb-phrase)
(define (maybe-extend verb-phrase)
(amb verb-phrase
2
Obseive that this denition is iecuisivea veib may be followed by any numbei
of piepositional phiases.
4
(maybe-extend
(list 'verb-phrase
verb-phrase
(parse-prepositional-phrase)))))
(maybe-extend (parse-word verbs)))
While weie at it, we can also elaboiate the denition of noun phiases
to peimit such things as a cat in the class. What we used to call a
noun phiase, well now call a simple noun phiase, and a noun phiase
will now be eithei a simple noun phiase oi a noun phiase extended by
a piepositional phiase
(define (parse-simple-noun-phrase)
(list 'simple-noun-phrase
(parse-word articles)
(parse-word nouns)))
(define (parse-noun-phrase)
(define (maybe-extend noun-phrase)
(amb noun-phrase
(maybe-extend
(list 'noun-phrase
noun-phrase
(parse-prepositional-phrase)))))
(maybe-extend (parse-simple-noun-phrase)))
Oui new giammai lets us paise moie complex sentences. loi example
(parse '(the student with the cat sleeps in the class))
pioduces
(sentence
(noun-phrase
(simple-noun-phrase (article the) (noun student))
(prep-phrase
(prep with)
(noun-phrase
(simple-noun-phrase (article the) (noun student))
(prep-phrase
(prep with)
(simple-noun-phrase (article the) (noun cat)))))))
Exercise 4.45: With the giammai given above, the follow-
ing sentence can be paised in ve dieient ways Te pio-
fessoi lectuies to the student in the class with the cat. Give
the ve paises and explain the dieiences in shades of mean-
ing among them.
Exercise 4.46: Te evaluatois in Section 4.1 and Section 4.2
do not deteimine what oidei opeiands aie evaluated in. We
will see that the amb evaluatoi evaluates them fiom lef to
iight. Explain why oui paising piogiam wouldnt woik if
the opeiands weie evaluated in some othei oidei.
Exercise 4.47: Louis Reasonei suggests that, since a veib
phiase is eithei a veib oi a veib phiase followed by a piepo-
sitional phiase, it would be much moie stiaightfoiwaid to
dene the pioceduie parse-verb-phrase as follows (and
similaily foi noun phiases)
(define (parse-verb-phrase)
(amb (parse-word verbs)
(list 'verb-phrase
(parse-verb-phrase)
(parse-prepositional-phrase))))
Does this woik` Does the piogiams behavioi change if we
inteichange the oidei of expiessions in the amb`
As in the analyz-
ing evaluatoi, evaluation of an expiession is accomplished by calling an
execution pioceduie pioduced by analysis of that expiession. Te dif-
feience between the inteipietation of oidinaiy Scheme and the intei-
pietation of nondeteiministic Scheme will be entiiely in the execution
pioceduies.
Execution procedures and continuations
Recall that the execution pioceduies foi the oidinaiy evaluatoi take one
aigument the enviionment of execution. ln contiast, the execution pio-
ceduies in the amb evaluatoi take thiee aiguments the enviionment,
and two pioceduies called con:nvo:on roceJvres. Te evaluation of
an expiession will nish by calling one of these two continuations lf
the evaluation iesults in a value, the svccess con:nvo:on is called with
that value, if the evaluation iesults in the discoveiy of a dead end, the
[o:|vre con:nvo:on is called. Constiucting and calling appiopiiate con-
tinuations is the mechanism by which the nondeteiministic evaluatoi
implements backtiacking.
lt is the job of the success continuation to ieceive a value and pio-
ceed with the computation. Along with that value, the success contin-
uation is passed anothei failuie continuation, which is to be called sub-
sequently if the use of that value leads to a dead end.
We assume that the evaluatoi suppoits let (see Exeicise 4.22), which we have used
in oui nondeteiministic piogiams.
81
(define (amb? exp) (tagged-list? exp 'amb))
(define (amb-choices exp) (cdr exp))
We must also add to the dispatch in analyze a clause that will iecognize
this special foim and geneiate an appiopiiate execution pioceduie
((amb? exp) (analyze-amb exp))
Te top-level pioceduie ambeval (similai to the veision of eval given
in Section 4.1.) analyzes the given expiession and applies the iesulting
execution pioceduie to the given enviionment, togethei with two given
continuations
(define (ambeval exp env succeed fail)
((analyze exp) env succeed fail))
A success continuation is a pioceduie of two aiguments the value just
obtained and anothei failuie continuation to be used if that value leads
to a subsequent failuie. A failuie continuation is a pioceduie of no ai-
guments. So the geneial foim of an execution pioceduie is
(lambda (env succeed fail)
,, succeed is (lambda (value fail) . . .)
,, fail is (lambda () . . .)
. . .)
loi example, executing
(ambeval exp
the-global-environment
(lambda (value fail) value)
(lambda () 'failed))
will auempt to evaluate the given expiession and will ietuin eithei the
expiessions value (if the evaluation succeeds) oi the symbol failed (if
the evaluation fails). Te call to ambeval in the diivei loop shown below
82
uses much moie complicated continuation pioceduies, which continue
the loop and suppoit the try-again iequest.
Most of the complexity of the amb evaluatoi iesults fiom the me-
chanics of passing the continuations aiound as the execution pioce-
duies call each othei. ln going thiough the following code, you should
compaie each of the execution pioceduies with the coiiesponding pio-
ceduie foi the oidinaiy evaluatoi given in Section 4.1..
Simple expressions
Te execution pioceduies foi the simplest kinds of expiessions aie es-
sentially the same as those foi the oidinaiy evaluatoi, except foi the
need to manage the continuations. Te execution pioceduies simply
succeed with the value of the expiession, passing along the failuie con-
tinuation that was passed to them.
(define (analyze-self-evaluating exp)
(lambda (env succeed fail)
(succeed exp fail)))
(define (analyze-quoted exp)
(let ((qval (text-of-quotation exp)))
(lambda (env succeed fail)
(succeed qval fail))))
(define (analyze-variable exp)
(lambda (env succeed fail)
(succeed (lookup-variable-value exp env) fail)))
(define (analyze-lambda exp)
(let ((vars (lambda-parameters exp))
(bproc (analyze-sequence (lambda-body exp))))
(lambda (env succeed fail)
(succeed (make-procedure vars bproc env) fail))))
83
Notice that looking up a vaiiable always succeeds. lf lookup-variable-
value fails to nd the vaiiable, it signals an eiioi, as usual. Such a fail-
uie indicates a piogiam buga iefeience to an unbound vaiiable, it is
not an indication that we should tiy anothei nondeteiministic choice
instead of the one that is cuiiently being tiied.
Conditionals and sequences
Conditionals aie also handled in a similai way as in the oidinaiy eval-
uatoi. Te execution pioceduie geneiated by analyze-if invokes the
piedicate execution pioceduie pproc with a success continuation that
checks whethei the piedicate value is tiue and goes on to execute ei-
thei the consequent oi the alteinative. lf the execution of pproc fails,
the oiiginal failuie continuation foi the if expiession is called.
(define (analyze-if exp)
(let ((pproc (analyze (if-predicate exp)))
(cproc (analyze (if-consequent exp)))
(aproc (analyze (if-alternative exp))))
(lambda (env succeed fail)
(pproc env
,, success continuation foi evaluating the piedicate
,, to obtain pred-value
(lambda (pred-value fail2)
(if (true? pred-value)
(cproc env succeed fail2)
(aproc env succeed fail2)))
,, failuie continuation foi evaluating the piedicate
fail))))
Sequences aie also handled in the same way as in the pievious evaluatoi,
except foi the machinations in the subpioceduie sequentially that aie
iequiied foi passing the continuations. Namely, to sequentially execute
84
a and then b, we call a with a success continuation that calls b.
(define (analyze-sequence exps)
(define (sequentially a b)
(lambda (env succeed fail)
(a env
,, success continuation foi calling a
(lambda (a-value fail2)
(b env succeed fail2))
,, failuie continuation foi calling a
fail)))
(define (loop first-proc rest-procs)
(if (null? rest-procs)
first-proc
(loop (sequentially first-proc
(car rest-procs))
(cdr rest-procs))))
(let ((procs (map analyze exps)))
(if (null? procs)
(error "Empty sequence: ANALYZE"))
(loop (car procs) (cdr procs))))
Definitions and assignments
Denitions aie anothei case wheie we must go to some tiouble to man-
age the continuations, because it is necessaiy to evaluate the denition-
value expiession befoie actually dening the new vaiiable. To accom-
plish this, the denition-value execution pioceduie vproc is called with
the enviionment, a success continuation, and the failuie continuation.
lf the execution of vproc succeeds, obtaining a value val foi the dened
vaiiable, the vaiiable is dened and the success is piopagated
(define (analyze-definition exp)
8
(let ((var (definition-variable exp))
(vproc (analyze (definition-value exp))))
(lambda (env succeed fail)
(vproc env
(lambda (val fail2)
(define-variable! var val env)
(succeed 'ok fail2))
fail))))
Assignments aie moie inteiesting. Tis is the ist place wheie we ieally
use the continuations, iathei than just passing them aiound. Te exe-
cution pioceduie foi assignments staits out like the one foi denitions.
lt ist auempts to obtain the new value to be assigned to the vaiiable.
lf this evaluation of vproc fails, the assignment fails.
lf vproc succeeds, howevei, and we go on to make the assignment,
we must considei the possibility that this bianch of the computation
might latei fail, which will iequiie us to backtiack out of the assign-
ment. Tus, we must aiiange to undo the assignment as pait of the
backtiacking piocess.
We didnt woiiy about undoing denitions, since we can assume that inteinal def-
initions aie scanned out (Section 4.1.).
8
(define (analyze-assignment exp)
(let ((var (assignment-variable exp))
(vproc (analyze (assignment-value exp))))
(lambda (env succeed fail)
(vproc env
(lambda (val fail2) , *1*
(let ((old-value
(lookup-variable-value var env)))
(set-variable-value! var val env)
(succeed 'ok
(lambda () , *2*
(set-variable-value!
var old-value env)
(fail2)))))
fail))))
Procedure applications
Te execution pioceduie foi applications contains no new ideas except
foi the technical complexity of managing the continuations. Tis com-
plexity aiises in analyze-application, due to the need to keep tiack of
the success and failuie continuations as we evaluate the opeiands. We
use a pioceduie get-args to evaluate the list of opeiands, iathei than
a simple map as in the oidinaiy evaluatoi.
(define (analyze-application exp)
(let ((fproc (analyze (operator exp)))
(aprocs (map analyze (operands exp))))
(lambda (env succeed fail)
(fproc env
(lambda (proc fail2)
(get-args aprocs
env
8
(lambda (args fail3)
(execute-application
proc args succeed fail3))
fail2))
fail))))
ln get-args, notice how cdr-ing down the list of aproc execution pio-
ceduies and consing up the iesulting list of args is accomplished by
calling each aproc in the list with a success continuation that iecui-
sively calls get-args. Each of these iecuisive calls to get-args has a
success continuation whose value is the cons of the newly obtained ai-
gument onto the list of accumulated aiguments
(define (get-args aprocs env succeed fail)
(if (null? aprocs)
(succeed '() fail)
((car aprocs)
env
;;success continuation foi this aproc
(lambda (arg fail2)
(get-args
(cdr aprocs)
env
;;success continuation foi
;;iecuisive call to get-args
(lambda (args fail3)
(succeed (cons arg args) fail3))
fail2))
fail)))
Te actual pioceduie application, which is peifoimed by execute-appli-
cation, is accomplished in the same way as foi the oidinaiy evaluatoi,
except foi the need to manage the continuations.
88
(define (execute-application proc args succeed fail)
(cond ((primitive-procedure? proc)
(succeed (apply-primitive-procedure proc args)
fail))
((compound-procedure? proc)
((procedure-body proc)
(extend-environment
(procedure-parameters proc)
args
(procedure-environment proc))
succeed
fail))
(else
(error "Unknown procedure type: EXECUTE-APPLICATION"
proc))))
Evaluating amb expressions
Te amb special foim is the key element in the nondeteiministic lan-
guage. Heie we see the essence of the inteipietation piocess and the
ieason foi keeping tiack of the continuations. Te execution pioceduie
foi amb denes a loop try-next that cycles thiough the execution pio-
ceduies foi all the possible values of the amb expiession. Each execution
pioceduie is called with a failuie continuation that will tiy the next one.
When theie aie no moie alteinatives to tiy, the entiie amb expiession
fails.
(define (analyze-amb exp)
(let ((cprocs (map analyze (amb-choices exp))))
(lambda (env succeed fail)
(define (try-next choices)
(if (null? choices)
(fail)
89
((car choices)
env
succeed
(lambda () (try-next (cdr choices))))))
(try-next cprocs))))
Driver loop
Te diivei loop foi the amb evaluatoi is complex, due to the mecha-
nism that peimits the usei to tiy again in evaluating an expiession. Te
diivei uses a pioceduie called internal-loop, which takes as aigument
a pioceduie try-again. Te intent is that calling try-again should go
on to the next untiied alteinative in the nondeteiministic evaluation.
Internal-loop eithei calls try-again in iesponse to the usei typing
try-again at the diivei loop, oi else staits a new evaluation by calling
ambeval.
Te failuie continuation foi this call to ambeval infoims the usei
that theie aie no moie values and ie-invokes the diivei loop.
Te success continuation foi the call to ambeval is moie subtle.
We piint the obtained value and then invoke the inteinal loop again
with a try-again pioceduie that will be able to tiy the next alteina-
tive. Tis next-alternative pioceduie is the second aigument that was
passed to the success continuation. Oidinaiily, we think of this second
aigument as a failuie continuation to be used if the cuiient evaluation
bianch latei fails. ln this case, howevei, we have completed a successful
evaluation, so we can invoke the failuie alteinative bianch in oidei to
seaich foi additional successful evaluations.
(define input-prompt ";;; Amb-Eval input:")
(define output-prompt ";;; Amb-Eval value:")
90
(define (driver-loop)
(define (internal-loop try-again)
(prompt-for-input input-prompt)
(let ((input (read)))
(if (eq? input 'try-again)
(try-again)
(begin
(newline) (display ";;; Starting a new problem ")
(ambeval
input
the-global-environment
;; ambeval success
(lambda (val next-alternative)
(announce-output output-prompt)
(user-print val)
(internal-loop next-alternative))
;; ambeval failuie
(lambda ()
(announce-output
";;; There are no more values of")
(user-print input)
(driver-loop)))))))
(internal-loop
(lambda ()
(newline) (display ";;; There is no current problem")
(driver-loop))))
Te initial call to internal-loop uses a try-again pioceduie that com-
plains that theie is no cuiient pioblem and iestaits the diivei loop. Tis
is the behavioi that will happen if the usei types try-again when theie
is no evaluation in piogiess.
Exercise 4.50: lmplement a new special foim ramb that is
91
like amb except that it seaiches alteinatives in a iandom oi-
dei, iathei than fiom lef to iight. Show how this can help
with Alyssas pioblem in Exeicise 4.49.
Exercise 4.51: lmplement a new kind of assignment called
permanent-set! that is not undone upon failuie. loi ex-
ample, we can choose two distinct elements fiom a list and
count the numbei of tiials iequiied to make a successful
choice as follows
(define count 0)
(let ((x (an-element-of '(a b c)))
(y (an-element-of '(a b c))))
(permanent-set! count (+ count 1))
(require (not (eq? x y)))
(list x y count))
;;; Starting a new problem
;;; Amb-Eval value:
(a b 2)
;;; Amb-Eval input:
try-again
;;; Amb-Eval value:
(a c 3)
What values would have been displayed if we had used
set! heie iathei than permanent-set! `
Exercise 4.52: lmplement a new constiuct called if-fail
that peimits the usei to catch the failuie of an expiession.
If-fail takes two expiessions. lt evaluates the ist expies-
sion as usual and ietuins as usual if the evaluation suc-
ceeds. lf the evaluation fails, howevei, the value of the sec-
ond expiession is ietuined, as in the following example
92
;;; Amb-Eval input:
(if-fail (let ((x (an-element-of '(1 3 5))))
(require (even? x))
x)
'all-odd)
;;; Starting a new problem
;;; Amb-Eval value:
all-odd
;;; Amb-Eval input:
(if-fail (let ((x (an-element-of '(1 3 5 8))))
(require (even? x))
x)
'all-odd)
;;; Starting a new problem
;;; Amb-Eval value:
8
Exercise 4.53: With permanent-set! as desciibed in Exei-
cise 4.1 and if-fail as in Exeicise 4.2, what will be the
iesult of evaluating
(let ((pairs '()))
(if-fail
(let ((p (prime-sum-pair '(1 3 5 8)
'(20 35 110))))
(permanent-set! pairs (cons p pairs))
(amb))
pairs))
Exercise 4.54: lf we had not iealized that require could be
implemented as an oidinaiy pioceduie that uses amb, to be
dened by the usei as pait of a nondeteiministic piogiam,
93
we would have had to implement it as a special foim. Tis
would iequiie syntax pioceduies
(define (require? exp)
(tagged-list? exp 'require))
(define (require-predicate exp)
(cadr exp))
and a new clause in the dispatch in analyze
((require? exp) (analyze-require exp))
as well the pioceduie analyze-require that handles require
expiessions. Complete the following denition of analyze-
require.
(define (analyze-require exp)
(let ((pproc (analyze (require-predicate exp))))
(lambda (env succeed fail)
(pproc env
(lambda (pred-value fail2)
(if ??
??
(succeed 'ok fail2)))
fail))))
4.4 Logic Programming
ln Chaptei 1 we stiessed that computei science deals with impeiative
(how to) knowledge, wheieas mathematics deals with declaiative (what
is) knowledge. lndeed, piogiamming languages iequiie that the pio-
giammei expiess knowledge in a foim that indicates the step-by-step
methods foi solving paiticulai pioblems. On the othei hand, high-level
94
languages piovide, as pait of the language implementation, a substantial
amount of methodological knowledge that fiees the usei fiom concein
with numeious details of how a specied computation will piogiess.
Most piogiamming languages, including Lisp, aie oiganized aiound
computing the values of mathematical functions. Expiession-oiiented
languages (such as Lisp, loitian, and Algol) capitalize on the pun that
an expiession that desciibes the value of a function may also be intei-
pieted as a means of computing that value. Because of this, most pio-
giamming languages aie stiongly biased towaid unidiiectional compu-
tations (computations with well-dened inputs and outputs). Teie aie,
howevei, iadically dieient piogiamming languages that ielax this bias.
We saw one such example in Section 3.3., wheie the objects of compu-
tation weie aiithmetic constiaints. ln a constiaint system the diiection
and the oidei of computation aie not so well specied, in caiiying out a
computation the system must theiefoie piovide moie detailed how to
knowledge than would be the case with an oidinaiy aiithmetic compu-
tation. Tis does not mean, howevei, that the usei is ieleased altogethei
fiom the iesponsibility of pioviding impeiative knowledge. Teie aie
many constiaint netwoiks that implement the same set of constiaints,
and the usei must choose fiom the set of mathematically equivalent
netwoiks a suitable netwoik to specify a paiticulai computation.
Te nondeteiministic piogiam evaluatoi of Section 4.3 also moves
away fiomthe viewthat piogiamming is about constiucting algoiithms
foi computing unidiiectional functions. ln a nondeteiministic language,
expiessions can have moie than one value, and, as a iesult, the compu-
tation is dealing with ielations iathei than with single-valued functions.
Logic piogiamming extends this idea by combining a ielational vision
of piogiamming with a poweiful kind of symbolic pauein matching
9
called vn:co:on.
8
Tis appioach, when it woiks, can be a veiy poweiful way to wiite
piogiams. Pait of the powei comes fiom the fact that a single what is
fact can be used to solve a numbei of dieient pioblems that would have
dieient how to components. As an example, considei the append op-
eiation, which takes two lists as aiguments and combines theii elements
to foim a single list. ln a pioceduial language such as Lisp, we could
dene append in teims of the basic list constiuctoi cons, as we did in
Section 2.2.1
8
Logic piogiamming has giown out of a long histoiy of ieseaich in automatic the-
oiem pioving. Eaily theoiem-pioving piogiams could accomplish veiy liule, because
they exhaustively seaiched the space of possible pioofs. Te majoi bieakthiough that
made such a seaich plausible was the discoveiy in the eaily 190s of the vn:co:on
o|gor:| and the reso|v:on r:nc:|e (Robinson 19). Resolution was used, foi exam-
ple, by Gieen and Raphael (198) (see also Gieen 199) as the basis foi a deductive
question-answeiing system. Duiing most of this peiiod, ieseaicheis concentiated on
algoiithms that aie guaianteed to nd a pioof if one exists. Such algoiithms weie dif-
cult to contiol and to diiect towaid a pioof. Hewiu (199) iecognized the possibility
of meiging the contiol stiuctuie of a piogiamming language with the opeiations of a
logic-manipulation system, leading to the woik in automatic seaich mentioned in Sec-
tion 4.3.1 (lootnote 4.4). At the same time that this was being done, Colmeiauei, in
Maiseille, was developing iule-based systems foi manipulating natuial language (see
Colmeiauei et al. 193). He invented a piogiamming language called Piolog foi iepie-
senting those iules. Kowalski (193, 199), in Edinbuigh, iecognized that execution of
a Piolog piogiam could be inteipieted as pioving theoiems (using a pioof technique
called lineai Hoin-clause iesolution). Te meiging of the last two stiands led to the
logic-piogiamming movement. Tus, in assigning ciedit foi the development of logic
piogiamming, the liench can point to Piologs genesis at the Univeisity of Maiseille,
while the Biitish can highlight the woik at the Univeisity of Edinbuigh. Accoiding to
people at xi1, logic piogiamming was developed by these gioups in an auempt to g-
uie out what Hewiu was talking about in his biilliant but impenetiable Ph.D. thesis.
loi a histoiy of logic piogiamming, see Robinson 1983.
9
(define (append x y)
(if (null? x) y (cons (car x) (append (cdr x) y))))
Tis pioceduie can be iegaided as a tianslation into Lisp of the follow-
ing two iules, the ist of which coveis the case wheie the ist list is
empty and the second of which handles the case of a nonempty list,
which is a cons of two paits
loi any list y, the empty list and y append to foim y.
loi any u, v, y, and z, (cons u v) and y append to foim (cons u
z) if v and y append to foim z.
9
Using the append pioceduie, we can answei questions such as
lind the append of (a b) and (c d).
But the same two iules aie also sucient foi answeiing the following
soits of questions, which the pioceduie cant answei
lind a list y that appends with (a b) to pioduce (a b c d).
lind all x and y that append to foim (a b c d).
ln a logic piogiamming language, the piogiammei wiites an append
pioceduie by stating the two iules about append given above. How
to knowledge is piovided automatically by the inteipietei to allow this
9
To see the coiiespondence between the iules and the pioceduie, let x in the pio-
ceduie (wheie x is nonempty) coiiespond to (cons u v) in the iule. Ten z in the iule
coiiesponds to the append of (cdr x) and y.
9
single paii of iules to be used to answei all thiee types of questions
about append.
0
Contempoiaiy logic piogiamming languages (including the one we
implement heie) have substantial deciencies, in that theii geneial how
to methods can lead them into spuiious innite loops oi othei unde-
siiable behavioi. Logic piogiamming is an active eld of ieseaich in
computei science.
1
Eailiei in this chaptei we exploied the technology of implementing
inteipieteis and desciibed the elements that aie essential to an intei-
pietei foi a Lisp-like language (indeed, to an inteipietei foi any con-
ventional language). Now we will apply these ideas to discuss an in-
teipietei foi a logic piogiamming language. We call this language the
qvery |ongvoge, because it is veiy useful foi ietiieving infoimation fiom
data bases by foimulating qver:es, oi questions, expiessed in the lan-
guage. Even though the queiy language is veiy dieient fiom Lisp, we
0
Tis ceitainly does not ielieve the usei of the entiie pioblem of how to compute
the answei. Teie aie many dieient mathematically equivalent sets of iules foi foi-
mulating the append ielation, only some of which can be tuined into eective devices
foi computing in any diiection. ln addition, sometimes what is infoimation gives no
clue how to compute an answei. loi example, considei the pioblem of computing the
y such that y
2
= x.
1
lnteiest in logic piogiamming peaked duiing the eaily 80s when the Japanese gov-
einment began an ambitious pioject aimed at building supeifast computeis optimized
to iun logic piogiamming languages. Te speed of such computeis was to be measuied
in LlPS (Logical lnfeiences Pei Second) iathei than the usual lLOPS (lLoating-point
Opeiations Pei Second). Although the pioject succeeded in developing haidwaie and
sofwaie as oiiginally planned, the inteinational computei industiy moved in a dif-
feient diiection. See leigenbaum and Shiobe 1993 foi an oveiview evaluation of the
Japanese pioject. Te logic piogiamming community has also moved on to considei
ielational piogiamming based on techniques othei than simple pauein matching, such
as the ability to deal with numeiical constiaints such as the ones illustiated in the
constiaint-piopagation system of Section 3.3..
98
will nd it convenient to desciibe the language in teims of the same gen-
eial fiamewoik we have been using all along as a collection of piimitive
elements, togethei with means of combination that enable us to com-
bine simple elements to cieate moie complex elements and means of ab-
stiaction that enable us to iegaid complex elements as single conceptual
units. An inteipietei foi a logic piogiamming language is consideiably
moie complex than an inteipietei foi a language like Lisp. Neveithe-
less, we will see that oui queiy-language inteipietei contains many of
the same elements found in the inteipietei of Section 4.1. ln paiticu-
lai, theie will be an eval pait that classies expiessions accoiding to
type and an apply pait that implements the languages abstiaction
mechanism (pioceduies in the case of Lisp, and rv|es in the case of logic
piogiamming). Also, a cential iole is played in the implementation by
a fiame data stiuctuie, which deteimines the coiiespondence between
symbols and theii associated values. One additional inteiesting aspect
of oui queiy-language implementation is that we make substantial use
of stieams, which weie intioduced in Chaptei 3.
4.4.1 Deductive Information Retrieval
Logic piogiamming excels in pioviding inteifaces to data bases foi in-
foimation ietiieval. Te queiy language we shall implement in this chap-
tei is designed to be used in this way.
ln oidei to illustiate what the queiy system does, we will show how
it can be used to manage the data base of peisonnel iecoids foi Mi-
cioshaf, a thiiving high-technology company in the Boston aiea. Te
language piovides pauein-diiected access to peisonnel infoimation and
can also take advantage of geneial iules in oidei to make logical deduc-
tions.
99
A sample data base
Te peisonnel data base foi Micioshaf contains osser:ons about com-
pany peisonnel. Heie is the infoimation about Ben Bitdiddle, the iesi-
dent computei wizaid
(address (Bitdiddle Ben) (Slumerville (Ridge Road) 10))
(job (Bitdiddle Ben) (computer wizard))
(salary (Bitdiddle Ben) 60000)
Each asseition is a list (in this case a tiiple) whose elements can them-
selves be lists.
As iesident wizaid, Ben is in chaige of the companys computei
division, and he supeivises two piogiammeis and one technician. Heie
is the infoimation about them
(address (Hacker Alyssa P) (Cambridge (Mass Ave) 78))
(job (Hacker Alyssa P) (computer programmer))
(salary (Hacker Alyssa P) 40000)
(supervisor (Hacker Alyssa P) (Bitdiddle Ben))
(address (Fect Cy D) (Cambridge (Ames Street) 3))
(job (Fect Cy D) (computer programmer))
(salary (Fect Cy D) 35000)
(supervisor (Fect Cy D) (Bitdiddle Ben))
(address (Tweakit Lem E) (Boston (Bay State Road) 22))
(job (Tweakit Lem E) (computer technician))
(salary (Tweakit Lem E) 25000)
(supervisor (Tweakit Lem E) (Bitdiddle Ben))
Teie is also a piogiammei tiainee, who is supeivised by Alyssa
(address (Reasoner Louis) (Slumerville (Pine Tree Road) 80))
(job (Reasoner Louis) (computer programmer trainee))
00
(salary (Reasoner Louis) 30000)
(supervisor (Reasoner Louis) (Hacker Alyssa P))
All of these people aie in the computei division, as indicated by the
woid computer as the ist item in theii job desciiptions.
Ben is a high-level employee. His supeivisoi is the companys big
wheel himself
(supervisor (Bitdiddle Ben) (Warbucks Oliver))
(address (Warbucks Oliver) (Swellesley (Top Heap Road)))
(job (Warbucks Oliver) (administration big wheel))
(salary (Warbucks Oliver) 150000)
Besides the computei division supeivised by Ben, the company has an
accounting division, consisting of a chief accountant and his assistant
(address (Scrooge Eben) (Weston (Shady Lane) 10))
(job (Scrooge Eben) (accounting chief accountant))
(salary (Scrooge Eben) 75000)
(supervisor (Scrooge Eben) (Warbucks Oliver))
(address (Cratchet Robert) (Allston (N Harvard Street) 16))
(job (Cratchet Robert) (accounting scrivener))
(salary (Cratchet Robert) 18000)
(supervisor (Cratchet Robert) (Scrooge Eben))
Teie is also a secietaiy foi the big wheel
(address (Aull DeWitt) (Slumerville (Onion Square) 5))
(job (Aull DeWitt) (administration secretary))
(salary (Aull DeWitt) 25000)
(supervisor (Aull DeWitt) (Warbucks Oliver))
Te data base also contains asseitions about which kinds of jobs can be
done by people holding othei kinds of jobs. loi instance, a computei
01
wizaid can do the jobs of both a computei piogiammei and a computei
technician
(can-do-job (computer wizard) (computer programmer))
(can-do-job (computer wizard) (computer technician))
A computei piogiammei could ll in foi a tiainee
(can-do-job (computer programmer)
(computer programmer trainee))
Also, as is well known,
(can-do-job (administration secretary)
(administration big wheel))
Simple queries
Te queiy language allows useis to ietiieve infoimation fiom the data
base by posing queiies in iesponse to the systems piompt. loi example,
to nd all computei piogiammeis one can say
;;; Query input:
(job ?x (computer programmer))
Te system will iespond with the following items
;;; Query results:
(job (Hacker Alyssa P) (computer programmer))
(job (Fect Cy D) (computer programmer))
Te input queiy species that we aie looking foi entiies in the data base
that match a ceitain ouern. ln this example, the pauein species en-
tiies consisting of thiee items, of which the ist is the liteial symbol job,
the second can be anything, and the thiid is the liteial list (computer
programmer). Te anything that can be the second item in the match-
ing list is specied by a ouern +or:o||e, ?x. Te geneial foimof a pauein
02
vaiiable is a symbol, taken to be the name of the vaiiable, pieceded by
a question maik. We will see below why it is useful to specify names
foi pauein vaiiables iathei than just puuing ? into paueins to iepie-
sent anything. Te system iesponds to a simple queiy by showing all
entiies in the data base that match the specied pauein.
A pauein can have moie than one vaiiable. loi example, the queiy
(address ?x ?y)
will list all the employees addiesses.
A pauein can have no vaiiables, in which case the queiy simply
deteimines whethei that pauein is an entiy in the data base. lf so, theie
will be one match, if not, theie will be no matches.
Te same pauein vaiiable can appeai moie than once in a queiy,
specifying that the same anything must appeai in each position. Tis
is why vaiiables have names. loi example,
(supervisor ?x ?x)
nds all people who supeivise themselves (though theie aie no such
asseitions in oui sample data base).
Te queiy
(job ?x (computer ?type))
matches all job entiies whose thiid itemis a two-element list whose ist
item is computer
(job (Bitdiddle Ben) (computer wizard))
(job (Hacker Alyssa P) (computer programmer))
(job (Fect Cy D) (computer programmer))
(job (Tweakit Lem E) (computer technician))
Tis same pauein does no match
(job (Reasoner Louis) (computer programmer trainee))
03
because the thiid item in the entiy is a list of thiee elements, and the
paueins thiid item species that theie should be two elements. lf we
wanted to change the pauein so that the thiid item could be any list
beginning with computer, we could specify
2
(job ?x (computer . ?type))
loi example,
(computer . ?type)
matches the data
(computer programmer trainee)
with ?type as the list (programmer trainee). lt also matches the data
(computer programmer)
with ?type as the list (programmer), and matches the data
(computer)
with ?type as the empty list ().
We can desciibe the queiy languages piocessing of simple queiies
as follows
Te system nds all assignments to vaiiables in the queiy pauein
that so:s[y the paueinthat is, all sets of values foi the vaiiables
such that if the pauein vaiiables aie :nson:oeJ +:| (ieplaced
by) the values, the iesult is in the data base.
Te system iesponds to the queiy by listing all instantiations of
the queiy pauein with the vaiiable assignments that satisfy it.
2
Tis uses the doued-tail notation intioduced in Exeicise 2.20.
04
Note that if the pauein has no vaiiables, the queiy ieduces to a detei-
mination of whethei that pauein is in the data base. lf so, the empty
assignment, which assigns no values to vaiiables, satises that pauein
foi that data base.
Exercise 4.55: Give simple queiies that ietiieve the follow-
ing infoimation fiom the data base
1. all people supeivised by Ben Bitdiddle,
2. the names and jobs of all people in the accounting di-
vision,
3. the names and addiesses of all people who live in Slumeiville.
Compound queries
Simple queiies foim the piimitive opeiations of the queiy language.
ln oidei to foim compound opeiations, the queiy language piovides
means of combination. One thing that makes the queiy language a logic
piogiamming language is that the means of combination miiioi the
means of combination used in foiming logical expiessions and, or, and
not. (Heie and, or, and not aie not the Lisp piimitives, but iathei opei-
ations built into the queiy language.)
We can use and as follows to nd the addiesses of all the computei
piogiammeis
(and (job ?person (computer programmer))
(address ?person ?where))
Te iesulting output is
(and (job (Hacker Alyssa P) (computer programmer))
(address (Hacker Alyssa P) (Cambridge (Mass Ave) 78)))
0
(and (job (Fect Cy D) (computer programmer))
(address (Fect Cy D) (Cambridge (Ames Street) 3)))
ln geneial,
(and query
1
query
2
. . . query
n
)
is satised by all sets of values foi the pauein vaiiables that simultane-
ously satisfy query
1
. . . query
n
.
As foi simple queiies, the system piocesses a compound queiy by
nding all assignments to the pauein vaiiables that satisfy the queiy,
then displaying instantiations of the queiy with those values.
Anothei means of constiucting compound queiies is thiough or.
loi example,
(or (supervisor ?x (Bitdiddle Ben))
(supervisor ?x (Hacker Alyssa P)))
will nd all employees supeivised by Ben Bitdiddle oi Alyssa P. Hackei
(or (supervisor (Hacker Alyssa P) (Bitdiddle Ben))
(supervisor (Hacker Alyssa P) (Hacker Alyssa P)))
(or (supervisor (Fect Cy D) (Bitdiddle Ben))
(supervisor (Fect Cy D) (Hacker Alyssa P)))
(or (supervisor (Tweakit Lem E) (Bitdiddle Ben))
(supervisor (Tweakit Lem E) (Hacker Alyssa P)))
(or (supervisor (Reasoner Louis) (Bitdiddle Ben))
(supervisor (Reasoner Louis) (Hacker Alyssa P)))
ln geneial,
(or query
1
query
2
. . . query
n
)
is satised by all sets of values foi the pauein vaiiables that satisfy at
least one of query
1
. . . query
n
.
Compound queiies can also be foimed with not. loi example,
0
(and (supervisor ?x (Bitdiddle Ben))
(not (job ?x (computer programmer))))
nds all people supeivised by Ben Bitdiddle who aie not computei pio-
giammeis. ln geneial,
(not query
1
)
is satised by all assignments to the pauein vaiiables that do not satisfy
query
1
.
3
Te nal combining foim is called lisp-value. When lisp-value
is the ist element of a pauein, it species that the next element is a
Lisp piedicate to be applied to the iest of the (instantiated) elements as
aiguments. ln geneial,
(lisp-value predicate ar
1
. . . ar
n
)
will be satised by assignments to the pauein vaiiables foi which the
reJ:coe applied to the instantiated ar
1
. . . ar
n
is tiue. loi ex-
ample, to nd all people whose salaiy is gieatei than S30,000 we could
wiite
4
(and (salary ?person ?amount) (lisp-value > ?amount 30000))
Exercise 4.56: loimulate compound queiies that ietiieve
the following infoimation
3
Actually, this desciiption of not is valid only foi simple cases. Te ieal behavioi of
not is moie complex. We will examine nots peculiaiities in sections Section 4.4.2 and
Section 4.4.3.
4
Lisp-value should be used only to peifoiman opeiation not piovided in the queiy
language. ln paiticulai, it should not be used to test equality (since that is what the
matching in the queiy language is designed to do) oi inequality (since that can be done
with the same iule shown below).
0
a. the names of all people who aie supeivised by Ben
Bitdiddle, togethei with theii addiesses,
b. all people whose salaiy is less than Ben Bitdiddles,
togethei with theii salaiy and Ben Bitdiddles salaiy,
c. all people who aie supeivised by someone who is not
in the computei division, togethei with the supeivi-
sois name and job.
Rules
ln addition to piimitive queiies and compound queiies, the queiy lan-
guage piovides means foi abstiacting queiies. Tese aie given by rv|es.
Te iule
(rule (lives-near ?person-1 ?person-2)
(and (address ?person-1 (?town . ?rest-1))
(address ?person-2 (?town . ?rest-2))
(not (same ?person-1 ?person-2))))
species that two people live neai each othei if they live in the same
town. Te nal not clause pievents the iule fiom saying that all peo-
ple live neai themselves. Te same ielation is dened by a veiy simple
iule
Notice that we do not need same in oidei to make two things be the same We
just use the same pauein vaiiable foi eachin eect, we have one thing instead of two
things in the ist place. loi example, see ?town in the lives-near iule and ?middle-
manager in the wheel iule below. Same is useful when we want to foice two things to
be dieient, such as ?person-1 and ?person-2 in the lives-near iule. Although using
the same pauein vaiiable in two paits of a queiy foices the same value to appeai in
both places, using dieient pauein vaiiables does not foice dieient values to appeai.
(Te values assigned to dieient pauein vaiiables may be the same oi dieient.)
08
Te following iule declaies that a peison is a wheel in an oiganization
if he supeivises someone who is in tuin a supeivisoi
(rule (wheel ?person)
(and (supervisor ?middle-manager ?person)
(supervisor ?x ?middle-manager)))
Te geneial foim of a iule is
(rule conclusion body)
wheie conc|vs:on is a pauein and |oJy is any queiy.
We can think
of a iule as iepiesenting a laige (even innite) set of asseitions, namely
all instantiations of the iule conclusion with vaiiable assignments that
satisfy the iule body. When we desciibed simple queiies (paueins), we
said that an assignment to vaiiables satises a pauein if the instantiated
pauein is in the data base. But the pauein neednt be explicitly in the
data base as an asseition. lt can be an implicit asseition implied by a
iule. loi example, the queiy
(lives-near ?x (Bitdiddle Ben))
iesults in
(lives-near (Reasoner Louis) (Bitdiddle Ben))
(lives-near (Aull DeWitt) (Bitdiddle Ben))
To nd all computei piogiammeis who live neai Ben Bitdiddle, we can
ask
(and (job ?x (computer programmer))
(lives-near ?x (Bitdiddle Ben)))
We will also allow iules without bodies, as in same, and we will inteipiet such a
iule to mean that the iule conclusion is satised by any values of the vaiiables.
09
As in the case of compound pioceduies, iules can be used as paits of
othei iules (as we saw with the lives-near iule above) oi even be de-
ned iecuisively. loi instance, the iule
(rule (outranked-by ?staff-person ?boss)
(or (supervisor ?staff-person ?boss)
(and (supervisor ?staff-person ?middle-manager)
(outranked-by ?middle-manager ?boss))))
says that a sta peison is outianked by a boss in the oiganization if the
boss is the peisons supeivisoi oi (iecuisively) if the peisons supeivisoi
is outianked by the boss.
Exercise 4.57: Dene a iule that says that peison 1 can ie-
place peison 2 if eithei peison 1 does the same job as peison
2 oi someone who does peison 1s job can also do peison 2s
job, and if peison 1 and peison 2 aie not the same peison.
Using youi iule, give queiies that nd the following
a. all people who can ieplace Cy D. lect,
b. all people who can ieplace someone who is being paid
moie than they aie, togethei with the two salaiies.
Exercise 4.58: Dene a iule that says that a peison is a big
shot in a division if the peison woiks in the division but
does not have a supeivisoi who woiks in the division.
Exercise 4.59: Ben Bitdiddle has missed one meeting too
many. leaiing that his habit of foigeuing meetings could
cost him his job, Ben decides to do something about it. He
adds all the weekly meetings of the im to the Micioshaf
data base by asseiting the following
10
(meeting accounting (Monday 9am))
(meeting administration (Monday 10am))
(meeting computer (Wednesday 3pm))
(meeting administration (Friday 1pm))
Each of the above asseitions is foi a meeting of an entiie di-
vision. Ben also adds an entiy foi the company-wide meet-
ing that spans all the divisions. All of the companys em-
ployees auend this meeting.
(meeting whole-company (Wednesday 4pm))
a. On liiday moining, Ben wants to queiy the data base
foi all the meetings that occui that day. What queiy
should he use`
b. Alyssa P. Hackei is unimpiessed. She thinks it would
be much moie useful to be able to ask foi hei meetings
by specifying hei name. So she designs a iule that says
that a peisons meetings include all whole-company
meetings plus all meetings of that peisons division.
lill in the body of Alyssas iule.
(rule (meeting-time ?person ?day-and-time)
rule-body)
c. Alyssa aiiives at woik on Wednesday moining and
wondeis what meetings she has to auend that day.
Having dened the above iule, what queiy should she
make to nd this out`
Exercise 4.60: By giving the queiy
(lives-near ?person (Hacker Alyssa P))
11
Alyssa P. Hackei is able to nd people who live neai hei,
with whom she can iide to woik. On the othei hand, when
she tiies to nd all paiis of people who live neai each othei
by queiying
(lives-near ?person-1 ?person-2)
she notices that each paii of people who live neai each
othei is listed twice, foi example,
(lives-near (Hacker Alyssa P) (Fect Cy D))
(lives-near (Fect Cy D) (Hacker Alyssa P))
Why does this happen` ls theie a way to nd a list of people
who live neai each othei, in which each paii appeais only
once` Explain.
Logic as programs
We can iegaid a iule as a kind of logical implication I[ an assignment
of values to pauein vaiiables satises the body, |en it satises the con-
clusion. Consequently, we can iegaid the queiy language as having the
ability to peifoim |og:co| JeJvc:ons based upon the iules. As an exam-
ple, considei the append opeiation desciibed at the beginning of Section
4.4. As we said, append can be chaiacteiized by the following two iules
loi any list y, the empty list and y append to foim y.
loi any u, v, y, and z, (cons u v) and y append to foim (cons u
z) if v and y append to foim z.
To expiess this in oui queiy language, we dene two iules foi a ielation
(append-to-form x y z)
12
which we can inteipiet to mean x and y append to foim z
(rule (append-to-form () ?y ?y))
(rule (append-to-form (?u . ?v) ?y (?u . ?z))
(append-to-form ?v ?y ?z))
Te ist iule has no body, which means that the conclusion holds foi
any value of ?y. Note how the second iule makes use of doued-tail no-
tation to name the car and cdr of a list.
Given these two iules, we can foimulate queiies that compute the
append of two lists
;;; Query input:
(append-to-form (a b) (c d) ?z)
;;; Query results:
(append-to-form (a b) (c d) (a b c d))
What is moie stiiking, we can use the same iules to ask the question
Which list, when appended to (a b), yields (a b c d)` Tis is done
as follows
;;; Query input:
(append-to-form (a b) ?y (a b c d))
;;; Query results:
(append-to-form (a b) (c d) (a b c d))
We can also ask foi all paiis of lists that append to foim (a b c d)
;;; Query input:
(append-to-form ?x ?y (a b c d))
;;; Query results:
(append-to-form () (a b c d) (a b c d))
(append-to-form (a) (b c d) (a b c d))
(append-to-form (a b) (c d) (a b c d))
(append-to-form (a b c) (d) (a b c d))
(append-to-form (a b c d) () (a b c d))
13
Te queiy systemmay seemto exhibit quite a bit of intelligence in using
the iules to deduce the answeis to the queiies above. Actually, as we
will see in the next section, the system is following a well-deteimined
algoiithm in uniaveling the iules. Unfoitunately, although the system
woiks impiessively in the append case, the geneial methods may bieak
down in moie complex cases, as we will see in Section 4.4.3.
Exercise 4.61: Te following iules implement a next-to
ielation that nds adjacent elements of a list
(rule (?x next-to ?y in (?x ?y . ?u)))
(rule (?x next-to ?y in (?v . ?z))
(?x next-to ?y in ?z))
What will the iesponse be to the following queiies`
(?x next-to ?y in (1 (2 3) 4))
(?x next-to 1 in (2 1 3 1))
Exercise 4.62: Dene iules to implement the last-pair
opeiation of Exeicise 2.1, which ietuins a list containing
the last element of a nonempty list. Check youi iules on
queiies such as (last-pair (3) ?x), (last-pair (1 2
3) ?x) and (last-pair (2 ?x) (3)). Do youi iules woik
coiiectly on queiies such as (last-pair ?x (3)) `
Exercise 4.63: Te following data base (see Genesis 4) tiaces
the genealogy of the descendants of Ada back to Adam, by
way of Cain
(son Adam Cain)
(son Cain Enoch)
(son Enoch Irad)
14
(son Irad Mehujael)
(son Mehujael Methushael)
(son Methushael Lamech)
(wife Lamech Ada)
(son Ada Jabal)
(son Ada Jubal)
loimulate iules such as lf S is the son of f , and f is the
son of G, then S is the giandson of G and lf W is the wife
of M, and S is the son of W, then S is the son of M (which
was supposedly moie tiue in biblical times than today) that
will enable the queiy system to nd the giandson of Cain,
the sons of Lamech, the giandsons of Methushael. (See Ex-
eicise 4.9 foi some iules to deduce moie complicated ie-
lationships.)
4.4.2 How the ery System Works
ln Section 4.4.4 we will piesent an implementation of the queiy intei-
pietei as a collection of pioceduies. ln this section we give an oveiview
that explains the geneial stiuctuie of the system independent of low-
level implementation details. Afei desciibing the implementation of the
inteipietei, we will be in a position to undeistand some of its limitations
and some of the subtle ways in which the queiy languages logical op-
eiations diei fiom the opeiations of mathematical logic.
lt should be appaient that the queiy evaluatoi must peifoim some
kind of seaich in oidei to match queiies against facts and iules in the
data base. One way to do this would be to implement the queiy system
as a nondeteiministic piogiam, using the amb evaluatoi of Section 4.3
(see Exeicise 4.8). Anothei possibility is to manage the seaich with the
aid of stieams. Oui implementation follows this second appioach.
1
Te queiy system is oiganized aiound two cential opeiations called
ouern oc|:ng and vn:co:on. We ist desciibe pauein matching and
explain how this opeiation, togethei with the oiganization of infoima-
tion in teims of stieams of fiames, enables us to implement both simple
and compound queiies. We next discuss unication, a geneialization of
pauein matching needed to implement iules. linally, we show how the
entiie queiy inteipietei ts togethei thiough a pioceduie that classies
expiessions in a mannei analogous to the way eval classies expies-
sions foi the inteipietei desciibed in Section 4.1.
Paern matching
A ouern oc|er is a piogiam that tests whethei some datum ts a
specied pauein. loi example, the data list ((a b) c (a b)) matches
the pauein (?x c ?x) with the pauein vaiiable ?x bound to (a b).
Te same data list matches the pauein (?x ?y ?z) with ?x and ?z both
bound to (a b) and ?y bound to c. lt also matches the pauein ((?x ?y)
c (?x ?y)) with ?x bound to a and ?y bound to b. Howevei, it does not
match the pauein (?x a ?y), since that pauein species a list whose
second element is the symbol a.
Te pauein matchei used by the queiy system takes as inputs a
pauein, a datum, and a [roe that species bindings foi vaiious pauein
vaiiables. lt checks whethei the datummatches the pauein in a way that
is consistent with the bindings alieady in the fiame. lf so, it ietuins the
given fiame augmented by any bindings that may have been deteimined
by the match. Otheiwise, it indicates that the match has failed.
loi example, using the pauein (?x ?y ?x) to match (a b a) given
an empty fiame will ietuin a fiame specifying that ?x is bound to a
and ?y is bound to b. Tiying the match with the same pauein, the same
datum, and a fiame specifying that ?y is bound to a will fail. Tiying the
1
match with the same pauein, the same datum, and a fiame in which ?y
is bound to b and ?x is unbound will ietuin the given fiame augmented
by a binding of ?x to a.
Te pauein matchei is all the mechanism that is needed to pio-
cess simple queiies that dont involve iules. loi instance, to piocess the
queiy
(job ?x (computer programmer))
we scan thiough all asseitions in the data base and select those that
match the pauein with iespect to an initially empty fiame. loi each
match we nd, we use the fiame ietuined by the match to instantiate
the pauein with a value foi ?x.
Streams of frames
Te testing of paueins against fiames is oiganized thiough the use of
stieams. Given a single fiame, the matching piocess iuns thiough the
data-base entiies one by one. loi each data-base entiy, the matchei gen-
eiates eithei a special symbol indicating that the match has failed oi an
extension to the fiame. Te iesults foi all the data-base entiies aie col-
lected into a stieam, which is passed thiough a ltei to weed out the
failuies. Te iesult is a stieam of all the fiames that extend the given
fiame via a match to some asseition in the data base.
Infinite loops
Aconsequence of the pioceduial inteipietation of logic piogiams is that
it is possible to constiuct hopelessly inecient piogiams foi solving
ceitain pioblems. An extieme case of ineciency occuis when the sys-
tem falls into innite loops in making deductions. As a simple example,
suppose we aie seuing up a data base of famous maiiiages, including
(assert! (married Minnie Mickey))
lf we now ask
(married Mickey ?who)
Tis is not a pioblem of the logic but one of the pioceduial inteipietation of the
logic piovided by oui inteipietei. We could wiite an inteipietei that would not fall
into a loop heie. loi example, we could enumeiate all the pioofs deiivable fiom oui
asseitions and oui iules in a bieadth-ist iathei than a depth-ist oidei. Howevei,
such a system makes it moie dicult to take advantage of the oidei of deductions
in oui piogiams. One auempt to build sophisticated contiol into such a piogiam is
desciibed in deKleei et al. 19. Anothei technique, which does not lead to such seiious
contiol pioblems, is to put in special knowledge, such as detectois foi paiticulai kinds of
loops (Exeicise 4.). Howevei, theie can be no geneial scheme foi ieliably pieventing a
system fiom going down innite paths in peifoiming deductions. lmagine a diabolical
iule of the foim To show P(x) is tiue, show that P(f (x)) is tiue, foi some suitably
chosen function f .
31
aie entiies of this foim, the not clause lteis out the empty fiame and
ietuins an empty stieam of fiames. Consequently, the entiie compound
queiy ietuins an empty stieam.
Te tiouble is that oui implementation of not ieally is meant to
seive as a ltei on values foi the vaiiables. lf a not clause is piocessed
with a fiame in which some of the vaiiables iemain unbound (as does
?x in the example above), the system will pioduce unexpected iesults.
Similai pioblems occui with the use of lisp-valuethe Lisp piedicate
cant woik if some of its aiguments aie unbound. See Exeicise 4..
Teie is also a much moie seiious way in which the not of the queiy
language dieis fiom the not of mathematical logic. ln logic, we intei-
piet the statement not P to mean that P is not tiue. ln the queiy sys-
tem, howevei, not P means that P is not deducible fiomthe knowledge
in the data base. loi example, given the peisonnel data base of Section
4.4.1, the system would happily deduce all soits of not statements, such
as that Ben Bitdiddle is not a baseball fan, that it is not iaining outside,
and that 2 2 is not 4.
8
ln othei woids, the not of logic piogiamming
languages ieects the so-called c|oseJ +or|J ossv:on that all ielevant
infoimation has been included in the data base.
9
Exercise 4.64: Louis Reasonei mistakenly deletes the outranked-
by iule (Section 4.4.1) fiom the data base. When he ieal-
izes this, he quickly ieinstalls it. Unfoitunately, he makes a
slight change in the iule, and types it in as
8
Considei the queiy (not (baseball-fan (Bitdiddle Ben))). Te system nds
that (baseball-fan (Bitdiddle Ben)) is not in the data base, so the empty fiame
does not satisfy the pauein and is not lteied out of the initial stieam of fiames. Te
iesult of the queiy is thus the empty fiame, which is used to instantiate the input queiy
to pioduce (not (baseball-fan (Bitdiddle Ben))).
9
A discussion and justication of this tieatment of not can be found in the aiticle
by Claik (198).
32
(rule (outranked-by ?staff-person ?boss)
(or (supervisor ?staff-person ?boss)
(and (outranked-by ?middle-manager ?boss)
(supervisor ?staff-person
?middle-manager))))
Just afei Louis types this infoimation into the system, De-
Wiu Aull comes by to nd out who outianks Ben Bitdiddle.
He issues the queiy
(outranked-by (Bitdiddle Ben) ?who)
Afei answeiing, the system goes into an innite loop. Ex-
plain why.
Exercise 4.65: Cy D. lect, looking foiwaid to the day when
he will iise in the oiganization, gives a queiy to nd all the
wheels (using the wheel iule of Section 4.4.1)
(wheel ?who)
To his suipiise, the system iesponds
;;; Query results:
(wheel (Warbucks Oliver))
(wheel (Bitdiddle Ben))
(wheel (Warbucks Oliver))
(wheel (Warbucks Oliver))
(wheel (Warbucks Oliver))
Why is Olivei Waibucks listed foui times`
Exercise 4.66: Ben has been geneializing the queiy sys-
tem to piovide statistics about the company. loi example,
33
to nd the total salaiies of all the computei piogiammeis
one will be able to say
(sum ?amount (and (job ?x (computer programmer))
(salary ?x ?amount)))
ln geneial, Bens newsystemallows expiessions of the foim
(accumulation-function variable query pattern)
wheie accumulation-function can be things like sum, average,
oi maximum. Ben ieasons that it should be a cinch to imple-
ment this. He will simply feed the queiy pauein to qeval.
Tis will pioduce a stieam of fiames. He will then pass this
stieam thiough a mapping function that extiacts the value
of the designated vaiiable fiom each fiame in the stieam
and feed the iesulting stieam of values to the accumulation
function. Just as Ben completes the implementation and is
about to tiy it out, Cy walks by, still puzzling ovei the wheel
queiy iesult in Exeicise 4.. When Cy shows Ben the sys-
tems iesponse, Ben gioans, Oh, no, my simple accumula-
tion scheme wont woik'
What has Ben just iealized` Outline a method he can use
to salvage the situation.
Exercise 4.67: Devise a way to install a loop detectoi in the
queiy system so as to avoid the kinds of simple loops illus-
tiated in the text and in Exeicise 4.4. Te geneial idea is
that the system should maintain some soit of histoiy of its
cuiient chain of deductions and should not begin piocess-
ing a queiy that it is alieady woiking on. Desciibe what
kind of infoimation (paueins and fiames) is included in
34
this histoiy, and how the check should be made. (Afei you
study the details of the queiy-system implementation in
Section 4.4.4, you may want to modify the system to in-
clude youi loop detectoi.)
Exercise 4.68: Dene iules to implement the reverse op-
eiation of Exeicise 2.18, which ietuins a list containing the
same elements as a given list in ieveise oidei. (Hint Use
append-to-form.) Can youi iules answei both (reverse
(1 2 3) ?x) and (reverse ?x (1 2 3)) `
Exercise 4.69: Beginning with the data base and the iules
you foimulated in Exeicise 4.3, devise a iule foi adding
gieats to a giandson ielationship. Tis should enable the
system to deduce that liad is the gieat-giandson of Adam,
oi that Jabal and Jubal aie the gieat-gieat-gieat-gieat-gieat-
giandsons of Adam. (Hint Repiesent the fact about liad, foi
example, as ((great grandson) Adam Irad). Wiite iules
that deteimine if a list ends in the woid grandson. Use this
to expiess a iule that allows one to deiive the ielationship
((great . ?rel) ?x ?y), wheie ?rel is a list ending in
grandson.) Check youi iules on queiies such as ((great
grandson) ?g ?ggs) and (?relationship Adam Irad).
4.4.4 Implementing the ery System
Section 4.4.2 desciibed how the queiy system woiks. Now we ll in the
details by piesenting a complete implementation of the system.
3
4.4.4.1 The Driver Loop and Instantiation
Te diivei loop foi the queiy systemiepeatedly ieads input expiessions.
lf the expiession is a iule oi asseition to be added to the data base, then
the infoimation is added. Otheiwise the expiession is assumed to be
a queiy. Te diivei passes this queiy to the evaluatoi qeval togethei
with an initial fiame stieam consisting of a single empty fiame. Te
iesult of the evaluation is a stieam of fiames geneiated by satisfying
the queiy with vaiiable values found in the data base. Tese fiames aie
used to foim a new stieam consisting of copies of the oiiginal queiy in
which the vaiiables aie instantiated with values supplied by the stieam
of fiames, and this nal stieam is piinted at the teiminal
(define input-prompt ";;; Query input:")
(define output-prompt ";;; Query results:")
(define (query-driver-loop)
(prompt-for-input input-prompt)
(let ((q (query-syntax-process (read))))
(cond ((assertion-to-be-added? q)
(add-rule-or-assertion! (add-assertion-body q))
(newline)
(display "Assertion added to data base.")
(query-driver-loop))
(else
(newline)
(display output-prompt)
(display-stream
(stream-map
(lambda (frame)
(instantiate
q
frame
3
(lambda (v f)
(contract-question-mark v))))
(qeval q (singleton-stream '()))))
(query-driver-loop)))))
Heie, as in the othei evaluatois in this chaptei, we use an abstiact syn-
tax foi the expiessions of the queiy language. Te implementation of the
expiession syntax, including the piedicate assertion-to-be-added?
and the selectoi add-assertion-body, is given in Section 4.4.4.. Add-
rule-or-assertion! is dened in Section 4.4.4..
Befoie doing any piocessing on an input expiession, the diivei loop
tiansfoims it syntactically into a foim that makes the piocessing moie
ecient. Tis involves changing the iepiesentation of pauein vaiiables.
When the queiy is instantiated, any vaiiables that iemain unbound
aie tiansfoimed back to the input iepiesentation befoie being piinted.
Tese tiansfoimations aie peifoimed by the two pioceduies query-
syntax-process and contract-question-mark (Section 4.4.4.).
To instantiate an expiession, we copy it, ieplacing any vaiiables in
the expiession by theii values in a given fiame. Te values aie them-
selves instantiated, since they could contain vaiiables (foi example, if
?x in exp is bound to ?y as the iesult of unication and ?y is in tuin
bound to ). Te action to take if a vaiiable cannot be instantiated is
given by a pioceduial aigument to instantiate.
(define (instantiate exp frame unbound-var-handler)
(define (copy exp)
(cond ((var? exp)
(let ((binding (binding-in-frame exp frame)))
(if binding
(copy (binding-value binding))
(unbound-var-handler exp frame))))
((pair? exp)
3
(cons (copy (car exp)) (copy (cdr exp))))
(else exp)))
(copy exp))
Te pioceduies that manipulate bindings aie dened in Section 4.4.4.8.
4.4.4.2 The Evaluator
Te qeval pioceduie, called by the query-driver-loop, is the basic
evaluatoi of the queiy system. lt takes as inputs a queiy and a stieam of
fiames, and it ietuins a stieam of extended fiames. lt identies special
foims by a data-diiected dispatch using get and put, just as we did in
implementing geneiic opeiations in Chaptei 2. Any queiy that is not
identied as a special foim is assumed to be a simple queiy, to be pio-
cessed by simple-query.
(define (qeval query frame-stream)
(let ((qproc (get (type query) 'qeval)))
(if qproc
(qproc (contents query) frame-stream)
(simple-query query frame-stream))))
Type and contents, dened in Section 4.4.4., implement the abstiact
syntax of the special foims.
Simple queries
Te simple-query pioceduie handles simple queiies. lt takes as aigu-
ments a simple queiy (a pauein) togethei with a stieam of fiames, and
it ietuins the stieam foimed by extending each fiame by all data-base
matches of the queiy.
(define (simple-query query-pattern frame-stream)
(stream-flatmap
38
(lambda (frame)
(stream-append-delayed
(find-assertions query-pattern frame)
(delay (apply-rules query-pattern frame))))
frame-stream))
loi each fiame in the input stieam, we use find-assertions (Section
4.4.4.3) to match the pauein against all asseitions in the data base, pio-
ducing a stieam of extended fiames, and we use apply-rules (Sec-
tion 4.4.4.4) to apply all possible iules, pioducing anothei stieam of ex-
tended fiames. Tese two stieams aie combined (using stream-append-
delayed, Section 4.4.4.) to make a stieam of all the ways that the given
pauein can be satised consistent with the oiiginal fiame (see Exei-
cise 4.1). Te stieams foi the individual input fiames aie combined us-
ing stream-flatmap (Section 4.4.4.) to foim one laige stieam of all the
ways that any of the fiames in the oiiginal input stieamcan be extended
to pioduce a match with the given pauein.
Compound queries
And queiies aie handled as illustiated in liguie 4. by the conjoin pio-
ceduie. Conjoin takes as inputs the conjuncts and the fiame stieam
and ietuins the stieam of extended fiames. liist, conjoin piocesses the
stieam of fiames to nd the stieam of all possible fiame extensions that
satisfy the ist queiy in the conjunction. Ten, using this as the new
fiame stieam, it iecuisively applies conjoin to the iest of the queiies.
(define (conjoin conjuncts frame-stream)
(if (empty-conjunction? conjuncts)
frame-stream
(conjoin (rest-conjuncts conjuncts)
(qeval (first-conjunct conjuncts) frame-stream))))
39
Te expiession
(put 'and 'qeval conjoin)
sets up qeval to dispatch to conjoin when an and foim is encounteied.
Or queiies aie handled similaily, as shown in liguie 4.. Te output
stieams foi the vaiious disjuncts of the or aie computed sepaiately and
meiged using the interleave-delayed pioceduie fiom Section 4.4.4..
(See Exeicise 4.1 and Exeicise 4.2.)
(define (disjoin disjuncts frame-stream)
(if (empty-disjunction? disjuncts)
the-empty-stream
(interleave-delayed
(qeval (first-disjunct disjuncts) frame-stream)
(delay (disjoin (rest-disjuncts disjuncts) frame-stream)))))
(put 'or 'qeval disjoin)
Te piedicates and selectois foi the syntax of conjuncts and disjuncts
aie given in Section 4.4.4..
Filters
Not is handled by the method outlined in Section 4.4.2. We auempt to ex-
tend each fiame in the input stieam to satisfy the queiy being negated,
and we include a given fiame in the output stieam only if it cannot be
extended.
(define (negate operands frame-stream)
(stream-flatmap
(lambda (frame)
(if (stream-null?
(qeval (negated-query operands)
(singleton-stream frame)))
(singleton-stream frame)
40
the-empty-stream))
frame-stream))
(put 'not 'qeval negate)
Lisp-value is a ltei similai to not. Each fiame in the stieam is used
to instantiate the vaiiables in the pauein, the indicated piedicate is ap-
plied, and the fiames foi which the piedicate ietuins false aie lteied
out of the input stieam. An eiioi iesults if theie aie unbound pauein
vaiiables.
(define (lisp-value call frame-stream)
(stream-flatmap
(lambda (frame)
(if (execute
(instantiate
call
frame
(lambda (v f)
(error "Unknown pat var: LISP-VALUE" v))))
(singleton-stream frame)
the-empty-stream))
frame-stream))
(put 'lisp-value 'qeval lisp-value)
Execute, which applies the piedicate to the aiguments, must eval the
piedicate expiession to get the pioceduie to apply. Howevei, it must not
evaluate the aiguments, since they aie alieady the actual aiguments,
not expiessions whose evaluation (in Lisp) will pioduce the aiguments.
Note that execute is implemented using eval and apply fiom the un-
deilying Lisp system.
(define (execute exp)
(apply (eval (predicate exp) user-initial-environment)
(args exp)))
41
Te always-true special foim piovides foi a queiy that is always satis-
ed. lt ignoies its contents (noimally empty) and simply passes thiough
all the fiames in the input stieam. Always-true is used by the rule-
body selectoi (Section 4.4.4.) to piovide bodies foi iules that weie de-
ned without bodies (that is, iules whose conclusions aie always satis-
ed).
(define (always-true ignore frame-stream) frame-stream)
(put 'always-true 'qeval always-true)
Te selectois that dene the syntax of not and lisp-value aie given in
Section 4.4.4..
4.4.4.3 Finding Assertions
by Paern Matching
Find-assertions, called by simple-query (Section 4.4.4.2), takes as in-
put a pauein and a fiame. lt ietuins a stieam of fiames, each extending
the given one by a data-base match of the given pauein. lt uses fetch-
assertions (Section 4.4.4.) to get a stieam of all the asseitions in the
data base that should be checked foi a match against the pauein and the
fiame. Te ieason foi fetch-assertions heie is that we can ofen ap-
ply simple tests that will eliminate many of the entiies in the data base
fiom the pool of candidates foi a successful match. Te system would
still woik if we eliminated fetch-assertions and simply checked a
stieam of all asseitions in the data base, but the computation would be
less ecient because we would need to make many moie calls to the
matchei.
(define (find-assertions pattern frame)
(stream-flatmap
(lambda (datum) (check-an-assertion datum pattern frame))
(fetch-assertions pattern frame)))
42
Check-an-assertion takes as aiguments a pauein, a data object (assei-
tion), and a fiame and ietuins eithei a one-element stieam containing
the extended fiame oi the-empty-stream if the match fails.
(define (check-an-assertion assertion query-pat query-frame)
(let ((match-result
(pattern-match query-pat assertion query-frame)))
(if (eq? match-result 'failed)
the-empty-stream
(singleton-stream match-result))))
Te basic pauein matchei ietuins eithei the symbol failed oi an ex-
tension of the given fiame. Te basic idea of the matchei is to check the
pauein against the data, element by element, accumulating bindings foi
the pauein vaiiables. lf the pauein and the data object aie the same, the
match succeeds and we ietuin the fiame of bindings accumulated so fai.
Otheiwise, if the pauein is a vaiiable we extend the cuiient fiame by
binding the vaiiable to the data, so long as this is consistent with the
bindings alieady in the fiame. lf the pauein and the data aie both paiis,
we (iecuisively) match the car of the pauein against the car of the data
to pioduce a fiame, in this fiame we then match the cdr of the pauein
against the cdr of the data. lf none of these cases aie applicable, the
match fails and we ietuin the symbol failed.
(define (pattern-match pat dat frame)
(cond ((eq? frame 'failed) 'failed)
((equal? pat dat) frame)
((var? pat) (extend-if-consistent pat dat frame))
((and (pair? pat) (pair? dat))
(pattern-match
(cdr pat)
(cdr dat)
(pattern-match (car pat) (car dat) frame)))
43
(else 'failed)))
Heie is the pioceduie that extends a fiame by adding a new binding, if
this is consistent with the bindings alieady in the fiame
(define (extend-if-consistent var dat frame)
(let ((binding (binding-in-frame var frame)))
(if binding
(pattern-match (binding-value binding) dat frame)
(extend var dat frame))))
lf theie is no binding foi the vaiiable in the fiame, we simply add the
binding of the vaiiable to the data. Otheiwise we match, in the fiame,
the data against the value of the vaiiable in the fiame. lf the stoied
value contains only constants, as it must if it was stoied duiing pat-
tein matching by extend-if-consistent, then the match simply tests
whethei the stoied and new values aie the same. lf so, it ietuins the un-
modied fiame, if not, it ietuins a failuie indication. Te stoied value
may, howevei, contain pauein vaiiables if it was stoied duiing uni-
cation (see Section 4.4.4.4). Te iecuisive match of the stoied pauein
against the new data will add oi check bindings foi the vaiiables in this
pauein. loi example, suppose we have a fiame in which ?x is bound
to (f ?y) and ?y is unbound, and we wish to augment this fiame by
a binding of ?x to (f b). We look up ?x and nd that it is bound to
(f ?y). Tis leads us to match (f ?y) against the pioposed new value
(f b) in the same fiame. Eventually this match extends the fiame by
adding a binding of ?y to b. ?X iemains bound to (f ?y). We nevei
modify a stoied binding and we nevei stoie moie than one binding foi
a given vaiiable.
Te pioceduies used by extend-if-consistent to manipulate bind-
ings aie dened in Section 4.4.4.8.
44
Paerns with doed tails
lf a pauein contains a dot followed by a pauein vaiiable, the pauein
vaiiable matches the iest of the data list (iathei than the next element
of the data list), just as one would expect with the doued-tail notation
desciibed in Exeicise 2.20. Although the pauein matchei we have just
implemented doesnt look foi dots, it does behave as we want. Tis is
because the Lisp read piimitive, which is used by query-driver-loop
to iead the queiy and iepiesent it as a list stiuctuie, tieats dots in a
special way.
When read sees a dot, instead of making the next item be the next
element of a list (the car of a cons whose cdr will be the iest of the list)
it makes the next item be the cdr of the list stiuctuie. loi example, the
list stiuctuie pioduced by read foi the pauein (computer ?type) could
be constiucted by evaluating the expiession (cons 'computer (cons
'?type '())), and that foi (computer . ?type) could be constiucted
by evaluating the expiession (cons 'computer '?type).
Tus, as pattern-match iecuisively compaies cars and cdrs of a
data list and a pauein that had a dot, it eventually matches the vaiiable
afei the dot (which is a cdr of the pauein) against a sublist of the data
list, binding the vaiiable to that list. loi example, matching the pauein
(computer . ?type) against (computer programmer trainee) will
match ?type against the list (programmer trainee).
4.4.4.4 Rules and Unification
Apply-rules is the iule analog of find-assertions (Section 4.4.4.3).
lt takes as input a pauein and a fiame, and it foims a stieam of exten-
sion fiames by applying iules fiomthe data base. Stream-flatmap maps
apply-a-rule down the stieam of possibly applicable iules (selected
4
by fetch-rules, Section 4.4.4.) and combines the iesulting stieams of
fiames.
(define (apply-rules pattern frame)
(stream-flatmap (lambda (rule)
(apply-a-rule rule pattern frame))
(fetch-rules pattern frame)))
Apply-a-rule applies iules using the method outlined in Section 4.4.2.
lt ist augments its aigument fiame by unifying the iule conclusion
with the pauein in the given fiame. lf this succeeds, it evaluates the
iule body in this new fiame.
Befoie any of this happens, howevei, the piogiam ienames all the
vaiiables in the iule with unique new names. Te ieason foi this is to
pievent the vaiiables foi dieient iule applications fiom becoming con-
fused with each othei. loi instance, if two iules both use a vaiiable
named ?x, then each one may add a binding foi ?x to the fiame when it
is applied. Tese two ?xs have nothing to do with each othei, and we
should not be fooled into thinking that the two bindings must be con-
sistent. Rathei than iename vaiiables, we could devise a moie clevei
enviionment stiuctuie, howevei, the ienaming appioach we have cho-
sen heie is the most stiaightfoiwaid, even if not the most ecient. (See
Exeicise 4.9.) Heie is the apply-a-rule pioceduie
(define (apply-a-rule rule query-pattern query-frame)
(let ((clean-rule (rename-variables-in rule)))
(let ((unify-result (unify-match query-pattern
(conclusion clean-rule)
query-frame)))
(if (eq? unify-result 'failed)
the-empty-stream
(qeval (rule-body clean-rule)
(singleton-stream unify-result))))))
4
Te selectois rule-body and conclusion that extiact paits of a iule aie
dened in Section 4.4.4..
We geneiate unique vaiiable names by associating a unique identi-
ei (such as a numbei) with each iule application and combining this
identiei with the oiiginal vaiiable names. loi example, if the iule-
application identiei is , we might change each ?x in the iule to ?x-7
and each ?y in the iule to ?y-7. (Make-new-variable and new-rule-
application-id aie included with the syntax pioceduies in Section
4.4.4..)
(define (rename-variables-in rule)
(let ((rule-application-id (new-rule-application-id)))
(define (tree-walk exp)
(cond ((var? exp)
(make-new-variable exp rule-application-id))
((pair? exp)
(cons (tree-walk (car exp))
(tree-walk (cdr exp))))
(else exp)))
(tree-walk rule)))
Te unication algoiithm is implemented as a pioceduie that takes as
inputs two paueins and a fiame and ietuins eithei the extended fiame
oi the symbol failed. Te uniei is like the pauein matchei except
that it is symmetiicalvaiiables aie allowed on both sides of the match.
Unify-match is basically the same as pattern-match, except that theie
is extia code (maiked *** below) to handle the case wheie the object
on the iight side of the match is a vaiiable.
(define (unify-match p1 p2 frame)
(cond ((eq? frame 'failed) 'failed)
((equal? p1 p2) frame)
((var? p1) (extend-if-possible p1 p2 frame))
4
((var? p2) (extend-if-possible p2 p1 frame)) , ***
((and (pair? p1) (pair? p2))
(unify-match (cdr p1)
(cdr p2)
(unify-match (car p1)
(car p2)
frame)))
(else 'failed)))
ln unication, as in one-sided pauein matching, we want to accept a
pioposed extension of the fiame only if it is consistent with existing
bindings. Te pioceduie extend-if-possible used in unication is the
same as the extend-if-consistent used in pauein matching except foi
two special checks, maiked *** in the piogiambelow. ln the ist case,
if the vaiiable we aie tiying to match is not bound, but the value we aie
tiying to match it with is itself a (dieient) vaiiable, it is necessaiy to
check to see if the value is bound, and if so, to match its value. lf both
paities to the match aie unbound, we may bind eithei to the othei.
Te second check deals with auempts to bind a vaiiable to a pat-
tein that includes that vaiiable. Such a situation can occui whenevei a
vaiiable is iepeated in both paueins. Considei, foi example, unifying
the two paueins (?x ?x) and (?y expression involving ?y) in a
fiame wheie both ?x and ?y aie unbound. liist ?x is matched against
?y, making a binding of ?x to ?y. Next, the same ?x is matched against
the given expiession involving ?y. Since ?x is alieady bound to ?y, this
iesults in matching ?y against the expiession. lf we think of the uniei
as nding a set of values foi the pauein vaiiables that make the paueins
the same, then these paueins imply instiuctions to nd a ?y such that
?y is equal to the expiession involving ?y. Teie is no geneial method
foi solving such equations, so we ieject such bindings, these cases aie
48
iecognized by the piedicate depends-on?.
80
On the othei hand, we do
not want to ieject auempts to bind a vaiiable to itself. loi example, con-
sidei unifying (?x ?x) and (?y ?y). Te second auempt to bind ?x to
?y matches ?y (the stoied value of ?x) against ?y (the new value of ?x).
Tis is taken caie of by the equal? clause of unify-match.
(define (extend-if-possible var val frame)
(let ((binding (binding-in-frame var frame)))
(cond (binding
80
ln geneial, unifying ?y with an expiession involving ?y would iequiie oui being
able to nd a xed point of the equation ?y e:ress:on :n+o|+:ng ?y. lt is sometimes
possible to syntactically foim an expiession that appeais to be the solution. loi exam-
ple, ?y (f ?y) seems to have the xed point (f (f (f . . . ))), which we can pioduce
by beginning with the expiession (f ?y) and iepeatedly substituting (f ?y) foi ?y.
Unfoitunately, not eveiy such equation has a meaningful xed point. Te issues that
aiise heie aie similai to the issues of manipulating innite seiies in mathematics. loi
example, we know that 2 is the solution to the equation y = 1 y/2. Beginning with
the expiession 1 y/2 and iepeatedly substituting 1 y/2 foi y gives
2 = y = 1
y
2
= 1
1
2
_
1
y
2
_
= 1
1
2
y
4
= . . . ,
which leads to
2 = 1
1
2
1
4
1
8
. . . .
Howevei, if we tiy the same manipulation beginning with the obseivation that -1 is the
solution to the equation y = 1 2y, we obtain
1 = y = 1 2y = 1 2(1 2y) = 1 2 4y = . . . ,
which leads to
1 = 1 2 4 8 . . . .
Although the foimal manipulations used in deiiving these two equations aie identical,
the ist iesult is a valid asseition about innite seiies but the second is not. Similaily, foi
oui unication iesults, ieasoning with an aibitiaiy syntactically constiucted expiession
may lead to eiiois.
49
(unify-match (binding-value binding) val frame))
((var? val) , ***
(let ((binding (binding-in-frame val frame)))
(if binding
(unify-match
var (binding-value binding) frame)
(extend var val frame))))
((depends-on? val var frame) , ***
'failed)
(else (extend var val frame)))))
Depends-on? is a piedicate that tests whethei an expiession pioposed
to be the value of a pauein vaiiable depends on the vaiiable. Tis must
be done ielative to the cuiient fiame because the expiession may con-
tain occuiiences of a vaiiable that alieady has a value that depends on
oui test vaiiable. Te stiuctuie of depends-on? is a simple iecuisive
tiee walk in which we substitute foi the values of vaiiables whenevei
necessaiy.
(define (depends-on? exp var frame)
(define (tree-walk e)
(cond ((var? e)
(if (equal? var e)
true
(let ((b (binding-in-frame e frame)))
(if b
(tree-walk (binding-value b))
false))))
((pair? e)
(or (tree-walk (car e))
(tree-walk (cdr e))))
(else false)))
(tree-walk exp))
0
4.4.4.5 Maintaining the Data Base
One impoitant pioblem in designing logic piogiamming languages is
that of aiianging things so that as few iiielevant data-base entiies as
possible will be examined in checking a given pauein. ln oui system, in
addition to stoiing all asseitions in one big stieam, we stoie all assei-
tions whose cars aie constant symbols in sepaiate stieams, in a table
indexed by the symbol. To fetch an asseition that may match a pauein,
we ist check to see if the car of the pauein is a constant symbol. lf
so, we ietuin (to be tested using the matchei) all the stoied asseitions
that have the same car. lf the paueins car is not a constant symbol,
we ietuin all the stoied asseitions. Cleveiei methods could also take
advantage of infoimation in the fiame, oi tiy also to optimize the case
wheie the car of the pauein is not a constant symbol. We avoid build-
ing oui ciiteiia foi indexing (using the car, handling only the case of
constant symbols) into the piogiam, instead we call on piedicates and
selectois that embody oui ciiteiia.
(define THE-ASSERTIONS the-empty-stream)
(define (fetch-assertions pattern frame)
(if (use-index? pattern)
(get-indexed-assertions pattern)
(get-all-assertions)))
(define (get-all-assertions) THE-ASSERTIONS)
(define (get-indexed-assertions pattern)
(get-stream (index-key-of pattern) 'assertion-stream))
Get-stream looks up a stieam in the table and ietuins an empty stieam
if nothing is stoied theie.
(define (get-stream key1 key2)
(let ((s (get key1 key2)))
(if s s the-empty-stream)))
1
Rules aie stoied similaily, using the car of the iule conclusion. Rule
conclusions aie aibitiaiy paueins, howevei, so they diei fiom assei-
tions in that they can contain vaiiables. A pauein whose car is a con-
stant symbol can match iules whose conclusions stait with a vaiiable as
well as iules whose conclusions have the same car. Tus, when fetch-
ing iules that might match a pauein whose car is a constant symbol we
fetch all iules whose conclusions stait with a vaiiable as well as those
whose conclusions have the same car as the pauein. loi this puipose
we stoie all iules whose conclusions stait with a vaiiable in a sepaiate
stieam in oui table, indexed by the symbol ?.
(define THE-RULES the-empty-stream)
(define (fetch-rules pattern frame)
(if (use-index? pattern)
(get-indexed-rules pattern)
(get-all-rules)))
(define (get-all-rules) THE-RULES)
(define (get-indexed-rules pattern)
(stream-append
(get-stream (index-key-of pattern) 'rule-stream)
(get-stream '? 'rule-stream)))
Add-rule-or-assertion! is used by query-driver-loop to add assei-
tions and iules to the data base. Each item is stoied in the index, if ap-
piopiiate, and in a stieam of all asseitions oi iules in the data base.
(define (add-rule-or-assertion! assertion)
(if (rule? assertion)
(add-rule! assertion)
(add-assertion! assertion)))
(define (add-assertion! assertion)
(store-assertion-in-index assertion)
(let ((old-assertions THE-ASSERTIONS))
2
(set! THE-ASSERTIONS
(cons-stream assertion old-assertions))
'ok))
(define (add-rule! rule)
(store-rule-in-index rule)
(let ((old-rules THE-RULES))
(set! THE-RULES (cons-stream rule old-rules))
'ok))
To actually stoie an asseition oi a iule, we check to see if it can be
indexed. lf so, we stoie it in the appiopiiate stieam.
(define (store-assertion-in-index assertion)
(if (indexable? assertion)
(let ((key (index-key-of assertion)))
(let ((current-assertion-stream
(get-stream key 'assertion-stream)))
(put key
'assertion-stream
(cons-stream
assertion
current-assertion-stream))))))
(define (store-rule-in-index rule)
(let ((pattern (conclusion rule)))
(if (indexable? pattern)
(let ((key (index-key-of pattern)))
(let ((current-rule-stream
(get-stream key 'rule-stream)))
(put key
'rule-stream
(cons-stream rule
current-rule-stream)))))))
Te following pioceduies dene howthe data-base index is used. Apat-
tein (an asseition oi a iule conclusion) will be stoied in the table if it
3
staits with a vaiiable oi a constant symbol.
(define (indexable? pat)
(or (constant-symbol? (car pat))
(var? (car pat))))
Te key undei which a pauein is stoied in the table is eithei ? (if it staits
with a vaiiable) oi the constant symbol with which it staits.
(define (index-key-of pat)
(let ((key (car pat)))
(if (var? key) '? key)))
Te index will be used to ietiieve items that might match a pauein if
the pauein staits with a constant symbol.
(define (use-index? pat) (constant-symbol? (car pat)))
Exercise 4.70: What is the puipose of the let bindings
in the pioceduies add-assertion! and add-rule! ` What
would be wiong with the following implementation of add-
assertion! ` Hint Recall the denition of the innite stieam
of ones in Section 3..2 (define ones (cons-stream 1
ones)).
(define (add-assertion! assertion)
(store-assertion-in-index assertion)
(set! THE-ASSERTIONS
(cons-stream assertion THE-ASSERTIONS))
'ok)
4.4.4.6 Stream Operations
Te queiy system uses a few stieam opeiations that weie not piesented
in Chaptei 3.
4
Stream-append-delayed and interleave-delayed aie just like stream-
append and interleave (Section 3..3), except that they take a delayed
aigument (like the integral pioceduie in Section 3..4). Tis postpones
looping in some cases (see Exeicise 4.1).
(define (stream-append-delayed s1 delayed-s2)
(if (stream-null? s1)
(force delayed-s2)
(cons-stream
(stream-car s1)
(stream-append-delayed
(stream-cdr s1)
delayed-s2))))
(define (interleave-delayed s1 delayed-s2)
(if (stream-null? s1)
(force delayed-s2)
(cons-stream
(stream-car s1)
(interleave-delayed
(force delayed-s2)
(delay (stream-cdr s1))))))
Stream-flatmap, which is used thioughout the queiy evaluatoi to map
a pioceduie ovei a stieam of fiames and combine the iesulting stieams
of fiames, is the stieam analog of the flatmap pioceduie intioduced
foi oidinaiy lists in Section 2.2.3. Unlike oidinaiy flatmap, howevei,
we accumulate the stieams with an inteileaving piocess, iathei than
simply appending them (see Exeicise 4.2 and Exeicise 4.3).
(define (stream-flatmap proc s)
(flatten-stream (stream-map proc s)))
(define (flatten-stream stream)
(if (stream-null? stream)
the-empty-stream
(interleave-delayed
(stream-car stream)
(delay (flatten-stream (stream-cdr stream))))))
Te evaluatoi also uses the following simple pioceduie to geneiate a
stieam consisting of a single element
(define (singleton-stream x)
(cons-stream x the-empty-stream))
4.4.4.7 ery Syntax Procedures
Type and contents, used by qeval (Section 4.4.4.2), specify that a special
foim is identied by the symbol in its car. Tey aie the same as the
type-tag and contents pioceduies in Section 2.4.2, except foi the eiioi
message.
(define (type exp)
(if (pair? exp)
(car exp)
(error "Unknown expression TYPE" exp)))
(define (contents exp)
(if (pair? exp)
(cdr exp)
(error "Unknown expression CONTENTS" exp)))
Te following pioceduies, used by query-driver-loop (in Section 4.4.4.1),
specify that iules and asseitions aie added to the data base by expies-
sions of the foim (assert! rule-or-assertion)
(define (assertion-to-be-added? exp)
(eq? (type exp) 'assert!))
(define (add-assertion-body exp) (car (contents exp)))
Heie aie the syntax denitions foi the and, or, not, and lisp-value
special foims (Section 4.4.4.2)
(define (empty-conjunction? exps) (null? exps))
(define (first-conjunct exps) (car exps))
(define (rest-conjuncts exps) (cdr exps))
(define (empty-disjunction? exps) (null? exps))
(define (first-disjunct exps) (car exps))
(define (rest-disjuncts exps) (cdr exps))
(define (negated-query exps) (car exps))
(define (predicate exps) (car exps))
(define (args exps) (cdr exps))
Te following thiee pioceduies dene the syntax of iules
(define (rule? statement)
(tagged-list? statement 'rule))
(define (conclusion rule) (cadr rule))
(define (rule-body rule)
(if (null? (cddr rule)) '(always-true) (caddr rule)))
Query-driver-loop (Section 4.4.4.1) calls query-syntax-process to tians-
foim pauein vaiiables in the expiession, which have the foim ?symbol,
into the inteinal foimat (? symbol). Tat is to say, a pauein such as
(job ?x ?y) is actually iepiesented inteinally by the system as (job
(? x) (? y)). Tis incieases the eciency of queiy piocessing, since
it means that the system can check to see if an expiession is a pauein
vaiiable by checking whethei the car of the expiession is the symbol
?, iathei than having to extiact chaiacteis fiom the symbol. Te syntax
tiansfoimation is accomplished by the following pioceduie
81
81
Most Lisp systems give the usei the ability to modify the oidinaiy read pio-
ceduie to peifoim such tiansfoimations by dening reoJer ocro c|orocers. Qoted
expiessions aie alieady handled in this way Te ieadei automatically tianslates
chines we design.
Most of the piimitive opeiations of oui iegistei machines aie veiy
simple. loi example, an opeiation might add the numbeis fetched fiom
two iegisteis, pioducing a iesult to be stoied into a thiid iegistei. Such
an opeiation can be peifoimed by easily desciibed haidwaie. ln oidei
to deal with list stiuctuie, howevei, we will also use the memoiy opeia-
tions car, cdr, and cons, which iequiie an elaboiate stoiage-allocation
mechanism. ln Section .3 we study theii implementation in teims of
moie elementaiy opeiations.
ln Section .4, afei we have accumulated expeiience foimulating
simple pioceduies as iegistei machines, we will design a machine that
caiiies out the algoiithmdesciibed by the metaciiculai evaluatoi of Sec-
tion 4.1. Tis will ll in the gap in oui undeistanding of howScheme ex-
piessions aie inteipieted, by pioviding an explicit model foi the mech-
anisms of contiol in the evaluatoi. ln Section . we will study a simple
compilei that tianslates Scheme piogiams into sequences of instiuc-
tions that can be executed diiectly with the iegisteis and opeiations of
the evaluatoi iegistei machine.
5.1 Designing Register Machines
To design a iegistei machine, we must design its Joo o|s (iegisteis
and opeiations) and the conro||er that sequences these opeiations. To
illustiate the design of a simple iegistei machine, let us examine Eu-
clids Algoiithm, which is used to compute the gieatest common divi-
soi (ccu) of two integeis. As we sawin Section 1.2., Euclids Algoiithm
can be caiiied out by an iteiative piocess, as specied by the following
pioceduie
8
(define (gcd a b)
(if (= b 0)
a
(gcd b (remainder a b))))
A machine to caiiy out this algoiithm must keep tiack of two numbeis,
a and b, so let us assume that these numbeis aie stoied in two iegisteis
with those names. Te basic opeiations iequiied aie testing whethei the
contents of iegistei b is zeio and computing the iemaindei of the con-
tents of iegistei a divided by the contents of iegistei b. Te iemaindei
opeiation is a complex piocess, but assume foi the moment that we have
a piimitive device that computes iemaindeis. On each cycle of the ccu
algoiithm, the contents of iegistei a must be ieplaced by the contents
of iegistei b, and the contents of b must be ieplaced by the iemaindei
of the old contents of a divided by the old contents of b. lt would be
convenient if these ieplacements could be done simultaneously, but in
oui model of iegistei machines we will assume that only one iegistei
can be assigned a new value at each step. To accomplish the ieplace-
ments, oui machine will use a thiid tempoiaiy iegistei, which we call
t. (liist the iemaindei will be placed in t, then the contents of b will be
placed in a, and nally the iemaindei stoied in t will be placed in b.)
We can illustiate the iegisteis and opeiations iequiied foi this ma-
chine by using the data-path diagiam shown in liguie .1. ln this di-
agiam, the iegisteis (a, b, and t) aie iepiesented by iectangles. Each
way to assign a value to a iegistei is indicated by an aiiow with an X
behind the head, pointing fiom the souice of data to the iegistei. We
can think of the X as a buuon that, when pushed, allows the value at
the souice to ow into the designated iegistei. Te label next to each
buuon is the name we will use to iefei to the buuon. Te names aie
aibitiaiy, and can be chosen to have mnemonic value (foi example, ab
9
a b
t
rem
a b
t r
b t
0
=
Figure 5.1: Data paths foi a ccu machine.
denotes pushing the buuon that assigns the contents of iegistei b to
iegistei a). Te souice of data foi a iegistei can be anothei iegistei (as
in the ab assignment), an opeiation iesult (as in the tr assignment),
oi a constant (a built-in value that cannot be changed, iepiesented in a
data-path diagiam by a tiiangle containing the constant).
An opeiation that computes a value fiom constants and the con-
tents of iegisteis is iepiesented in a data-path diagiam by a tiapezoid
containing a name foi the opeiation. loi example, the box maiked rem
in liguie .1 iepiesents an opeiation that computes the iemaindei of
the contents of the iegisteis a and b to which it is auached. Aiiows
(without buuons) point fiom the input iegisteis and constants to the
box, and aiiows connect the opeiations output value to iegisteis. A
test is iepiesented by a ciicle containing a name foi the test. loi exam-
ple, oui ccu machine has an opeiation that tests whethei the contents
of iegistei b is zeio. A test also has aiiows fiom its input iegisteis and
constants, but it has no output aiiows, its value is used by the contiollei
iathei than by the data paths. Oveiall, the data-path diagiam shows the
iegisteis and opeiations that aie iequiied foi the machine and howthey
0
start
no
done
yes
= t r
a b
b t
Figure 5.2: Contiollei foi a ccu machine.
must be connected. lf we view the aiiows as wiies and the X buuons as
switches, the data-path diagiam is veiy like the wiiing diagiam foi a
machine that could be constiucted fiom electiical components.
ln oidei foi the data paths to actually compute ccus, the buuons
must be pushed in the coiiect sequence. We will desciibe this sequence
in teims of a contiollei diagiam, as illustiated in liguie .2. Te ele-
ments of the contiollei diagiamindicate howthe data-path components
should be opeiated. Te iectangulai boxes in the contiollei diagiam
identify data-path buuons to be pushed, and the aiiows desciibe the
sequencing fiom one step to the next. Te diamond in the diagiam iep-
iesents a decision. One of the two sequencing aiiows will be followed,
depending on the value of the data-path test identied in the diamond.
We can inteipiet the contiollei in teims of a physical analogy Tink
of the diagiam as a maze in which a maible is iolling. When the mai-
ble iolls into a box, it pushes the data-path buuon that is named by the
box. When the maible iolls into a decision node (such as the test foi
1
b 0), it leaves the node on the path deteimined by the iesult of the
indicated test. Taken togethei, the data paths and the contiollei com-
pletely desciibe a machine foi computing ccus. We stait the contiollei
(the iolling maible) at the place maiked start, afei placing numbeis
in iegisteis a and b. When the contiollei ieaches done, we will nd the
value of the ccu in iegistei a.
Exercise 5.1: Design a iegistei machine to compute facto-
iials using the iteiative algoiithmspecied by the following
pioceduie. Diawdata-path and contiollei diagiams foi this
machine.
(define (factorial n)
(define (iter product counter)
(if (> counter n)
product
(iter (* counter product)
(+ counter 1))))
(iter 1 1))
5.1.1 A Language for Describing Register Machines
Data-path and contiollei diagiams aie adequate foi iepiesenting simple
machines such as ccu, but they aie unwieldy foi desciibing laige ma-
chines such as a Lisp inteipietei. To make it possible to deal with com-
plex machines, we will cieate a language that piesents, in textual foim,
all the infoimation given by the data-path and contiollei diagiams. We
will stait with a notation that diiectly miiiois the diagiams.
We dene the data paths of a machine by desciibing the iegisteis
and the opeiations. To desciibe a iegistei, we give it a name and specify
the buuons that contiol assignment to it. We give each of these buuons
2
a name and specify the souice of the data that enteis the iegistei undei
the buuons contiol. (Te souice is a iegistei, a constant, oi an opeia-
tion.) To desciibe an opeiation, we give it a name and specify its inputs
(iegisteis oi constants).
We dene the contiollei of a machine as a sequence of :nsrvc:ons
togethei with |o|e|s that identify enry o:ns in the sequence. An in-
stiuction is one of the following
Te name of a data-path buuon to push to assign a value to a
iegistei. (Tis coiiesponds to a box in the contiollei diagiam.)
A test instiuction, that peifoims a specied test.
A conditional bianch (branch instiuction) to a location indicated
by a contiollei label, based on the iesult of the pievious test. (Te
test and bianch togethei coiiespond to a diamond in the con-
tiollei diagiam.) lf the test is false, the contiollei should continue
with the next instiuction in the sequence. Otheiwise, the con-
tiollei should continue with the instiuction afei the label.
An unconditional bianch (goto instiuction) naming a contiollei
label at which to continue execution.
Te machine staits at the beginning of the contiollei instiuction se-
quence and stops when execution ieaches the end of the sequence. Ex-
cept when a bianch changes the ow of contiol, instiuctions aie exe-
cuted in the oidei in which they aie listed.
Figure 5.3: A specication of the ccu machine.
(data-paths
(registers
3
((name a)
(buttons ((name a<-b) (source (register b)))))
((name b)
(buttons ((name b<-t) (source (register t)))))
((name t)
(buttons ((name t<-r) (source (operation rem))))))
(operations
((name rem) (inputs (register a) (register b)))
((name =) (inputs (register b) (constant 0)))))
(controller
test-b , label
(test =) , test
(branch (label gcd-done)) , conditional bianch
(t<-r) , buuon push
(a<-b) , buuon push
(b<-t) , buuon push
(goto (label test-b)) , unconditional bianch
gcd-done) , label
liguie .3 shows the ccu machine desciibed in this way. Tis example
only hints at the geneiality of these desciiptions, since the ccu machine
is a veiy simple case Each iegistei has only one buuon, and each buuon
and test is used only once in the contiollei.
Unfoitunately, it is dicult to iead such a desciiption. ln oidei to
undeistand the contiollei instiuctions we must constantly iefei back
to the denitions of the buuon names and the opeiation names, and to
undeistand what the buuons do we may have to iefei to the denitions
of the opeiation names. We will thus tiansfoimoui notation to combine
the infoimation fiom the data-path and contiollei desciiptions so that
we see it all togethei.
To obtain this foim of desciiption, we will ieplace the aibitiaiy but-
ton and opeiation names by the denitions of theii behavioi. Tat is,
4
instead of saying (in the contiollei) Push buuon tr and sepaiately
saying (in the data paths) Buuon tr assigns the value of the rem op-
eiation to iegistei t and Te rem opeiations inputs aie the contents
of iegisteis a and b, we will say (in the contiollei) Push the buuon
that assigns to iegistei t the value of the rem opeiation on the con-
tents of iegisteis a and b. Similaily, instead of saying (in the contiollei)
Peifoim the = test and sepaiately saying (in the data paths) Te =
test opeiates on the contents of iegistei b and the constant 0, we will
say Peifoim the = test on the contents of iegistei b and the constant
0. We will omit the data-path desciiption, leaving only the contiollei
sequence. Tus, the ccu machine is desciibed as follows
(controller
test-b
(test (op =) (reg b) (const 0))
(branch (label gcd-done))
(assign t (op rem) (reg a) (reg b))
(assign a (reg b))
(assign b (reg t))
(goto (label test-b))
gcd-done)
Tis foim of desciiption is easiei to iead than the kind illustiated in
liguie .3, but it also has disadvantages
lt is moie veibose foi laige machines, because complete desciip-
tions of the data-path elements aie iepeated whenevei the ele-
ments aie mentioned in the contiollei instiuction sequence. (Tis
is not a pioblem in the ccu example, because each opeiation and
buuon is used only once.) Moieovei, iepeating the data-path de-
sciiptions obscuies the actual data-path stiuctuie of the machine,
it is not obvious foi a laige machine how many iegisteis, opeia-
tions, and buuons theie aie and how they aie inteiconnected.
Because the contiollei instiuctions in a machine denition look
like Lisp expiessions, it is easy to foiget that they aie not aibitiaiy
Lisp expiessions. Tey can notate only legal machine opeiations.
loi example, opeiations can opeiate diiectly only on constants
and the contents of iegisteis, not on the iesults of othei opeia-
tions.
ln spite of these disadvantages, we will use this iegistei-machine lan-
guage thioughout this chaptei, because we will be moie conceined with
undeistanding contiolleis than with undeistanding the elements and
connections in data paths. We should keep in mind, howevei, that data-
path design is ciucial in designing ieal machines.
Exercise 5.2: Use the iegistei-machine language to desciibe
the iteiative factoiial machine of Exeicise .1.
Actions
Let us modify the ccu machine so that we can type in the numbeis
whose ccu we want and get the answei piinted at oui teiminal. We
will not discuss how to make a machine that can iead and piint, but
will assume (as we do when we use read and display in Scheme) that
they aie available as piimitive opeiations.
1
Read is like the opeiations we have been using in that it pioduces
a value that can be stoied in a iegistei. But read does not take inputs
fiomany iegisteis, its value depends on something that happens outside
the paits of the machine we aie designing. We will allow oui machines
1
Tis assumption glosses ovei a gieat deal of complexity. Usually a laige poition of
the implementation of a Lisp system is dedicated to making ieading and piinting woik.
(controller
gcd-loop
(assign a (op read))
(assign b (op read))
test-b
(test (op =)
(reg b)
(const 0))
(branch (label gcd-done))
(assign t
(op rem)
(reg a)
(reg b))
(assign a (reg b))
(assign b (reg t))
(goto (label test-b))
gcd-done
(perform (op print)
(reg a))
(goto (label gcd-loop)))
read
a b
t
rem
a b
t r
b t
0
=
print
a rd b rd
P
Figure 5.4: A ccu machine that ieads inputs and piints iesults.
opeiations to have such behavioi, and thus will diaw and notate the use
of read just as we do any othei opeiation that computes a value.
Print, on the othei hand, dieis fiom the opeiations we have been
using in a fundamental way lt does not pioduce an output value to be
stoied in a iegistei. Tough it has an eect, this eect is not on a pait of
the machine we aie designing. We will iefei to this kind of opeiation as
an oc:on. We will iepiesent an action in a data-path diagiam just as we
iepiesent an opeiation that computes a valueas a tiapezoid that con-
tains the name of the action. Aiiows point to the action box fiom any
inputs (iegisteis oi constants). We also associate a buuon with the ac-
tion. Pushing the buuon makes the action happen. To make a contiollei
ln oidei to desciibe
memoiy opeiations, we use two piimitive Scheme pioceduies foi ma-
nipulating vectois
(vector-ref vector n) ietuins the n
th
element of the vectoi.
(vector-set! vector n value) sets the n
th
element of the
vectoi to the designated value.
We could iepiesent memoiy as lists of items. Howevei, the access time would then
not be independent of the index, since accessing the n
th
element of a list iequiies n 1
cdr opeiations.
24
loi example, if v is a vectoi, then (vector-ref v 5) gets the fh entiy
in the vectoi v and (vector-set! v 5 7) changes the value of the fh
entiy of the vectoi v to .
Tis is piecisely the same tagged data idea we intioduced in Chaptei 2 foi dealing
with geneiic opeiations. Heie, howevei, the data types aie included at the piimitive
machine level iathei than constiucted thiough the use of lists.
2
same (eq?) if theii pointeis aie identical.
8
liguie .14 illustiates the use
of this method to iepiesent the list ((1 2) 3 4), whose box-and-pointei
diagiam is also shown. We use leuei piexes to denote the data-type
infoimation. Tus, a pointei to the paii with index is denoted p5, the
empty list is denoted by the pointei e0, and a pointei to the numbei 4
is denoted n4. ln the box-and-pointei diagiam, we have indicated at the
lowei lef of each paii the vectoi index that species wheie the car and
cdr of the paii aie stoied. Te blank locations in the-cars and the-
cdrs may contain paits of othei list stiuctuies (not of inteiest heie).
A pointei to a numbei, such as n4, might consist of a type indicating
numeiic data togethei with the actual iepiesentation of the numbei 4.
9
To deal with numbeis that aie too laige to be iepiesented in the xed
amount of space allocated foi a single pointei, we could use a distinct
|:gnv data type, foi which the pointei designates a list in which the
paits of the numbei aie stoied.
10
8
Type infoimation may be encoded in a vaiiety of ways, depending on the details of
the machine on which the Lisp system is to be implemented. Te execution eciency
of Lisp piogiams will be stiongly dependent on how cleveily this choice is made, but it
is dicult to foimulate geneial design iules foi good choices. Te most stiaightfoiwaid
way to implement typed pointeis is to allocate a xed set of bits in each pointei to be a
ye e|J that encodes the data type. lmpoitant questions to be addiessed in designing
such a iepiesentation include the following How many type bits aie iequiied` How
laige must the vectoi indices be` How eciently can the piimitive machine instiuc-
tions be used to manipulate the type elds of pointeis` Machines that include special
haidwaie foi the ecient handling of type elds aie said to have oggeJ orc|:ecvres.
9
Tis decision on the iepiesentation of numbeis deteimines whethei eq?, which
tests equality of pointeis, can be used to test foi equality of numbeis. lf the pointei
contains the numbei itself, then equal numbeis will have the same pointei. But if the
pointei contains the index of a location wheie the numbei is stoied, equal numbeis
will be guaianteed to have equal pointeis only if we aie caieful nevei to stoie the same
numbei in moie than one location.
10
Tis is just like wiiting a numbei as a sequence of digits, except that each digit
2
0 1 2 3 4 5 6 7 8 ... Index
the-cars
the-cdrs
p5 n3 n4 n1 n2 ...
p2 p4 e0 p7 e0 ...
((1 2) 3 4)
1 2 4
5 7
4
1 2
3
Figure 5.14: Box-and-pointei and memoiy-vectoi iepie-
sentations of the list ((1 2) 3 4).
A symbol might be iepiesented as a typed pointei that designates a
sequence of the chaiacteis that foim the symbols piinted iepiesenta-
tion. Tis sequence is constiucted by the Lisp ieadei when the chaiactei
stiing is initially encounteied in input. Since we want two instances of
a symbol to be iecognized as the same symbol by eq? and we want
eq? to be a simple test foi equality of pointeis, we must ensuie that
if the ieadei sees the same chaiactei stiing twice, it will use the same
pointei (to the same sequence of chaiacteis) to iepiesent both occui-
iences. To accomplish this, the ieadei maintains a table, tiaditionally
called the o|orroy, of all the symbols it has evei encounteied. When the
ieadei encounteis a chaiactei stiing and is about to constiuct a symbol,
is a numbei between 0 and the laigest numbei that can be stoied in a single pointei.
2
it checks the obaiiay to see if it has evei befoie seen the same chaiactei
stiing. lf it has not, it uses the chaiacteis to constiuct a new symbol (a
typed pointei to a new chaiactei sequence) and enteis this pointei in
the obaiiay. lf the ieadei has seen the stiing befoie, it ietuins the sym-
bol pointei stoied in the obaiiay. Tis piocess of ieplacing chaiactei
stiings by unique pointeis is called :nern:ng symbols.
Implementing the primitive list operations
Given the above iepiesentation scheme, we can ieplace each piimi-
tive list opeiation of a iegistei machine with one oi moie piimitive
vectoi opeiations. We will use two iegisteis, the-cars and the-cdrs,
to identify the memoiy vectois, and will assume that vector-ref and
vector-set! aie available as piimitive opeiations. We also assume that
numeiic opeiations on pointeis (such as inciementing a pointei, using
a paii pointei to index a vectoi, oi adding two numbeis) use only the
index poition of the typed pointei.
loi example, we can make a iegistei machine suppoit the instiuc-
tions
(assign re
1
(op car) (reg re
2
))
(assign re
1
(op cdr) (reg re
2
))
if we implement these, iespectively, as
(assign re
1
(op vector-ref) (reg the-cars) (reg re
2
))
(assign re
1
(op vector-ref) (reg the-cdrs) (reg re
2
))
Te instiuctions
(perform (op set-car!) (reg re
1
) (reg re
2
))
(perform (op set-cdr!) (reg re
1
) (reg re
2
))
aie implemented as
28
(perform
(op vector-set!) (reg the-cars) (reg re
1
) (reg re
2
))
(perform
(op vector-set!) (reg the-cdrs) (reg re
1
) (reg re
2
))
Cons is peifoimed by allocating an unused index and stoiing the aigu-
ments to cons in the-cars and the-cdrs at that indexed vectoi posi-
tion. We piesume that theie is a special iegistei, free, that always holds
a paii pointei containing the next available index, and that we can in-
ciement the index pait of that pointei to nd the next fiee location.
11
loi example, the instiuction
(assign re
1
(op cons) (reg re
2
) (reg re
3
))
is implemented as the following sequence of vectoi opeiations
12
(perform
(op vector-set!) (reg the-cars) (reg free) (reg re
2
))
(perform
(op vector-set!) (reg the-cdrs) (reg free) (reg re
3
))
(assign re
1
(reg free))
(assign free (op +) (reg free) (const 1))
Te eq? opeiation
(op eq?) (reg re
1
) (reg re
2
)
simply tests the equality of all elds in the iegisteis, and piedicates such
as pair?, null?, symbol?, and number? need only check the type eld.
11
Teie aie othei ways of nding fiee stoiage. loi example, we could link togethei
all the unused paiis into a [ree |:s. Oui fiee locations aie consecutive (and hence can
be accessed by inciementing a pointei) because we aie using a compacting gaibage
collectoi, as we will see in Section .3.2.
12
Tis is essentially the implementation of cons in teims of set-car! and set-cdr!,
as desciibed in Section 3.3.1. Te opeiation get-new-pair used in that implementation
is iealized heie by the free pointei.
29
Implementing stacks
Although oui iegistei machines use stacks, we need do nothing special
heie, since stacks can be modeled in teims of lists. Te stack can be a
list of the saved values, pointed to by a special iegistei the-stack. Tus,
(save reg) can be implemented as
(assign the-stack (op cons) (reg reg) (reg the-stack))
Similaily, (restore reg) can be implemented as
(assign reg (op car) (reg the-stack))
(assign the-stack (op cdr) (reg the-stack))
and (perform (op initialize-stack)) can be implemented as
(assign the-stack (const ()))
Tese opeiations can be fuithei expanded in teims of the vectoi opeia-
tions given above. ln conventional computei aichitectuies, howevei, it
is usually advantageous to allocate the stack as a sepaiate vectoi. Ten
pushing and popping the stack can be accomplished by inciementing
oi deciementing an index into that vectoi.
Exercise 5.20: Diaw the box-and-pointei iepiesentation
and the memoiy-vectoi iepiesentation (as in liguie .14)
of the list stiuctuie pioduced by
(define x (cons 1 2))
(define y (list x x))
with the free pointei initially p1. What is the nal value of
free ` What pointeis iepiesent the values of x and y `
Exercise 5.21: lmplement iegistei machines foi the follow-
ing pioceduies. Assume that the list-stiuctuie memoiy op-
eiations aie available as machine piimitives.
30
a. Recuisive count-leaves
(define (count-leaves tree)
(cond ((null? tree) 0)
((not (pair? tree)) 1)
(else (+ (count-leaves (car tree))
(count-leaves (cdr tree))))))
b. Recuisive count-leaves with explicit countei
(define (count-leaves tree)
(define (count-iter tree n)
(cond ((null? tree) n)
((not (pair? tree)) (+ n 1))
(else
(count-iter (cdr tree)
(count-iter (car tree)
n)))))
(count-iter tree 0))
Exercise 5.22: Exeicise 3.12 of Section 3.3.1 piesented an
append pioceduie that appends two lists to foim a new list
and an append! pioceduie that splices two lists togethei.
Design a iegistei machine to implement each of these pio-
ceduies. Assume that the list-stiuctuie memoiy opeiations
aie available as piimitive opeiations.
5.3.2 Maintaining the Illusion of Infinite Memory
Te iepiesentation method outlined in Section .3.1 solves the pioblem
of implementing list stiuctuie, piovided that we have an innite amount
of memoiy. With a ieal computei we will eventually iun out of fiee
31
space in which to constiuct new paiis.
13
Howevei, most of the paiis
geneiated in a typical computation aie used only to hold inteimediate
iesults. Afei these iesults aie accessed, the paiis aie no longei needed
they aie gor|oge. loi instance, the computation
(accumulate + 0 (filter odd? (enumerate-interval 0 n)))
constiucts two lists the enumeiation and the iesult of lteiing the enu-
meiation. When the accumulation is complete, these lists aie no longei
needed, and the allocated memoiy can be ieclaimed. lf we can aiiange
to collect all the gaibage peiiodically, and if this tuins out to iecycle
memoiy at about the same iate at which we constiuct newpaiis, we will
have pieseived the illusion that theie is an innite amount of memoiy.
ln oidei to iecycle paiis, we must have a way to deteimine which
allocated paiis aie not needed (in the sense that theii contents can no
longei inuence the futuie of the computation). Te method we shall
examine foi accomplishing this is known as gor|oge co||ec:on. Gaibage
collection is based on the obseivation that, at any moment in a Lisp
inteipietation, the only objects that can aect the futuie of the compu-
tation aie those that can be ieached by some succession of car and cdr
opeiations staiting fiom the pointeis that aie cuiiently in the machine
13
Tis may not be tiue eventually, because memoiies may get laige enough so that
it would be impossible to iun out of fiee memoiy in the lifetime of the computei. loi
example, theie aie about 3 10
13
micioseconds in a yeai, so if we weie to cons once
pei miciosecond we would need about 10
1
cells of memoiy to build a machine that
could opeiate foi 30 yeais without iunning out of memoiy. Tat much memoiy seems
absuidly laige by todays standaids, but it is not physically impossible. On the othei
hand, piocessois aie geuing fastei and a futuie computei may have laige numbeis of
piocessois opeiating in paiallel on a single memoiy, so it may be possible to use up
memoiy much fastei than we have postulated.
32
iegisteis.
14
Any memoiy cell that is not so accessible may be iecycled.
Teie aie many ways to peifoim gaibage collection. Te method
we shall examine heie is called soonJcoy. Te basic idea is to divide
memoiy into two halves woiking memoiy and fiee memoiy. When
cons constiucts paiis, it allocates these in woiking memoiy. When woik-
ing memoiy is full, we peifoim gaibage collection by locating all the
use- ful paiis in woiking memoiy and copying these into consecutive
locations in fiee memoiy. (Te useful paiis aie located by tiacing all the
car and cdr pointeis, staiting with the machine iegisteis.) Since we do
not copy the gaibage, theie will piesumably be additional fiee memoiy
that we can use to allocate new paiis. ln addition, nothing in the woik-
ing memoiy is needed, since all the useful paiis in it have been copied.
Tus, if we intei- change the ioles of woiking memoiy and fiee mem-
oiy, we can continue piocessing, new paiis will be allocated in the new
woiking memoiy (which was the old fiee memoiy). When this is full,
we can copy the useful paiis into the new fiee memoiy (which was the
old woiking memoiy).
1
14
We assume heie that the stack is iepiesented as a list as desciibed in Section .3.1,
so that items on the stack aie accessible via the pointei in the stack iegistei.
1
Tis idea was invented and ist implemented by Minsky, as pait of the imple-
mentation of Lisp foi the PDP-1 at the xi1 Reseaich Laboiatoiy of Electionics. lt was
fuithei developed by lenichel and Yochelson (199) foi use in the Lisp implementa-
tion foi the Multics time-shaiing system. Latei, Bakei (198) developed a ieal-time
veision of the method, which does not iequiie the computation to stop duiing gaibage
collection. Bakeis idea was extended by Hewiu, Liebeiman, and Moon (see Liebeiman
and Hewiu 1983) to take advantage of the fact that some stiuctuie is moie volatile and
othei stiuctuie is moie peimanent.
An alteinative commonly used gaibage-collection technique is the or|s+ee
method. Tis consists of tiacing all the stiuctuie accessible fiom the machine iegisteis
and maiking each paii we ieach. We then scan all of memoiy, and any location that is
unmaiked is swept up as gaibage and made available foi ieuse. A full discussion of
33
Implementation of a stop-and-copy garbage collector
We now use oui iegistei-machine language to desciibe the stop-and-
copy algoiithm in moie detail. We will assume that theie is a iegistei
called root that contains a pointei to a stiuctuie that eventually points
at all accessible data. Tis can be aiianged by stoiing the contents of
all the machine iegisteis in a pie-allocated list pointed at by root just
befoie staiting gaibage collection.
1
We also assume that, in addition to
the cuiient woiking memoiy, theie is fiee memoiy available into which
we can copy the useful data. Te cuiient woiking memoiy consists of
vectois whose base addiesses aie in iegisteis called the-cars and the-
cdrs, and the fiee memoiy is in iegisteis called new-cars and new-cdrs.
Gaibage collection is tiiggeied when we exhaust the fiee cells in
the cuiient woiking memoiy, that is, when a cons opeiation auempts
to inciement the free pointei beyond the end of the memoiy vectoi.
When the gaibage-collection piocess is complete, the root pointei will
point into the new memoiy, all objects accessible fiom the root will
have been moved to the newmemoiy, and the free pointei will indicate
the next place in the new memoiy wheie a new paii can be allocated.
ln addition, the ioles of woiking memoiy and new memoiy will have
the maik-sweep method can be found in Allen 198.
Te Minsky-lenichel-Yochelson algoiithmis the dominant algoiithmin use foi laige-
memoiy systems because it examines only the useful pait of memoiy. Tis is in contiast
to maik-sweep, in which the sweep phase must check all of memoiy. A second advan-
tage of stop-and-copy is that it is a cooc:ng gaibage collectoi. Tat is, at the end
of the gaibage-collection phase the useful data will have been moved to consecutive
memoiy locations, with all gaibage paiis compiessed out. Tis can be an extiemely
impoitant peifoimance consideiation in machines with viitual memoiy, in which ac-
cesses to widely sepaiated memoiy addiesses may iequiie extia paging opeiations.
1
Tis list of iegisteis does not include the iegisteis used by the stoiage-allocation
systemroot, the-cars, the-cdrs, and the othei iegisteis that will be intioduced in
this section.
34
been inteichangednew paiis will be constiucted in the new memoiy,
beginning at the place indicated by free, and the (pievious) woiking
memoiy will be available as the new memoiy foi the next gaibage col-
lection. liguie .1 shows the aiiangement of memoiy just befoie and
just afei gaibage collection.
Te state of the gaibage-collection piocess is contiolled by main-
taining two pointeis free and scan. Tese aie initialized to point to
the beginning of the new memoiy. Te algoiithm begins by ielocating
the paii pointed at by root to the beginning of the new memoiy. Te
paii is copied, the root pointei is adjusted to point to the new location,
and the free pointei is inciemented. ln addition, the old location of the
paii is maiked to show that its contents have been moved. Tis maik-
ing is done as follows ln the car position, we place a special tag that
signals that this is an alieady-moved object. (Such an object is tiadition-
ally called a |ro|en |eor.)
1
ln the cdr position we place a [or+orJ:ng
oJJress that points at the location to which the object has been moved.
Afei ielocating the ioot, the gaibage collectoi enteis its basic cy-
cle. At each step in the algoiithm, the scan pointei (initially pointing
at the ielocated ioot) points at a paii that has been moved to the new
memoiy but whose car and cdr pointeis still iefei to objects in the old
memoiy. Tese objects aie each ielocated, and the scan pointei is in-
ciemented. To ielocate an object (foi example, the object indicated by
the car pointei of the paii we aie scanning) we check to see if the object
has alieady been moved (as indicated by the piesence of a bioken-heait
tag in the car position of the object). lf the object has not alieady been
moved, we copy it to the place indicated by free, update free, set up
a bioken heait at the objects old location, and update the pointei to
1
Te teim |ro|en |eor was coined by David Ciessey, who wiote a gaibage collectoi
foi MDL, a dialect of Lisp developed at xi1 duiing the eaily 190s.
3
free
free
Just before garbage collection
mixture of useful data and garbage
free memory
Just aer garbage collection
discarded memory
useful data free area
working
memory
free
memory
new
free
memory
new
working
memory
the-cars
the-cdrs
the-cars
the-cdrs
new-cars
new-cdrs
new-cars
new-cdrs
Figure 5.15: Reconguiation of memoiy by the gaibage-
collection piocess.
3
the object (in this example, the car pointei of the paii we aie scanning)
to point to the new location. lf the object has alieady been moved, its
foiwaiding addiess (found in the cdr position of the bioken heait) is
substituted foi the pointei in the paii being scanned. Eventually, all ac-
cessible objects will have been moved and scanned, at which point the
scan pointei will oveitake the free pointei and the piocess will teimi-
nate.
We can specify the stop-and-copy algoiithm as a sequence of in-
stiuctions foi a iegistei machine. Te basic step of ielocating an object
is accomplished by a subioutine called relocate-old-result-in-new.
Tis subioutine gets its aigument, a pointei to the object to be ielo-
cated, fiom a iegistei named old. lt ielocates the designated object (in-
ciementing free in the piocess), puts a pointei to the ielocated object
into a iegistei called new, and ietuins by bianching to the entiy point
stoied in the iegistei relocate-continue. To begin gaibage collection,
we invoke this subioutine to ielocate the root pointei, afei initializing
free and scan. When the ielocation of root has been accomplished, we
install the new pointei as the new root and entei the main loop of the
gaibage collectoi.
begin-garbage-collection
(assign free (const 0))
(assign scan (const 0))
(assign old (reg root))
(assign relocate-continue (label reassign-root))
(goto (label relocate-old-result-in-new))
reassign-root
(assign root (reg new))
(goto (label gc-loop))
ln the main loop of the gaibage collectoi we must deteimine whethei
theie aie any moie objects to be scanned. We do this by testing whethei
3
the scan pointei is coincident with the free pointei. lf the pointeis aie
equal, then all accessible objects have been ielocated, and we bianch
to gc-flip, which cleans things up so that we can continue the intei-
iupted computation. lf theie aie still paiis to be scanned, we call the
ielocate subioutine to ielocate the car of the next paii (by placing the
car pointei in old). Te relocate-continue iegistei is set up so that
the subioutine will ietuin to update the car pointei.
gc-loop
(test (op =) (reg scan) (reg free))
(branch (label gc-flip))
(assign old (op vector-ref) (reg new-cars) (reg scan))
(assign relocate-continue (label update-car))
(goto (label relocate-old-result-in-new))
At update-car, we modify the car pointei of the paii being scanned,
then pioceed to ielocate the cdr of the paii. We ietuin to update-cdr
when that ielocation has been accomplished. Afei ielocating and up-
dating the cdr, we aie nished scanning that paii, so we continue with
the main loop.
update-car
(perform (op vector-set!)
(reg new-cars)
(reg scan)
(reg new))
(assign old (op vector-ref) (reg new-cdrs) (reg scan))
(assign relocate-continue (label update-cdr))
(goto (label relocate-old-result-in-new))
update-cdr
(perform (op vector-set!)
(reg new-cdrs)
(reg scan)
(reg new))
38
(assign scan (op +) (reg scan) (const 1))
(goto (label gc-loop))
Te subioutine relocate-old-result-in-new ielocates objects as fol-
lows lf the object to be ielocated (pointed at by old) is not a paii, then
we ietuin the same pointei to the object unchanged (in new). (loi exam-
ple, we may be scanning a paii whose car is the numbei 4. lf we iepie-
sent the car by n4, as desciibed in Section .3.1, then we want the ielo-
cated car pointei to still be n4.) Otheiwise, we must peifoim the ielo-
cation. lf the car position of the paii to be ielocated contains a bioken-
heait tag, then the paii has in fact alieady been moved, so we ietiieve
the foiwaiding addiess (fiom the cdr position of the bioken heait) and
ietuin this in new. lf the pointei in old points at a yet-unmoved paii,
then we move the paii to the ist fiee cell in new memoiy (pointed at
by free) and set up the bioken heait by stoiing a bioken-heait tag and
foiwaiding addiess at the old location. Relocate-old-result-in-new
uses a iegistei oldcr to hold the car oi the cdr of the object pointed at
by old.
18
relocate-old-result-in-new
(test (op pointer-to-pair?) (reg old))
(branch (label pair))
(assign new (reg old))
(goto (reg relocate-continue))
pair
(assign oldcr (op vector-ref) (reg the-cars) (reg old))
18
Te gaibage collectoi uses the low-level piedicate pointer-to-pair? instead of
the list-stiuctuie pair? opeiation because in a ieal systemtheie might be vaiious things
that aie tieated as paiis foi gaibage-collection puiposes. loi example, in a Scheme
system that confoims to the irrr standaid a pioceduie object may be implemented as
a special kind of paii that doesnt satisfy the pair? piedicate. loi simulation puiposes,
pointer-to-pair? can be implemented as pair?.
39
(test (op broken-heart?) (reg oldcr))
(branch (label already-moved))
(assign new (reg free)) , new location foi paii
,, Update free pointei.
(assign free (op +) (reg free) (const 1))
,, Copy the car and cdr to new memoiy.
(perform (op vector-set!)
(reg new-cars) (reg new) (reg oldcr))
(assign oldcr (op vector-ref) (reg the-cdrs) (reg old))
(perform (op vector-set!)
(reg new-cdrs) (reg new) (reg oldcr))
,, Constiuct the bioken heait.
(perform (op vector-set!)
(reg the-cars) (reg old) (const broken-heart))
(perform
(op vector-set!) (reg the-cdrs) (reg old) (reg new))
(goto (reg relocate-continue))
already-moved
(assign new (op vector-ref) (reg the-cdrs) (reg old))
(goto (reg relocate-continue))
At the veiy end of the gaibage-collection piocess, we inteichange the
iole of old and new memoiies by inteichanging pointeis inteichanging
the-cars with new-cars, and the-cdrs with new-cdrs. We will then
be ieady to peifoim anothei gaibage collection the next time memoiy
iuns out.
gc-flip
(assign temp (reg the-cdrs))
(assign the-cdrs (reg new-cdrs))
(assign new-cdrs (reg temp))
(assign temp (reg the-cars))
(assign the-cars (reg new-cars))
(assign new-cars (reg temp))
40
5.4 The Explicit-Control Evaluator
ln Section .1 we saw how to tiansfoim simple Scheme piogiams into
desciiptions of iegistei machines. We will nowpeifoimthis tiansfoima-
tion on a moie complex piogiam, the metaciiculai evaluatoi of Section
4.1.1Section 4.1.4, which shows how the behavioi of a Scheme intei-
pietei can be desciibed in teims of the pioceduies eval and apply. Te
e:|:c:conro| e+o|voor that we develop in this section shows how the
undeilying pioceduie-calling and aigument-passing mechanisms used
in the evaluation piocess can be desciibed in teims of opeiations on ieg-
isteis and stacks. ln addition, the explicit-contiol evaluatoi can seive as
an implementation of a Scheme inteipietei, wiiuen in a language that
is veiy similai to the native machine language of conventional comput-
eis. Te evaluatoi can be executed by the iegistei-machine simulatoi
of Section .2. Alteinatively, it can be used as a staiting point foi build-
ing a machine-language implementation of a Scheme evaluatoi, oi even
a special-puipose machine foi evaluating Scheme expiessions. liguie
.1 shows such a haidwaie implementation a silicon chip that acts as
an evaluatoi foi Scheme. Te chip designeis staited with the data-path
and contiollei specications foi a iegistei machine similai to the evalu-
atoi desciibed in this section and used design automation piogiams to
constiuct the integiated-ciicuit layout.
19
Registers and operations
ln designing the explicit-contiol evaluatoi, we must specify the opeia-
tions to be used in oui iegistei machine. We desciibed the metaciiculai
evaluatoi in teims of abstiact syntax, using pioceduies such as quoted?
19
See Batali et al. 1982 foi moie infoimation on the chip and the method by which it
was designed.
41
Figure 5.16: Asilicon-chip implementation of an evaluatoi
foi Scheme.
and make-procedure. ln implementing the iegistei machine, we could
expand these pioceduies into sequences of elementaiy list-stiuctuie
memoiy opeiations, and implement these opeiations on oui iegistei
machine. Howevei, this would make oui evaluatoi veiy long, obscui-
ing the basic stiuctuie with details. To claiify the piesentation, we will
include as piimitive opeiations of the iegistei machine the syntax pio-
ceduies given in Section 4.1.2 and the pioceduies foi iepiesenting en-
viionments and othei iun-time data given in sections Section 4.1.3 and
Section 4.1.4. ln oidei to completely specify an evaluatoi that could be
piogiammed in a low-level machine language oi implemented in haid-
42
waie, we would ieplace these opeiations by moie elementaiy opeia-
tions, using the list-stiuctuie implementation we desciibed in Section
.3.
Oui Scheme evaluatoi iegistei machine includes a stack and seven
iegisteis exp, env, val, continue, proc, argl, and unev. Exp is used to
hold the expiession to be evaluated, and env contains the enviionment
in which the evaluation is to be peifoimed. At the end of an evalua-
tion, val contains the value obtained by evaluating the expiession in
the designated enviionment. Te continue iegistei is used to imple-
ment iecuision, as explained in Section .1.4. (Te evaluatoi needs to
call itself iecuisively, since evaluating an expiession iequiies evaluat-
ing its subexpiessions.) Te iegisteis proc, argl, and unev aie used in
evaluating combinations.
We will not piovide a data-path diagiam to show how the iegis-
teis and opeiations of the evaluatoi aie connected, noi will we give the
complete list of machine opeiations. Tese aie implicit in the evalua-
tois contiollei, which will be piesented in detail.
5.4.1 The Core of the Explicit-Control Evaluator
Te cential element in the evaluatoi is the sequence of instiuctions be-
ginning at eval-dispatch. Tis coiiesponds to the eval pioceduie of
the metaciiculai evaluatoi desciibed in Section 4.1.1. When the con-
tiollei staits at eval-dispatch, it evaluates the expiession specied by
exp in the enviionment specied by env. When evaluation is complete,
the contiollei will go to the entiy point stoied in continue, and the val
iegistei will hold the value of the expiession. As with the metaciiculai
eval, the stiuctuie of eval-dispatch is a case analysis on the syntactic
43
type of the expiession to be evaluated.
20
eval-dispatch
(test (op self-evaluating?) (reg exp))
(branch (label ev-self-eval))
(test (op variable?) (reg exp))
(branch (label ev-variable))
(test (op quoted?) (reg exp))
(branch (label ev-quoted))
(test (op assignment?) (reg exp))
(branch (label ev-assignment))
(test (op definition?) (reg exp))
(branch (label ev-definition))
(test (op if?) (reg exp))
(branch (label ev-if))
(test (op lambda?) (reg exp))
(branch (label ev-lambda))
(test (op begin?) (reg exp))
(branch (label ev-begin))
(test (op application?) (reg exp))
(branch (label ev-application))
(goto (label unknown-expression-type))
Evaluating simple expressions
Numbeis and stiings (which aie self-evaluating), vaiiables, quotations,
and lambda expiessions have no subexpiessions to be evaluated. loi
20
ln oui contiollei, the dispatch is wiiuen as a sequence of test and branch in-
stiuctions. Alteinatively, it could have been wiiuen in a data-diiected style (and in a
ieal system it piobably would have been) to avoid the need to peifoim sequential tests
and to facilitate the denition of new expiession types. A machine designed to iun Lisp
would piobably include a dispatch-on-type instiuction that would eciently execute
such data-diiected dispatches.
44
these, the evaluatoi simply places the coiiect value in the val iegistei
and continues execution at the entiy point specied by continue. Eval-
uation of simple expiessions is peifoimed by the following contiollei
code
ev-self-eval
(assign val (reg exp))
(goto (reg continue))
ev-variable
(assign val (op lookup-variable-value) (reg exp) (reg env))
(goto (reg continue))
ev-quoted
(assign val (op text-of-quotation) (reg exp))
(goto (reg continue))
ev-lambda
(assign unev (op lambda-parameters) (reg exp))
(assign exp (op lambda-body) (reg exp))
(assign val (op make-procedure) (reg unev) (reg exp) (reg env))
(goto (reg continue))
Obseive how ev-lambda uses the unev and exp iegisteis to hold the pa-
iameteis and body of the lambda expiession so that they can be passed
to the make-procedure opeiation, along with the enviionment in env.
Evaluating procedure applications
Apioceduie application is specied by a combination containing an op-
eiatoi and opeiands. Te opeiatoi is a subexpiession whose value is a
pioceduie, and the opeiands aie subexpiessions whose values aie the
aiguments to which the pioceduie should be applied. Te metaciicu-
lai eval handles applications by calling itself iecuisively to evaluate
each element of the combination, and then passing the iesults to apply,
which peifoims the actual pioceduie application. Te explicit-contiol
4
evaluatoi does the same thing, these iecuisive calls aie implemented
by goto instiuctions, togethei with use of the stack to save iegisteis
that will be iestoied afei the iecuisive call ietuins. Befoie each call we
will be caieful to identify which iegisteis must be saved (because theii
values will be needed latei).
21
We begin the evaluation of an application by evaluating the opeia-
toi to pioduce a pioceduie, which will latei be applied to the evaluated
opeiands. To evaluate the opeiatoi, we move it to the exp iegistei and go
to eval-dispatch. Te enviionment in the env iegistei is alieady the
coiiect one in which to evaluate the opeiatoi. Howevei, we save env
because we will need it latei to evaluate the opeiands. We also extiact
the opeiands into unev and save this on the stack. We set up continue
so that eval-dispatch will iesume at ev-appl-did-operator afei the
opeiatoi has been evaluated. liist, howevei, we save the old value of
continue, which tells the contiollei wheie to continue afei the appli-
cation.
ev-application
(save continue)
(save env)
(assign unev (op operands) (reg exp))
(save unev)
(assign exp (op operator) (reg exp))
(assign continue (label ev-appl-did-operator))
(goto (label eval-dispatch))
21
Tis is an impoitant but subtle point in tianslating algoiithms fiom a pioceduial
language, such as Lisp, to a iegistei-machine language. As an alteinative to saving only
what is needed, we could save all the iegisteis (except val) befoie each iecuisive call.
Tis is called a [roeJsoc| discipline. Tis would woik but might save moie iegisteis
than necessaiy, this could be an impoitant consideiation in a system wheie stack op-
eiations aie expensive. Saving iegisteis whose contents will not be needed latei may
also hold onto useless data that could otheiwise be gaibage-collected, fieeing space to
be ieused.
4
Upon ietuining fiom evaluating the opeiatoi subexpiession, we pio-
ceed to evaluate the opeiands of the combination and to accumulate the
iesulting aiguments in a list, held in argl. liist we iestoie the unevalu-
ated opeiands and the enviionment. We initialize argl to an empty list.
Ten we assign to the proc iegistei the pioceduie that was pioduced
by evaluating the opeiatoi. lf theie aie no opeiands, we go diiectly to
apply-dispatch. Otheiwise we save proc on the stack and stait the
aigument-evaluation loop
22
ev-appl-did-operator
(restore unev) , the opeiands
(restore env)
(assign argl (op empty-arglist))
(assign proc (reg val)) , the opeiatoi
(test (op no-operands?) (reg unev))
(branch (label apply-dispatch))
(save proc)
Each cycle of the aigument-evaluation loop evaluates an opeiand fiom
the list in unev and accumulates the iesult into argl. To evaluate an
opeiand, we place it in the exp iegistei and go to eval-dispatch, af-
tei seuing continue so that execution will iesume with the aigument-
accumulation phase. But ist we save the aiguments accumulated so
22
We add to the evaluatoi data-stiuctuie pioceduies in Section 4.1.3 the following
two pioceduies foi manipulating aigument lists
(define (empty-arglist) '())
(define (adjoin-arg arg arglist) (append arglist (list arg)))
We also use an additional syntax pioceduie to test foi the last opeiand in a combi-
nation
(define (last-operand? ops) (null? (cdr ops)))
4
fai (held in argl), the enviionment (held in env), and the iemaining
opeiands to be evaluated (held in unev). A special case is made foi the
evaluation of the last opeiand, which is handled at ev-appl-last-arg.
ev-appl-operand-loop
(save argl)
(assign exp (op first-operand) (reg unev))
(test (op last-operand?) (reg unev))
(branch (label ev-appl-last-arg))
(save env)
(save unev)
(assign continue (label ev-appl-accumulate-arg))
(goto (label eval-dispatch))
When an opeiand has been evaluated, the value is accumulated into the
list held in argl. Te opeiand is then iemoved fiom the list of unevalu-
ated opeiands in unev, and the aigument-evaluation continues.
ev-appl-accumulate-arg
(restore unev)
(restore env)
(restore argl)
(assign argl (op adjoin-arg) (reg val) (reg argl))
(assign unev (op rest-operands) (reg unev))
(goto (label ev-appl-operand-loop))
Evaluation of the last aigument is handled dieiently. Teie is no need
to save the enviionment oi the list of unevaluated opeiands befoie going
to eval-dispatch, since they will not be iequiied afei the last opeiand
is evaluated. Tus, we ietuin fiomthe evaluation to a special entiy point
ev-appl-accum-last-arg, which iestoies the aigument list, accumu-
lates the new aigument, iestoies the saved pioceduie, and goes o to
peifoim the application.
23
23
Te optimization of tieating the last opeiand specially is known as e+|:s o:| recvr
48
ev-appl-last-arg
(assign continue (label ev-appl-accum-last-arg))
(goto (label eval-dispatch))
ev-appl-accum-last-arg
(restore argl)
(assign argl (op adjoin-arg) (reg val) (reg argl))
(restore proc)
(goto (label apply-dispatch))
Te details of the aigument-evaluation loop deteimine the oidei in which
the inteipietei evaluates the opeiands of a combination (e.g., lefto iight
oi iight to lefsee Exeicise 3.8). Tis oidei is not deteimined by the
metaciiculai evaluatoi, which inheiits its contiol stiuctuie fiom the
undeilying Scheme in which it is implemented.
24
Because the first-
operand selectoi (used in ev-appl-operand-loop to extiact successive
opeiands fiom unev) is implemented as car and the rest-operands se-
lectoi is implemented as cdr, the explicit-contiol evaluatoi will evaluate
the opeiands of a combination in lef-to-iight oidei.
Procedure application
Te entiy point apply-dispatch coiiesponds to the apply pioceduie
of the metaciiculai evaluatoi. By the time we get to apply-dispatch,
the proc iegistei contains the pioceduie to apply and argl contains
s:on (see Wand 1980). We could be somewhat moie ecient in the aigument evaluation
loop if we made evaluation of the ist opeiand a special case too. Tis would peimit us
to postpone initializing argl until afei evaluating the ist opeiand, so as to avoid sav-
ing argl in this case. Te compilei in Section . peifoims this optimization. (Compaie
the construct-arglist pioceduie of Section ..3.)
24
Te oidei of opeiand evaluation in the metaciiculai evaluatoi is deteimined by
the oidei of evaluation of the aiguments to cons in the pioceduie list-of-values of
Section 4.1.1 (see Exeicise 4.1).
49
the list of evaluated aiguments to which it must be applied. Te saved
value of continue (oiiginally passed to eval-dispatch and saved at ev-
application), which tells wheie to ietuin with the iesult of the pioce-
duie application, is on the stack. When the application is complete, the
contiollei tiansfeis to the entiy point specied by the saved continue,
with the iesult of the application in val. As with the metaciiculai apply,
theie aie two cases to considei. Eithei the pioceduie to be applied is a
piimitive oi it is a compound pioceduie.
apply-dispatch
(test (op primitive-procedure?) (reg proc))
(branch (label primitive-apply))
(test (op compound-procedure?) (reg proc))
(branch (label compound-apply))
(goto (label unknown-procedure-type))
We assume that each piimitive is implemented so as to obtain its aigu-
ments fiomargl and place its iesult in val. To specify howthe machine
handles piimitives, we would have to piovide a sequence of contiollei
instiuctions to implement each piimitive and aiiange foi primitive-
apply to dispatch to the instiuctions foi the piimitive identied by the
contents of proc. Since we aie inteiested in the stiuctuie of the evalua-
tion piocess iathei than the details of the piimitives, we will instead just
use an apply-primitive-procedure opeiation that applies the pioce-
duie in proc to the aiguments in argl. loi the puipose of simulating
the evaluatoi with the simulatoi of Section .2 we use the pioceduie
apply-primitive-procedure, which calls on the undeilying Scheme
system to peifoim the application, just as we did foi the metaciiculai
evaluatoi in Section 4.1.4. Afei computing the value of the piimitive
application, we iestoie continue and go to the designated entiy point.
0
primitive-apply
(assign val (op apply-primitive-procedure)
(reg proc)
(reg argl))
(restore continue)
(goto (reg continue))
To apply a compound pioceduie, we pioceed just as with the metacii-
culai evaluatoi. We constiuct a fiame that binds the pioceduies pa-
iameteis to the aiguments, use this fiame to extend the enviionment
caiiied by the pioceduie, and evaluate in this extended enviionment
the sequence of expiessions that foims the body of the pioceduie. Ev-
sequence, desciibed below in Section .4.2, handles the evaluation of
the sequence.
compound-apply
(assign unev (op procedure-parameters) (reg proc))
(assign env (op procedure-environment) (reg proc))
(assign env (op extend-environment)
(reg unev) (reg argl) (reg env))
(assign unev (op procedure-body) (reg proc))
(goto (label ev-sequence))
Compound-apply is the only place in the inteipietei wheie the env ieg-
istei is evei assigned a new value. Just as in the metaciiculai evaluatoi,
the new enviionment is constiucted fiom the enviionment caiiied by
the pioceduie, togethei with the aigument list and the coiiesponding
list of vaiiables to be bound.
5.4.2 Sequence Evaluation and Tail Recursion
Te poition of the explicit-contiol evaluatoi at ev-sequence is analo-
gous to the metaciiculai evaluatois eval-sequence pioceduie. lt han-
1
dles sequences of expiessions in pioceduie bodies oi in explicit begin
expiessions.
Explicit begin expiessions aie evaluated by placing the sequence of
expiessions to be evaluated in unev, saving continue on the stack, and
jumping to ev-sequence.
ev-begin
(assign unev (op begin-actions) (reg exp))
(save continue)
(goto (label ev-sequence))
Te implicit sequences in pioceduie bodies aie handled by jumping to
ev-sequence fiomcompound-apply, at which point continue is alieady
on the stack, having been saved at ev-application.
Te entiies at ev-sequence and ev-sequence-continue foima loop
that successively evaluates each expiession in a sequence. Te list of
unevaluated expiessions is kept in unev. Befoie evaluating each expies-
sion, we check to see if theie aie additional expiessions to be evaluated
in the sequence. lf so, we save the iest of the unevaluated expiessions
(held in unev) and the enviionment in which these must be evaluated
(held in env) and call eval-dispatch to evaluate the expiession. Te
two saved iegisteis aie iestoied upon the ietuin fiom this evaluation,
at ev-sequence-continue.
Te nal expiession in the sequence is handled dieiently, at the en-
tiy point ev-sequence-last-exp. Since theie aie no moie expiessions
to be evaluated afei this one, we need not save unev oi env befoie go-
ing to eval-dispatch. Te value of the whole sequence is the value of
the last expiession, so afei the evaluation of the last expiession theie is
nothing lef to do except continue at the entiy point cuiiently held on
the stack (which was saved by ev-application oi ev-begin.) Rathei
than seuing up continue to aiiange foi eval-dispatch to ietuin heie
2
and then iestoiing continue fiom the stack and continuing at that en-
tiy point, we iestoie continue fiom the stack befoie going to eval-
dispatch, so that eval-dispatch will continue at that entiy point af-
tei evaluating the expiession.
ev-sequence
(assign exp (op first-exp) (reg unev))
(test (op last-exp?) (reg unev))
(branch (label ev-sequence-last-exp))
(save unev)
(save env)
(assign continue (label ev-sequence-continue))
(goto (label eval-dispatch))
ev-sequence-continue
(restore env)
(restore unev)
(assign unev (op rest-exps) (reg unev))
(goto (label ev-sequence))
ev-sequence-last-exp
(restore continue)
(goto (label eval-dispatch))
Tail recursion
ln Chaptei 1 we said that the piocess desciibed by a pioceduie such as
(define (sqrt-iter guess x)
(if (good-enough? guess x)
guess
(sqrt-iter (improve guess x) x)))
is an iteiative piocess. Even though the pioceduie is syntactically ie-
cuisive (dened in teims of itself), it is not logically necessaiy foi an
evaluatoi to save infoimation in passing fiom one call to sqrt-iter to
3
the next.
2
An evaluatoi that can execute a pioceduie such as sqrt-iter
without iequiiing incieasing stoiage as the pioceduie continues to call
itself is called a o:|recvrs:+e evaluatoi. Te metaciiculai implementa-
tion of the evaluatoi in Chaptei 4 does not specify whethei the eval-
uatoi is tail-iecuisive, because that evaluatoi inheiits its mechanism
foi saving state fiom the undeilying Scheme. With the explicit-contiol
evaluatoi, howevei, we can tiace thiough the evaluation piocess to see
when pioceduie calls cause a net accumulation of infoimation on the
stack.
Oui evaluatoi is tail-iecuisive, because in oidei to evaluate the nal
expiession of a sequence we tiansfei diiectly to eval-dispatch without
saving any infoimation on the stack. Hence, evaluating the nal expies-
sion in a sequenceeven if it is a pioceduie call (as in sqrt-iter, wheie
the if expiession, which is the last expiession in the pioceduie body,
ieduces to a call to sqrt-iter)will not cause any infoimation to be
accumulated on the stack.
2
lf we did not think to take advantage of the fact that it was unneces-
saiy to save infoimation in this case, we might have implemented eval-
sequence by tieating all the expiessions in a sequence in the same way
saving the iegisteis, evaluating the expiession, ietuining to iestoie the
iegisteis, and iepeating this until all the expiessions have been evalu-
ated
2
2
We saw in Section .1 how to implement such a piocess with a iegistei machine
that had no stack, the state of the piocess was stoied in a xed set of iegisteis.
2
Tis implementation of tail iecuision in ev-sequence is one vaiiety of a well-
known optimization technique used by many compileis. ln compiling a pioceduie that
ends with a pioceduie call, one can ieplace the call by a jump to the called pioceduies
entiy point. Building this stiategy into the inteipietei, as we have done in this section,
piovides the optimization unifoimly thioughout the language.
2
We can dene no-more-exps? as follows
(define (no-more-exps? seq) (null? seq))
4
ev-sequence
(test (op no-more-exps?) (reg unev))
(branch (label ev-sequence-end))
(assign exp (op first-exp) (reg unev))
(save unev)
(save env)
(assign continue (label ev-sequence-continue))
(goto (label eval-dispatch))
ev-sequence-continue
(restore env)
(restore unev)
(assign unev (op rest-exps) (reg unev))
(goto (label ev-sequence))
ev-sequence-end
(restore continue)
(goto (reg continue))
Tis may seem like a minoi change to oui pievious code foi evaluation
of a sequence Te only dieience is that we go thiough the save-iestoie
cycle foi the last expiession in a sequence as well as foi the otheis. Te
inteipietei will still give the same value foi any expiession. But this
change is fatal to the tail-iecuisive implementation, because we must
now ietuin afei evaluating the nal expiession in a sequence in oidei
to undo the (useless) iegistei saves. Tese extia saves will accumulate
duiing a nest of pioceduie calls. Consequently, piocesses such as sqrt-
iter will iequiie space piopoitional to the numbei of iteiations iathei
than iequiiing constant space. Tis dieience can be signicant. loi
example, with tail iecuision, an innite loop can be expiessed using
only the pioceduie-call mechanism
(define (count n)
(newline) (display n) (count (+ n 1)))
ev-if-decide
(restore continue)
(restore env)
(restore exp)
(test (op true?) (reg val))
(branch (label ev-if-consequent))
ev-if-alternative
(assign exp (op if-alternative) (reg exp))
(goto (label eval-dispatch))
ev-if-consequent
(assign exp (op if-consequent) (reg exp))
(goto (label eval-dispatch))
Assignments and definitions
Assignments aie handled by ev-assignment, which is ieached fiom
eval-dispatch with the assignment expiession in exp. Te code at ev-
assignment ist evaluates the value pait of the expiession and then
installs the new value in the enviionment. Set-variable-value! is as-
sumed to be available as a machine opeiation.
ev-assignment
(assign unev (op assignment-variable) (reg exp))
(save unev) , save vaiiable foi latei
(assign exp (op assignment-value) (reg exp))
(save env)
(save continue)
(assign continue (label ev-assignment-1))
(goto (label eval-dispatch)) , evaluate the assignment value
ev-assignment-1
(restore continue)
(restore env)
(restore unev)
(perform
seq
2
(preserving (list re
1
re
2
) se
1
se
2
)
pioduces one of the following foui sequences of instiuctions, de-
pending on how seq
1
and seq
2
use re
1
and re
2
seq
1
(save re
1
) (save re
2
) (save re
2
)
seq
2
seq
1
seq
1
(save re
1
)
(restore re
1
) (restore re
2
) seq
1
seq
2
seq
2
(restore re
1
)
(restore re
2
)
seq
2