Designing Functions for Mixed Data

The function development in the preceding section suggests some amendments to our design recipe. Specifically, the data analysis step, the template construction step, and the definition of the function's body require adjustments.
Data Analysis & Design:
To determine whether a function consumes several distinct classes of data, we must carefully inspect the problem statement. In other words, the data analysis must take into account several aspects now. First, we must determine how many distinct classes of ``problem objects'' exist and what their important attributes are. If there are several different kinds of objects, we are mixing data. Second, we must understand whether the various objects have several properties. If an object has several attributes, we need a class of compound data for its representation. The resulting data definition may have several clauses that enumerate several possibilities, and indeed, we may develop a hierarchy of data definitions. The example of the preceding section deals with two distinct kinds of shapes, each of which has several properties. We captured this idea with the following data definition:
A <#61512#><#6404#>shape<#6404#><#61512#> is either
  1. a structure:

    <#70789#><#61513#><#6406#>(make-circle<#6406#>\ <#6407#>p<#6407#>\ <#6408#>s)<#6408#><#61513#><#70789#> where <#61514#><#6409#>p<#6409#><#61514#> is a <#61515#><#6410#>posn<#6410#><#61515#> and <#61516#><#6411#>s<#6411#><#61516#> is a number; or

  2. a structure:

    <#70790#><#61517#><#6412#>(make-square<#6412#>\ <#6413#>p<#6413#>\ <#6414#>s)<#6414#><#61517#><#70790#> where <#61518#><#6415#>p<#6415#><#61518#> is a <#61519#><#6416#>posn<#6416#><#61519#> and <#61520#><#6417#>s<#6417#><#61520#> is a number.

It specifies that every <#61521#><#6420#>shape<#6420#><#61521#> belongs to one of two subclasses of data. For a data definition to make sense, it must be possible to formulate conditions that distinguish the various subclasses in a definition. That is, if <#61522#><#6421#>x<#6421#><#61522#> stands for a piece of data in the defined class, we must be able to use built-in and user-defined predicates to distinguish the enumerated subclasses from each other. In our running example, the two conditions would be <#61523#><#6422#>(square?<#6422#>\ <#6423#>x)<#6423#><#61523#> and <#61524#><#6424#>(circle?<#6424#>\ <#6425#>x)<#6425#><#61524#>.
Template:
Recall that the template is a translation of the input data definition into Scheme. Thus, imagine that we have a data definition that enumerates several distinct possibilities. The first step is to write down a <#61525#><#6426#>cond<#6426#>-expression<#61525#> with as many clauses as there are enumerated possibilities in the data definition. The second step is to add a condition to each line. Each condition should hold if the input belongs to the corresponding subclass of data mentioned in the data definition. Here is the template for our running example:
  <#70791#>;; <#61526#><#6430#>f<#6430#> <#6431#>:<#6431#> <#6432#>shape<#6432#> <#6433#><#6433#><#6434#>-;SPMgt;<#6434#><#6435#><#6435#> <#6436#>?<#6436#><#6437#>?<#6437#><#6438#>?<#6438#><#61526#><#70791#>
  <#6439#>(d<#6439#><#6440#>efine<#6440#> <#6441#>(f<#6441#> <#6442#>a-shape)<#6442#> 
    <#6443#>(c<#6443#><#6444#>ond<#6444#> 
      <#6445#>[<#6445#><#6446#>(square?<#6446#> <#6447#>a-shape)<#6447#> <#6448#>...]<#6448#> 
      <#6449#>[<#6449#><#6450#>(circle?<#6450#> <#6451#>a-shape)<#6451#> <#6452#>...]<#6452#><#6453#>))<#6453#> 
The output specification and the purpose statement are missing to emphasize that a template has no connection to the output or the purpose of a function. Once we have formulated the template for the conditional, we refine the template further, <#61527#><#6456#>cond<#6456#><#61527#>-line by <#61528#><#6457#>cond<#6457#><#61528#>-line. If the purpose of a line is to process atomic information, we are done. If a line processes compound data, we enrich the template with appropriate selector expressions. Let's illustrate the idea with our running example again:
  <#6461#>(d<#6461#><#6462#>efine<#6462#> <#6463#>(f<#6463#> <#6464#>a-shape)<#6464#>
    <#6465#>(c<#6465#><#6466#>ond<#6466#> 
      <#6467#>[<#6467#><#6468#>(square?<#6468#> <#6469#>a-shape)<#6469#> 
       <#6470#>...<#6470#> <#6471#>(square-nw<#6471#> <#6472#>a-shape)<#6472#> <#6473#>...<#6473#> <#6474#>(square-length<#6474#> <#6475#>a-shape)<#6475#> <#6476#>...]<#6476#> 
      <#6477#>[<#6477#><#6478#>(circle?<#6478#> <#6479#>a-shape)<#6479#> 
       <#6480#>...<#6480#> <#6481#>(circle-center<#6481#> <#6482#>a-shape)<#6482#> <#6483#>...<#6483#> <#6484#>(circle-radius<#6484#> <#6485#>a-shape)<#6485#> <#6486#>...]<#6486#><#6487#>))<#6487#> 
