rawhtml62
A user should be able to control each traffic light independently
of the others. Indeed, the operator should be able to add or shut down
traffic lights while the rest of the system remains unchanged and running.
The sample program of figure~#figsvrecipe1example#50588> deals with a
single traffic light, though without the drawing operation. The problem
is now to turn this into a program that can create as many traffic lights
as needed, each with its own state variables and switching functions and
at its own location. If we were to copy the definitions in
figure~#figsvrecipe1example#50589> and add definitions for dealing with
the canvas, the various instances would differ in only one aspect: the
data concerning the location of the traffic light. This thought experiment
suggests that we should develop an abstract function for creates and
manages traffic lights at various locations.
Because the original program consists of several top-level definitions, we use
the recipe of section~#secdesignabs1st#50590>, which suggests to wrap the
definitions in a <#69050#><#50591#>local<#50591#>-expression<#69050#> inside a function. When local definitions
include state variables, as in this example, we prefer to say that we
<#69051#><#50592#>ENCAPSULATE<#50592#><#69051#> definitions. This terminology emphasizes that the
abstract function hides the state variables from other parts of the
program, and deemphasizes the act of abstraction. Still,
the definition encapsulates and abstracts at the same time, and a
programmer must keep this in mind.
<#69053#>;; <#50598#>View:<#50598#><#69053#>
<#71920#>;; <#69054#><#50599#>draw-light<#50599#> <#50600#>:<#50600#> <#50601#>TL-color<#50601#> <#50602#>number<#50602#> <#50603#><#50603#><#50604#>-;SPMgt;<#50604#><#50605#><#50605#> <#50606#>true<#50606#><#69054#><#71920#>
<#50607#>;; to (re)draw the traffic light on the canvas <#50607#>
<#50608#>(define<#50608#> <#50609#>(draw-light<#50609#> <#50610#>current-color<#50610#> <#50611#>x-posn)<#50611#> <#50612#>...))<#50612#>
<#69055#>;; <#50613#>Model:<#50613#><#69055#>
<#71921#>;; <#69056#><#50614#>make-traffic-light<#50614#> <#50615#>:<#50615#> <#50616#>symbol<#50616#> <#50617#>number<#50617#> <#50618#><#50618#><#50619#>-;SPMgt;<#50619#><#50620#><#50620#> <#50621#>(<#50621#><#50622#><#50622#><#50623#>-;SPMgt;<#50623#><#50624#><#50624#> <#50625#>true)<#50625#><#69056#><#71921#>
<#71922#>;; to create a red light with <#69057#><#50626#>(make-posn<#50626#> <#50627#>x-posn<#50627#> <#50628#>0)<#50628#><#69057#> as the upper-left corner<#71922#>
<#50629#>;; effect: draw the traffic light on the canvas<#50629#>
<#50630#>(d<#50630#><#50631#>efine<#50631#> <#50632#>(make-traffic-light<#50632#> <#50633#>street<#50633#> <#50634#>x-posn)<#50634#>
<#50635#>(l<#50635#><#50636#>ocal<#50636#> <#50637#>(<#50637#><#71923#>;; <#69058#><#50638#>current-color<#50638#> <#50639#>:<#50639#> <#50640#>TL-color<#50640#><#69058#><#71923#>
<#50641#>;; to keep track of the current color of the traffic light<#50641#>
<#50642#>(define<#50642#> <#50643#>current-color<#50643#> <#50644#>'<#50644#><#50645#>red)<#50645#>
<#71924#>;; <#69059#><#50646#>init-traffic-light<#50646#> <#50647#>:<#50647#> <#50648#><#50648#><#50649#>-;SPMgt;<#50649#><#50650#><#50650#> <#50651#>true<#50651#><#69059#><#71924#>
<#71925#>;; to (re)set <#69060#><#50652#>current-color<#50652#><#69060#> to red and to (re)create the view <#71925#>
<#50653#>(d<#50653#><#50654#>efine<#50654#> <#50655#>(init-traffic-light)<#50655#>
<#50656#>(b<#50656#><#50657#>egin<#50657#>
<#50658#>(set!<#50658#> <#50659#>current-color<#50659#> <#50660#>'<#50660#><#50661#>red)<#50661#>
<#50662#>(draw-light<#50662#> <#50663#>current-color<#50663#> <#50664#>x-posn)))<#50664#>
<#71926#>;; <#69061#><#50665#>next<#50665#> <#50666#>:<#50666#> <#50667#><#50667#><#50668#>-;SPMgt;<#50668#><#50669#><#50669#> <#50670#>true<#50670#><#69061#><#71926#>
<#71927#>;; effect: to change <#69062#><#50671#>current-color<#50671#><#69062#> from <#69063#><#50672#>'<#50672#><#50673#>green<#50673#><#69063#> to <#69064#><#50674#>'<#50674#><#50675#>yellow<#50675#><#69064#>, <#71927#>
<#71928#>;; <#69065#><#50676#>'<#50676#><#50677#>yellow<#50677#><#69065#> to <#69066#><#50678#>'<#50678#><#50679#>red<#50679#><#69066#>, and <#69067#><#50680#>'<#50680#><#50681#>red<#50681#><#69067#> to <#69068#><#50682#>'<#50682#><#50683#>green<#50683#><#69068#><#71928#>
<#50684#>(d<#50684#><#50685#>efine<#50685#> <#50686#>(next)<#50686#>
<#50687#>(b<#50687#><#50688#>egin<#50688#>
<#50689#>(set!<#50689#> <#50690#>current-color<#50690#> <#50691#>(next-color<#50691#> <#50692#>current-color))<#50692#>
<#50693#>(draw-light<#50693#> <#50694#>current-color<#50694#> <#50695#>x-posn)))<#50695#>
<#71929#>;; <#69069#><#50696#>next-color<#50696#> <#50697#>:<#50697#> <#50698#>TL-color<#50698#> <#50699#><#50699#><#50700#>-;SPMgt;<#50700#><#50701#><#50701#> <#50702#>TL-color<#50702#><#69069#><#71929#>
<#71930#>;; to compute the successor of <#69070#><#50703#>current-color<#50703#><#69070#> based on the traffic laws<#71930#>
<#50704#>(d<#50704#><#50705#>efine<#50705#> <#50706#>(next-color<#50706#> <#50707#>current-color)<#50707#>
<#50708#>(c<#50708#><#50709#>ond<#50709#>
<#50710#>[<#50710#><#50711#>(symbol=?<#50711#> <#50712#>'<#50712#><#50713#>green<#50713#> <#50714#>current-color)<#50714#> <#50715#>'<#50715#><#50716#>yellow]<#50716#>
<#50717#>[<#50717#><#50718#>(symbol=?<#50718#> <#50719#>'<#50719#><#50720#>yellow<#50720#> <#50721#>current-color)<#50721#> <#50722#>'<#50722#><#50723#>red]<#50723#>
<#50724#>[<#50724#><#50725#>(symbol=?<#50725#> <#50726#>'<#50726#><#50727#>red<#50727#> <#50728#>current-color)<#50728#> <#50729#>'<#50729#><#50730#>green]<#50730#><#50731#>))<#50731#>
<#50732#>(b<#50732#><#50733#>egin<#50733#>
<#71931#>;; Initialize and produce <#69071#><#50734#>next<#50734#><#69071#><#71931#>
<#50735#>(init-traffic-light)<#50735#>
<#50736#>next)))<#50736#>
<#50740#>Figure: Managing multiple traffic lights<#50740#>
The next step is to consider what this function should do, that is, what it
should consume, what it should produce, and what effects it should have, if
any. Let's start with the name. We call the new function
<#69072#><#50742#>make-traffic-light<#50742#><#69072#>; after all, making a simulated traffic light is
the purpose of the abstracted program. Furthermore, according to our
abstraction recipes, an abstraction must consume values that represent the
unique aspects of an instance. The unique aspect of a traffic light is its
position on the canvas; for clarity, let's add a physical address, too.
Every use of <#69073#><#50743#>make-traffic-light<#50743#><#69073#> should create a traffic light and
enable the operator to switch it from one state to the next. The first part
suggests an effect. Specifically, the function should initialize the state
variable and draw the initial state of the traffic light at the designated
position on the canvas. The second part of the statement suggests a result:
a function for switching the state of the traffic light.
Figure~#figmultipletl#50744> contains the outline of the traffic simulator,
including the complete definition of <#69074#><#50745#>make-traffic-light<#50745#><#69074#>. The
simulator consists of a model and a view. The model is
<#69075#><#50746#>make-traffic-light<#50746#><#69075#>. The view is called <#69076#><#50747#>draw-light<#50747#><#69076#> and is
only sketched; the full definition of the view is left as an exercise.
The definition of <#69077#><#50748#>make-traffic-light<#50748#><#69077#> is an ordinary function
definition. It uses a <#69078#><#50749#>local<#50749#><#69078#> definition to set up the single state
variable, the initializer, and the state-changing function. The body of the
<#69079#><#50750#>local<#50750#>-expression<#69079#> uses the initializer and then produces <#69080#><#50751#>next<#50751#><#69080#> as the
function's value.
Using <#69081#><#50752#>make-traffic-light<#50752#><#69081#> we can create svereal individual traffic
lights or entire collections of them. We could also add lights as time goes
by. First, we create a sufficient large canvas:
<#50757#>;; create the canvas first <#50757#>
<#50758#>(start<#50758#> <#50759#>300<#50759#> <#50760#>160)<#50760#>
Second, we apply <#69082#><#50764#>make-traffic-light<#50764#><#69082#> as often as needed:
<#71932#>;; <#69083#><#50769#>lights<#50769#> <#50770#>:<#50770#> <#50771#>(listof<#50771#> <#50772#>traffic-light)<#50772#><#69083#><#71932#>
<#50773#>;; to manage the lights along Sunrise <#50773#>
<#50774#>(d<#50774#><#50775#>efine<#50775#> <#50776#>lights<#50776#>
<#50777#>(list<#50777#> <#50778#>(make-traffic-light<#50778#> <#50779#>'<#50779#><#50780#>sunrise@<#50780#><#50781#>rice<#50781#> <#50782#>50)<#50782#>
<#50783#>(make-traffic-light<#50783#> <#50784#>'<#50784#><#50785#>sunrise@<#50785#><#50786#>cmu<#50786#> <#50787#>150)))<#50787#>
Here we define <#69084#><#50791#>lights<#50791#><#69084#> to be a list of two traffic lights. Each
traffic light is a function, so <#69085#><#50792#>lights<#50792#><#69085#> stands for a list of two
functions.
After creating the traffic lights, we can change their states as desired.
To do so, we must keep in mind that each traffic light is represented by a
function that consumes nothing and produces <#69086#><#50793#>true<#50793#><#69086#>. Its effect is to
change the hidden state variable and the drawing on the canvas. In our
running example, we could use the <#50794#>Interactions<#50794#> window as follows:
<#50799#>;SPMgt;<#50799#> <#50800#>((second<#50800#> <#50801#>lights))<#50801#>
<#50802#>true<#50802#>
<#50803#>;SPMgt;<#50803#> <#50804#>(andmap<#50804#> <#50805#>(lambda<#50805#> <#50806#>(a-light)<#50806#> <#50807#>(a-light))<#50807#> <#50808#>lights)<#50808#>
<#50809#>true<#50809#>
The first interaction extracts the second item from <#69087#><#50813#>lights<#50813#><#69087#> and
applies it. This sets the light at <#69088#><#50814#>'<#50814#><#50815#>sunrise@<#50815#><#50816#>cmu<#50816#><#69088#> to red. The
second one switches the state of all items on <#69089#><#50817#>lights<#50817#><#69089#>.
Each application of <#69090#><#50818#>make-traffic-light<#50818#><#69090#> turns variants of the
<#69091#><#50819#>local<#50819#><#69091#> definitions into top-level definitions, after renaming
them. Because the above <#69092#><#50820#>define<#50820#><#69092#> contains two applications of
<#69093#><#50821#>make-traffic-light<#50821#><#69093#>, it creates two copies of each <#69094#><#50822#>local<#50822#><#69094#>ly
defined function and state variable during an evaluation:
<#71933#>;; definitions for <#69095#><#50827#>'<#50827#><#50828#>sunrise@<#50828#><#50829#>rice<#50829#><#69095#><#71933#>
<#50830#>(define<#50830#> <#50831#>current-color@<#50831#><#50832#>rice<#50832#> <#50833#>'<#50833#><#50834#>red)<#50834#>
<#50835#>(d<#50835#><#50836#>efine<#50836#> <#50837#>(init-traffic-light@<#50837#><#50838#>rice)<#50838#>
<#50839#>(b<#50839#><#50840#>egin<#50840#>
<#50841#>(set!<#50841#> <#50842#>current-color@<#50842#><#50843#>rice<#50843#> <#50844#>'<#50844#><#50845#>red)<#50845#>
<#50846#>(draw-light<#50846#> <#50847#>current-color@<#50847#><#50848#>rice<#50848#> <#50849#>50)))<#50849#>
<#50850#>(define<#50850#> <#50851#>(next@<#50851#><#50852#>rice)<#50852#> <#50853#>...)<#50853#>
<#50854#>(define<#50854#> <#50855#>(next-color@<#50855#><#50856#>rice<#50856#> <#50857#>current-color)<#50857#> <#50858#>...)<#50858#>
<#71934#>;; definitions for <#69096#><#50859#>'<#50859#><#50860#>sunrise@<#50860#><#50861#>cmu<#50861#><#69096#><#71934#>
<#50862#>(define<#50862#> <#50863#>current-color@<#50863#><#50864#>cmu<#50864#> <#50865#>'<#50865#><#50866#>red)<#50866#>
<#50867#>(d<#50867#><#50868#>efine<#50868#> <#50869#>(init-traffic-light@<#50869#><#50870#>cmu)<#50870#>
<#50871#>(b<#50871#><#50872#>egin<#50872#>
<#50873#>(set!<#50873#> <#50874#>current-color@<#50874#><#50875#>cmu<#50875#> <#50876#>'<#50876#><#50877#>red)<#50877#>
<#50878#>(draw-light<#50878#> <#50879#>current-color@<#50879#><#50880#>cmu<#50880#> <#50881#>150)))<#50881#>
<#50882#>(define<#50882#> <#50883#>(next@<#50883#><#50884#>cmu)<#50884#> <#50885#>...)<#50885#>
<#50886#>(define<#50886#> <#50887#>(next-color@<#50887#><#50888#>cmu<#50888#> <#50889#>current-color)<#50889#> <#50890#>...)<#50890#>
<#50891#>(d<#50891#><#50892#>efine<#50892#> <#50893#>lights<#50893#>
<#50894#>(list<#50894#> <#50895#>next@<#50895#><#50896#>rice<#50896#>
<#50897#>next@<#50897#><#50898#>cmu))<#50898#>
The new top-level definitions of <#69097#><#50902#>init-traffic-light<#50902#><#69097#> show how the
renaming ensures that one of them takes care of <#69098#><#50903#>'<#50903#><#50904#>sunrise@<#50904#><#50905#>rice<#50905#><#69098#> and
the other one of <#69099#><#50906#>'<#50906#><#50907#>sunrise@<#50907#><#50908#>cmu<#50908#><#69099#>.
<#50911#>Exercise 39.1.1<#50911#>
What is the concrete effect of the second interaction
above?~ Solution<#69100#><#69100#>
<#50918#>Exercise 39.1.2<#50918#>
Fill in the bodies of <#69101#><#50920#>next@<#50920#><#50921#>rice<#50921#><#69101#> and <#69102#><#50922#>next@<#50922#><#50923#>cmu<#50923#><#69102#> in the
hand-evaluated program. Then evaluate <#69103#><#50924#>((second<#50924#>\ <#50925#>lights))<#50925#><#69103#> in the
context of these definitions.~ Solution<#69104#><#69104#>
<#50931#>Exercise 39.1.3<#50931#>
Develop the function <#69105#><#50933#>draw-light<#50933#><#69105#>. It realizes the view part of the
traffic light simulation in figure~#figmultipletl#50934>. Each traffic
light should be as tall as the canvas, delineated by solid lines on the
left and right. The suggested dimensions of a single light are
<#50939#>(define<#50939#> <#50940#>WIDTH<#50940#> <#50941#>50)<#50941#>
<#50942#>(define<#50942#> <#50943#>RADIUS<#50943#> <#50944#>20)<#50944#>
<#50945#>(define<#50945#> <#50946#>DISTANCE-BETWEEN-BULBS<#50946#> <#50947#>10)<#50947#>
<#50948#>;; the minimum canvas height<#50948#>
<#50949#>(d<#50949#><#50950#>efine<#50950#> <#50951#>HEIGHT<#50951#>
<#50952#>(+<#50952#> <#50953#>DISTANCE-BETWEEN-BULBS<#50953#>
<#50954#>(*<#50954#> <#50955#>2<#50955#> <#50956#>RADIUS)<#50956#>
<#50957#>DISTANCE-BETWEEN-BULBS<#50957#>
<#50958#>(*<#50958#> <#50959#>2<#50959#> <#50960#>RADIUS)<#50960#>
<#50961#>DISTANCE-BETWEEN-BULBS<#50961#>
<#50962#>(*<#50962#> <#50963#>2<#50963#> <#50964#>RADIUS)<#50964#>
<#50965#>DISTANCE-BETWEEN-BULBS))<#50965#>
Develop the necessary definitons separately from the rest of the traffic
light program, then create a single function definition using
<#69106#><#50969#>local<#50969#><#69106#>.~ Solution<#69107#><#69107#>
Now suppose we wish to provide the additional service of resetting an
individual traffic light. That is, in addition to switching from the
current color to the next, an operator should be able to set a traffic
light to red. The function for doing so already exists:
<#69108#><#50977#>init-traffic-light<#50977#><#69108#>. It sets <#69109#><#50978#>current-color<#50978#><#69109#> to
<#69110#><#50979#>'<#50979#><#50980#>red<#50980#><#69110#> and re-draws the image on the canvas. But,
<#69111#><#50981#>init-traffic-light<#50981#><#69111#> is inaccessible because it is defined within
the <#69112#><#50982#>local<#50982#>-expression<#69112#> of <#69113#><#50983#>make-traffic-light<#50983#><#69113#>. If we wish the function to
be visible, it must be the result of <#69114#><#50984#>make-traffic-light<#50984#><#69114#> just like
<#69115#><#50985#>next<#50985#><#69115#>.
To make both <#69116#><#50986#>next<#50986#><#69116#> and <#69117#><#50987#>init-traffic-light<#50987#><#69117#> a result of
<#69118#><#50988#>make-traffic-light<#50988#><#69118#> requires some way of combining the two
functions into a single value. Since functions are values in Scheme, we
could combine the two functions in a list, a structure, or even a vector.
Another possibility is to combine the two functions in a third function.
Here we discuss this third possibility because it is an important technique
in the context of managing state variables and services.
We call the new kind of function <#69119#><#50989#>service-manager<#50989#><#69119#>, because it hides
and manages functions that implement services. The function accepts two
symbols:
- <#69120#><#50991#>'<#50991#><#50992#>next<#50992#><#69120#>, which indicates that <#69121#><#50993#>(next)<#50993#><#69121#> should be
evaluated
- <#69122#><#50994#>'<#50994#><#50995#>reset<#50995#><#69122#>, which indicates that <#69123#><#50996#>(reset)<#50996#><#69123#> should be
evaluated.
Furthermore, the function is the result of the revised version of
<#69124#><#50998#>make-traffic-light<#50998#><#69124#>.
Figure~#figmultipletlreset#50999> contains the modified definition of
<#69125#><#51000#>make-traffic-light<#51000#><#69125#>. Since an operator may mistakenly apply
functions to inappropriate arguments, <#69126#><#51001#>service-manager<#51001#><#69126#> is a checked
function in the sense of section~#secinputerrors#51002>. It signals an
error if the input is a symbol other than <#69127#><#51003#>'<#51003#><#51004#>next<#51004#><#69127#> or
<#69128#><#51005#>'<#51005#><#51006#>reset<#51006#><#69128#>.
<#71935#>;; <#69129#><#51011#>make-traffic-light<#51011#> <#51012#>:<#51012#> <#51013#>symbol<#51013#> <#51014#>number<#51014#> <#51015#><#51015#><#51016#>-;SPMgt;<#51016#><#51017#><#51017#> <#51018#>(symbol<#51018#> <#51019#><#51019#><#51020#>-;SPMgt;<#51020#><#51021#><#51021#> <#51022#>true)<#51022#><#69129#><#71935#>
<#71936#>;; to create a red light with <#69130#><#51023#>(make-posn<#51023#> <#51024#>x-posn<#51024#> <#51025#>0)<#51025#><#69130#> as the upper-left corner<#71936#>
<#51026#>;; effect: draw the traffic light on the canvas<#51026#>
<#51027#>(d<#51027#><#51028#>efine<#51028#> <#51029#>(make-traffic-light<#51029#> <#51030#>street<#51030#> <#51031#>x-posn)<#51031#>
<#51032#>(l<#51032#><#51033#>ocal<#51033#> <#51034#>(<#51034#><#69131#>;; <#51035#>Model:<#51035#> <#69131#>
<#71937#>;; <#69132#><#51036#>current-color<#51036#> <#51037#>:<#51037#> <#51038#>TL-color<#51038#><#69132#><#71937#>
<#51039#>;; to keep track of the current color of the traffic light<#51039#>
<#51040#>(define<#51040#> <#51041#>current-color<#51041#> <#51042#>'<#51042#><#51043#>red)<#51043#>
<#71938#>;; <#69133#><#51044#>init-traffic-light<#51044#> <#51045#>:<#51045#> <#51046#><#51046#><#51047#>-;SPMgt;<#51047#><#51048#><#51048#> <#51049#>true<#51049#><#69133#><#71938#>
<#71939#>;; to (re)set <#69134#><#51050#>current-color<#51050#><#69134#> to red and to (re)create the view <#71939#>
<#51051#>(define<#51051#> <#51052#>(init-traffic-light)<#51052#> <#51053#>...)<#51053#>
<#71940#>;; <#69135#><#51054#>next<#51054#> <#51055#>:<#51055#> <#51056#><#51056#><#51057#>-;SPMgt;<#51057#><#51058#><#51058#> <#51059#>true<#51059#><#69135#><#71940#>
<#71941#>;; effect: to change <#69136#><#51060#>current-color<#51060#><#69136#> from <#69137#><#51061#>'<#51061#><#51062#>green<#51062#><#69137#> to <#69138#><#51063#>'<#51063#><#51064#>yellow<#51064#><#69138#>, <#71941#>
<#71942#>;; <#69139#><#51065#>'<#51065#><#51066#>yellow<#51066#><#69139#> to <#69140#><#51067#>'<#51067#><#51068#>red<#51068#><#69140#>, and <#69141#><#51069#>'<#51069#><#51070#>red<#51070#><#69141#> to <#69142#><#51071#>'<#51071#><#51072#>green<#51072#><#69142#><#71942#>
<#51073#>(define<#51073#> <#51074#>(next)<#51074#> <#51075#>...)<#51075#>
<#71943#>;; <#69143#><#51076#>next-color<#51076#> <#51077#>:<#51077#> <#51078#>TL-color<#51078#> <#51079#><#51079#><#51080#>-;SPMgt;<#51080#><#51081#><#51081#> <#51082#>TL-color<#51082#><#69143#><#71943#>
<#71944#>;; to compute the successor of <#69144#><#51083#>current-color<#51083#><#69144#> based on the traffic laws<#71944#>
<#51084#>(define<#51084#> <#51085#>(next-color<#51085#> <#51086#>current-color)<#51086#> <#51087#>...)<#51087#>
<#71945#>;; <#69145#><#51088#>service-manager<#51088#> <#51089#>:<#51089#> <#51090#>(symbol<#51090#> <#51091#><#51091#><#51092#>-;SPMgt;<#51092#><#51093#><#51093#> <#51094#>true)<#51094#><#69145#><#71945#>
<#71946#>;; to apply either <#69146#><#51095#>next<#51095#><#69146#> or <#69147#><#51096#>init-traffic-light<#51096#><#69147#><#71946#>
<#51097#>(d<#51097#><#51098#>efine<#51098#> <#51099#>(service-manager<#51099#> <#51100#>msg)<#51100#>
<#51101#>(c<#51101#><#51102#>ond<#51102#>
<#51103#>[<#51103#><#51104#>(symbol=?<#51104#> <#51105#>msg<#51105#> <#51106#>'<#51106#><#51107#>next)<#51107#> <#51108#>(next)]<#51108#>
<#51109#>[<#51109#><#51110#>(symbol=?<#51110#> <#51111#>msg<#51111#> <#51112#>'<#51112#><#51113#>reset)<#51113#> <#51114#>(init-traffic-light)]<#51114#>
<#51115#>[<#51115#><#51116#>else<#51116#> <#51117#>(error<#51117#> <#51118#>'<#51118#><#51119#>traffic-light<#51119#> <#51120#>``message<#51120#> <#51121#>not<#51121#> <#51122#>understood'')]<#51122#><#51123#>)))<#51123#>
<#51124#>(b<#51124#><#51125#>egin<#51125#>
<#71947#>;; Initialize and produce <#69148#><#51126#>service-manager<#51126#><#69148#><#71947#>
<#51127#>(init-traffic-light)<#51127#>
<#51128#>service-manager)))<#51128#>
<#51132#>Figure: Managing multiple traffic lights with a reset service<#51132#>
We use the new <#69149#><#51134#>make-traffic-light<#51134#><#69149#> function exactly like the old one:
<#51139#>;; create the canvas first <#51139#>
<#51140#>(start<#51140#> <#51141#>300<#51141#> <#51142#>160)<#51142#>
<#71948#>;; <#69150#><#51143#>lights<#51143#> <#51144#>:<#51144#> <#51145#>(listof<#51145#> <#51146#>traffic-light)<#51146#><#69150#><#71948#>
<#51147#>;; to manage the lights along Sunrise <#51147#>
<#51148#>(d<#51148#><#51149#>efine<#51149#> <#51150#>lights<#51150#>
<#51151#>(list<#51151#> <#51152#>(make-traffic-light<#51152#> <#51153#>'<#51153#><#51154#>sunrise@<#51154#><#51155#>rice<#51155#> <#51156#>50)<#51156#>
<#51157#>(make-traffic-light<#51157#> <#51158#>'<#51158#><#51159#>sunrise@<#51159#><#51160#>cmu<#51160#> <#51161#>150)))<#51161#>
The result is, however, that now every traffic light is represented as a
function on symbols:
<#51169#>;SPMgt;<#51169#> <#51170#>((second<#51170#> <#51171#>lights)<#51171#> <#51172#>'<#51172#><#51173#>next)<#51173#>
<#51174#>true<#51174#>
<#51175#>;SPMgt;<#51175#> <#51176#>(andmap<#51176#> <#51177#>(lambda<#51177#> <#51178#>(a-light)<#51178#> <#51179#>(a-light<#51179#> <#51180#>'<#51180#><#51181#>reset))<#51181#> <#51182#>lights)<#51182#>
<#51183#>true<#51183#>
The first interaction switches the initially red light labeld
<#69151#><#51187#>'<#51187#><#51188#>sunrise@<#51188#><#51189#>cmu<#51189#><#69151#> to <#69152#><#51190#>'<#51190#><#51191#>green<#51191#><#69152#>. The second one changes the state
of every light back to <#69153#><#51192#>'<#51192#><#51193#>red<#51193#><#69153#> skipping the <#69154#><#51194#>'<#51194#><#51195#>yellow<#51195#><#69154#> stage
for the light at <#69155#><#51196#>'<#51196#><#51197#>sunrise@<#51197#><#51198#>cmu<#51198#><#69155#>.
<#51201#>Exercise 39.1.4<#51201#>
Complete the definition of the program in
figure~#figmultipletlreset#51203>, using the function from
exercise~#exmultipltldraw#51204>. Then use DrScheme's
<#51205#>Interactions<#51205#> window to switch and reset traffic
lights.~ Solution<#69156#><#69156#>
<#51211#>Exercise 39.1.5<#51211#>
Evaluate the above program by hand and confirm that the light labeled
<#69157#><#51213#>'<#51213#><#51214#>sunrise@<#51214#><#51215#>rice<#51215#><#69157#> switches from <#69158#><#51216#>'<#51216#><#51217#>green<#51217#><#69158#> directly back to
<#69159#><#51218#>'<#51218#><#51219#>red<#51219#><#69159#>.~ Solution<#69160#><#69160#>
For the address-book example from part~#partstate#51227>, the need for
managing two services is even more apparent. After all, the motivating idea
behind the example is that users can access one state variable with two
different services: <#69161#><#51228#>add-to-address-book<#51228#><#69161#> for adding new entries and
<#69162#><#51229#>lookup<#51229#><#69162#> for looking up the phone number for a given name. Following
our encapsulation receipe, we must
- define a function <#69163#><#51231#>make-address-book<#51231#><#69163#> whose body is a
<#69164#><#51232#>local<#51232#>-expression<#69164#>;
- place the definitions in this <#69165#><#51233#>local<#51233#>-expression<#69165#>; and
- introduce a function called <#69166#><#51234#>service-manager<#51234#><#69166#> to manage the
two services.
By now, we have the first two steps firmly under control; the last one,
however, is complex here, because unlike in the previous case, the two
functions that implement the services consume different numbers of
arguments and produce different kinds of results.
Let's first agree on the inputs for <#69167#><#51236#>service-manager<#51236#><#69167#>. Two good
mnemonic symbols are <#69168#><#51237#>'<#51237#><#51238#>add<#51238#><#69168#> for adding phone numbers and
<#69169#><#51239#>'<#51239#><#51240#>search<#51240#><#69169#> for looking up the number for some given name. This
suggests the following template:
<#51245#>(d<#51245#><#51246#>efine<#51246#> <#51247#>(service-manager<#51247#> <#51248#>msg)<#51248#>
<#51249#>(c<#51249#><#51250#>ond<#51250#>
<#51251#>[<#51251#><#51252#>(symbol=?<#51252#> <#51253#>msg<#51253#> <#51254#>'<#51254#><#51255#>add)<#51255#> <#51256#>...<#51256#> <#51257#>A<#51257#> <#51258#>...]<#51258#>
<#51259#>[<#51259#><#51260#>(symbol=?<#51260#> <#51261#>msg<#51261#> <#51262#>'<#51262#><#51263#>search)<#51263#> <#51264#>...<#51264#> <#51265#>B<#51265#> <#51266#>...]<#51266#>
<#51267#>[<#51267#><#51268#>else<#51268#> <#51269#>(error<#51269#> <#51270#>'<#51270#><#51271#>address-book<#51271#> <#51272#>``message<#51272#> <#51273#>not<#51273#> <#51274#>understood'')]<#51274#><#51275#>))<#51275#>
The problem is that it is not clear how to replace <#69170#><#51279#>A<#51279#><#69170#> and
<#69171#><#51280#>B<#51280#><#69171#> with valid Scheme expressions that compute the appropriate value
and effect. For <#69172#><#51281#>A<#51281#><#69172#>, we not only need <#69173#><#51282#>msg<#51282#><#69173#> but also a name
and a phone number. For <#69174#><#51283#>B<#51283#><#69174#>, we need the name.
One solution is to produce functions that consume the additional arguments
and then perform the appropriate computation. In other words,
<#69175#><#51284#>service-manager<#51284#><#69175#> is a now function that produces a function for two
symbols. Since we have not encountered this kind of result before, we
introduce a new form of data definition:
An <#69176#><#51286#>address-book<#51286#><#69176#> is an interface:
- <#69177#><#51288#>'<#51288#><#51289#>add<#51289#><#69177#> :: <#69178#><#51290#>symbol<#51290#>\ <#51291#>number<#51291#>\ <#51292#><#51292#><#51293#>-;SPMgt;<#51293#><#51294#><#51294#>\ <#51295#>void<#51295#><#69178#>
- <#69179#><#51296#>'<#51296#><#51297#>search<#51297#><#69179#> :: <#69180#><#51298#>symbol<#51298#>\ <#51299#><#51299#><#51300#>-;SPMgt;<#51300#><#51301#><#51301#>\ <#51302#>number<#51302#><#69180#>
The data definition refers to the concept of <#69181#><#51305#>INTERFACE<#51305#><#69181#>,
which is a function that consumes a finite number of symbols and produces
functions with different types in return. Because this kind of function is
radically different from what we have seen before, we use a different
name.
Now it is possible to write a contract and a purpose statement:
<#71949#>;; <#69182#><#51310#>service-manager<#51310#> <#51311#>:<#51311#> <#51312#>address-book<#51312#><#69182#><#71949#>
<#51313#>;; to manage addition to, and searches in, the address book <#51313#>
<#51314#>(define<#51314#> <#51315#>(service-manager<#51315#> <#51316#>msg)<#51316#> <#51317#>...)<#51317#>
To define the function, we distinguish the two cases. In the case of
<#69183#><#51321#>'<#51321#><#51322#>add<#51322#><#69183#>, it is obvious what we produce:
<#69184#><#51323#>add-to-address-book<#51323#><#69184#>. In the case of <#69185#><#51324#>'<#51324#><#51325#>search<#51325#><#69185#>, we need a
function that consumes a symbol and then applies <#69186#><#51326#>lookup<#51326#><#69186#> to this
symbol and the <#69187#><#51327#>local<#51327#><#69187#>ly defined <#69188#><#51328#>address-book<#51328#><#69188#>:
<#51333#>(l<#51333#><#51334#>ambda<#51334#> <#51335#>(name)<#51335#>
<#51336#>(lookup<#51336#> <#51337#>name<#51337#> <#51338#>address-book))<#51338#>
The use of <#69189#><#51342#>lambda<#51342#><#69189#> is appropriate here, because the function
doesn't need a name.
<#71950#>;; <#69190#><#51347#>make-address-book<#51347#> <#51348#>:<#51348#> <#51349#>string<#51349#> <#51350#><#51350#><#51351#>-;SPMgt;<#51351#><#51352#><#51352#> <#51353#>address-book<#51353#><#69190#><#71950#>
<#51354#>;; to create a function that manages all the services for a hidden address book<#51354#>
<#51355#>(d<#51355#><#51356#>efine<#51356#> <#51357#>(make-address-book<#51357#> <#51358#>title)<#51358#>
<#51359#>(l<#51359#><#51360#>ocal<#51360#> <#51361#>(<#51361#><#51362#>(define-struct<#51362#> <#51363#>entry<#51363#> <#51364#>(name<#51364#> <#51365#>number))<#51365#>
<#71951#>;; <#69191#><#51366#>address-book<#51366#> <#51367#>:<#51367#> <#51368#>(listof<#51368#> <#51369#>(list<#51369#> <#51370#>name<#51370#> <#51371#>number))<#51371#><#69191#><#71951#>
<#51372#>;; to maintain a list of name-phone number associations<#51372#>
<#51373#>(define<#51373#> <#51374#>address-book<#51374#> <#51375#>empty)<#51375#>
<#71952#>;; <#69192#><#51376#>add-to-address-book<#51376#> <#51377#>:<#51377#> <#51378#>symbol<#51378#> <#51379#>number<#51379#> <#51380#><#51380#><#51381#>-;SPMgt;<#51381#><#51382#><#51382#> <#51383#>void<#51383#><#69192#><#71952#>
<#71953#>;; effect: to add a name-phone number association to <#69193#><#51384#>address-book<#51384#><#69193#><#71953#>
<#51385#>(d<#51385#><#51386#>efine<#51386#> <#51387#>(add-to-address-book<#51387#> <#51388#>name<#51388#> <#51389#>phone)<#51389#>
<#51390#>(set!<#51390#> <#51391#>address-book<#51391#> <#51392#>(cons<#51392#> <#51393#>(make-entry<#51393#> <#51394#>name<#51394#> <#51395#>phone)<#51395#> <#51396#>address-book)))<#51396#>
<#71954#>;; <#69194#><#51397#>lookup<#51397#> <#51398#>:<#51398#> <#51399#>symbol<#51399#> <#51400#>(listof<#51400#> <#51401#>(list<#51401#> <#51402#>symbol<#51402#> <#51403#>number))<#51403#> <#51404#><#51404#><#51405#>-;SPMgt;<#51405#><#51406#><#51406#> <#51407#>number<#51407#> <#51408#>or<#51408#> <#51409#>false<#51409#><#69194#><#71954#>
<#71955#>;; to lookup the phone number for <#69195#><#51410#>name<#51410#><#69195#> in <#69196#><#51411#>address-book<#51411#><#69196#><#71955#>
<#51412#>(d<#51412#><#51413#>efine<#51413#> <#51414#>(lookup<#51414#> <#51415#>name<#51415#> <#51416#>ab)<#51416#>
<#51417#>(c<#51417#><#51418#>ond<#51418#>
<#51419#>[<#51419#><#51420#>(empty?<#51420#> <#51421#>ab)<#51421#> <#51422#>false<#51422#><#51423#>]<#51423#>
<#51424#>[<#51424#><#51425#>else<#51425#> <#51426#>(c<#51426#><#51427#>ond<#51427#>
<#51428#>[<#51428#><#51429#>(symbol=?<#51429#> <#51430#>(entry-name<#51430#> <#51431#>(first<#51431#> <#51432#>ab))<#51432#> <#51433#>name)<#51433#>
<#51434#>(entry-number<#51434#> <#51435#>(first<#51435#> <#51436#>ab))]<#51436#>
<#51437#>[<#51437#><#51438#>else<#51438#> <#51439#>(lookup<#51439#> <#51440#>name<#51440#> <#51441#>(rest<#51441#> <#51442#>ab))]<#51442#><#51443#>)]<#51443#><#51444#>))<#51444#>
<#71956#>;; <#69197#><#51445#>service-manager<#51445#> <#51446#>:<#51446#> <#51447#>address-book<#51447#> <#51448#>object<#51448#><#69197#><#71956#>
<#51449#>;; to manage addition to, and searches in, the address book <#51449#>
<#51450#>(d<#51450#><#51451#>efine<#51451#> <#51452#>(service-manager<#51452#> <#51453#>msg)<#51453#>
<#51454#>(c<#51454#><#51455#>ond<#51455#>
<#51456#>[<#51456#><#51457#>(symbol=?<#51457#> <#51458#>msg<#51458#> <#51459#>'<#51459#><#51460#>add)<#51460#>
<#51461#>add-to-address-book]<#51461#>
<#51462#>[<#51462#><#51463#>(symbol=?<#51463#> <#51464#>msg<#51464#> <#51465#>'<#51465#><#51466#>search)<#51466#>
<#51467#>(l<#51467#><#51468#>ambda<#51468#> <#51469#>(name)<#51469#>
<#51470#>(lookup<#51470#> <#51471#>name<#51471#> <#51472#>address-book))]<#51472#>
<#51473#>[<#51473#><#51474#>else<#51474#> <#51475#>(error<#51475#> <#51476#>'<#51476#><#51477#>address-book<#51477#> <#51478#>``message<#51478#> <#51479#>not<#51479#> <#51480#>understood'')]<#51480#><#51481#>)))<#51481#>
<#51482#>service-manager))<#51482#>
<#51486#>Figure: Managing multiple address books<#51486#>
Figure~#figmultipleab#51488> shows the complete definition of
<#69198#><#51489#>make-address-book<#51489#><#69198#>. The definition is standard by now. It consists
of a <#69199#><#51490#>local<#51490#>-expression<#69199#>, which in turn produces the <#69200#><#51491#>local<#51491#><#69200#>ly defined
<#69201#><#51492#>service-manager<#51492#><#69201#> as the result. There is no need for an initializer
because the only state variable is immediately initialized and there is no
graphical view.
To use the address books, we first create one with <#69202#><#51493#>make-address-book<#51493#><#69202#>,
using an appropriate title:
<#71957#>;; <#69203#><#51498#>friends<#51498#> <#51499#>:<#51499#> <#51500#>an<#51500#> <#51501#>address<#51501#> <#51502#>book<#51502#><#69203#><#71957#>
<#51503#>;; to maintain an address book for friends <#51503#>
<#51504#>(d<#51504#><#51505#>efine<#51505#> <#51506#>friends<#51506#>
<#51507#>(make-address-book<#51507#> <#51508#>``Friends<#51508#> <#51509#>of<#51509#> <#51510#>Charles''))<#51510#>
<#71958#>;; <#69204#><#51511#>business<#51511#> <#51512#>:<#51512#> <#51513#>an<#51513#> <#51514#>address<#51514#> <#51515#>book<#51515#><#69204#><#71958#>
<#51516#>;; to maintain an address book for business colleagues<#51516#>
<#51517#>(d<#51517#><#51518#>efine<#51518#> <#51519#>business<#51519#>
<#51520#>(make-address-book<#51520#> <#51521#>``Colleagues<#51521#> <#51522#>@<#51522#> <#51523#>Rice,<#51523#> <#51524#>Inc.''))<#51524#>
Then we can add names and phone numbers to the address book, or we can
retrieve numbers as desired:
<#51532#>;SPMgt;<#51532#> <#51533#>((friends<#51533#> <#51534#>'<#51534#><#51535#>add)<#51535#> <#51536#>'<#51536#><#51537#>Bill<#51537#> <#51538#>2)<#51538#>
<#51539#>;SPMgt;<#51539#> <#51540#>((friends<#51540#> <#51541#>'<#51541#><#51542#>add)<#51542#> <#51543#>'<#51543#><#51544#>Sally<#51544#> <#51545#>3)<#51545#>
<#51546#>;SPMgt;<#51546#> <#51547#>((friends<#51547#> <#51548#>'<#51548#><#51549#>add)<#51549#> <#51550#>'<#51550#><#51551#>Dave<#51551#> <#51552#>4)<#51552#>
<#51553#>;SPMgt;<#51553#> <#51554#>((business<#51554#> <#51555#>'<#51555#><#51556#>add)<#51556#> <#51557#>'<#51557#><#51558#>Emil<#51558#> <#51559#>5)<#51559#>
<#51560#>;SPMgt;<#51560#> <#51561#>((business<#51561#> <#51562#>'<#51562#><#51563#>add)<#51563#> <#51564#>'<#51564#><#51565#>Faye<#51565#> <#51566#>18)<#51566#>
In this case, we added three entries to the address book named
<#69205#><#51570#>friends<#51570#><#69205#> and two to the one called <#69206#><#51571#>business<#51571#><#69206#>.
An addition to, say, <#69207#><#51572#>friends<#51572#><#69207#> works in two steps. The first step is
to apply <#69208#><#51573#>friends<#51573#><#69208#> to <#69209#><#51574#>'<#51574#><#51575#>add<#51575#><#69209#>. This yields the (hidden) function
<#69210#><#51576#>add-to-address-book<#51576#><#69210#>. The second step is to apply this resulting
function to a name and a number. In a similar vein, looking up a phone number
also works in two steps. The application of, say, <#69211#><#51577#>friends<#51577#><#69211#> to
<#69212#><#51578#>'<#51578#><#51579#>search<#51579#><#69212#> yields a function that consumes a name. This function is then
applied to a symbol:
<#51584#>;SPMgt;<#51584#> <#51585#>((friends<#51585#> <#51586#>'<#51586#><#51587#>search)<#51587#> <#51588#>'<#51588#><#51589#>Bill)<#51589#>
<#51590#>2<#51590#>
<#51591#>;SPMgt;<#51591#> <#51592#>((business<#51592#> <#51593#>'<#51593#><#51594#>search)<#51594#> <#51595#>'<#51595#><#51596#>Bill)<#51596#>
<#51597#>false<#51597#>
The two applications show that the number for <#69213#><#51601#>'<#51601#><#51602#>Bill<#51602#><#69213#> in
<#69214#><#51603#>friends<#51603#><#69214#> is <#69215#><#51604#>2<#51604#><#69215#> and that there is no number for <#69216#><#51605#>'<#51605#><#51606#>Bill<#51606#><#69216#>
in colleagues. According to the above additions, that's exactly what we should
expect. Of course, we could also co-mingle the two actions in the
<#51607#>Interactions<#51607#> window, adding and searching for phone numbers at will.
<#51610#>Exercise 39.1.6<#51610#>
Develop an interface definition for the results of the revised version of
<#69217#><#51612#>make-traffic-light<#51612#><#69217#> (see
figure~#figmultipletlreset#51613>).~ Solution<#69218#><#69218#>
<#51619#>Exercise 39.1.7<#51619#>
Show the top-level definitions that the evaluation of <#69219#><#51621#>friends<#51621#><#69219#> and
<#69220#><#51622#>colleagues<#51622#><#69220#> creates.
What is the state of these definitions after the five <#69221#><#51623#>'<#51623#><#51624#>add<#51624#><#69221#>
expressions have been evaluated? Evaluate <#69222#><#51625#>((friends<#51625#>\ <#51626#>'<#51626#><#51627#>search)<#51627#>\ <#51628#>'<#51628#><#51629#>Bill)<#51629#><#69222#>
in this context.~ Solution<#69223#><#69223#>
<#51635#>Exercise 39.1.8<#51635#>
Design <#69224#><#51637#>gui-for-address-boook<#51637#><#69224#>. The function consumes a list
of strings and creates a new address book for each one of them. It also
creates and displays a graphical user interface for an address book with a
choice menu that lets users choose to which address book they want to add an
entry and in which address book the program should search for a
number.~ Solution<#69225#><#69225#>