mirror of
https://github.com/EiffelSoftware/eiffel-org.git
synced 2025-12-08 07:42:33 +01:00
Author:halw
Date:2008-10-25T08:00:01.000000Z git-svn-id: https://svn.eiffel.com/eiffel-org/trunk@97 abb3cda0-5349-4a8f-a601-0c33ac3a8c38
This commit is contained in:
@@ -14,17 +14,23 @@ The list and chain classes are characterized, for their traversal properties, as
|
||||
[[ref:/libraries/base/reference/linear_chart|LINEAR]] describes sequential structures that may be traversed one way. It introduces in particular the following features, illustrated on the figure below:
|
||||
* <eiffel>after</eiffel>, a boolean-valued query which determines whether you have moved past the last position (a more precise specification is given below).
|
||||
* <eiffel>off</eiffel>, a boolean-valued query which is false if and only if there is no item at the current position; for [[ref:/libraries/base/reference/linear_chart|LINEAR]] this is the same as:
|
||||
<code>is_empty and not after</code>
|
||||
<code>
|
||||
is_empty and not after</code>
|
||||
|
||||
* <eiffel>item</eiffel>, a query which returns the item at the current position - provided of course there is one, as expressed by the precondition:
|
||||
<code>not off</code>
|
||||
<code>
|
||||
not off</code>
|
||||
|
||||
* <eiffel>start</eiffel>, a command to move to the first position if any (if is_empty is true the command has no effect).
|
||||
* <eiffel>forth</eiffel>, a command to advance by one position; the precondition is not after.
|
||||
* <eiffel>finish</eiffel>, a command to move to the last position; the precondition is: <br/>
|
||||
<code>not is_empty</code>
|
||||
<code>
|
||||
not is_empty</code>
|
||||
|
||||
|
||||
[[Image:linear|fig.1: Linear Structure]]
|
||||
|
||||
|
||||
[[Image:linear|fig.1: Linear Structure]]
|
||||
There is also a procedure <eiffel>search</eiffel> with one argument, which determines whether the value of that argument appears in the structure at or after the current position, and if not makes <eiffel>after</eiffel> become true. This procedure is internally used by the default implementation of the <eiffel>has</eiffel> function (the general membership test) for linear structures. Like has for all containers, <eiffel>search</eiffel> uses object or reference equality depending on the value set for <eiffel>object_comparison</eiffel>.
|
||||
|
||||
An invariant property of [[ref:/libraries/base/reference/linear_chart|LINEAR]] structures is that the current position may go off one step past the last item if any, but no further. The precondition of <eiffel>forth</eiffel> - not after - helps ensure this. The first item (if any) being at position 1, the maximum allowable position is <eiffel>count</eiffel> + 1, where <eiffel>count</eiffel> is the number of items.
|
||||
@@ -36,57 +42,69 @@ An invariant property of [[ref:/libraries/base/reference/linear_chart|LINEAR]]
|
||||
* <eiffel>back</eiffel>, a command to move backward by one position; the precondition is not before.
|
||||
|
||||
For bilinear structures the position can range between 0 (not just 1) and count + 1. Query off is accordingly redefined so as to yield the value of after or before.
|
||||
[[Image:bilinear|fig.2: Bilinear Structure]]
|
||||
|
||||
|
||||
[[Image:bilinear|fig.2: Bilinear Structure]]
|
||||
|
||||
|
||||
==Invariant properties for after, before and off==
|
||||
|
||||
The redefinition of <eiffel>off</eiffel> illustrates a general methodological advice about invariants: be careful about not over-constraining the invariant by including properties that may be made more general in descendants. It might have been tempting to include in [[ref:/libraries/base/reference/linear_chart|LINEAR]] an invariant clause of the form
|
||||
<code>off = is_empty or after</code>
|
||||
<code>
|
||||
off = is_empty or after</code>
|
||||
|
||||
This property, however, would be too constraining. More precisely, it is always true that the right-hand side implies the left-hand-side: if a linear structure is either empty or after, then it is off. But the converse is not true, since certain kinds of linear structure, for example bilinear ones, may be off but neither empty nor after. <br/>
|
||||
The actual invariant for class [[ref:/libraries/base/reference/bilinear_chart|BILINEAR]] is obtained in three stages. In class [[ref:/libraries/base/reference/traversable_chart|TRAVERSABLE]] the feature off is deferred and a basic property of that feature is expressed by the invariant clause
|
||||
<code>empty_constraint:is_empty implies off</code>
|
||||
<code>
|
||||
empty_constraint:is_empty implies off</code>
|
||||
|
||||
In [[ref:/libraries/base/reference/linear_chart|LINEAR]] , feature <eiffel>off</eiffel> is effected through an implementation which returns the value of the expression <eiffel>is_empty</eiffel> or <eiffel>after</eiffel>. The class adds an invariant clause which, however, says less than the implementation to leave some room for variation:
|
||||
<code>after_constraint:after implies off</code>
|
||||
<code>
|
||||
after_constraint:after implies off</code>
|
||||
|
||||
Finally [[ref:/libraries/base/reference/bilinear_chart|BILINEAR]] , an heir of [[ref:/libraries/base/reference/linear_chart|LINEAR]] , redefines <eiffel>off</eiffel> to return the value of the expression
|
||||
<code>before or after</code>
|
||||
<code>
|
||||
before or after</code>
|
||||
|
||||
and adds the invariant clause
|
||||
<code>before_constraint: before implies off</code>
|
||||
<code>
|
||||
before_constraint: before implies off</code>
|
||||
|
||||
The new implementation of <eiffel>off</eiffel>
|
||||
<code>after or before</code>
|
||||
<code>
|
||||
after or before</code>
|
||||
|
||||
would not guarantee the invariant clause inherited from [[ref:/libraries/base/reference/traversable_chart|TRAVERSABLE]] were it not for another clause introduced in [[ref:/libraries/base/reference/bilinear_chart|BILINEAR]] :
|
||||
<code>empty_property: is_empty implies (after or before )</code>
|
||||
<code>
|
||||
empty_property: is_empty implies (after or before )</code>
|
||||
|
||||
which indicates that an empty bilinear structure must always be <eiffel>after</eiffel> or <eiffel>before</eiffel> but not both, however, as stated by the last new clause, the reason for which is discussed in detail below:
|
||||
<code>not_both: not(after and before)</code>
|
||||
<code>
|
||||
not_both: not(after and before)</code>
|
||||
|
||||
The flat-short form of [[ref:/libraries/base/reference/bilinear_chart|BILINEAR]] shows the complete reconstructed invariant:
|
||||
<code>
|
||||
not_both: not (after and before)
|
||||
empty_property: is_empty implies (after or before)
|
||||
before_constraint: before implies off
|
||||
after_constraint: after implies off
|
||||
empty_constraint: is_empty implies off</code>
|
||||
not_both: not (after and before)
|
||||
empty_property: is_empty implies (after or before)
|
||||
before_constraint: before implies off
|
||||
after_constraint: after implies off
|
||||
empty_constraint: is_empty implies off</code>
|
||||
|
||||
==Iteration patterns==
|
||||
|
||||
For a more general form of this scheme, applicable to circular chains as well as other linear structures, replace <eiffel>off</eiffel> by <eiffel>exhausted</eiffel>. With the features shown above, a typical iteration mechanism on a non-empty linear structure 'lin' is of the form:
|
||||
<code>
|
||||
from
|
||||
lin.start
|
||||
some_optional_initializing_operation (lin)
|
||||
until
|
||||
lin.off
|
||||
loop
|
||||
lin.some_action (lin.item)
|
||||
lin.forth
|
||||
end</code>
|
||||
from
|
||||
lin.start
|
||||
some_optional_initializing_operation (lin)
|
||||
until
|
||||
lin.off
|
||||
loop
|
||||
lin.some_action (lin.item)
|
||||
lin.forth
|
||||
end</code>
|
||||
|
||||
The value of <code> lin </code> <code> . </code>off is always true for an empty structure, so in this case the loop will, correctly, execute only its initialization actions if present. <br/>
|
||||
The value of <code>lin.off</code> is always true for an empty structure, so in this case the loop will, correctly, execute only its initialization actions if present. <br/>
|
||||
This is a very common pattern, which you will find in the library classes themselves (for example has is implemented in this way) and many application clients. The iterator classes corresponding to linear structures ([[ref:/libraries/base/reference/linear_iterator_chart|LINEAR_ITERATOR]] , [[ref:/libraries/base/reference/two_way_chain_iterator_chart|TWO_WAY_CHAIN_ITERATOR]] ) turn this pattern and several related ones into actual reusable routines. <br/>
|
||||
For bilinear structures there is another traversal mechanism going backward rather than forward; it is the same as above except that <eiffel>finish</eiffel> replaces <eiffel>start</eiffel> and <eiffel>back</eiffel> replaces <eiffel>finish</eiffel>. The exit condition remains <eiffel>off</eiffel> since <eiffel>before</eiffel>, like <eiffel>after</eiffel>, implies <eiffel>off</eiffel>.
|
||||
|
||||
@@ -98,23 +116,27 @@ For every one of the structures under discussion there is a notion of current po
|
||||
* <eiffel>finish</eiffel>, <eiffel>back</eiffel>, <eiffel>before</eiffel>.
|
||||
|
||||
So for an empty list both <eiffel>before</eiffel> and <eiffel>after</eiffel> should be true. This scheme was used in early version of the Base libraries. It has some disadvantages, however; in particular it is not compatible with the simple, symmetric properties:
|
||||
<code>after = (index = count + 1)
|
||||
before = (index = 0)</code>
|
||||
<code>
|
||||
after = (index = count + 1)
|
||||
before = (index = 0)</code>
|
||||
|
||||
which express elementary definitions for <eiffel>after</eiffel> and <eiffel>before</eiffel> in terms of <eiffel>index</eiffel>, the current position, and <eiffel>count</eiffel>, the number of items (items being numbered from 1 to count). For an empty structure <eiffel>count</eiffel> is zero, so if we want <eiffel>after</eiffel> and <eiffel>before</eiffel> to be both true in this case we have to sacrifice one of the above properties, since the first would imply index to 1 and the second to 0. But again symmetry reigns supreme: we should either keep both properties or renounce both. The solution was to renounce both and replace them by slightly more complicated ones:
|
||||
<code>after = (is_empty or (index = count + 1))
|
||||
before = (is_empty or (index = 0))</code>
|
||||
<code>
|
||||
after = (is_empty or (index = count + 1))
|
||||
before = (is_empty or (index = 0))</code>
|
||||
|
||||
When a structure is created, some initializations will have to be made; the default initializations will usually lead to a value of 0 rather than 1 for index, although this dissymetry is not apparent in the assertions. Although acceptable, this solution leads to small but unpleasant complications, in particular frequent conditional instructions of the form
|
||||
<code>if after and not is_empty then...</code>
|
||||
<code>
|
||||
if after and not is_empty then...</code>
|
||||
|
||||
The solution finally retained for the Base libraries uses a different technique, which has turned out to be preferable. The idea is to replace the conceptual picture by one in which there are always two fictitious sentinel items. The two sentinel items are only present conceptually. They are of course not taken into account for the computation of <eiffel>count</eiffel> and, although it is possible to conceive of an implementation which would actually reserve space for them (for example in an array representation), none of the implementations used in Base for the classes of this documentation and other descendants of [[ref:/libraries/base/reference/linear_chart|LINEAR]] do. The only purpose of the sentinels is to provide two valid theoretical cursor positions, which exist regardless of the number of actual (non-sentinel) items in the structure. <br/>
|
||||
The sentinel items always appear at positions 0 and <eiffel>count</eiffel> + 1; this property is true even if the structure is empty of items, in which case count is zero. As a result, the following properties are part of the invariant:
|
||||
<code>0 <= index
|
||||
index <= count + 1
|
||||
before = (index = 0)
|
||||
after = (index = count + 1)
|
||||
not (after and before)</code>
|
||||
<code>
|
||||
0 <= index
|
||||
index <= count + 1
|
||||
before = (index = 0)
|
||||
after = (index = count + 1)
|
||||
not (after and before)</code>
|
||||
|
||||
The last property given indicates that a structure can never be both <eiffel>after</eiffel> <code>and</code> <eiffel>before</eiffel>, since even in an empty structure the two sentinels are still present, with the cursor on one of them. For an empty structure, <eiffel>index</eiffel> will be zero by convention, so that <eiffel>before</eiffel> will be true and <eiffel>after</eiffel> false. But this property is not reflected in any of the invariant clauses.
|
||||
|
||||
@@ -137,20 +159,20 @@ SEQUENCE is constructed with the full extent of the technique described in the d
|
||||
* FINITE, from the storage hierarchy, indicates that the class describes finite sequences. (A class [[ref:/libraries/base/reference/countable_sequence_chart|COUNTABLE_SEQUENCE]] is also present, as described below.)
|
||||
|
||||
To the features of [[ref:/libraries/base/reference/bilinear_chart|BILINEAR]] , [[ref:/libraries/base/reference/sequence_chart|SEQUENCE]] principally adds features for adding, changing and removing items. A few procedures in particular serve to insert items at the end:
|
||||
* <code> s .put ( v ) adds v at the end of a sequence s </code>.
|
||||
* <code>s .put ( v )</code> adds v at the end of a sequence s.
|
||||
* <eiffel> extend</eiffel> and <eiffel>force</eiffel>, at the [[ref:/libraries/base/reference/sequence_chart|SEQUENCE]] level, do the same as <eiffel>put</eiffel>.
|
||||
* <code> s .append ( s1 )</code> adds to the end of s the items of s1 (another sequence), preserving their s1 order.
|
||||
* <code>s .append ( s1 )</code> adds to the end of s the items of s1 (another sequence), preserving their s1 order.
|
||||
|
||||
Other procedures work on the current position:
|
||||
* <code> s . </code><eiffel>remove</eiffel> removes the item at current position.
|
||||
* <code> s .replace ( v ) replaces by v </code> the item at current position.
|
||||
* <code>s.</code><eiffel>remove</eiffel> removes the item at current position.
|
||||
* <code>s.replace ( v )</code> replaces by v the item at current position.
|
||||
|
||||
SEQUENCE, however, does not provide a procedure to insert an item at the current position, since not all implementations of sequences support this possibility; you will find it in descendants of [[ref:/libraries/base/reference/sequence_chart|SEQUENCE]] seen below. <br/>
|
||||
Yet another group of features are based on the first occurrence of a certain item, or on all occurrences:
|
||||
* <code> s .prune ( v ) removes the first occurrence of v in s </code>, if any.
|
||||
* <code> s .prune_all ( v ) removes all occurrences of v </code>.
|
||||
* <code>s.prune ( v ) removes the first occurrence of v in s</code>, if any.
|
||||
* <code>s.prune_all ( v ) removes all occurrences of v</code>.
|
||||
|
||||
These procedures have various abstract preconditions: <code> s .extendible for additions, s .writable for replacements, s . </code><eiffel>prunable</eiffel> for removals. Properties <eiffel>extendible</eiffel> and <eiffel>prunable</eiffel> characterize general categories of container structures rather than individual instances; for example <eiffel>extendible</eiffel> is always true for the 'dynamic' structures seen below. In contrast, <eiffel>writable</eiffel> depends on the current status of each instance. In general <eiffel>writable</eiffel> will be true if there is an item at the current position.
|
||||
These procedures have various abstract preconditions: <code>s .extendible for additions, s .writable for replacements, s .</code><eiffel>prunable</eiffel> for removals. Properties <eiffel>extendible</eiffel> and <eiffel>prunable</eiffel> characterize general categories of container structures rather than individual instances; for example <eiffel>extendible</eiffel> is always true for the 'dynamic' structures seen below. In contrast, <eiffel>writable</eiffel> depends on the current status of each instance. In general <eiffel>writable</eiffel> will be true if there is an item at the current position.
|
||||
|
||||
==Chains==
|
||||
|
||||
@@ -168,7 +190,7 @@ By default, chains can only be extended at the end, through <eiffel>extend</eiff
|
||||
* Procedure <eiffel>put_front</eiffel> adds an item before the first. (As noted, the procedures to add an item after the last are already available in chains.)
|
||||
* Procedures <eiffel>put_left</eiffel> and <eiffel>put_right</eiffel> add an item at the left and right of the cursor position.
|
||||
* Procedures <eiffel>remove_left</eiffel> and remove_right remove an item at the left and right or the cursor position.
|
||||
* Procedures <eiffel>merge_left</eiffel> and <eiffel>merge_right</eiffel> are similar to <eiffel>put_left</eiffel> and <eiffel>put_right</eiffel> but insert another dynamic chain rather than a single item. As the word 'merge' suggests, the merged structure, passed as argument, does not survive the process; it is emptied of its items. To preserve it, perform a <eiffel>clone</eiffel> or <eiffel>copy</eiffel> before the merge operation.
|
||||
* Procedures <eiffel>merge_left</eiffel> and <eiffel>merge_right</eiffel> are similar to <eiffel>put_left</eiffel> and <eiffel>put_right</eiffel> but insert another dynamic chain rather than a single item. As the word 'merge' suggests, the merged structure, passed as argument, does not survive the process; it is emptied of its items. To preserve it, perform a <eiffel>twin</eiffel> or <eiffel>copy</eiffel> before the merge operation.
|
||||
|
||||
The class also provides implementations of <eiffel>prune</eiffel>, <eiffel>prune_all</eiffel> and <eiffel>wipe_out</eiffel> from [[ref:libraries/base/reference/collection_chart|COLLECTION]] . To make these implementations useful, it defines queries <eiffel>extendible</eiffel> and <eiffel>prunable</eiffel> so that they return the value true.
|
||||
|
||||
@@ -185,10 +207,12 @@ first. The symmetric property applies to <eiffel>back</eiffel>. The cyclic natur
|
||||
<code>not after</code>
|
||||
|
||||
Similarly, the precondition for back is
|
||||
<code>not before</code>
|
||||
<code>
|
||||
not before</code>
|
||||
|
||||
For lists, after becomes true when the cursor moves past the last item. For circular chains, however, after and before are never true except for an empty structure; this is expressed by the invariant clauses of class [[ref:/libraries/base/reference/circular_chart|CIRCULAR]] :
|
||||
<code>not before</code>
|
||||
<code>
|
||||
not before</code>
|
||||
|
||||
For a non-empty circular chain, then, you can circle forever around the items, using forth or back.
|
||||
|
||||
@@ -199,43 +223,45 @@ A circular chain also needs a notion of first item, if only to enable a client t
|
||||
For circular chains, however, there is no reason why the first item should always remain the same. One of the benefits that clients may expect from the use of a circular <br/>
|
||||
structure is the ability to choose any item as the logical first. Class [[ref:/libraries/base/reference/circular_chart|CIRCULAR]] offers for that purpose the procedure <eiffel>set_start</eiffel> which designates the current cursor position as the first in the circular chain. Subsequent calls to <eiffel>start</eiffel> will move the cursor to this position; calls to <eiffel>finish</eiffel> will move the cursor to the cyclically preceding position. With most implementations, there will then be two notions of first position: the logical first, which clients may freely choose through calls to <eiffel>set_start</eiffel>; and the <eiffel>physical first</eiffel>, which results from the implementation. In a representation using an array with indices from 1 to <eiffel>capacity</eiffel>, for example, the physical first is position 1, and the logical first may be any index in the permitted range. In a linked representation, there will be a cell first element corresponding to the physical first, but the logical first is any cell in the chain. <br/>
|
||||
In such cases the circular chain classes have features called <eiffel>standard_first</eiffel>, <eiffel>standard_last</eiffel>, <eiffel>standard_start</eiffel> and so on, which are not exported (so that you will not see them in the flat-short forms) but serve to implement visible features such as <eiffel>first</eiffel>, <eiffel>last</eiffel> and <eiffel>forth</eiffel>. For example a possible implementation of <eiffel>forth</eiffel> for circular chains is
|
||||
<code>forth is
|
||||
-- Move cursor to next item, cyclically.
|
||||
do
|
||||
standard_forth
|
||||
if standard_after then
|
||||
standard_start
|
||||
end
|
||||
if isfirst then
|
||||
exhausted := True
|
||||
end
|
||||
end</code>
|
||||
<code>
|
||||
forth
|
||||
-- Move cursor to next item, cyclically.
|
||||
do
|
||||
standard_forth
|
||||
if standard_after then
|
||||
standard_start
|
||||
end
|
||||
if isfirst then
|
||||
exhausted := True
|
||||
end
|
||||
end</code>
|
||||
|
||||
==Traversing a list or circular chain==
|
||||
|
||||
The properties of <eiffel>forth</eiffel> for circular chains imply that a traversal loop written as
|
||||
<code>from
|
||||
lin.start
|
||||
until
|
||||
lin.off
|
||||
loop
|
||||
...
|
||||
lin.forth
|
||||
end </code>
|
||||
<code>
|
||||
from
|
||||
lin.start
|
||||
until
|
||||
lin.off
|
||||
loop
|
||||
...
|
||||
lin.forth
|
||||
end</code>
|
||||
|
||||
would not work if <code> lin </code> is a non-empty circular structure: <eiffel>off</eiffel> would never become true, so that the loop would forever cycle over the structure's items. The same would apply to a loop using finish and back instead of <eiffel>start</eiffel> and <eiffel>forth</eiffel>. This behavior is the natural result of the semantics defined for <eiffel>off</eiffel> , <eiffel>forth</eiffel> and <eiffel>back</eiffel> for circular structures. But it prevents us from using these features to perform a single traversal which will visit every item once. <br/>
|
||||
would not work if <code>lin</code> is a non-empty circular structure: <eiffel>off</eiffel> would never become true, so that the loop would forever cycle over the structure's items. The same would apply to a loop using finish and back instead of <eiffel>start</eiffel> and <eiffel>forth</eiffel>. This behavior is the natural result of the semantics defined for <eiffel>off</eiffel> , <eiffel>forth</eiffel> and <eiffel>back</eiffel> for circular structures. But it prevents us from using these features to perform a single traversal which will visit every item once. <br/>
|
||||
Using <eiffel>exhausted</eiffel> in lieu of off solves this problem. In class [[ref:/libraries/base/reference/circular_chart|CIRCULAR]] , <eiffel>exhausted</eiffel> is an attribute which is set to false by <eiffel>start</eiffel> and <eiffel>finish</eiffel>, and is set to true by <eiffel>forth</eiffel> when advancing from the last item to the first and by <eiffel>back</eiffel> when backing up from the first item to the last. So you should write the loop as
|
||||
<code>from
|
||||
lin.start
|
||||
|
||||
some_optional_initializing_operation (lin)
|
||||
until
|
||||
lin.exhausted
|
||||
loop
|
||||
...
|
||||
lin.some_action (lin.item)
|
||||
lin.forth
|
||||
end</code>
|
||||
<code>
|
||||
from
|
||||
lin.start
|
||||
some_optional_initializing_operation (lin)
|
||||
until
|
||||
lin.exhausted
|
||||
loop
|
||||
...
|
||||
lin.some_action (lin.item)
|
||||
lin.forth
|
||||
end</code>
|
||||
|
||||
This form is applicable to all linear structures, circular or not, since <eiffel>exhausted</eiffel> is introduced in class [[ref:/libraries/base/reference/linear_chart|LINEAR]] as a function which returns the same value as <eiffel>off</eiffel> .Its redefinition into an attribute, modified by <eiffel>start</eiffel>,<eiffel> finish</eiffel>, <eiffel>forth</eiffel> and <eiffel>back</eiffel>, does not occur until class [[ref:/libraries/base/reference/circular_chart|CIRCULAR]] . <br/>
|
||||
Because <eiffel>exhausted</eiffel> is more general than <eiffel>off</eiffel> , the iteration scheme just given (and its equivalent going backwards) is preferable to the earlier one using <eiffel>off </eiffel>, especially if there is any chance that the iteration might one day be applied to a lin structure that is circular. Classes of the Iteration library, in particular [[ref:/libraries/base/reference/linear_iterator_chart|LINEAR_ITERATOR]] , rely on this scheme for iterating over linear structures.
|
||||
@@ -268,20 +294,28 @@ An arrayed implementation uses an array to represent a linear structure. If the
|
||||
==Linked structures==
|
||||
|
||||
A linked structure requires two classes: one, such as [[ref:/libraries/base/reference/linked_list_chart|LINKED_LIST]] , describes the list proper; the other, such as [[ref:/libraries/base/reference/linkable_chart|LINKABLE]] , describes the individual list cells. The figure should help understand the difference; it describes a linked list, but the implementation of linked circular chains is similar.
|
||||
[[Image:linked-list|fig.3: Linked list and linked cells]]
|
||||
|
||||
|
||||
[[Image:linked-list|fig.3: Linked list and linked cells]]
|
||||
|
||||
|
||||
The instance of type [[ref:/libraries/base/reference/linked_list_chart|LINKED_LIST]] shown at the top contains general information about the list, such as the number of items (<eiffel>count</eiffel>) and a reference to the first element (first). Because lists are active structures with a notion of current position, there is also a reference active to the cell at the current position. An entity declared as
|
||||
<code>my_list: LINKED_LIST [SOME_TYPE] </code>
|
||||
<code>my_list: LINKED_LIST [SOME_TYPE]</code>
|
||||
|
||||
will have as its run-time value (if not void) a reference to such an object, which is really a list header. The actual list content is given by the [[ref:/libraries/base/reference/linkable_chart|LINKABLE]] instances, each of which contains a value of type <eiffel>SOME_TYPE</eiffel> and a reference to the next item, called <eiffel>right</eiffel>. <br/>
|
||||
Clearly, a header of type <code>LINKED_LIST [SOME_TYPE]</code> will be associated with cells of type <code>LINKABLE [SOME_TYPE]</code>. <br/>
|
||||
Features such as active and first are used only for the implementation; they are not exported, and so you will not find them in the flat-short specifications, although the figures show them to illustrate the representation technique. <br/>
|
||||
A similar implementation is used for two-way-linked structures such as two-way lists and two-way circular chains.
|
||||
[[Image:two-way-list|fig.4: Two way linked list]]
|
||||
|
||||
|
||||
[[Image:two-way-list|fig.4: Two way linked list]]
|
||||
|
||||
|
||||
==Linked cells==
|
||||
|
||||
The classes describing list cells are descendants of a deferred class called [[ref:/libraries/base/reference/cell_chart|CELL]] , whose features are:
|
||||
* <eiffel>item</eiffel>, the contents of the cell.
|
||||
* <eiffel>put </eiffel> <code> ( v : </code> <code> like </code> <eiffel>item</eiffel> <code> ) </code>, which replaces the contents of the cell by a new value.
|
||||
* <eiffel>put </eiffel> <code>(v : like</code> <eiffel>item</eiffel><code>)</code>, which replaces the contents of the cell by a new value.
|
||||
|
||||
Class [[ref:/libraries/base/reference/linkable_chart|LINKABLE]] is an effective descendant of [[ref:/libraries/base/reference/cell_chart|CELL]] , used for one-way linked structures. It introduces features <eiffel>right</eiffel>, a reference to another cell to which the current <br/>
|
||||
cell will be linked. Two-way linked structures use [[ref:/libraries/base/reference/bi_linkable_chart|BI_LINKABLE]] , an heir of [[ref:/libraries/base/reference/linkable_chart|LINKABLE]] which to the above features adds <eiffel>left</eiffel>, a reference to the preceding cell in the structure.
|
||||
@@ -289,11 +323,12 @@ cell will be linked. Two-way linked structures use [[ref:/libraries/base/referen
|
||||
{{caution|Do not confuse the <eiffel>item</eiffel> feature of [[ref:/libraries/base/reference/cell_chart|CELL]] and its descendants, such as [[ref:/libraries/base/reference/linkable_chart|LINKABLE]] , with the <eiffel>item</eiffel> feature of the classes describing linear structures, such as [[ref:/libraries/base/reference/linked_list_chart|LINKED_LIST]] . For a linked list, <eiffel>item</eiffel> returns the item at cursor position. }}
|
||||
|
||||
It may be implemented as
|
||||
<code>item: G is
|
||||
-- Current item
|
||||
do
|
||||
Result := active.item
|
||||
end</code>
|
||||
<code>
|
||||
item: G
|
||||
-- Current item
|
||||
do
|
||||
Result := active.item
|
||||
end</code>
|
||||
|
||||
using the <eiffel>item</eiffel> feature of [[ref:/libraries/base/reference/linkable_chart|LINKABLE]] , applied to <eiffel>active</eiffel>.
|
||||
|
||||
@@ -328,12 +363,13 @@ divided into a number of blocks. Each block is an array, but the successive arra
|
||||
=Sorted Linear Structures=
|
||||
|
||||
The class [[ref:/libraries/base/reference/comparable_struct_chart|COMPARABLE_STRUCT]] , an heir of [[ref:/libraries/base/reference/bilinear_chart|BILINEAR]] , is declared as
|
||||
<code>deferred class
|
||||
COMPARABLE_STRUCT [G -> COMPARABLE]
|
||||
inherit
|
||||
BILINEAR
|
||||
feature
|
||||
... </code>
|
||||
<code>
|
||||
deferred class
|
||||
COMPARABLE_STRUCT [G -> COMPARABLE]
|
||||
inherit
|
||||
BILINEAR
|
||||
feature
|
||||
...</code>
|
||||
|
||||
As indicated by the constrained generic parameter it describes bilinear structures whose items may be compared by a total order relation.
|
||||
|
||||
@@ -342,8 +378,9 @@ As indicated by the constrained generic parameter it describes bilinear structur
|
||||
COMPARABLE_STRUCT introduces the features <eiffel>min</eiffel> and <eiffel>max</eiffel>, giving access to the minimum and maximum elements of a structure; these are always present for a finite <br/>
|
||||
structure with a total order relation. [[ref:/libraries/base/reference/sorted_struct_chart|SORTED_STRUCT]] , an heir of [[ref:/libraries/base/reference/comparable_struct_chart|COMPARABLE_STRUCT]] , describes structures that can be sorted; it introduces the query sorted and the command sort. <br/>
|
||||
The deferred class [[ref:/libraries/base/reference/part_sorted_list_chart|PART_SORTED_LIST]] describes lists whose items are kept ordered in a way that is compatible with a partial order relation defined on them. The class is declared as
|
||||
<code>deferred class
|
||||
PART_SORTED_LIST [G -> COMPARABLE]...</code>
|
||||
<code>
|
||||
deferred class
|
||||
PART_SORTED_LIST [G -> COMPARABLE]...</code>
|
||||
|
||||
An implementation based on two-way linked lists is available through the effective heir [[ref:/libraries/base/reference/sorted_two_way_list_chart|SORTED_TWO_WAY_LIST]] . <br/>
|
||||
The deferred class [[ref:/libraries/base/reference/sorted_list_chart|SORTED_LIST]] , which inherits from [[ref:/libraries/base/reference/part_sorted_list_chart|PART_SORTED_LIST]] , assumes that the order relation on G is a total order. As a result, the class is able to introduce features <eiffel>min</eiffel>, <eiffel>max</eiffel> and <eiffel>median</eiffel>. Here too a two-way linked list implementation is available, through the effective class [[ref:/libraries/base/reference/sorted_two_way_list_chart|SORTED_TWO_WAY_LIST]] .
|
||||
|
||||
@@ -53,13 +53,16 @@ The classes of the Iteration library address this need. Using them offers two be
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
To get a first grasp of how one can work with the Iteration library, let us look at a typical iteration class and a typical iteration client.
|
||||
|
||||
==An example iterator routine==
|
||||
|
||||
Here, given with its full implementation, is a typical Iteration library routine: the procedure until_do from [[ref:libraries/base/reference/linear_iterator_chart]] , the class defining iteration mechanisms on linear (sequential) structures.
|
||||
<code>
|
||||
until_do is
|
||||
until_do
|
||||
-- Apply action to every item of target,
|
||||
-- up to but excluding first one satisfying test.
|
||||
-- (Apply to full list if no item satisfies test.)
|
||||
@@ -86,7 +89,7 @@ This procedure will traverse the linear structure identified by target and apply
|
||||
The class similarly offers <eiffel>do_all</eiffel>, <eiffel>do_while</eiffel>, <eiffel>do_for</eiffel>, <eiffel>do_if</eiffel> and other procedures representing the common control structures. It also includes functions such as <eiffel>exists</eiffel> and <eiffel>forall</eiffel>, corresponding to the usual quantifiers. <br/>
|
||||
These iteration schemes depend on the procedure <eiffel>action</eiffel>, defining the action to be applied to successive elements, and on the function <eiffel>test</eiffel>, defining the boolean query to be applied to these elements. These features are declared in class [[ref:/libraries/base/reference/iterator_chart|ITERATOR]] (the highest-level deferred class of the Iteration library); here is <eiffel>test</eiffel>:
|
||||
<code>
|
||||
test: BOOLEAN is
|
||||
test: BOOLEAN
|
||||
-- Test to be applied to item at current position in
|
||||
-- target (default: value of item_test on item)
|
||||
require
|
||||
@@ -128,7 +131,7 @@ inherit
|
||||
|
||||
feature
|
||||
|
||||
resize_paragraphs (t: TEXT) is
|
||||
resize_paragraphs (t: TEXT)
|
||||
-- Resize all the paragraphs of t up to but excluding
|
||||
-- the first one that has been modified.
|
||||
do
|
||||
@@ -138,13 +141,13 @@ feature
|
||||
|
||||
feature {NONE}
|
||||
|
||||
item_test (p PARAGRAPH): BOOLEAN is
|
||||
item_test (p PARAGRAPH): BOOLEAN
|
||||
-- Has p been modified?
|
||||
do
|
||||
Result := p.modified
|
||||
end
|
||||
|
||||
item_action (p: PARAGRAPH) is
|
||||
item_action (p: PARAGRAPH)
|
||||
-- Resize p.
|
||||
do
|
||||
p.resize
|
||||
@@ -153,7 +156,7 @@ feature {NONE}
|
||||
end</code>
|
||||
|
||||
Thanks to the iteration mechanism, the procedure <eiffel>resize_paragraphs</eiffel> simply needs two procedure calls:
|
||||
* To set its argument <code> t </code> as the iteration target, it uses procedure <eiffel>set</eiffel>. (This procedure is from class [[ref:/libraries/base/reference/iterator_chart|ITERATOR]] which passes it on to all iterator classes.)
|
||||
* To set its argument <code>t</code> as the iteration target, it uses procedure <eiffel>set</eiffel>. (This procedure is from class [[ref:/libraries/base/reference/iterator_chart|ITERATOR]] which passes it on to all iterator classes.)
|
||||
* Then it simply calls <eiffel>until_do</eiffel> as defined above.
|
||||
|
||||
Procedure <eiffel>item_action</eiffel> is redefined to describe the operation to be performed on each successive element. Function <eiffel>item_test</eiffel> is redefined to describe the exit test. <br/>
|
||||
@@ -195,17 +198,17 @@ Each one of the iterator classes is paired with a traversal class (or two in one
|
||||
|}
|
||||
|
||||
|
||||
Each iterator class relies on the corresponding traversal class to provide the features for traversing the corresponding data structures, such as <eiffel>start</eiffel>, <eiffel>forth</eiffel> and <eiffel>exhausted</eiffel> for linear structures. <br/>
|
||||
Each iterator class relies on the corresponding traversal class to provide the features for traversing the corresponding data structures, such as <eiffel>start</eiffel>, <eiffel>forth</eiffel>, and <eiffel>exhausted</eiffel> for linear structures. <br/>
|
||||
Of course the data structure class used in connection with a given iterator class does not need to be the iterator's exact correspondent as given by the above table; it may be any one of its descendants. For example you may use [[ref:/libraries/base/reference/linear_iterator_chart|LINEAR_ITERATOR]] to iterate over data structures described not just by [[ref:/libraries/base/reference/linear_chart|LINEAR]] but also by such descendants as [[ref:/libraries/base/reference/list_chart|LIST]] , [[ref:/libraries/base/reference/linked_list_chart|LINKED_LIST]] , [[ref:/libraries/base/reference/arrayed_list_chart|ARRAYED_LIST]] , or even [[ref:/libraries/base/reference/two_way_list_chart|TWO_WAY_LIST]] if you do not need the backward iteration features (for which you will have to use [[ref:/libraries/base/reference/two_way_chain_iterator_chart|TWO_WAY_CHAIN_ITERATOR]] ).
|
||||
|
||||
==General iteration facilities==
|
||||
|
||||
Class [[ref:/libraries/base/reference/iterator_chart|ITERATOR]] defines the features that apply to all forms of iterator. <br/>
|
||||
An iterator will always apply to a certain target structure. The target is introduced in [[ref:/libraries/base/reference/iterator_chart|ITERATOR]] by the feature target: [[ref:/libraries/base/reference/traversable_chart|TRAVERSABLE]] [G] <br/>
|
||||
Both the iterator classes and the traversal classes are generic, with a formal generic parameter G. The actual generic parameters will be matched through the choice of iteration target: for a generic derivation of the form <eiffel>SOME_ITERATOR</eiffel> [ <eiffel>ACTUAL_TYPE</eiffel>] the target can only be of type <eiffel>SOME_TRAVERSABLE</eiffel> [ <eiffel>ACTUAL_TYPE</eiffel>] for the same <eiffel>ACTUAL_TYPE</eiffel>, where <eiffel>SOME_TRAVERSABLE</eiffel> is the traversal class matching <eiffel>SOME_ITERATOR</eiffel> according to the preceding table ([[ref:/libraries/base/reference/linear_chart|LINEAR]] for [[ref:/libraries/base/reference/linear_iterator_chart|LINEAR_ITERATOR]] and so on), or one of its proper descendants. <br/>
|
||||
Both the iterator classes and the traversal classes are generic, with a formal generic parameter G. The actual generic parameters will be matched through the choice of iteration target: for a generic derivation of the form <eiffel>SOME_ITERATOR</eiffel> [<eiffel>ACTUAL_TYPE</eiffel>] the target can only be of type <eiffel>SOME_TRAVERSABLE</eiffel> [<eiffel>ACTUAL_TYPE</eiffel>] for the same <eiffel>ACTUAL_TYPE</eiffel>, where <eiffel>SOME_TRAVERSABLE</eiffel> is the traversal class matching <eiffel>SOME_ITERATOR</eiffel> according to the preceding table ([[ref:/libraries/base/reference/linear_chart|LINEAR]] for [[ref:/libraries/base/reference/linear_iterator_chart|LINEAR_ITERATOR]] and so on), or one of its proper descendants. <br/>
|
||||
Each of the proper descendants of [[ref:/libraries/base/reference/iterator_chart|ITERATOR]] redefines the type of target to the matching proper descendant of [[ref:/libraries/base/reference/traversable_chart|TRAVERSABLE]] , to cover more specific variants of the iteration target, For example in [[ref:/libraries/base/reference/linear_iterator_chart|LINEAR_ITERATOR]] the feature is redefined to be of type [[ref:/libraries/base/reference/linear_chart|LINEAR]] . [[ref:/libraries/base/reference/iterator_chart|ITERATOR]] also introduces the procedure for selecting a target:
|
||||
<code>
|
||||
set (s: like target) is
|
||||
set (s: like target)
|
||||
-- Make s the new target of iterations.
|
||||
require
|
||||
s /= Void
|
||||
@@ -218,7 +221,7 @@ Each of the proper descendants of [[ref:/libraries/base/reference/iterator_chart
|
||||
|
||||
Next [[ref:/libraries/base/reference/iterator_chart|ITERATOR]] introduces the routines describing the elementary action and test that will be applied to items of the iteration targets:
|
||||
<code>
|
||||
action is
|
||||
action
|
||||
-- Action to be applied to item at current position in
|
||||
-- target.
|
||||
-- (default: item_action on item at current position.)
|
||||
@@ -236,7 +239,7 @@ Next [[ref:/libraries/base/reference/iterator_chart|ITERATOR]] introduces the r
|
||||
invariant_satisfied: invariant_value
|
||||
end
|
||||
|
||||
test: BOOLEAN is
|
||||
test: BOOLEAN
|
||||
-- Test to be applied to item at current position in
|
||||
-- target (default: value of item_test on item)
|
||||
require
|
||||
@@ -269,11 +272,12 @@ All these features, and most of the other iteration features introduced in prope
|
||||
[[ref:/libraries/base/reference/linear_iterator_chart|LINEAR_ITERATOR]] , an effective class, refines the iteration mechanisms for cases in which the target is a linear structure, such as a list in any implementation or a circular chain. <br/>
|
||||
The class effects all the deferred features inherited from [[ref:/libraries/base/reference/iterator_chart|ITERATOR]] , taking advantage of the linear traversal mechanisms present in the corresponding traversal class, [[ref:/libraries/base/reference/linear_chart|LINEAR]] . Here for example is the effecting of <eiffel>do_if</eiffel>:
|
||||
<code>
|
||||
do_if is
|
||||
do_if
|
||||
-- Apply action to every item of target satisfying
|
||||
-- test.
|
||||
do
|
||||
from
|
||||
|
||||
target.start
|
||||
invariant
|
||||
invariant_value
|
||||
@@ -289,7 +293,7 @@ The class effects all the deferred features inherited from [[ref:/libraries/base
|
||||
|
||||
This routine text relies on features <eiffel>start</eiffel>, <eiffel>forth</eiffel> and <eiffel>exhausted</eiffel> which, together with <eiffel>off</eiffel>, have for convenience been carried over to [[ref:/libraries/base/reference/linear_iterator_chart|LINEAR_ITERATOR]] from their counterparts in [[ref:/libraries/base/reference/linear_chart|LINEAR]] , with feature declarations such as
|
||||
<code>
|
||||
off: BOOLEAN is
|
||||
off: BOOLEAN
|
||||
-- Is position of target off?
|
||||
require
|
||||
traversable_exists: target /= Void
|
||||
@@ -363,13 +367,13 @@ feature -- Status report
|
||||
|
||||
feature -- Cursor movement
|
||||
|
||||
finish is
|
||||
finish
|
||||
-- Move cursor of target to last position.
|
||||
do
|
||||
target.finish
|
||||
end
|
||||
|
||||
back is
|
||||
back
|
||||
-- Move cursor of target backward one position.
|
||||
do
|
||||
target.back
|
||||
@@ -383,11 +387,11 @@ This class provides a good example of the economy of expression that the full in
|
||||
|
||||
Tree iterations, provided by class [[ref:/libraries/base/reference/cursor_tree_iterator_chart|CURSOR_TREE_ITERATOR]] , work on trees of the cursor tree form; only with this form of tree are traversal operations possible. Three forms of iteration are provided: preorder, postorder and breadth-first. They correspond to the three traversal policies described in the discussion of trees. Here too it would seem that a rather lengthy class is needed, but repeated inheritance works wonders. <br/>
|
||||
[[ref:/libraries/base/reference/cursor_tree_iterator_chart|CURSOR_TREE_ITERATOR]] simply inherits three times from [[ref:/libraries/base/reference/linear_iterator_chart|LINEAR_ITERATOR]] , renaming the features appropriately in each case:
|
||||
* <eiffel>pre_do_all</eiffel>, <eiffel>pre_until</eiffel> and so on.
|
||||
* <eiffel>post_do_all</eiffel>, <eiffel>post_until</eiffel> and so on.
|
||||
* <eiffel>breadth_do_all</eiffel>, <eiffel>breadth_until</eiffel> and so on.
|
||||
* <eiffel>pre_do_all</eiffel>, <eiffel>pre_until</eiffel>, and so on.
|
||||
* <eiffel>post_do_all</eiffel>, <eiffel>post_until</eiffel>, and so on.
|
||||
* <eiffel>breadth_do_all</eiffel>, <eiffel>breadth_until</eiffel>, and so on.
|
||||
|
||||
All it needs to do then is to redefine the type of target to be [[ref:/libraries/base/reference/cursor_tree_chart|CURSOR_TREE]] <code> [ </code> <eiffel>G</eiffel> <code> ] </code>, and to redefine six features: the three renamed start ( <eiffel>pre_start</eiffel> etc.) and the three forth ( <eiffel>pre_ forth</eiffel> and so on). These seven redefinitions give us a full-fledged battery of tree iteration mechanisms.
|
||||
All it needs to do then is to redefine the type of target to be [[ref:/libraries/base/reference/cursor_tree_chart|CURSOR_TREE [G]]] , and to redefine six features: the three renamed start (<eiffel>pre_start</eiffel>, etc.) and the three forth ( <eiffel>pre_ forth</eiffel>, and so on). These seven redefinitions give us a full-fledged battery of tree iteration mechanisms.
|
||||
|
||||
=Building and Using Iterators=
|
||||
|
||||
@@ -396,7 +400,7 @@ An application class may use one of the iteration classes in either of two ways:
|
||||
|
||||
==The single descendant technique==
|
||||
|
||||
Assume an application class <eiffel>PROCESSOR</eiffel> that is a proper descendant of one of the effective iteration classes studied in this chapter. Then a routine of <eiffel>PROCESSOR</eiffel>, say <eiffel>iterate</eiffel>, may iterate a certain action over a data structure, subject to a certain test. First, class <eiffel>PROCESSOR</eiffel> must specify the action by redefining item_action and item_test (or, in the most general case, action and test). Then routine iterate must specify the target data structure through a call of the form set <code> ( </code> <code> t ) where t represents the selected target data structure. The type of t must correspond tothe iteration class selected as ancestor of </code> <eiffel>PROCESSOR</eiffel>: for [[ref:/libraries/base/reference/linear_iterator_chart|LINEAR_ITERATOR]] it must be a descendant of [[ref:/libraries/base/reference/linear_chart|LINEAR]] (such as [[ref:/libraries/base/reference/linked_list_chart|LINKED_LIST]] , [[ref:/libraries/base/reference/arrayed_list_chart|ARRAYED_LIST]] , LINKED_CIRCULAR or any other list or circular chain classes); for [[ref:/libraries/base/reference/two_way_chain_iterator_chart|TWO_WAY_CHAIN_ITERATOR]] it must be a descendant of [[ref:/libraries/base/reference/linear_chart|BILINEAR]] such as [[ref:/libraries/base/reference/two_way_list_chart|TWO_WAY_LIST]] or [[ref:/libraries/base/reference/two_way_circular_chart|TWO_WAY_CIRCULAR]] ; for [[ref:/libraries/base/reference/cursor_tree_iterator_chart|CURSOR_TREE_ITERATOR]] it must be a descendant of [[ref:/libraries/base/reference/cursor_tree_chart|CURSOR_TREE]] . In all cases the actual generic parameters of the iterator class and ofthe data structure class must be compatible. Then the iteration proper is obtained simply by calling the appropriate procedure, without any qualification or arguments, for example: do_ if <br/>
|
||||
Assume an application class <eiffel>PROCESSOR</eiffel> that is a proper descendant of one of the effective iteration classes studied in this chapter. Then a routine of <eiffel>PROCESSOR</eiffel>, say <eiffel>iterate</eiffel>, may iterate a certain action over a data structure, subject to a certain test. First, class <eiffel>PROCESSOR</eiffel> must specify the action by redefining item_action and item_test (or, in the most general case, action and test). Then routine iterate must specify the target data structure through a call of the form <code>set (t)</code> where t represents the selected target data structure. The type of t must correspond to the iteration class selected as ancestor of <eiffel>PROCESSOR</eiffel>: for [[ref:/libraries/base/reference/linear_iterator_chart|LINEAR_ITERATOR]] it must be a descendant of [[ref:/libraries/base/reference/linear_chart|LINEAR]] (such as [[ref:/libraries/base/reference/linked_list_chart|LINKED_LIST]] , [[ref:/libraries/base/reference/arrayed_list_chart|ARRAYED_LIST]] , LINKED_CIRCULAR or any other list or circular chain classes); for [[ref:/libraries/base/reference/two_way_chain_iterator_chart|TWO_WAY_CHAIN_ITERATOR]] it must be a descendant of [[ref:/libraries/base/reference/linear_chart|BILINEAR]] such as [[ref:/libraries/base/reference/two_way_list_chart|TWO_WAY_LIST]] or [[ref:/libraries/base/reference/two_way_circular_chart|TWO_WAY_CIRCULAR]] ; for [[ref:/libraries/base/reference/cursor_tree_iterator_chart|CURSOR_TREE_ITERATOR]] it must be a descendant of [[ref:/libraries/base/reference/cursor_tree_chart|CURSOR_TREE]] . In all cases the actual generic parameters of the iterator class and ofthe data structure class must be compatible. Then the iteration proper is obtained simply by calling the appropriate procedure, without any qualification or arguments, for example: do_ if <br/>
|
||||
It is hard to imagine a simpler scheme: no loops, no initialization, no arguments. Feature item_action may need to rely on some variable values. Because it does not take any argument, such values will have to be treated as attributes, with the corresponding <eiffel>set_...</eiffel> procedures to set and change their values. This also applies to the two schemes set next. <br/>
|
||||
The single descendant technique has one drawback: it provides the iterating class, <eiffel>PROCESSOR</eiffel>, with only one set of iteration particulars. This limitation does not affect the number of targets: you may use as many targets as you wish, as long as they are of compatible types, by calling a routine such as iterate several times, or calling several such routines, each call being preceded by a call to set to define a new target. The limitation also does not affect the iterating scheme: one iteration could use do_if, the next do_all and so on. But it does require the action and test to be the same in all cases. <br/>
|
||||
The next two techniques will remove this limitation.
|
||||
@@ -431,38 +435,38 @@ inherit
|
||||
|
||||
feature
|
||||
|
||||
action1 is
|
||||
action1
|
||||
-- Action for the first scheme
|
||||
do
|
||||
...
|
||||
end
|
||||
|
||||
test1: BOOLEAN is
|
||||
test1: BOOLEAN
|
||||
-- Test for the first scheme
|
||||
do
|
||||
...
|
||||
end
|
||||
|
||||
action2 is
|
||||
action2
|
||||
-- Action for the second scheme
|
||||
do
|
||||
...
|
||||
end
|
||||
|
||||
test2: BOOLEAN is
|
||||
test2: BOOLEAN
|
||||
-- Test for the second scheme
|
||||
do
|
||||
...
|
||||
end
|
||||
|
||||
iterate1 is
|
||||
iterate1
|
||||
-- Execute iteration of first kind.
|
||||
do
|
||||
set (...)
|
||||
do_if1
|
||||
end
|
||||
|
||||
iterate2 is
|
||||
iterate2
|
||||
-- Execute iteration of second kind.
|
||||
do
|
||||
set (...)
|
||||
@@ -495,7 +499,7 @@ end
|
||||
In the feature clause we want to provide the appropriate effectings for the deferred features of class <eiffel>FIGURE</eiffel>: <eiffel>display</eiffel>, <eiffel>hide</eiffel>, <eiffel>translate</eiffel> and all other basic figure operations. <br/>
|
||||
We can use loops for that purpose, for example
|
||||
<code>
|
||||
display is
|
||||
display
|
||||
-- Recursively display all components of the complex
|
||||
-- figure
|
||||
do
|
||||
@@ -548,7 +552,7 @@ create
|
||||
|
||||
feature
|
||||
|
||||
item_action (f: FIGURE) is
|
||||
item_action (f: FIGURE)
|
||||
-- Action to be applied to each figure: display
|
||||
-- it.
|
||||
do
|
||||
@@ -559,7 +563,7 @@ end
|
||||
|
||||
Similarly, you may define <eiffel>FIGURE_HIDER</eiffel>, <eiffel>FIGURE_MOVER</eiffel> and others. Then the features of <eiffel>COMPLEX_FIGURE</eiffel> are written almost trivially, without any explicit loops; for example:
|
||||
<code>
|
||||
display is
|
||||
display
|
||||
-- Recursively display all components of the complex
|
||||
-- figure
|
||||
local
|
||||
|
||||
@@ -15,18 +15,18 @@ The most general class describing sets is [[ref:libraries/base/reference/set_cha
|
||||
|
||||
The deferred class [[ref:libraries/base/reference/comparable_set_chart|COMPARABLE_SET]] , declared as
|
||||
<code>
|
||||
deferred class
|
||||
COMPARABLE_SET [G -> COMPARABLE]
|
||||
deferred class
|
||||
COMPARABLE_SET [G -> COMPARABLE]
|
||||
|
||||
inherit
|
||||
SUBSET [G]
|
||||
COMPARABLE_STRUCT [G]
|
||||
...
|
||||
inherit
|
||||
SUBSET [G]
|
||||
COMPARABLE_STRUCT [G]
|
||||
...
|
||||
</code>
|
||||
|
||||
describes sets whose items may be compared by a total order relation. The class has the features [[ref:libraries/base/reference/comparable_set_chart|min]] and [[ref:libraries/base/reference/comparable_set_chart|max]] . <br/>
|
||||
Two implementations of [[ref:libraries/base/reference/comparable_set_chart|COMPARABLE_SET]] are provided. One, [[ref:libraries/base/reference/two_way_sorted_set_chart|TWO_WAY_SORTED_SET]] , uses sorted two-way lists. The other, [[ref:libraries/base/reference/binary_search_tree_set_chart|BINARY_SEARCH_TREE_SET]] , uses binary search trees. <br/>
|
||||
If the items are partially rather than totally ordered, you may use the class [[ref:libraries/base/reference/part_sorted_set_chart|PART_SORTED_SET]] [G -> [[ref:libraries/base/reference/part_comparable_chart|PART_COMPARABLE]] ], which uses a two-way sorted list implementation.
|
||||
If the items are partially rather than totally ordered, you may use the class PART_SORTED_SET [G -> PART_COMPARABLE]], which uses a two-way sorted list implementation.
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -6,9 +6,9 @@ Hash tables are a convenient mechanism tostore and retrieve objects identified b
|
||||
The main advantage of hash tables is the efficiency of the basic operations: store ( [[ref:libraries/base/reference/hash_table_chart|put]] ) and retrieve ( [[ref:libraries/base/reference/hash_table_chart|item]] , [[ref:libraries/base/reference/hash_table_chart|remove]] ). <br/>
|
||||
The idea behind hash tables is to try to emulate the data structure that provides the ultimate in efficiency: the array. On an array <eiffel>a</eiffel>, for some integer <eiffel>i</eiffel> whose value lies within the bounds of <eiffel>a</eiffel>, the basic operations are
|
||||
<code>
|
||||
a.put (x, i)
|
||||
x := a.item (i)
|
||||
x := a @ i </code>
|
||||
a.put (x, i)
|
||||
x := a.item (i)
|
||||
x := a @ i </code>
|
||||
|
||||
The first causes the value of a at index <eiffel>i</eiffel> to be <eiffel>x</eiffel>; the second (and the third, which is simply a syntactical variant) access the value at index <eiffel>i</eiffel> and assign it to <eiffel>x</eiffel>. With the usual computer architectures, these operations are very fast: because arrays items are stored contiguously in memory, a computer will need just one addition (base address plus index) and one memory access to perform a put or item. <br/>
|
||||
Not only are the operation times small; they are constant (or more precisely bounded by a constant). This is a great advantage over structures such as lists or trees which you must traverse at least in part to retrieve an item, so that access and modification times grow with the number of items. With an array, disregarding the influence of other factors such as memory paging, the time for a put or item is for all practical purposes the same whether the array has five items or five hundred thousand. These properties make arrays excellent data structures for keeping objects. Unfortunately, they are only applicable if the objects satisfy three requirements:
|
||||
@@ -22,8 +22,9 @@ Thanks to the hashing mechanism we will indeed be able to store suitable objects
|
||||
* Different keys may hash into the same integer value, requiring extra processing to find an acceptable index.
|
||||
|
||||
With good implementations, however, it is possible to use hash tables with a performance that is not much worse than that of arrays and, most importantly, may be treated as if the time for a put, an item or a remove were constant. This will mean that you can consider operations such as
|
||||
<code>h.put (x, k)
|
||||
h := a.item (k)</code>
|
||||
<code>
|
||||
h.put (x, k)
|
||||
h := a.item (k)</code>
|
||||
|
||||
where <eiffel>h</eiffel> is a hash-table and <eiffel>k</eiffel> is a key (for example a string) as conceptually equivalentto the array operations mentioned above. <br/>
|
||||
The quality of a hashed implementation will depend both on the data structure that will store the objects, and on the choice of hashing function. Class [[ref:libraries/base/reference/hash_table_chart|HASH_TABLE]] attempts to address the first concern; for the second, client developers will be responsible for choosing the proper hashing function, although Base provides a few predefined functions, in particular for class [[ref:/libraries/base/reference/string_8_chart|STRING]] .
|
||||
@@ -45,7 +46,8 @@ class HASH_TABLE [G, H -> HASHABLE] ... </code>
|
||||
|
||||
G represents the type of the objects to be stored in the hash table, H the type of their keys. <br/>
|
||||
When viewed as an implementation of containers, [[ref:libraries/base/reference/hash_table_chart|HASH_TABLE]] , in a strict sense, represents bags rather than sets: unlike the other classes in this chapter, it allows an object to have two or more distinct occurrences in a single container. But this is only true if we consider a hash table as a repository of objects of type G. In reality each item of the table is identified by a pair of values, one from G and one from H. Because the keys must uniquely identify objects, the hash table viewed as a container of such pairs is indeed a set. The creation procedure make takes an integer argument, as in
|
||||
<code>create my_table.make (n)</code>
|
||||
<code>
|
||||
create my_table.make (n)</code>
|
||||
|
||||
The value of <eiffel>n</eiffel> indicates how many items the hash table is expected to have to accommodate. This number of items is not a hardwired size, just information passed to the class. In particular:
|
||||
* The actual size of the underlying array representation will be higher than n since efficient operation of hash table algorithms require the presence of enough breathing space - unoccupied positions.
|
||||
|
||||
@@ -7,7 +7,11 @@ Trees, in particular binary trees and their variants, also provide convenient im
|
||||
==Basic Terminology==
|
||||
|
||||
A tree consists of a set of nodes. Each node may have zero or more children, other nodes of which it is the parent. Each node has at most one parent, although it may have an arbitrary number of children.
|
||||
[[Image:tree|tree]]
|
||||
|
||||
|
||||
[[Image:tree|tree]]
|
||||
|
||||
|
||||
A node with no parent, such as the node marked ''A'' on the figure, is called a root; a node with no children, such as ''E'', ''F'', ''G'', ''H'' and ''I'', is called a leaf. The length of a path from the root to a leaf (that is to say, the number of nodes on the path minus one) is the height of the tree; the average length of all such paths is the average height. On the figure the height is 2 and the average height is 11/6 since of the six paths five have length 2 and one has length 1.
|
||||
|
||||
The children of a common node are called siblings. For example ''G'', ''H'' and ''I'' are siblings on the figure. The siblings of a node are usually considered to be ordered; the order corresponds to the direction from left to right on conventional figures such as this one.
|
||||
@@ -60,10 +64,12 @@ Binary trees are a special case of fixed trees in which nodes always have two ch
|
||||
|
||||
===Basic binary trees===
|
||||
|
||||
Class [[ref:libraries/base/reference/binary_tree_chart|BINARY_TREE]] describes binary trees. <br/>
|
||||
<br/>
|
||||
[[Image:binary-tree|binary_tree]] <br/>
|
||||
<br/>
|
||||
Class [[ref:libraries/base/reference/binary_tree_chart|BINARY_TREE]] describes binary trees.
|
||||
|
||||
|
||||
[[Image:binary-tree|binary_tree]]
|
||||
|
||||
|
||||
The children are represented by features [[ref:libraries/base/reference/binary_tree_chart|left_child]] and [[ref:libraries/base/reference/binary_tree_chart|right_child]] . Queries [[ref:libraries/base/reference/binary_tree_chart|has_left]] and [[ref:libraries/base/reference/binary_tree_chart|has_right]] indicate whether any of these is non-void; arity is redefined to yield the number of non-void children (0, 1 or 2).
|
||||
|
||||
===Binary representations of trees===
|
||||
@@ -106,7 +112,11 @@ The cursor attached to a cursor tree is not just a conceptual notion but an actu
|
||||
|
||||
A useful notion associated with trees and particularly applicable to cursor trees is that of traversal. <br/>
|
||||
A traversal is a certain policy for ordering all the nodes in a tree - usually to apply an operation to all these nodes in the resulting order. [[ref:libraries/base/reference/cursor_tree_chart|CURSOR_TREE]] and its descendants support three forms of traversal: preorder, postorder and breadth-first. They correspond to the most commonly used traversal policies on trees, illustrated on the figure (where the children of each node are assumed to be ordered from left to right):
|
||||
[[Image:tree|tree]]
|
||||
|
||||
|
||||
[[Image:tree|tree]]
|
||||
|
||||
|
||||
* Preorder is the traversal that visits the root first, then (recursively) traverses each subtree in order. On the figure we will visit node ''A'' first then, recursively, the subtrees rooted at ''B'' (which implies visiting ''E'' and ''F''), ''C'' and ''D''. The resulting order is: ''A B E F C D G H I''.
|
||||
* Postorder first traverses (recursively) the subtrees, then visits the root. On the example this gives: '' E F B C G H I D A''.
|
||||
* Breadth-first visits the nodes level by level, starting with the root: first the root, then all its children, then all their children and so on. Here the resulting order is: ''A B C D E F G H I''.
|
||||
|
||||
@@ -31,7 +31,7 @@ The Eiffel inheritance mechanism is set up in such a way that every class is a d
|
||||
Every class which has no inheritance clause is understood to have an inheritance clause of the form
|
||||
<code>
|
||||
inherit
|
||||
ANY
|
||||
ANY
|
||||
</code>
|
||||
|
||||
As a result, every developer-defined class is a descendant of [[ref:/libraries/base/reference/any_chart|ANY]] . You may introduce your own project specific features in [[ref:/libraries/base/reference/any_chart|ANY]] so that all the classes of your system will be able to use these features.
|
||||
@@ -41,24 +41,31 @@ As a result, every developer-defined class is a descendant of [[ref:/libraries/b
|
||||
If you need to rename or redefine a feature inherited from one of the universal classes, you should include an explicit inheritance clause, as in
|
||||
<code>
|
||||
class
|
||||
C
|
||||
C
|
||||
inherit
|
||||
ANY
|
||||
rename
|
||||
out as basic_out
|
||||
redefine
|
||||
print
|
||||
end
|
||||
...
|
||||
ANY
|
||||
rename
|
||||
out as basic_out
|
||||
redefine
|
||||
print
|
||||
end
|
||||
...
|
||||
feature
|
||||
...
|
||||
...
|
||||
end
|
||||
</code>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
The features of [[ref:/libraries/base/reference/any_chart|ANY]] are usable in both qualified and unqualified form. For example, the argumentless function out, which produces a printable representation of any object, may be called under either of the forms
|
||||
<code>
|
||||
x := out
|
||||
x := a.out
|
||||
x := out
|
||||
x := a.out
|
||||
</code>
|
||||
|
||||
The first call yields a printable representation of the current object; the second, which assumes that a is not void, yields a printable representation of the object attached to a.
|
||||
@@ -66,26 +73,31 @@ The first call yields a printable representation of the current object; the seco
|
||||
==Input and output features==
|
||||
|
||||
Some of the features of [[ref:/libraries/base/reference/any_chart|ANY]] cover common input and output needs. <br/>
|
||||
Feature <eiffel>io</eiffel>, of type [[ref:/libraries/base/reference/std_files_chart|STD_FILES]] , gives access to standard input and output facilities. For example, <eiffel>io</eiffel> <code> . </code><eiffel>input</eiffel> is the standard input file and <eiffel>io</eiffel>.<eiffel>new_line</eiffel> will print a line feed on the standard output. Feature <eiffel>io</eiffel> is declared as a once function which, when first called, returns the value of an instance of [[ref:/libraries/base/reference/std_files_chart|STD_FILES]] that provides access to the standard input, the standard output and the error output. As a result, <eiffel>io</eiffel> is never void, so that operations such as <eiffel>io</eiffel> <code> . </code><eiffel>new_line</eiffel> are always possible. <br/>
|
||||
Function <eiffel>out</eiffel>, of type [[ref:/libraries/base/reference/string_8_chart|STRING]] , is a universal mechanism for obtaining a simple external representation of any object. For non-void <code> x </code> of any type, the string <code> x </code> <code> . </code><eiffel>out</eiffel> is a printable representation of <code> x </code>. This works for <code> x </code> of all types, reference or expanded. For example, if <code> x </code> is an integer expression, <code> x </code> <code> . </code><eiffel>out</eiffel> is its string representation, such as <code> -897 </code>; if n is a non-void reference, <code> x </code> <code> . </code><eiffel>out</eiffel> is (recursively) the concatenation of the result of applying out to the successive fields of the attached object, each labeled by the name of the corresponding attribute. You may redefine out in any class to specify any suitable format for displaying instances of the class. To obtain the default representation regardless of any redefinition of out, use tagged_out, declared as a frozen synonym of the original out. <br/>
|
||||
The call print <code> ( </code> <code> x </code> <code> ) </code> will output the value of <code> x </code> <code> . </code><eiffel>out</eiffel> on the default output if <code> x </code> is not void, and do nothing otherwise.
|
||||
Feature <eiffel>io</eiffel>, of type [[ref:/libraries/base/reference/std_files_chart|STD_FILES]] , gives access to standard input and output facilities. For example, <eiffel>io.input</eiffel> is the standard input file and <eiffel>io</eiffel>.<eiffel>new_line</eiffel> will print a line feed on the standard output. Feature <eiffel>io</eiffel> is declared as a once function which, when first called, returns the value of an instance of [[ref:/libraries/base/reference/std_files_chart|STD_FILES]] that provides access to the standard input, the standard output and the error output. As a result, <eiffel>io</eiffel> is never void, so that operations such as <eiffel>io</eiffel>.<eiffel>new_line</eiffel> are always possible. <br/>
|
||||
Function <eiffel>out</eiffel>, of type [[ref:/libraries/base/reference/string_8_chart|STRING]] , is a universal mechanism for obtaining a simple external representation of any object. For non-void <code>x</code> of any type, the string <code>x.out</code> is a printable representation of <code>x</code>. This works for <code>x</code> of all types, reference or expanded. For example, if <code>x</code> is an integer expression, <code>x.out</code> is its string representation, such as <code>-897</code>; if n is a non-void reference, <code>x.out</code> is (recursively) the concatenation of the result of applying out to the successive fields of the attached object, each labeled by the name of the corresponding attribute. You may redefine out in any class to specify any suitable format for displaying instances of the class. To obtain the default representation regardless of any redefinition of out, use tagged_out, declared as a frozen synonym of the original out. <br/>
|
||||
The call <code>print (x)</code> will output the value of <code>x.out</code> on the default output if <code>x</code> is not void, and do nothing otherwise.
|
||||
|
||||
==Copy and comparison routines==
|
||||
|
||||
Procedure copy copies the fields of an object onto those of another. It is used under the form
|
||||
<code>
|
||||
target.copy (source)
|
||||
target.copy (source)
|
||||
</code>
|
||||
|
||||
Here both target and source must be non-void; this means that <eiffel>copy</eiffel> is only good for copying onto an object that already exists. If you need both to allocate a new object and to initialize it as a copy of another, use the function clone. For non-void source, the assignment
|
||||
Here both target and source must be non-void; this means that <eiffel>copy</eiffel> is only good for copying onto an object that already exists. If you need both to allocate a new object and to initialize it as a copy of another, use the function <code>twin</code>. For non-void source, the assignment
|
||||
<code>
|
||||
target := clone (source)
|
||||
target := source.twin
|
||||
</code>
|
||||
starts by creating a new object, then populates its fields to be identical to <code>source</code>.
|
||||
|
||||
starts by creating a new object. If <code> source </code> is void, <code> target </code> will be made void too. The boolean function <eiffel>equal</eiffel> compares two objects for field-by-field equality. This is different from the equality operators <code> = </code> and <code> /= </code> which, in the case of reference types, compare references, not objects. <br/>
|
||||
The function <eiffel>deep_clone</eiffel> produces a duplicate of an entire object structure. The boolean function <eiffel>deep_equal</eiffel> determines whether two object structures are recursively identical. These routines are the ''deep'' counterparts of the shallow copy and equality tests provided by <eiffel>clone</eiffel> and <eiffel>equal</eiffel>. <br/>
|
||||
A class that needs a specific notion of equality and the corresponding copy semantics may redefine <eiffel>copy</eiffel> and <eiffel>is_equal</eiffel> (from which equal follows, since equal <code> ( </code> <code> a </code> <code> , </code> <code> b </code> <code> ) </code> is defined as <code> a </code> <code> . </code><eiffel>is_equal </eiffel> <code> ( </code> <code> b </code> <code> ) </code> for non-void <code> a </code>). You will find such redefinitions in a number of classes of the Base libraries. For example an instance of [[ref:/libraries/base/reference/string_8_chart|STRING]] is a string descriptor containing a reference to the actual character sequence, not that sequence itself, so that what the default equal compares and the default copy copies is the descriptor, not the string. Class [[ref:/libraries/base/reference/string_8_chart|STRING]] redefines these routines to yield the semantics normally expected by string clients; the frozen variants <eiffel>standard_copy</eiffel> and <eiffel>standard_equal</eiffel>, originally declared as synonyms to <eiffel>equal</eiffel> and <eiffel>copy</eiffel>, remain available with the default semantics. <br/>
|
||||
The function <eiffel>clone</eiffel> is defined in terms of <eiffel>copy</eiffel>, and so will follow any redefinition of <eiffel>copy</eiffel>. This makes it impossible to change the semantics of one but not of the other, which would be a mistake. The variant <eiffel>standard_clone</eiffel> is defined in terms of <eiffel>standard_copy</eiffel>.
|
||||
The boolean function <eiffel>equal</eiffel> compares two objects for field-by-field equality. This is different from the equality operators <code>=</code> and <code>/=</code> which, in the case of reference types, compare references, not objects. <br/>
|
||||
The function <eiffel>deep_twin</eiffel> produces a duplicate of an entire object structure. The boolean function <eiffel>deep_equal</eiffel> determines whether two object structures are recursively identical. These routines are the ''deep'' counterparts of the shallow copy and equality tests provided by <eiffel>twin</eiffel> and <eiffel>equal</eiffel>. <br/>
|
||||
A class that needs a specific notion of equality and the corresponding copy semantics may redefine <eiffel>copy</eiffel> and <eiffel>is_equal</eiffel> (from which <code>equal</code> follows, since <code>equal (a, b)</code> is defined as <code>a.is_equal (b)</code> for non-void <code>a</code>). You will find such redefinitions in a number of classes of the Base libraries. For example an instance of [[ref:/libraries/base/reference/string_8_chart|STRING]] is a string descriptor containing a reference to the actual character sequence, not that sequence itself, so that what the default equal compares and the default copy copies is the descriptor, not the string. Class [[ref:/libraries/base/reference/string_8_chart|STRING]] redefines these routines to yield the semantics normally expected by string clients; the frozen variants <eiffel>standard_copy</eiffel> and <eiffel>standard_equal</eiffel>, originally declared as synonyms to <eiffel>equal</eiffel> and <eiffel>copy</eiffel>, remain available with the default semantics. <br/>
|
||||
The function <eiffel>twin</eiffel> is defined in terms of <eiffel>copy</eiffel>, and so will follow any redefinition of <eiffel>copy</eiffel>. This makes it impossible to change the semantics of one but not of the other, which would be a mistake. The variant <eiffel>standard_twin</eiffel> is defined in terms of <eiffel>standard_copy</eiffel>.
|
||||
|
||||
|
||||
{{note|In some existing Eiffel code you may encounter the use of a function <code>clone</code> which is used to do the job of <code>twin</code>, but has a form like <code>copy</code>, as in <code>target.clone (source)</code>. <code>clone</code> is an obsolete function. Use <code>twin</code> instead.
|
||||
}}
|
||||
|
||||
==Type information==
|
||||
|
||||
@@ -96,7 +108,7 @@ These two features enable clients to ascertain the dynamic type of an entity at
|
||||
|
||||
The query Void, of type <eiffel>NONE</eiffel>, denotes a reference that is always void - not attached to any object. <br/>
|
||||
Procedure <eiffel>do_nothing</eiffel> does what its name implies. <br/>
|
||||
Function default also has an empty body; its result type is <code> like Current </code>, so what it returns is the default value of the current type. This is mostly interesting for expanded types, since for reference types the default value is simply a void reference.
|
||||
Function default also has an empty body; its result type is <code>like Current</code>, so what it returns is the default value of the current type. This is mostly interesting for expanded types, since for reference types the default value is simply a void reference.
|
||||
|
||||
=Language-related Facilities=
|
||||
|
||||
@@ -107,7 +119,7 @@ A number of classes offer facilities which are very close to the language level.
|
||||
The basic types [[ref:/libraries/base/reference/boolean_chart|BOOLEAN]] , [[ref:/libraries/base/reference/character_8_chart|CHARACTER]] , [[ref:/libraries/base/reference/integer_32_chart|INTEGER]] , [[ref:/libraries/base/reference/real_32_chart|REAL]] and [[ref:/libraries/base/reference/real_64_chart|DOUBLE]] are defined by classes of the Kernel library. <br/>
|
||||
In reading the class specifications for the numeric types [[ref:/libraries/base/reference/integer_32_chart|INTEGER]] , [[ref:/libraries/base/reference/real_32_chart|REAL]] and [[ref:/libraries/base/reference/real_64_chart|DOUBLE]] , you might think that the type declarations are too restrictive. For example the addition operation in class [[ref:/libraries/base/reference/real_32_chart|REAL]] reads
|
||||
<code>
|
||||
infix "+" (other: REAL): REAL
|
||||
infix "+" (other: REAL): REAL
|
||||
</code>
|
||||
|
||||
but there is actually no problem here. A language convention applicable to all arithmetic expressions, the Balancing rule, states that in any such expression all operands are considered to be converted to the heaviest type, where [[ref:/libraries/base/reference/real_64_chart|DOUBLE]] is heavier than [[ref:/libraries/base/reference/real_32_chart|REAL]] and [[ref:/libraries/base/reference/real_32_chart|REAL]] is heavier than [[ref:/libraries/base/reference/integer_32_chart|INTEGER]] . So mixed-type arithmetic, consistent with common practice, is possible and indeed frequent.
|
||||
@@ -117,52 +129,54 @@ but there is actually no problem here. A language convention applicable to all a
|
||||
To create and manipulate one-dimensional arrays, use class [[ref:/libraries/base/reference/array_chart|ARRAY]] of the Kernel Library. Arrays are not primitive language elements; instead, they are handled through class [[ref:/libraries/base/reference/array_chart|ARRAY]] . This class is 'normal' in the sense that it may be used just as any other class by client and descendant classes. It is also somewhat special, however, in that the Eiffel compiler knows about it and uses this knowledge to generate efficient code for array operations. <br/>
|
||||
To create an instance of [[ref:/libraries/base/reference/array_chart|ARRAY]] , use the creation instruction
|
||||
<code>
|
||||
create my_array.make (1, u)
|
||||
create my_array.make (1, u)
|
||||
</code>
|
||||
|
||||
where the arguments indicate the lower and upper bounds. These bounds will then be accessible as <code> my_array </code> <code> . </code>lower and <code> my_array </code> <code> . </code><eiffel>upper</eiffel>. The number of items is <code> my_array </code> <code> . </code><eiffel>count</eiffel>; feature <eiffel>capacity</eiffel> is a synonym for <eiffel>count</eiffel>. The class invariant expresses the relation between <eiffel>count</eiffel>, <eiffel>lower</eiffel> and <eiffel>upper</eiffel>. <br/>
|
||||
where the arguments indicate the lower and upper bounds. These bounds will then be accessible as <code>my_array</code> <code>.</code>lower and <code>my_array</code> <code>.</code><eiffel>upper</eiffel>. The number of items is <code>my_array</code> <code>.</code><eiffel>count</eiffel>; feature <eiffel>capacity</eiffel> is a synonym for <eiffel>count</eiffel>. The class invariant expresses the relation between <eiffel>count</eiffel>, <eiffel>lower</eiffel> and <eiffel>upper</eiffel>. <br/>
|
||||
To access and change the item at index ''i'' in array a, you may use features <eiffel>item</eiffel> and <eiffel>put</eiffel>, as in
|
||||
<code>
|
||||
x := my_array.item (i)
|
||||
my_array.put (new_value, i)
|
||||
x := my_array.item (i)
|
||||
my_array.put (new_value, i)
|
||||
</code>
|
||||
|
||||
Function item has an infix synonym, <code> infix </code> <code> " </code>@ <code> " </code>, so that you may also write the first assignment above more concisely as
|
||||
Function item has an infix synonym, <code>infix</code> <code>"</code>@ <code>"</code>, so that you may also write the first assignment above more concisely as
|
||||
<code>
|
||||
x := my_array @ i
|
||||
x := my_array @ i
|
||||
</code>
|
||||
<br/>
|
||||
Features <eiffel>item</eiffel>, <code> infix </code> <code> " </code>@ <code> " </code>and <eiffel>put</eiffel> have preconditions requiring the index ( <code> i </code>in the above calls) to be within the bounds of the array. This means that you can detect bounds violations (which correspond to bugs in the client software) by using a version of class [[ref:/libraries/base/reference/array_chart|ARRAY]] compiled with precondition checking on. The bounds of an array may be changed dynamically through procedure <eiffel>resize</eiffel>. Previously entered elements are retained. Rather than an explicit resize, you may use calls to procedure <eiffel>force</eiffel> which has the same signature as put but no precondition; if the index is not within the current bounds force will perform a resize as necessary.
|
||||
Features <eiffel>item</eiffel>, <code>infix</code> <code>"</code>@ <code>" </code>and <eiffel>put</eiffel> have preconditions requiring the index ( <code>i</code>in the above calls) to be within the bounds of the array. This means that you can detect bounds violations (which correspond to bugs in the client software) by using a version of class [[ref:/libraries/base/reference/array_chart|ARRAY]] compiled with precondition checking on. The bounds of an array may be changed dynamically through procedure <eiffel>resize</eiffel>. Previously entered elements are retained. Rather than an explicit resize, you may use calls to procedure <eiffel>force</eiffel> which has the same signature as put but no precondition; if the index is not within the current bounds force will perform a resize as necessary.
|
||||
==Optimizing array computations==
|
||||
|
||||
''' CAUTION''': Although [[ref:/libraries/base/reference/array_chart|ARRAY]] benefits from an efficient implementation, its more advanced facilities such as resizing do not come for free. For extensive computations on large arrays, an optimization may be desirable, bypassing these facilities. The technique yields loops that run at about the same speed as the corresponding loops written in C or Fortran (the usual references for array computations). It is of interest for advanced uses only, so that you may safely skip this section on first reading unless your domain of application is numerical computation or some other area requiring high-performance array manipulations.
|
||||
|
||||
The optimization relies on the class SPECIAL, used internally by [[ref:/libraries/base/reference/array_chart|ARRAY]] but of no direct interest to client developers in most common uses. With the declarations
|
||||
<code>
|
||||
my_array: ARRAY [SOME_TYPE]
|
||||
direct_access: SPECIAL [SOME_TYPE]
|
||||
my_array: ARRAY [SOME_TYPE]
|
||||
direct_access: SPECIAL [SOME_TYPE]
|
||||
</code>
|
||||
|
||||
you may use <eiffel>direct_access</eiffel> in lieu of 'my_array' within a critical loop, provided none of the operations may resize the array. Typically, the operations should only include put and item. In such a case you can use the following scheme:
|
||||
<code>
|
||||
direct_access:= my_array.area
|
||||
direct_access:= my_array.area
|
||||
|
||||
-- The critical loop:
|
||||
from
|
||||
some_initialization
|
||||
index := some_initial_index
|
||||
until
|
||||
index = some_final_index
|
||||
loop
|
||||
...
|
||||
x := direct_access.item (index)
|
||||
...
|
||||
direct_access.put (some_value, index)
|
||||
...
|
||||
end
|
||||
-- The critical loop:
|
||||
from
|
||||
some_initialization
|
||||
index := some_initial_index
|
||||
until
|
||||
index = some_final_index
|
||||
loop
|
||||
...
|
||||
|
||||
|
||||
x := direct_access.item (index)
|
||||
...
|
||||
direct_access.put (some_value, index)
|
||||
...
|
||||
end
|
||||
</code>
|
||||
|
||||
This replaces an original loop where the operations were on <code> my_array </code>. Feature <eiffel>area</eiffel> of [[ref:/libraries/base/reference/array_chart|ARRAY]] gives direct access to the special object, an instance of SPECIAL, containing the array values. Features <eiffel>put</eiffel> and <eiffel>item</eiffel> are available in SPECIAL as in [[ref:/libraries/base/reference/array_chart|ARRAY]] , but without the preconditions; in other words, you will not get any bounds checking. Instances of SPECIAL are always indexed from zero, in contrast with arrays, whose lower bound is arbitrary, 1 being the most common value. But rather than performing index translations (that is to say, subtracting <code> my_array </code> <code> . </code><eiffel>lower</eiffel> from index throughout the loop) it is preferable to use the following simple technique: if the lower bound 'lb' of <code> my_array </code> is 1 or another small integer, use 0 as lower bound instead when creating <code> my_array </code>, but only use the positions starting at 'lb'. You will waste a few memory positions (0 to lb-1), but will not have to change anything in your algorithm and will avoid costly subtractions. <br/>
|
||||
This replaces an original loop where the operations were on <code>my_array</code>. Feature <eiffel>area</eiffel> of [[ref:/libraries/base/reference/array_chart|ARRAY]] gives direct access to the special object, an instance of SPECIAL, containing the array values. Features <eiffel>put</eiffel> and <eiffel>item</eiffel> are available in SPECIAL as in [[ref:/libraries/base/reference/array_chart|ARRAY]] , but without the preconditions; in other words, you will not get any bounds checking. Instances of SPECIAL are always indexed from zero, in contrast with arrays, whose lower bound is arbitrary, 1 being the most common value. But rather than performing index translations (that is to say, subtracting <code>my_array</code> <code>.</code><eiffel>lower</eiffel> from index throughout the loop) it is preferable to use the following simple technique: if the lower bound 'lb' of <code>my_array</code> is 1 or another small integer, use 0 as lower bound instead when creating <code>my_array</code>, but only use the positions starting at 'lb'. You will waste a few memory positions (0 to lb-1), but will not have to change anything in your algorithm and will avoid costly subtractions. <br/>
|
||||
It is important to note that this optimization, if at all necessary, should at most affect a few loops in a large system. You should always begin by writing your software using the normal [[ref:/libraries/base/reference/array_chart|ARRAY]] facilities; then once you have the certainty that the software is correct, if you detect that a large array computation is hampering the efficiency of the system, you may apply the above technique to get the fastest performance out of that computation. The change to the software will be minimal - a few lines - and will be easy to undo if necessary.
|
||||
|
||||
==Tuples==
|
||||
@@ -273,9 +287,9 @@ The implementation of tuples has the following limitations:
|
||||
|
||||
Strings are handled by class [[ref:/libraries/base/reference/string_8_chart|STRING]] , similar in many respects to [[ref:/libraries/base/reference/array_chart|ARRAY]] . Strings are of arbitrary size. The make creation procedure takes an integer argument, as in:
|
||||
<code>
|
||||
s, s1, s2, s3: STRING
|
||||
...
|
||||
create s.make (30)</code>
|
||||
s, s1, s2, s3: STRING
|
||||
...
|
||||
create s.make (30)</code>
|
||||
|
||||
The argument indicates the number of characters for the initial allocation. This is not an absolute limit: the string will automatically grow or shrink as a result of future operations. You may always request a resizing explicitly by calling procedure resize.
|
||||
|
||||
@@ -284,12 +298,12 @@ The argument indicates the number of characters for the initial allocation. This
|
||||
The object attached at run-time to an entity such declared of type [[ref:/libraries/base/reference/string_8_chart|STRING]] is not the actual sequence of characters but a string descriptor, which contains a reference to the actual string contents.
|
||||
|
||||
As a result, four assignment or assignment-like operations are possible:
|
||||
* '''A1''' <code> s1 := s </code>
|
||||
* '''A2''' <code> s2.share (s) </code>
|
||||
* '''A3''' <code> s3 := clone (s) </code>
|
||||
* '''A4''' <code> s4.copy (s) </code>
|
||||
* '''A1''' <code>s1 := s</code>
|
||||
* '''A2''' <code>s2.share (s)</code>
|
||||
* '''A3''' <code>s3 := s.twin</code>
|
||||
* '''A4''' <code>s4.copy (s)</code>
|
||||
|
||||
As illustrated below, ''' A1''' is a reference assignment: <code> s1 </code> will be attached to the same descriptor as s. ''' A2''' keeps the descriptors distinct, but make them refer to the same sequence of characters. ''' A3''' uses the redefinition of clone for class [[ref:/libraries/base/reference/string_8_chart|STRING]] : <code> s3 </code> will be attached to a new string, completely distinct from the string attached to <code> s1 </code> although made of identical characters. ''' A4''' has almost the same effect as '''A3''', but is only applicable if <code> s4 </code> was not void, and will override the existing descriptor rather than creating a new one.
|
||||
As illustrated below, ''' A1''' is a reference assignment: <code>s1</code> will be attached to the same descriptor as s. ''' A2''' keeps the descriptors distinct, but make them refer to the same sequence of characters. In ''' A3''', <code>s3</code> will be attached to a new string, completely distinct from the string attached to <code>s1</code> although made of identical characters. ''' A4''' has almost the same effect as '''A3''', but is only applicable if <code>s4</code> was not void, and will override the existing descriptor rather than creating a new one.
|
||||
[[Image:strings]]
|
||||
fig. 1: Effect of string assignment and copy operations
|
||||
|
||||
@@ -314,7 +328,7 @@ Queries are available to determine the status of a file, in particular <eiffel>e
|
||||
|
||||
{{caution|You will notice in the flat-short form that all these queries except the first have exists as a precondition. This precondition is good for efficiency since it saves an existence test - a relatively expensive operation - when you know that a certain file exists. But it also means that if you have any doubt about the file's existence you must use the queries in the style <br/>
|
||||
<br/>
|
||||
<code> if my_file.exists and then my_file.is_readable then </code>... }}
|
||||
<code>if my_file.exists and then my_file.is_readable then</code>... }}
|
||||
|
||||
FILE is a deferred class. Various implementations are possible. A quite detailed one is PLAIN_TEXT_FILE, which adds many features for accessing reading and writing data from/to a file. <br/>
|
||||
[[ref:/libraries/base/reference/unix_file_info_chart|UNIX_FILE_INFO]] describes objects that contain internal information, such as protection mode and size, about a file. <br/>
|
||||
@@ -341,7 +355,7 @@ Storing an object just by itself would usually result in wrong semantics: most o
|
||||
|
||||
==Using the storage and retrieval facilities==
|
||||
|
||||
Class [[ref:/libraries/base/reference/storable_chart|STORABLE]] is meant to be used as ancestor. You can use its features in any descendant <eiffel>C</eiffel>; for example a routine of <eiffel>C</eiffel> may contain a call of the form <eiffel>basic_store</eiffel> <code> ( </code> <code> my_descriptor </code> <code> ) </code>. <br/>
|
||||
Class [[ref:/libraries/base/reference/storable_chart|STORABLE]] is meant to be used as ancestor. You can use its features in any descendant <eiffel>C</eiffel>; for example a routine of <eiffel>C</eiffel> may contain a call of the form <eiffel>basic_store</eiffel> <code>(</code> <code>my_descriptor</code> <code>)</code>. <br/>
|
||||
The effect of this call will be to store the current object and all its dependents into the file denoted by my_descriptor. <br/>
|
||||
Although basic_store and other procedures of [[ref:/libraries/base/reference/storable_chart|STORABLE]] will in general process objects of many different types, only the generating class of the structure's initial object, <eiffel>C</eiffel> in our example, needs to be a descendant of [[ref:/libraries/base/reference/storable_chart|STORABLE]] .
|
||||
|
||||
@@ -357,7 +371,7 @@ Feature <eiffel>retrieved</eiffel> returns a result of type [[ref:/libraries/bas
|
||||
<code>x ?= retrieved (my_descriptor)</code>
|
||||
<br/>
|
||||
|
||||
The assignment attempt is necessary because <eiffel>retrieved</eiffel> returns a result of type [[ref:/libraries/base/reference/storable_chart|STORABLE]] whereas the type of <code> x </code> will be based on a proper descendant of [[ref:/libraries/base/reference/storable_chart|STORABLE]] . If the structure in the file has been corrupted and <eiffel>retrieved</eiffel> is unable to do its job, it will trigger an exception. The code for that exception in class [[ref:/libraries/base/reference/exceptions_chart|EXCEPTIONS]] (which inherits it from EXCEP_CONST and is discussed in the next section, together with the notion of exception code) is <eiffel>Retrieve_exception</eiffel>.
|
||||
The assignment attempt is necessary because <eiffel>retrieved</eiffel> returns a result of type [[ref:/libraries/base/reference/storable_chart|STORABLE]] whereas the type of <code>x</code> will be based on a proper descendant of [[ref:/libraries/base/reference/storable_chart|STORABLE]] . If the structure in the file has been corrupted and <eiffel>retrieved</eiffel> is unable to do its job, it will trigger an exception. The code for that exception in class [[ref:/libraries/base/reference/exceptions_chart|EXCEPTIONS]] (which inherits it from EXCEP_CONST and is discussed in the next section, together with the notion of exception code) is <eiffel>Retrieve_exception</eiffel>.
|
||||
|
||||
==Recoverable storable==
|
||||
|
||||
@@ -435,7 +449,7 @@ As you will see from the header comments in the flat-short form of class [[ref:/
|
||||
|
||||
The features of class [[ref:/libraries/base/reference/exceptions_chart|EXCEPTIONS]] enable you to determine whether a certain exception is a signal - an operating system event such as may result from a child process that disappears, a window that is resized, a user that hits the Break key and many others. But they do not give you more details because the exact set of possible signals is highly platform-dependent. <br/>
|
||||
Class [[ref:/libraries/base/reference/unix_signals_chart|UNIX_SIGNALS]] complements EXCEP_CONST by providing codes for the signals of Unix and similar systems, such as Sigkill for the 'kill' signal and Sigbus for bus error. <br/>
|
||||
Query <eiffel>is_defined </eiffel> <code> ( </code> <code> some_signal </code> <code> ) </code>, where <code> some_signal </code> is an integer code, will determine whether some_signal is supported on the platform. <br/>
|
||||
Query <eiffel>is_defined </eiffel> <code>(</code> <code>some_signal</code> <code>)</code>, where <code>some_signal</code> is an integer code, will determine whether some_signal is supported on the platform. <br/>
|
||||
A class whose routines need to perform specific processing depending on the nature of signals received should inherit from [[ref:/libraries/base/reference/unix_signals_chart|UNIX_SIGNALS]] , or a similar class for another platform. <br/>
|
||||
Because signal codes are platform-dependent, the features of [[ref:/libraries/base/reference/unix_signals_chart|UNIX_SIGNALS]] are implemented as once functions - computed on the first call - rather than constants, although this makes no difference to clients.
|
||||
|
||||
@@ -452,11 +466,11 @@ MEM_INFO, the result type for query <eiffel>memory_statistics</eiffel> in [[ref:
|
||||
==Command-line arguments==
|
||||
|
||||
Writing, assembling and compiling a system yields an executable command. The system's users will call that command with arguments. These are normally provided in textual form on the command line, as in
|
||||
<code>your_system arg1 arg2 arg3 </code>
|
||||
<code>your_system arg1 arg2 arg3</code>
|
||||
|
||||
although one may conceive of other ways of entering the command arguments, such as tabular or graphical form-filling. In any case the software must be able to access the values passed as command arguments. <br/>
|
||||
A language mechanism is available for that purpose: the Root Class rule indicates that the creation procedure of the root class may have a single argument (in the Eiffel sense of argument to a routine) of type [[ref:/libraries/base/reference/array_chart|ARRAY]] [ [[ref:/libraries/base/reference/string_8_chart|STRING]] <code> ] </code>. The corresponding array of strings will be initialized at the beginning of the system's execution with the values entered as arguments to that execution of the command. <br/>
|
||||
A language mechanism is available for that purpose: the Root Class rule indicates that the creation procedure of the root class may have a single argument (in the Eiffel sense of argument to a routine) of type [[ref:/libraries/base/reference/array_chart|ARRAY]] [ [[ref:/libraries/base/reference/string_8_chart|STRING]] <code>]</code>. The corresponding array of strings will be initialized at the beginning of the system's execution with the values entered as arguments to that execution of the command. <br/>
|
||||
Although this facility suffices in many cases, it is not always convenient if you suddenly need to access the command arguments in a class that is far-away from the root. An alternative mechanism, class ARGUMENTS, is available. Once again, this is a class from which you should inherit if you need its facilities. It has just two exported features:
|
||||
* <eiffel>argument_count</eiffel>, a non-negative integer, is the number of command arguments.
|
||||
* <eiffel>argument</eiffel> <code>(</code>i<code>)</code>, a string, is the i-th command argument. Here <code> i </code> must be between 0 and <eiffel>argument_count</eiffel>; the convention is that for <code> i </code> <code> = 0 </code> the result is the name of the command itself.
|
||||
* <eiffel>argument</eiffel> <code>(</code>i<code>)</code>, a string, is the i-th command argument. Here <code>i</code> must be between 0 and <eiffel>argument_count</eiffel>; the convention is that for <code>i </code> <code>= 0</code> the result is the name of the command itself.
|
||||
|
||||
|
||||
Reference in New Issue
Block a user