A <#63050#><#14466#>list of posns<#14466#><#63050#> (<#63051#><#14467#>list-of-posns<#14467#><#63051#>) is eitherEach <#63057#><#14478#>posn<#14478#><#63057#> represents one corner of the polygon. For example,
- the empty list, <#63052#><#14469#>empty<#14469#><#63052#>, or
- <#63053#><#14470#>(cons<#14470#>\ <#14471#>p<#14471#>\ <#14472#>lop)<#14472#><#63053#> where <#63054#><#14473#>p<#14473#><#63054#> is a <#63055#><#14474#>posn<#14474#><#63055#> structure and <#63056#><#14475#>lop<#14475#><#63056#> is a list of posns.
<#14483#>(c<#14483#><#14484#>ons<#14484#> <#14485#>(make-posn<#14485#> <#14486#>10<#14486#> <#14487#>10)<#14487#> <#14488#>(c<#14488#><#14489#>ons<#14489#> <#14490#>(make-posn<#14490#> <#14491#>60<#14491#> <#14492#>60)<#14492#> <#14493#>(c<#14493#><#14494#>ons<#14494#> <#14495#>(make-posn<#14495#> <#14496#>10<#14496#> <#14497#>60)<#14497#> <#14498#>empty)))<#14498#>represents a triangle. The question is what <#63058#><#14502#>empty<#14502#><#63058#> means as a polygon. The answer is that <#63059#><#14503#>empty<#14503#><#63059#> does not represent a polygon and therefore shouldn't be included in the class of polygon representations. A polygon should always have at least one corner, and the lists that represent polygons should always contain at least one <#63060#><#14504#>posn<#14504#><#63060#>. This suggest the following data definition:
A <#63061#><#14506#>polygon<#14506#><#63061#> is eitherIn short, a discussion of how the chosen set of data (lists of <#63069#><#14521#>posn<#14521#><#63069#>s) represents the intended information (geometric polygons) revealed that our choice was inadequate. Revising the data definition brought us closer to our intentions and makes it easier to design the program. Because our drawing primitives always produce <#63070#><#14522#>true<#14522#><#63070#> (if anything), it is natural to suggest the following contract and purpose statement:
- <#63062#><#14508#>(cons<#14508#>\ <#14509#>p<#14509#>\ <#14510#>empty)<#14510#><#63062#> where <#63063#><#14511#>p<#14511#><#63063#> is a <#63064#><#14512#>posn<#14512#><#63064#>, or
- <#63065#><#14513#>(cons<#14513#>\ <#14514#>p<#14514#>\ <#14515#>lop)<#14515#><#63065#> where <#63066#><#14516#>p<#14516#><#63066#> is a <#63067#><#14517#>posn<#14517#><#63067#> structure and <#63068#><#14518#>lop<#14518#><#63068#> is a polygon.
<#71029#>;; <#63071#><#14527#>draw-polygon<#14527#> <#14528#>:<#14528#> <#14529#>polygon<#14529#> <#14530#><#14530#><#14531#>-;SPMgt;<#14531#><#14532#><#14532#> <#14533#>true<#14533#><#63071#><#71029#> <#71030#>;; to draw the polygon specified by <#63072#><#14534#>a-poly<#14534#><#63072#> <#71030#> <#14535#>(define<#14535#> <#14536#>(draw-polygon<#14536#> <#14537#>a-poly)<#14537#> <#14538#>...)<#14538#>In other words, the function draws the lines between the corners and, if all primitive drawing steps work out, it produces <#63073#><#14542#>true<#14542#><#63073#>. For example, the above list of <#63074#><#14543#>posn<#14543#><#63074#>s should produce a triangle. Although the data definition is not just a variant on our well-worn list theme, the template is close to that of a list-processing function:
<#71031#>;; <#63075#><#14548#>draw-polygon<#14548#> <#14549#>:<#14549#> <#14550#>polygon<#14550#> <#14551#><#14551#><#14552#>-;SPMgt;<#14552#><#14553#><#14553#> <#14554#>true<#14554#><#63075#><#71031#> <#71032#>;; to draw the polygon specified by <#63076#><#14555#>a-poly<#14555#><#63076#> <#71032#> <#14556#>(d<#14556#><#14557#>efine<#14557#> <#14558#>(draw-polygon<#14558#> <#14559#>a-poly)<#14559#> <#14560#>(c<#14560#><#14561#>ond<#14561#> <#14562#>[<#14562#><#14563#>(empty?<#14563#> <#14564#>(rest<#14564#> <#14565#>a-poly))<#14565#> <#14566#>...<#14566#> <#14567#>(first<#14567#> <#14568#>a-poly)<#14568#> <#14569#>...]<#14569#> <#14570#>[<#14570#><#14571#>els<#14571#><#14572#>e<#14572#> <#14573#>...<#14573#> <#14574#>(first<#14574#> <#14575#>a-poly)<#14575#> <#14576#>...<#14576#> <#14577#>...<#14577#> <#14578#>(second<#14578#> <#14579#>a-poly)<#14579#> <#14580#>...<#14580#> <#14581#>...<#14581#> <#14582#>(draw-polygon<#14582#> <#14583#>(rest<#14583#> <#14584#>a-poly))<#14584#> <#14585#>...]<#14585#><#14586#>))<#14586#>Given that both clauses in the data definition use <#63077#><#14590#>cons<#14590#><#63077#>, the first condition must inspect the rest of the list, which is <#63078#><#14591#>empty<#14591#><#63078#> for the first case and non-empty for the second one. Furthermore, in the first clause, we can add <#63079#><#14592#>(first<#14592#>\ <#14593#>a-poly)<#14593#><#63079#>; and in the second case, we not only have the first item on the list but the second one, too. After all, polygons generated according to the second clause consist of at least two <#63080#><#14594#>posn<#14594#><#63080#>s. Now we can replace the ``...'' in the template to obtain a complete function definition. For the first clause, the answer must be <#63081#><#14595#>true<#14595#><#63081#>, because we don't have two <#63082#><#14596#>posn<#14596#><#63082#>s that we could connect to form a line. For the second clause, we have two <#63083#><#14597#>posn<#14597#><#63083#>s, we can draw a line between them, and we know that <#63084#><#14598#>(draw-polygon<#14598#><#14599#> <#14599#><#14600#>(rest<#14600#>\ <#14601#>a-poly))<#14601#><#63084#> draws all the remaining lines. Put differently, we can write
<#14606#>(draw-solid-line<#14606#> <#14607#>(first<#14607#> <#14608#>a-poly)<#14608#> <#14609#>(second<#14609#> <#14610#>a-poly))<#14610#>in the second clause because we know that <#63085#><#14614#>a-poly<#14614#><#63085#> has a second item. Both <#63086#><#14615#>(draw-solid-line<#14615#>\ <#14616#>...)<#14616#><#63086#> and <#63087#><#14617#>(draw-poly<#14617#>\ <#14618#>...)<#14618#><#63087#> produce <#63088#><#14619#>true<#14619#><#63088#> if everything goes fine. By combining the two expressions with <#63089#><#14620#>and<#14620#><#63089#>, <#63090#><#14621#>draw-poly<#14621#><#63090#> draws all lines. Here is the complete function definition:
<#14626#>(d<#14626#><#14627#>efine<#14627#> <#14628#>(draw-polygon<#14628#> <#14629#>a-poly)<#14629#> <#14630#>(c<#14630#><#14631#>ond<#14631#> <#14632#>[<#14632#><#14633#>(empty?<#14633#> <#14634#>(rest<#14634#> <#14635#>a-poly))<#14635#> <#14636#>true]<#14636#> <#14637#>[<#14637#><#14638#>else<#14638#> <#14639#>(and<#14639#> <#14640#>(draw-solid-line<#14640#> <#14641#>(first<#14641#> <#14642#>a-poly)<#14642#> <#14643#>(second<#14643#> <#14644#>a-poly))<#14644#> <#14645#>(draw-polygon<#14645#> <#14646#>(rest<#14646#> <#14647#>a-poly)))]<#14647#><#14648#>))<#14648#>Unfortunately, testing it with our triangle example immediately reveals a flaw. Instead of drawing a polygon with three sides, the function draws only an open curve, connecting all the corners but not closing the curve:
Mathematically put, we have defined a more general function than the one we
wanted. The function we defined should be called ``connect-the-dots'' and
not <#63100#><#14668#>draw-polygon<#14668#><#63100#>.
To get from the more general function to what we want, we need to figure
out some way to connect the last dot to the first one. There are several
different ways to accomplish this goal, but all of them mean that we define
the main function in terms of the function we just defined or something
like it. In other words, we define one auxiliary function in terms of a
more general one.
One way to define the new function is to add the first position of a
polygon to the end and to have this new list drawn. A symmetric method is
to pick the last one and add it to the front of the polygon. A third
alternative is to modify the above version of <#63101#><#14669#>draw-polygon<#14669#><#63101#> so that
it connects the last <#63102#><#14670#>posn<#14670#><#63102#> to the first one. Here we discuss the
first alternative; the exercises cover the other two.
To add the last item of <#63103#><#14671#>a-poly<#14671#><#63103#> at the beginning, we need something
like
<#14860#>Accumulator:<#14860#>:\ The new version of <#63132#><#14861#>connect-dots<#14861#><#63132#> is a simple
instance of an accumulator-style function. In part~#partloops#14862><#14676#>(cons<#14676#> <#14677#>(last<#14677#> <#14678#>a-poly)<#14678#> <#14679#>a-poly)<#14679#>
where <#63104#><#14683#>last<#14683#><#63104#> is some auxiliary function that extracts the last item
from a non-empty list. Indeed, this expression is the definition of
<#63105#><#14684#>draw-polygon<#14684#><#63105#> assuming we define <#63106#><#14685#>last<#14685#><#63106#>: see
figure~#figdrawpoly#14686><#71033#>;; <#63108#><#14692#>last<#14692#> <#14693#>:<#14693#> <#14694#>polygon<#14694#> <#14695#><#14695#><#14696#>-;SPMgt;<#14696#><#14697#><#14697#> <#14698#>posn<#14698#><#63108#><#71033#>
<#71034#>;; to extract the last <#63109#><#14699#>posn<#14699#><#63109#> on <#63110#><#14700#>a-poly<#14700#><#63110#><#71034#>
<#14701#>(define<#14701#> <#14702#>(last<#14702#> <#14703#>a-poly)<#14703#> <#14704#>...)<#14704#>
And, because <#63111#><#14708#>last<#14708#><#63111#> consumes a polygon, we can reuse the template
from above:
<#14713#>(d<#14713#><#14714#>efine<#14714#> <#14715#>(last<#14715#> <#14716#>a-poly)<#14716#>
<#14717#>(c<#14717#><#14718#>ond<#14718#>
<#14719#>[<#14719#><#14720#>(empty?<#14720#> <#14721#>(rest<#14721#> <#14722#>a-poly))<#14722#> <#14723#>...<#14723#> <#14724#>(first<#14724#> <#14725#>a-poly)<#14725#> <#14726#>...]<#14726#>
<#14727#>[<#14727#><#14728#>els<#14728#><#14729#>e<#14729#> <#14730#>...<#14730#> <#14731#>(first<#14731#> <#14732#>a-poly)<#14732#> <#14733#>...<#14733#>
<#14734#>...<#14734#> <#14735#>(second<#14735#> <#14736#>a-poly)<#14736#> <#14737#>...<#14737#>
<#14738#>...<#14738#> <#14739#>(last<#14739#> <#14740#>(rest<#14740#> <#14741#>a-poly))<#14741#> <#14742#>...]<#14742#><#14743#>))<#14743#>
Turning the template into a complete function is a short step. If the list
is empty except for one item, this item is the desired result. If
<#63112#><#14747#>(rest<#14747#>\ <#14748#>a-poly)<#14748#><#63112#> is not empty, <#63113#><#14749#>(last<#14749#>\ <#14750#>(rest<#14750#>\ <#14751#>a-poly))<#14751#><#63113#>
determines the last item of <#63114#><#14752#>a-poly<#14752#><#63114#>. The complete definition of
<#63115#><#14753#>last<#14753#><#63115#> is displayed at the bottom of figure~#figdrawpoly#14754><#71035#>;; <#63116#><#14759#>draw-polygon<#14759#> <#14760#>:<#14760#> <#14761#>polygon<#14761#> <#14762#><#14762#><#14763#>-;SPMgt;<#14763#><#14764#><#14764#> <#14765#>true<#14765#><#63116#><#71035#>
<#14766#>;; to draw the polygon specified by a-poly <#14766#>
<#14767#>(d<#14767#><#14768#>efine<#14768#> <#14769#>(draw-polygon<#14769#> <#14770#>a-poly)<#14770#>
<#14771#>(connect-dots<#14771#> <#14772#>(cons<#14772#> <#14773#>(last<#14773#> <#14774#>a-poly)<#14774#> <#14775#>a-poly)))<#14775#>
<#71036#>;; <#63117#><#14776#>connect-dots<#14776#> <#14777#>:<#14777#> <#14778#>polygon<#14778#> <#14779#><#14779#><#14780#>-;SPMgt;<#14780#><#14781#><#14781#> <#14782#>true<#14782#><#63117#><#71036#>
<#71037#>;; to draw connections between the dots of <#63118#><#14783#>a-poly<#14783#><#63118#><#71037#>
<#14784#>(d<#14784#><#14785#>efine<#14785#> <#14786#>(connect-dots<#14786#> <#14787#>a-poly)<#14787#>
<#14788#>(c<#14788#><#14789#>ond<#14789#>
<#14790#>[<#14790#><#14791#>(empty?<#14791#> <#14792#>(rest<#14792#> <#14793#>a-poly))<#14793#> <#14794#>true]<#14794#>
<#14795#>[<#14795#><#14796#>else<#14796#> <#14797#>(and<#14797#> <#14798#>(draw-solid-line<#14798#> <#14799#>(first<#14799#> <#14800#>a-poly)<#14800#> <#14801#>(second<#14801#> <#14802#>a-poly)<#14802#> <#14803#>RED)<#14803#>
<#14804#>(connect-dots<#14804#> <#14805#>(rest<#14805#> <#14806#>a-poly)))]<#14806#><#14807#>))<#14807#>
<#71038#>;; <#63119#><#14808#>last<#14808#> <#14809#>:<#14809#> <#14810#>polygon<#14810#> <#14811#><#14811#><#14812#>-;SPMgt;<#14812#><#14813#><#14813#> <#14814#>posn<#14814#><#63119#><#71038#>
<#71039#>;; to extract the last <#63120#><#14815#>posn<#14815#><#63120#> on <#63121#><#14816#>a-poly<#14816#><#63121#><#71039#>
<#14817#>(d<#14817#><#14818#>efine<#14818#> <#14819#>(last<#14819#> <#14820#>a-poly)<#14820#>
<#14821#>(c<#14821#><#14822#>ond<#14822#>
<#14823#>[<#14823#><#14824#>(empty?<#14824#> <#14825#>(rest<#14825#> <#14826#>a-poly))<#14826#> <#14827#>(first<#14827#> <#14828#>a-poly)]<#14828#>
<#14829#>[<#14829#><#14830#>else<#14830#> <#14831#>(last<#14831#> <#14832#>(rest<#14832#> <#14833#>a-poly))]<#14833#><#14834#>))<#14834#>
<#14838#>Figure: Drawing a polygon<#14838#>
In summary, the development of <#63122#><#14840#>draw-polygon<#14840#><#63122#> naturally led us to
consider a more general problem: connecting a list of dots. We solved the
originally problem by defining a function that uses (a variant of) the more
general function. As we will see again and again, generalizing the purpose
of a function is often the best method to simplify the problem.
<#14843#>Exercise 12.3.1<#14843#>