Body:
Using the conditional template, we split the task into simpler tasks. Specifically, we can focus on each <#61529#><#6490#>cond<#6490#><#61529#>-line separately, simply considering the question what is the output if we are given this kind of input. All other cases are ignored as we work out one particular clause. For example, if we wish to define a function that computes the perimeter of a shape, we start from the template and fill in the gaps:
  <#70792#>;; <#61530#><#6494#>perimeter<#6494#> <#6495#>:<#6495#> <#6496#>shape<#6496#> <#6497#><#6497#><#6498#>-;SPMgt;<#6498#><#6499#><#6499#> <#6500#>number<#6500#><#61530#><#70792#>
  <#70793#>;; to compute the perimeter of <#61531#><#6501#>a-shape<#6501#><#61531#><#70793#> 
  <#6502#>(d<#6502#><#6503#>efine<#6503#> <#6504#>(perimeter<#6504#> <#6505#>a-shape)<#6505#> 
    <#6506#>(c<#6506#><#6507#>ond<#6507#> 
      <#6508#>[<#6508#><#6509#>(square?<#6509#> <#6510#>a-shape)<#6510#> <#6511#>(*<#6511#> <#6512#>(square-length<#6512#> <#6513#>a-shape)<#6513#> <#6514#>4)]<#6514#> 
      <#6515#>[<#6515#><#6516#>(circle?<#6516#> <#6517#>a-shape)<#6517#> <#6518#>(*<#6518#> <#6519#>(*<#6519#> <#6520#>2<#6520#> <#6521#>(circle-radius<#6521#> <#6522#>a-shape))<#6522#> <#6523#>pi)]<#6523#><#6524#>))<#6524#> 
Figure~#figdesign4example#6527> summarizes the development of our running example.
The remaining steps of the recipes in figures~#figdesign1#6529>, #figdesign2#6530>, and~#figdesign3#6531> should be followed on an as-is basis. Figure~#figdesign4#6532> summarizes the design recipe, with all steps included.
<#61532#>;; <#6537#>Data Definition<#6537#>:<#61532#>
<#6538#>(define-struct<#6538#> <#6539#>circle<#6539#> <#6540#>(center<#6540#> <#6541#>radius))<#6541#> 
<#6542#>(define-struct<#6542#> <#6543#>square<#6543#> <#6544#>(nw<#6544#> <#6545#>length<#6545#><#6546#>))<#6546#> 
<#70794#>;; A <#61533#><#6547#>shape<#6547#><#61533#> is either<#70794#> 
<#70795#>;; 1. a structure: <#61534#><#6548#>(make-circle<#6548#> <#6549#>p<#6549#> <#6550#>s)<#6550#><#61534#><#70795#> 
<#70796#>;; ~~ where <#61535#><#6551#>p<#6551#><#61535#> is a <#61536#><#6552#>posn<#6552#><#61536#>, <#61537#><#6553#>s<#6553#><#61537#> a number;<#70796#> 
<#70797#>;; 2. a structure: <#61538#><#6554#>(make-square<#6554#> <#6555#>p<#6555#> <#6556#>s)<#6556#><#61538#><#70797#> 
<#70798#>;; ~~ where <#61539#><#6557#>p<#6557#><#61539#> is a <#61540#><#6558#>posn<#6558#><#61540#>, <#61541#><#6559#>s<#6559#><#61541#> a number.<#70798#> 
<#61542#>;; <#6560#>Contract, Purpose, Header<#6560#>: <#61542#> 
<#70799#>;; <#61543#><#6561#>perimeter<#6561#> <#6562#>:<#6562#> <#6563#>shape<#6563#> <#6564#><#6564#><#6565#>-;SPMgt;<#6565#><#6566#><#6566#> <#6567#>number<#6567#><#61543#><#70799#> 
<#70800#>;; to compute the perimeter of <#61544#><#6568#>a-shape<#6568#><#61544#><#70800#> 
<#61545#>;; <#6569#>Examples<#6569#>:<#61545#> 
<#70801#>;; <#61546#><#6570#>(perimeter<#6570#> <#6571#>(make-square<#6571#> <#6572#>...<#6572#> <#6573#>3))<#6573#><#61546#><#70801#> 
<#70802#>;; = <#61547#><#6574#>12<#6574#><#61547#><#70802#> 
<#70803#>;; <#61548#><#6575#>(perimeter<#6575#> <#6576#>(make-circle<#6576#> <#6577#>...<#6577#> <#6578#>1))<#6578#><#61548#><#70803#> 
<#70804#>;; = <#61549#><#6579#>6.28<#6579#><#61549#><#70804#> 
<#61550#>;; <#6580#>Template<#6580#>:<#61550#> 
<#70805#>;; <#61551#><#6581#>(define<#6581#> <#6582#>(f<#6582#> <#6583#>a-shape)<#6583#><#61551#><#70805#> 
<#70806#>;; ~~<#61552#> <#6584#>(cond<#6584#><#61552#><#70806#> 
<#70807#>;; <#61553#> <#6585#>[<#6585#><#6586#>(square?<#6586#> <#6587#>a-shape)<#6587#><#61553#><#70807#> 
<#70808#>;; <#61554#> <#6588#>...<#6588#> <#6589#>(square-nw<#6589#> <#6590#>a-shape)<#6590#> <#6591#>...<#6591#> <#6592#>(square-length<#6592#> <#6593#>a-shape)<#6593#> <#6594#>...]<#6594#><#61554#><#70808#> 
<#70809#>;; <#61555#> <#6595#>[<#6595#><#6596#>(circle?<#6596#> <#6597#>a-shape)<#6597#><#61555#><#70809#> 
<#70810#>;; <#61556#> <#6598#>...<#6598#> <#6599#>(circle-center<#6599#> <#6600#>a-shape)<#6600#> <#6601#>...<#6601#> <#6602#>(circle-radius<#6602#> <#6603#>a-shape)<#6603#> <#6604#>...]<#6604#><#6605#>))<#6605#><#61556#><#70810#> 
<#61557#>;; <#6606#>Definition<#6606#>: <#61557#> 
<#6607#>(d<#6607#><#6608#>efine<#6608#> <#6609#>(perimeter<#6609#> <#6610#>a-shape)<#6610#> 
  <#6611#>(c<#6611#><#6612#>ond<#6612#> 
    <#6613#>[<#6613#><#6614#>(circle?<#6614#> <#6615#>a-shape)<#6615#> 
     <#6616#>(*<#6616#> <#6617#>(*<#6617#> <#6618#>2<#6618#> <#6619#>(circle-radius<#6619#> <#6620#>a-shape))<#6620#> <#6621#>pi)]<#6621#> 
    <#6622#>[<#6622#><#6623#>(square?<#6623#> <#6624#>a-shape)<#6624#> 
     <#6625#>(*<#6625#> <#6626#>(square-length<#6626#> <#6627#>a-shape)<#6627#> <#6628#>4)]<#6628#><#6629#>))<#6629#> 
