At the very beginning of this part of the book, we discussed how we design
sets of functions from the same template. More specifically, when we
design a set of functions that all consume the same kind of data, we reuse
the same template over and over again. It is therefore not surprising that
the function definitions look similar and that we will abstract them
later.
Indeed, we could abstract from the templates directly. While this topic is
highly advanced and still subject of research in the area of programming
languages, we can discuss it with a short example. Consider the template
for lists:
<#29457#>(d<#29457#><#29458#>efine<#29458#> <#29459#>(fun-for-l<#29459#> <#29460#>l)<#29460#>
<#29461#>(c<#29461#><#29462#>ond<#29462#>
<#29463#>[<#29463#><#29464#>(empty?<#29464#> <#29465#>l)<#29465#> <#29466#>...]<#29466#>
<#29467#>[<#29467#><#29468#>else<#29468#> <#29469#>...<#29469#> <#29470#>(first<#29470#> <#29471#>l)<#29471#> <#29472#>...<#29472#> <#29473#>(fun-for-l<#29473#> <#29474#>(rest<#29474#> <#29475#>l))<#29475#> <#29476#>...]<#29476#><#29477#>))<#29477#>
It contains two gaps, one in each clause. When we define a list-processing
functions, we fill these gaps. In the first clause, we typically place a
plain value. For the second one, we combine <#65559#><#29481#>(first<#29481#>\ <#29482#>l)<#29482#><#65559#> and
<#65560#><#29483#>(f<#29483#>\ <#29484#>(rest<#29484#>\ <#29485#>l))<#29485#><#65560#> where <#65561#><#29486#>f<#29486#><#65561#> is the recursive function.
We can abstract over this programming task with the following function:
<#71372#>;; <#65562#><#29491#>reduce<#29491#> <#29492#>:<#29492#> <#29493#>X<#29493#> <#29494#>(X<#29494#> <#29495#>Y<#29495#> <#29496#><#29496#><#29497#>-;SPMgt;<#29497#><#29498#><#29498#> <#29499#>Y)<#29499#> <#29500#>(listof<#29500#> <#29501#>Y)<#29501#> <#29502#><#29502#><#29503#>-;SPMgt;<#29503#><#29504#><#29504#> <#29505#>Y<#29505#><#65562#><#71372#>
<#29506#>(d<#29506#><#29507#>efine<#29507#> <#29508#>(reduce<#29508#> <#29509#>base<#29509#> <#29510#>combine<#29510#> <#29511#>l)<#29511#>
<#29512#>(c<#29512#><#29513#>ond<#29513#>
<#29514#>[<#29514#><#29515#>(empty?<#29515#> <#29516#>l)<#29516#> <#29517#>base]<#29517#>
<#29518#>[<#29518#><#29519#>else<#29519#> <#29520#>(c<#29520#><#29521#>ombine<#29521#> <#29522#>(first<#29522#> <#29523#>l)<#29523#>
<#29524#>(reduce<#29524#> <#29525#>base<#29525#> <#29526#>combine<#29526#> <#29527#>(rest<#29527#> <#29528#>l)))]<#29528#><#29529#>))<#29529#>
It consumes two extra arguments: <#65563#><#29533#>base<#29533#><#65563#>, which is the value for the
base case, and <#65564#><#29534#>combine<#29534#><#65564#>, which is a function that performs the
value combination for the second clause.
Using <#65565#><#29535#>reduce<#29535#><#65565#> we can define many plain list-processing functions as
well as almost all the functions of figure~#figschemeholist#29536>. Here
are two of them:
<#71373#>;; <#65566#><#29541#>sum<#29541#> <#29542#>:<#29542#> <#29543#>(listof<#29543#> <#29544#>number)<#29544#> <#29545#><#29545#><#29546#>-;SPMgt;<#29546#><#29547#><#29547#> <#29548#>number<#29548#><#65566#><#71373#>
<#29549#>(define<#29549#> <#29550#>(sum<#29550#> <#29551#>l)<#29551#> <#29552#>(reduce<#29552#> <#29553#>0<#29553#> <#29554#>+<#29554#> <#29555#>l))<#29555#>
<#71374#>;; <#65567#><#29561#>product<#29561#> <#29562#>:<#29562#> <#29563#>(listof<#29563#> <#29564#>number)<#29564#> <#29565#><#29565#><#29566#>-;SPMgt;<#29566#><#29567#><#29567#> <#29568#>number<#29568#><#65567#><#71374#>
<#29569#>(define<#29569#> <#29570#>(product<#29570#> <#29571#>l)<#29571#> <#29572#>(reduce<#29572#> <#29573#>1<#29573#> <#29574#>*<#29574#> <#29575#>l))<#29575#>
For <#65568#><#29579#>sum<#29579#><#65568#>, the base case always produces <#65569#><#29580#>0<#29580#><#65569#>; adding the
first item and the result of the natural recursion combines the values of
the second clause. Analogous reasoning explains <#65570#><#29581#>product<#29581#><#65570#>.
To define <#65571#><#29582#>sort<#29582#><#65571#>, we need to define an auxiliary function first:
<#71375#>;; <#65572#><#29587#>sort<#29587#> <#29588#>:<#29588#> <#29589#>(listof<#29589#> <#29590#>number)<#29590#> <#29591#><#29591#><#29592#>-;SPMgt;<#29592#><#29593#><#29593#> <#29594#>(listof<#29594#> <#29595#>number)<#29595#><#65572#><#71375#>
<#29596#>(d<#29596#><#29597#>efine<#29597#> <#29598#>sort<#29598#>
<#29599#>(l<#29599#><#29600#>ocal<#29600#> <#29601#>((d<#29601#><#29602#>efine<#29602#> <#29603#>(insert<#29603#> <#29604#>an<#29604#> <#29605#>alon)<#29605#>
<#29606#>(c<#29606#><#29607#>ond<#29607#>
<#29608#>[<#29608#><#29609#>(empty?<#29609#> <#29610#>alon)<#29610#> <#29611#>(list<#29611#> <#29612#>an)]<#29612#>
<#29613#>[<#29613#><#29614#>else<#29614#> <#29615#>(c<#29615#><#29616#>ond<#29616#>
<#29617#>[<#29617#><#29618#>(;SPMgt;<#29618#> <#29619#>an<#29619#> <#29620#>(first<#29620#> <#29621#>alon))<#29621#> <#29622#>(cons<#29622#> <#29623#>an<#29623#> <#29624#>alon)]<#29624#>
<#29625#>[<#29625#><#29626#>else<#29626#> <#29627#>(cons<#29627#> <#29628#>(first<#29628#> <#29629#>alon)<#29629#> <#29630#>(insert<#29630#> <#29631#>an<#29631#> <#29632#>(rest<#29632#> <#29633#>alon)))]<#29633#><#29634#>)]<#29634#><#29635#>)))<#29635#>
<#29636#>(reduce<#29636#> <#29637#>empty<#29637#> <#29638#>insert)))<#29638#>
Other list-processing functions can be defined in a similar manner.