<#37397#>(d<#37397#><#37398#>efine<#37398#> <#37399#>(how-many<#37399#> <#37400#>a-list)<#37400#> <#37401#>(c<#37401#><#37402#>ond<#37402#> <#37403#>[<#37403#><#37404#>(empty?<#37404#> <#37405#>a-list)<#37405#> <#37406#>0]<#37406#> <#37407#>[<#37407#><#37408#>else<#37408#> <#37409#>(+<#37409#> <#37410#>(how-many<#37410#> <#37411#>(rest<#37411#> <#37412#>a-list))<#37412#> <#37413#>1)]<#37413#><#37414#>))<#37414#>It consumes a list and computes how many items the list contains. Here is a sample evaluation:
<#37422#>(how-many<#37422#> <#37423#>(list<#37423#> <#37424#>'<#37424#><#37425#>a<#37425#> <#37426#>'<#37426#><#37427#>b<#37427#> <#37428#>'<#37428#><#37429#>c))<#37429#>
<#37437#>=<#37437#> <#37438#>(+<#37438#> <#37439#>(how-many<#37439#> <#37440#>(list<#37440#> <#37441#>'<#37441#><#37442#>b<#37442#> <#37443#>'<#37443#><#37444#>c))<#37444#> <#37445#>1)<#37445#>
<#37453#>=<#37453#> <#37454#>(+<#37454#> <#37455#>(+<#37455#> <#37456#>(how-many<#37456#> <#37457#>(list<#37457#> <#37458#>'<#37458#><#37459#>c))<#37459#> <#37460#>1)<#37460#> <#37461#>1)<#37461#>
<#37469#>=<#37469#> <#37470#>(+<#37470#> <#37471#>(+<#37471#> <#37472#>(+<#37472#> <#37473#>(how-many<#37473#> <#37474#>empty)<#37474#> <#37475#>1)<#37475#> <#37476#>1)<#37476#> <#37477#>1)<#37477#>
<#37485#>=<#37485#> <#37486#>3<#37486#>It consists of only those steps that are natural recursions. The steps in between are always the same. For example, to get from the original application to the first natural recursion, we go through the following steps:
<#37494#>(how-many<#37494#> <#37495#>(list<#37495#> <#37496#>'<#37496#><#37497#>a<#37497#> <#37498#>'<#37498#><#37499#>b<#37499#> <#37500#>'<#37500#><#37501#>c))<#37501#>
<#37509#>=<#37509#> <#37510#>(c<#37510#><#37511#>ond<#37511#> <#37512#>[<#37512#><#37513#>(empty?<#37513#> <#37514#>(list<#37514#> <#37515#>'<#37515#><#37516#>a<#37516#> <#37517#>'<#37517#><#37518#>b<#37518#> <#37519#>'<#37519#><#37520#>c))<#37520#> <#37521#>0]<#37521#> <#37522#>[<#37522#><#37523#>else<#37523#> <#37524#>(+<#37524#> <#37525#>(how-many<#37525#> <#37526#>(rest<#37526#> <#37527#>(list<#37527#> <#37528#>'<#37528#><#37529#>a<#37529#> <#37530#>'<#37530#><#37531#>b<#37531#> <#37532#>'<#37532#><#37533#>c)))<#37533#> <#37534#>1)]<#37534#><#37535#>)<#37535#>
<#37543#>=<#37543#> <#37544#>(c<#37544#><#37545#>ond<#37545#> <#37546#>[<#37546#><#37547#>false<#37547#> <#37548#>0]<#37548#> <#37549#>[<#37549#><#37550#>else<#37550#> <#37551#>(+<#37551#> <#37552#>(how-many<#37552#> <#37553#>(rest<#37553#> <#37554#>(list<#37554#> <#37555#>'<#37555#><#37556#>a<#37556#> <#37557#>'<#37557#><#37558#>b<#37558#> <#37559#>'<#37559#><#37560#>c)))<#37560#> <#37561#>1)]<#37561#><#37562#>)<#37562#>
<#37570#>=<#37570#> <#37571#>(c<#37571#><#37572#>ond<#37572#> <#37573#>[<#37573#><#37574#>else<#37574#> <#37575#>(+<#37575#> <#37576#>(how-many<#37576#> <#37577#>(rest<#37577#> <#37578#>(list<#37578#> <#37579#>'<#37579#><#37580#>a<#37580#> <#37581#>'<#37581#><#37582#>b<#37582#> <#37583#>'<#37583#><#37584#>c)))<#37584#> <#37585#>1)]<#37585#><#37586#>)<#37586#>
<#37594#>=<#37594#> <#37595#>(+<#37595#> <#37596#>(how-many<#37596#> <#37597#>(rest<#37597#> <#37598#>(list<#37598#> <#37599#>'<#37599#><#37600#>a<#37600#> <#37601#>'<#37601#><#37602#>b<#37602#> <#37603#>'<#37603#><#37604#>c)))<#37604#> <#37605#>1)<#37605#>The steps between the remaing natural recursions differ only as far as the substitution for <#66955#><#37609#>a-list<#37609#><#66955#> is concerned. If we apply <#66956#><#37610#>how-many<#37610#><#66956#> to a shorter list, we need fewer natural recursion steps:
<#37615#>(how-many<#37615#> <#37616#>(list<#37616#> <#37617#>'<#37617#><#37618#>e))<#37618#> <#37619#>=<#37619#> <#37620#>(+<#37620#> <#37621#>(how-many<#37621#> <#37622#>empty)<#37622#> <#37623#>1)<#37623#> <#37624#>=<#37624#> <#37625#>1<#37625#>If we apply <#66957#><#37629#>how-many<#37629#><#66957#> to a longer list, we need more natural recursion steps. The number of steps between natural recursions remains the same. The example suggests that, not surprisingly, the number of evaluation steps depends on the size of the input. More importantly, though, it also implies that the number of natural recrusions is a good measure of the size of an evaluation sequence. After all, we can reconstruct the actual number of steps from this measure and the function definition. For this reason, programmers have come to express the <#66958#><#37630#>ABSTRACT RUNNING TIME<#37630#><#66958#> of a program as a relationship between the size of the input and the number of natural recursion steps in an evaluation.
<#37638#>(d<#37638#><#37639#>efine<#37639#> <#37640#>(contains-doll?<#37640#> <#37641#>a-list-of-symbols)<#37641#> <#37642#>(c<#37642#><#37643#>ond<#37643#> <#37644#>[<#37644#><#37645#>(empty?<#37645#> <#37646#>a-list-of-symbols)<#37646#> <#37647#>false<#37647#><#37648#>]<#37648#> <#37649#>[<#37649#><#37650#>else<#37650#> <#37651#>(c<#37651#><#37652#>ond<#37652#> <#37653#>[<#37653#><#37654#>(symbol=?<#37654#> <#37655#>(first<#37655#> <#37656#>a-list-of-symbols)<#37656#> <#37657#>'<#37657#><#37658#>doll)<#37658#> <#37659#>true<#37659#><#37660#>]<#37660#> <#37661#>[<#37661#><#37662#>else<#37662#> <#37663#>(contains-doll?<#37663#> <#37664#>(rest<#37664#> <#37665#>a-list-of-symbols))]<#37665#><#37666#>)]<#37666#><#37667#>))<#37667#>If we evaluate
<#37675#>(contains-doll?<#37675#> <#37676#>(list<#37676#> <#37677#>'<#37677#><#37678#>doll<#37678#> <#37679#>'<#37679#><#37680#>robot<#37680#> <#37681#>'<#37681#><#37682#>ball<#37682#> <#37683#>'<#37683#><#37684#>game-boy<#37684#> <#37685#>'<#37685#><#37686#>pokemon))<#37686#>the application requires no natural recursion step. In contrast, for the expression
<#37694#>(contains-doll?<#37694#> <#37695#>(list<#37695#> <#37696#>'<#37696#><#37697#>robot<#37697#> <#37698#>'<#37698#><#37699#>ball<#37699#> <#37700#>'<#37700#><#37701#>game-boy<#37701#> <#37702#>'<#37702#><#37703#>pokemon<#37703#> <#37704#>'<#37704#><#37705#>doll))<#37705#>the evaluation requires as many natural recursion steps as there are items in the list. Put differently, in the best case, the function can find the answer immediately; in the worst case, the function must search the entire input list. Programmers cannot assume that inputs are always of the best posisble shape; and they must hope that the inputs are not of the worst possible shape. Instead, they must analyze how much time their functions take <#37709#>on the average<#37709#>. For exanple, <#66959#><#37710#>contains-doll?<#37710#><#66959#> may---on the average---find <#66960#><#37711#>'<#37711#><#37712#>doll<#37712#><#66960#> somewhere in the middle of the list. Thus, we could say that if the input contains <#37713#>N<#37713#> items, the abstract running time of <#66961#><#37714#>contains-doll?<#37714#><#66961#> is (roughly)
that is, it naturally recurs half as often as the number of items on the
input. Because we already measure the running time of a function in an
abstract manner, we can ignore the division by 2. More precisely, we assume
that each basic step takes K units of time. If, instead, we use K/2 as
the constant, we can calculate
which shows that we can ignore other constant factors. To indicate that we
are hiding such constants we say that <#66962#><#37721#>contains-doll?<#37721#><#66962#> takes ``on
the order of <#37722#>N<#37722#> steps'' to find <#66963#><#37723#>'<#37723#><#37724#>doll<#37724#><#66963#> in a list of <#37725#>N<#37725#> items.
Now consider our standard sorting function from figure~#figsort#37726>
steps, but we will see in exercise~#exbigo1#37887>
Altogether the hand-evaluation requires eight natural recursions for a list
of four items. If we add <#66996#><#38081#>4<#38081#><#66996#> (or a larger number) at the end of the
list, we need to double the number of natural recursions. Thus, in general
we need on the order of recursions for a list of <#38082#>N<#38082#> numbers
when the last number is the maximum. <#37731#>(sort<#37731#> <#37732#>(list<#37732#> <#37733#>3<#37733#> <#37734#>1<#37734#> <#37735#>2))<#37735#>
<#37743#>=<#37743#> <#37744#>(insert<#37744#> <#37745#>3<#37745#> <#37746#>(sort<#37746#> <#37747#>(list<#37747#> <#37748#>1<#37748#> <#37749#>2)))<#37749#>
<#37757#>=<#37757#> <#37758#>(insert<#37758#> <#37759#>3<#37759#> <#37760#>(insert<#37760#> <#37761#>1<#37761#> <#37762#>(sort<#37762#> <#37763#>(list<#37763#> <#37764#>2))))<#37764#>
<#37772#>=<#37772#> <#37773#>(insert<#37773#> <#37774#>3<#37774#> <#37775#>(insert<#37775#> <#37776#>1<#37776#> <#37777#>(insert<#37777#> <#37778#>2<#37778#> <#37779#>(sort<#37779#> <#37780#>empty))))<#37780#>
<#37788#>=<#37788#> <#37789#>(insert<#37789#> <#37790#>3<#37790#> <#37791#>(insert<#37791#> <#37792#>1<#37792#> <#37793#>(insert<#37793#> <#37794#>2<#37794#> <#37795#>empty)))<#37795#>
<#37803#>=<#37803#> <#37804#>(insert<#37804#> <#37805#>3<#37805#> <#37806#>(insert<#37806#> <#37807#>1<#37807#> <#37808#>(list<#37808#> <#37809#>2)))<#37809#>
<#37817#>=<#37817#> <#37818#>(insert<#37818#> <#37819#>3<#37819#> <#37820#>(cons<#37820#> <#37821#>2<#37821#> <#37822#>(insert<#37822#> <#37823#>1<#37823#> <#37824#>empty)))<#37824#>
<#37832#>=<#37832#> <#37833#>(insert<#37833#> <#37834#>3<#37834#> <#37835#>(list<#37835#> <#37836#>2<#37836#> <#37837#>1))<#37837#>
<#37845#>=<#37845#> <#37846#>(insert<#37846#> <#37847#>3<#37847#> <#37848#>(list<#37848#> <#37849#>2<#37849#> <#37850#>1))<#37850#>
<#37858#>=<#37858#> <#37859#>(list<#37859#> <#37860#>3<#37860#> <#37861#>2<#37861#> <#37862#>1)<#37862#>
The evaluation is more complicated than those for <#66964#><#37866#>how-many<#37866#><#66964#> or
<#66965#><#37867#>contains-doll?<#37867#><#66965#>. It also consists of two phases. During the first
one, the natural recursions for <#66966#><#37868#>sort<#37868#><#66966#> set up as many applications
of <#66967#><#37869#>insert<#37869#><#66967#> as there are items in the list. During the second phase,
each application of <#66968#><#37870#>insert<#37870#><#66968#> traverses a list of 1, 2, 3, ...\ up
to the number of items in the original list (minus one).
Inserting an item is similar to finding an item, so it is not surprising
that <#66969#><#37871#>insert<#37871#><#66969#> behaves like <#66970#><#37872#>contains-doll?<#37872#><#66970#>. More
specifically, the applications of <#66971#><#37873#>insert<#37873#><#66971#> to a list of <#37874#>N<#37874#>
items may trigger <#37875#>N<#37875#> natural recursions or none. On the average, we
assume it requires N/2, which means on the order of <#37876#>N<#37876#>. Because
there are <#37877#>N<#37877#> applications of <#66972#><#37878#>insert<#37878#><#66972#>, we have an average of
on the order of <#71541#>;; <#66979#><#37893#>max<#37893#> <#37894#>:<#37894#> <#37895#>ne-list-of-numbers<#37895#> <#37896#><#37896#><#37897#>-;SPMgt;<#37897#><#37898#><#37898#> <#37899#>number<#37899#><#66979#><#71541#>
<#37900#>;; to determine the maximum of a non-empty list of numbers <#37900#>
<#37901#>(d<#37901#><#37902#>efine<#37902#> <#37903#>(max<#37903#> <#37904#>alon)<#37904#>
<#37905#>(c<#37905#><#37906#>ond<#37906#>
<#37907#>[<#37907#><#37908#>(empty?<#37908#> <#37909#>(rest<#37909#> <#37910#>alon))<#37910#> <#37911#>(first<#37911#> <#37912#>alon)]<#37912#>
<#37913#>[<#37913#><#37914#>else<#37914#> <#37915#>(c<#37915#><#37916#>ond<#37916#>
<#37917#>[<#37917#><#37918#>(;SPMgt;<#37918#> <#37919#>(max<#37919#> <#37920#>(rest<#37920#> <#37921#>alon))<#37921#> <#37922#>(first<#37922#> <#37923#>alon))<#37923#> <#37924#>(max<#37924#> <#37925#>(rest<#37925#> <#37926#>alon))]<#37926#>
<#37927#>[<#37927#><#37928#>else<#37928#> <#37929#>(first<#37929#> <#37930#>alon)]<#37930#><#37931#>)]<#37931#><#37932#>))<#37932#>
In exercise~#exlocalinterm2#37936> <#37949#>(max<#37949#> <#37950#>(list<#37950#> <#37951#>0<#37951#> <#37952#>1<#37952#> <#37953#>2<#37953#> <#37954#>3))<#37954#>
<#37955#>=<#37955#> <#37956#>(c<#37956#><#37957#>ond<#37957#>
<#37958#>[<#37958#><#37959#>(;SPMgt;<#37959#> <#72346#>
From here, we must evaluate the left of the two underlined natural
recursions. Because the result is <#66985#><#37979#>3<#37979#><#66985#> and the condition is thus
<#66986#><#37980#>true<#37980#><#66986#>, we must evaluate the second underlined natural recursion as
well.
Focusing on just the natural recursion we see that its hand-evaluation
begins with similar steps:
<#37985#>(max<#37985#> <#37986#>(list<#37986#> <#37987#>1<#37987#> <#37988#>2<#37988#> <#37989#>3))<#37989#>
<#37990#>=<#37990#> <#37991#>(c<#37991#><#37992#>ond<#37992#>
<#37993#>[<#37993#><#37994#>(;SPMgt;<#37994#> <#37995#>(max<#37995#> <#37996#>(list<#37996#> <#37997#>2<#37997#> <#37998#>3))<#37998#> <#37999#>1)<#37999#> <#38000#>(max<#38000#> <#38001#>(list<#38001#> <#38002#>2<#38002#> <#38003#>3))]<#38003#>
<#38004#>[<#38004#><#38005#>else<#38005#> <#38006#>1]<#38006#><#38007#>)<#38007#>
Again, <#66987#><#38011#>(max<#38011#>\ <#38012#>(list<#38012#>\ <#38013#>2<#38013#>\ <#38014#>3))<#38014#><#66987#> is evaluated twice because it produces the
maximum. Finally, even determining the maximum of <#66988#><#38015#>(max<#38015#>\ <#38016#>(list<#38016#>\ <#38017#>2<#38017#>\ <#38018#>3))<#38018#><#66988#>
requires two natural recursions:
<#38023#>(max<#38023#> <#38024#>(list<#38024#> <#38025#>2<#38025#> <#38026#>3))<#38026#>
<#38027#>=<#38027#> <#38028#>(c<#38028#><#38029#>ond<#38029#>
<#38030#>[<#38030#><#38031#>(;SPMgt;<#38031#> <#38032#>(max<#38032#> <#38033#>(list<#38033#> <#38034#>3))<#38034#> <#38035#>2)<#38035#> <#38036#>(max<#38036#> <#38037#>(list<#38037#> <#38038#>3))]<#38038#>
<#38039#>[<#38039#><#38040#>else<#38040#> <#38041#>2]<#38041#><#38042#>)<#38042#>
To summarize, <#66989#><#38046#>max<#38046#><#66989#> requires two natural recursion for each natural
recursion. The following table counts the instances for our example:
<#71544#>;; <#67001#><#38095#>max2<#38095#> <#38096#>:<#38096#> <#38097#>ne-list-of-numbers<#38097#> <#38098#><#38098#><#38099#>-;SPMgt;<#38099#><#38100#><#38100#> <#38101#>number<#38101#><#67001#><#71544#>
<#38102#>;; to determine the maximum of a list of numbers <#38102#>
<#38103#>(d<#38103#><#38104#>efine<#38104#> <#38105#>(max2<#38105#> <#38106#>alon)<#38106#>
<#38107#>(c<#38107#><#38108#>ond<#38108#>
<#38109#>[<#38109#><#38110#>(empty?<#38110#> <#38111#>(rest<#38111#> <#38112#>alon))<#38112#> <#38113#>(first<#38113#> <#38114#>alon)]<#38114#>
<#38115#>[<#38115#><#38116#>else<#38116#> <#38117#>(l<#38117#><#38118#>ocal<#38118#> <#38119#>((define<#38119#> <#38120#>max-of-rest<#38120#> <#38121#>(max2<#38121#> <#38122#>(rest<#38122#> <#38123#>alon))))<#38123#>
<#38124#>(c<#38124#><#38125#>ond<#38125#>
<#38126#>[<#38126#><#38127#>(;SPMgt;<#38127#> <#38128#>max-of-rest<#38128#> <#38129#>(first<#38129#> <#38130#>alon))<#38130#> <#38131#>max-of-rest]<#38131#>
<#38132#>[<#38132#><#38133#>else<#38133#> <#38134#>(first<#38134#> <#38135#>alon)]<#38135#><#38136#>)]<#38136#><#38137#>)))<#38137#>
Instead of recomputing the maximum of the rest of the list, this version
just refers to the variable twice when the variable stands for the
maximum of the rest of the list.
<#38143#>Exercise 29.1.1<#38143#>