<#61558#>;; <#6630#>Tests<#6630#>: (same as examples)<#61558#> 
<#6634#>Figure: The design recipe for mixed data: a complete example<#6634#>

#tabular6637#

<#6721#>Figure: The revised design recipe for mixed data<#6721#>

<#61562#>(Refines the recipes in figures~#figdesign1#6723> (pg.~#figdesign1#6724>) and~#figdesign3#6725> (pg.~#figdesign3#6726>))<#61562#>


Even a cursory comparative reading of the design recipes in sections~#secdesign1#6727>, ~#secdesign2#6728>, ~#secdesign3#6729>, and the current one suggests that the data analysis and the template design steps are becoming more and more important. If we do not understand what kind of data a function consumes, we cannot design it and organize it properly. If, however, we do understand the structure of the data definition and organize our template properly, it is easy to modify or to extend a function. For example, if we add new information to the representation of a <#61563#><#6730#>circle<#6730#><#61563#>, then only those <#61564#><#6731#>cond<#6731#><#61564#>-clauses related to circles may require changes. Similarly, if we add a new kind of shape to our data definition, say, rectangles, we must add new <#61565#><#6732#>cond<#6732#><#61565#>-clauses to our functions.
<#6735#>Exercise 7.2.1<#6735#> Develop structure and data definitions for a collection of zoo animals. The collection includes
spiders,
whose relevant attributes are the number of remaining legs (we assume that spiders can lose legs in accidents) and the space they need in case of transport;
elephants,
whose only attributes are the space they need in case of transport;
monkeys,
whose attributes are intelligence and space needed for transportation.
Then develop a template for functions that consume zoo animals. Develop the function <#61566#><#6739#>fits?<#6739#><#61566#>. The function consumes a zoo animal and the volume of a cage. It determines whether the cage is large enough for the animal.~ external Solution<#61567#><#61567#> <#6745#>Exercise 7.2.2<#6745#> The administrators of metropolitan transportation agencies manage fleets of vehicles. Develop structure and data definitions for a collection of such vehicles. The collection should at least include buses, limos, cars, and subways. Add at least two attributes per class of vehicle. Then develop a template for functions that consume vehicles. ~ external Solution<#61568#><#61568#>