If a problem statement discusses
several different kinds of compound information of arbitrary size, we need
a group of data definitions that are self-referential and that refer to
each other. We define such groups in one block. As usual, we identify the
self-references, but we also identify the cross-references.
In our example in the preceding section, the data definition consisted of
two sub-definitions:
#picture18600#
The first one concerns <#18630#>parents<#18630#> and another one for <#18631#>list of children<#18631#>. The first (unconditionally) defines a parent in terms of symbols,
numbers, and a list of children, that is, it contains a cross-reference to the
second definition. This second definition is a conditional definition. Its first
clause is simple; its second clause references both the definition for
<#63810#><#18632#>parent<#18632#><#63810#>s and <#63811#><#18633#>list-of-children<#18633#><#63811#>.
Contract, Purpose, Header:
To process interrelated classes of data, we
typically need as many functions as there are class definitions. Hence, we must
formulate as many contracts, purpose statements, and headers in parallel as there
are data definitions.
#tabular18635#
<#18675#>Figure: Designing programs for definitions with mutual references<#18675#>
<#18677#>The essential steps<#18677#>
<#63815#>(Refines the recipes in figures~#figdesign1#18678> (pg.~#figdesign1#18679>), #figdesign3#18680> (pg.~#figdesign3#18681>) #figdesign4#18682> (pg.~#figdesign4#18683>))<#63815#>
Templates:
The templates are created in parallel, following the advice
concerning compound data, mixed data, and self-referential data. Finally, we must
determine for each selector expression in each template whether it corresponds to a
cross-reference to some definition. If so, we annotate it in the same way we
annotate cross-references.
Here are the templates for our running example:
#picture18684#
The <#63820#><#18749#>fun-for-parent<#18749#><#63820#> template is unconditional because the data definition
for <#63821#><#18750#>parent<#18750#><#63821#>s does not contain any clauses. It does contain one
cross-reference to the second template: to process the <#63822#><#18751#>children<#18751#><#63822#> field of a
<#63823#><#18752#>parent<#18752#><#63823#> structure. By the same rules, <#63824#><#18753#>fun-for-loc<#18753#><#63824#> is
conditional. The second <#63825#><#18754#>cond<#18754#><#63825#>-clause contains one self-reference, for the
<#63826#><#18755#>rest<#18755#><#63826#> of the list, and one cross-reference for the <#63827#><#18756#>first<#18756#><#63827#> item of
the list, which is a parent structure.
A comparison of the data definitions and the templates shows how analogous the two
are. To emphasize the similarity in self-references and cross-references, the data
definitions and templates have been annotated with arrows. It is easy to see how
corresponding arrows have the same origin and destination in the two pictures.
The body:
As we proceed to create the final definitions, we start
with a template or a <#63828#><#18757#>cond<#18757#><#63828#>-clause that does not contain
self-references to the template and cross-references to other templates.
The results are typically easy to formulate for such templates or
<#63829#><#18758#>cond<#18758#><#63829#>-clauses.
The rest of this step proceeds as before. When we deal with other clauses
or functions, we remind ourselves what each expression in the template
computes, assuming that <#18759#>all<#18759#> functions already work as specified in
the contracts. Then we decide how to combine these pieces of data into a
final answer. As we do that, we must not forget the guidelines concerning
the composition of complex functions (sections~#seccompounding1#18760>
and~#seccompounding2#18761>).