• No results found

if

(T.frontonil) /*empty list*/

then

begin

temp := T.front;

if

T.front = T.last /*1 elem list*/

then

begin

T.front := nil; T.last := nil

end

else

begin

with

(T.front)''

do

if nextl = nil

then

begin

next2'".nextl := nil; next2 := nil

end

else

begin

nextl''.next2 := nil; nextl := nil

end

end;

dispose

(temp)

end

end;

procedure

REV

(var

T:Headcell);

var

temp : ''Datacell ;

begin

temp := T.last; T.last := T.front; T.front := temp

end;

function

HD(T:Headcell): Integer;

begin

HD := (T.front)''.contents

end;

Remark: The expression (T. front)''.nextl = nil is evaluated often. In effect, this is a test to establish which field: nextl or next2, currently represents the "direction" of the list; i.e. when we traverse the list starting from one end, we follow either the

nextl links or the next2 links. The choice of links to follow depends on which end we

start at. A further space-time optimisation would be to allocate a variable for storing the value of the expression (T.front) ''.nextl = nil. The variable would only be updated (to another proper Boolean value) in the REV procedure because the other operations either preserve the value of the expression, or cause it to become undefined

6.6 The Efficiency of Imperative Implementations

The representation mappings between the storage graphs and the imperative languages are complex because of the nature of the Imperative language, in general, we consider the representation of a storage graph as a composition of the representation of its components; i.e. we have three kinds of representation mappings:

repn:ncol - > state, (the representation of nodes), repe: ecol ~> state (the

representation of edges), and repa; ecol ~> state (the representation of access nodes). The operations on each of these components; I.e. operations on the nodes, edges, and access nodes, can affect the program state. For example, in the

Queue

example, we specify the representation of the +1^ by:

Vn:nat,N:ncol. repn(n +1^ N) =

<i(repn(N)),e(repn(N)),-f(repn(N)),

lookup(p, p bindto new(f(repn(N))) := mkdcell(n,nil) in s(repn(N))>

. For a given

ImpLang,

(a suitable enrichment of

Program^State

by the chosen data structures) it is easy to see that the operations from

Nodes&Edges'

(cf. §5.6.2.1), excepting the operations ma p t and tg rtn , can be represented by compositions (without recursion) of operations from

ImpLang.

m ap t and tg-cn cannot be represented, (without recursion,) because we have no primitive operations for selecting the incoming pointers to a datacell; we can only select outgoing pointers.

We conclude that if for a given object

Spec,

we can give a presentation of

Spec/SGraph

such that it contains only operations from

- the object specification, - the access operations,

-

NodesSEdges',

excluding mapt and t g tn ,

- the operations hd^, t l ^ , hd^, t l ^ (cf. §5.8), and

- we use the linked data structure selection method given in §6.3, then it is possible to synthesise a constant time

ImpLang

implementation.

Often, we may be able to synthesise constant time implementations for specifications which do not meet these requirements. Our point is that in general, we can only predict efficient implementations when the derived operators are specified in a particular way.

In the Q u eu e example, the specification of Q u e u e /S G ra p h meets the above conditions and the example Imperative implementation runs in constant time.

In the R e v e r s i b l e _ _ L i s t _ l example, we find that the operation t g tn

occurs in part of the R e v e r s i b l e ^ L i s t _ _ l / S R e l a t i o n 5 specification which is Included in the S G ra p h Implementation. Specifically, t g tn occurs in an equation specifying the TL operation (cf. Example 5.11, §5.6.4). However, it is possible to give an alternate specification which does not Include t g tn ; namely:

Vn:nat,l:nel. TL(rep(n:l)) =

[U(n;l) hd(n:l),

(=>(n:l) \\g seen (=>(n:l) ,hd^(h:l) ) ) <hd(n:l) ,hdtl (n: 1) >]

where hdtl is a new access operator:

Vn:nat,l:nel.

hdtl(n:l) = hd(l),

We justify our chosen data structures by considering only the 1-optimised access operators, hdt 1 Is not a 1 -optimised access operator and is specified (in the language of

storage relations) by:

Vnrnat,l;nel.

hdtl(n:l) == adj (=>.(n:l) ,hd(n;l) ) .

This presentation meets our conditions and we see that our example implementation runs in constant time.

The remaining example specifications from Appendix Two conform to the conditions described above; excepting the B i n a r y _ _ T r e e examples and C i r c u l a r _ L i s t _ l e f t . The former specifications do not meet the conditions because the auxiliary " c h i l d ^ " and " c h i l d g " operations are required. The latter specification does not meet the conditions because t g tn occurs in the specification of

hdshift (shift (n : 1) ), the specification of the effect of the s h i ft operation on the

h d s hi ft access operator, (cf. Example 5.18, §5.9.1). If we do not insist that unused storage is explicitly released, then constant time implementations for the B in a r y ^ T r e e examples can be synthesised. However, we cannot give a specification

for hdshift (shift (n : 1 ) ) which conforms to the above conditions. At best, using our

method, we can only synthesise a linear time implementation for C i r c u l a r _ _ L i 8 t _ L e f t . If efficiency is crucial, then it appears that an

implementation strategy based on the storage relations is not appropriate for this specification.

6.7 Summary

In this chapter we have considered the specification of an imperative language and the choice of data structures in that language. We have described a method for choosing linked data structures; the method depends on the properties of

SGraph

and the storage type of the object specification. The specification of the language with the chosen (abstract) data structures is called

ImpLang.

Two example implementations are presented in detail:

Q u e u e

and

Revers ible__Li st__l.

We have described the circumstances under which constant time implementations for object specifications can be synthesised using our method. The method leads to efficient implementations for most of our example keyless specifications. For example, the implementations for

Queue

arid

Reversible__List__l

run in constant time, but the method does not lead to a constant time implementation for the

Circular List left

specification.