mirror of
https://github.com/EiffelSoftware/eiffel-org.git
synced 2025-12-07 07:12:25 +01:00
Added release 19.05.
git-svn-id: https://svn.eiffel.com/eiffel-org/trunk@2152 abb3cda0-5349-4a8f-a601-0c33ac3a8c38
This commit is contained in:
@@ -0,0 +1,7 @@
|
||||
[[Property:title|EiffelBase Class Reference]]
|
||||
[[Property:weight|2]]
|
||||
[[Property:uuid|9308c4f5-43a5-c07f-2f66-a4597c4ee4ea]]
|
||||
==View the [[ref:libraries/base/reference/index|EiffelBase Class Reference]]==
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,103 @@
|
||||
[[Property:title|EiffelBase Sample: Calculator]]
|
||||
[[Property:weight|0]]
|
||||
[[Property:uuid|12c3409d-165b-b1df-e26a-b05d49969661]]
|
||||
This sample consists of a command line reverse Polish notation (RPN) calculator.
|
||||
|
||||
{{note|A RPN calculator works slightly differently from standard calculators. It consists of a stack of numbers. Operations are applied to the two numbers on top of the stack. The result is then put on top of the stack so that it can be used in the next operation. This sample refers to the top of the stack as ''Accumulator''.This sample consists of a command line reverse Polish notation (RPN) calculator. }}
|
||||
|
||||
==Compiling==
|
||||
|
||||
To compile the example:
|
||||
# Launch EiffelStudio.
|
||||
# Click '''Add project'''
|
||||
# Browse to ''$ISE_EIFFEL\examples\base\calculator\''
|
||||
# Choose ''calculator.ecf''
|
||||
# Choose the location where the project will be compiled, by default the same directory containing the configuration file.
|
||||
# Click '''Open'''.
|
||||
|
||||
==Running==
|
||||
|
||||
After you launch the sample, the following text appears in a console:
|
||||
<code>
|
||||
*********************************
|
||||
Calculator in reverse Polish form
|
||||
*********************************
|
||||
|
||||
Allowable operations are:
|
||||
'/': Divide top two numbers on the stack.
|
||||
'0': Empty the stack.
|
||||
'a': Enter operand onto stack.
|
||||
'?': Help.
|
||||
'*': Multiply top two numbers on the stack.
|
||||
'+': Add top two numbers on the stack
|
||||
'q': Quit.
|
||||
'-': Subtract top two numbers on the stack.
|
||||
Enter a number, followed by :
|
||||
</code>
|
||||
|
||||
Enter the first number to be put onto the stack, for example <code>3</code>.
|
||||
|
||||
{{caution|Failing to enter a number at this stage will cause the sample to stop. This sample was designed to showcase the use of EiffelBase data structures and is not protected against unexpected entries. }}
|
||||
|
||||
You may then add another number on the stack by entering the character <code>a</code>:
|
||||
<code>
|
||||
*********************************
|
||||
Calculator in reverse Polish form
|
||||
*********************************
|
||||
Allowable operations are:
|
||||
'/': Divide top two numbers on the stack.
|
||||
'0': Empty the stack.
|
||||
'a': Enter operand onto stack.
|
||||
'?': Help.
|
||||
'*': Multiply top two numbers on the stack.
|
||||
'+': Add top two numbers on the stack
|
||||
'q': Quit.
|
||||
'-': Subtract top two numbers on the stack.
|
||||
Enter a number, followed by : 3
|
||||
|
||||
Accumulator = 3
|
||||
|
||||
Next operation? a
|
||||
Enter a number, followed by :
|
||||
</code>
|
||||
|
||||
Enter a second number, for example <code>2</code>. You can then apply any operation to the two operands such as minus:
|
||||
<code>
|
||||
...
|
||||
Next operation? a
|
||||
Enter a number, followed by : 2
|
||||
|
||||
Accumulator = 2
|
||||
|
||||
Next operation? -
|
||||
|
||||
Accumulator = 1
|
||||
|
||||
Next operation?
|
||||
</code>
|
||||
|
||||
You may use the operation <code>0</code> to clear the stack at any time. You may use <code>q</code> to quit the program.
|
||||
|
||||
{{tip|You can use the command <code>?</code> to display the list of available operations. }}
|
||||
|
||||
==Under the Hood==
|
||||
|
||||
This sample shows how to leverage EiffelBase data structures in a simple Eiffel system. The root class CALCULATOR first instantiates all ''state'' objects, each corresponding to one possible operation. The state classes all inherit from STATE. They are:
|
||||
* PLUS: Add stack's two top numbers.
|
||||
* MINUS: Substract stack's two top numbers.
|
||||
* MULTIPLY: Multiply stack's two top numbers.
|
||||
* DIVIDE: Divide stack's two top numbers.
|
||||
* EMPTY: Empty stack.
|
||||
* HELP: Prints available commands.
|
||||
* QUESTION: Get number from user.
|
||||
* QUIT: Close application.
|
||||
Each of these classes implement the feature do_one_state from STATE which performs the operation associated with the state. The initial state is QUESTION which asks for the initial number to put in the ''accumulator''. Following states depend on the user input. <br/>
|
||||
Every descendant of STATE implement the feature operation which performs the corresponding stack transformation.
|
||||
|
||||
See the reference for the class interfaces.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
[[Property:title|EiffelBase Samples]]
|
||||
[[Property:weight|3]]
|
||||
[[Property:uuid|5095bbdf-0dd6-7d7b-68d4-59a8293950ee]]
|
||||
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
[[Property:title|EiffelBase, Abstract Container Structures: The Taxonomy]]
|
||||
[[Property:weight|0]]
|
||||
[[Property:uuid|15ec3e87-45c6-ab53-ddad-d1348a9d8d75]]
|
||||
A container data structure (or container in the sequel) is an object which serves to store and access collections of objects, called the '''items''' of the container. All classes describing containers are descendants of the deferred class [[ref:libraries/base/reference/container_chart|CONTAINER]] .
|
||||
|
||||
A container can be studied from three viewpoints: access, storage and traversal.
|
||||
* The '''access''' criterion affects how the clients of a container can access its items. For example, in a stack or queue, only one item is accessible at any given time, and clients do not choose that item; in contrast, clients of an array or hash table must provide an index, or more generally a key, to access an item.
|
||||
* The '''storage''' criterion affects how the items are put together. For example some containers are finite, others potentially infinite; among finite structures, some are bounded, others unbounded.
|
||||
* The '''traversal''' criterion affects how, if in any way, the items of a container can be traversed. A traversal is a mechanism which makes it possible to examine each item of a container once, in a clearly specified order. For example some containers can be traversed sequentially, in one direction or two; tree structures lend themselves to preorder, postorder and breadth-first traversals.
|
||||
|
||||
For each of these criteria the Base library offers a single-inheritance hierarchy of deferred classes. The top of the access hierarchy is class [[ref:libraries/base/reference/collection_chart|COLLECTION]] ; the top of the storage hierarchy is class [[ref:libraries/base/reference/box_chart|BOX]] ; the top of the traversal hierarchy is class [[ref:libraries/base/reference/traversable_chart|TRAVERSABLE]] . These three classes are heirs of the most general class [[ref:libraries/base/reference/container_chart|CONTAINER]] .
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,385 @@
|
||||
[[Property:title|EiffelBase Data Structures, Lists]]
|
||||
[[Property:weight|1]]
|
||||
[[Property:uuid|23f540e0-16d5-807c-30af-74d3416a5709]]
|
||||
Many applications need sequential structures, also called linear structures, in particular lists and circular chains. Apart from three classes describing individual list cells, all the classes involved are descendants of class [[ref:libraries/base/reference/linear_chart|LINEAR]] , one of the deferred classes describing general traversal properties and introduced in the chapter that described the general data structure taxonomy. More precisely, all but one of the classes of interest for the present discussion are descendants, direct or indirect, from a class called [[ref:libraries/base/reference/chain_chart|CHAIN]] which describes general sequential structures possessing a cursor as well as insertion properties. The exception is class [[ref:libraries/base/reference/countable_sequence_chart|COUNTABLE_SEQUENCE]] , which describes infinite structures; all the others describe finite structures. <br/>
|
||||
[[ref:libraries/base/reference/chain_chart|CHAIN]] is an heir of [[ref:libraries/base/reference/sequence_chart|SEQUENCE]] , which describes a more general notion of sequence. [[ref:libraries/base/reference/sequence_chart|SEQUENCE]] is a descendant of [[ref:libraries/base/reference/linear_chart|LINEAR]] . There are two main categories of sequential structures: some, called circular chains, are cyclic; others, called lists, are not. Another distinction exists between dynamic structures, which may be extended at will, and fixed ones, which have a bounded capacity. <br/>
|
||||
In all of the structures under review you may insert two or more occurrences of a given item in such a way that the occurrences are distinguishable. In other words, the structures are bags rather than just sets, although it is possible to use them to implement sets.
|
||||
|
||||
=Higher Level Traversal Classes=
|
||||
|
||||
The list and chain classes are characterized, for their traversal properties, as being linear and, more precisely, bilinear. In the traversal hierarchy, the relevant deferred classes are [[ref:libraries/base/reference/linear_chart|LINEAR]] and [[ref:libraries/base/reference/bilinear_chart|BILINEAR]] , introduced in the [[EiffelBase, Abstract Container Structures: The Taxonomy|discussion]] of the general taxonomy.
|
||||
|
||||
==Linear structures==
|
||||
|
||||
[[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>
|
||||
|
||||
* <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>
|
||||
|
||||
* <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>
|
||||
|
||||
|
||||
[[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.
|
||||
|
||||
==Bilinear structures==
|
||||
|
||||
[[ref:libraries/base/reference/bilinear_chart|BILINEAR]] describes linear structures which may be traversed both ways. It inherits from [[ref:libraries/base/reference/linear_chart|LINEAR]] and extends it with two new features which ensure complete symmetry between the two directions of movement:
|
||||
* <eiffel>before</eiffel>, a boolean-valued query which determines whether you have moved to the left of the first position (a more precise specification is given below).
|
||||
* <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]]
|
||||
|
||||
|
||||
==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>
|
||||
|
||||
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>
|
||||
|
||||
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>
|
||||
|
||||
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>
|
||||
|
||||
and adds the invariant clause
|
||||
<code>
|
||||
before_constraint: before implies off</code>
|
||||
|
||||
The new implementation of <eiffel>off</eiffel>
|
||||
<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>
|
||||
|
||||
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>
|
||||
|
||||
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>
|
||||
|
||||
==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>
|
||||
|
||||
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>.
|
||||
|
||||
==A precise view of after and before==
|
||||
|
||||
Getting the specification of <eiffel>after</eiffel> and before <eiffel>right</eiffel>, so that it will handle all cases properly, requires some care. <br/>
|
||||
For every one of the structures under discussion there is a notion of current position, which we may call the cursor position even though for the moment the cursor is a virtual notion only. (Actual cursor objects will come later when we combine [[ref:libraries/base/reference/linear_chart|LINEAR]] , [[ref:libraries/base/reference/bilinear_chart|BILINEAR]] and other classes from the traversal hierarchy with [[ref:libraries/base/reference/cursor_structure_chart|CURSOR_STRUCTURE]] and other classes from the collection hierarchy.) The informal definition is that <eiffel>after</eiffel> is true if and only if the cursor - in this informal sense of a fictitious marker signaling the current position - is one position after the last item, if any, and that <eiffel>before</eiffel> is true if and only if the cursor is one position before the first item. When the cursor is on any of the items, <eiffel>after</eiffel> and <eiffel>before</eiffel> are false; <eiffel>after</eiffel> holds when the cursor is to the right of the last item, and <eiffel>before</eiffel> when it is to the left of the first item. This leaves open the question of what conventions to take for an empty structure. If iteration schemes of the above type are to work, then <eiffel>after</eiffel> must be true for an empty structure. For a bilinear structure, however, we should have total symmetry between the two pairs of features
|
||||
* <eiffel>start</eiffel>, <eiffel>forth</eiffel>, <eiffel>after</eiffel>.
|
||||
* <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>
|
||||
|
||||
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>
|
||||
|
||||
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>
|
||||
|
||||
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>
|
||||
|
||||
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.
|
||||
|
||||
==Some lessons==
|
||||
|
||||
This discussion has illustrated some of the important patterns of reasoning that are frequently involved in serious object-oriented design. Among the lessons are four ideas which you may find useful in many different cases. First, consistency is once again the central principle. Throughout the design of a class library we must constantly ask ourselves:
|
||||
* How do I make my next design decision compatible with the previous ones?
|
||||
* How do I take my next design decision so that it will be easy - or at least possible - to make future ones compatible with it?
|
||||
|
||||
Another frequent concern, partly a consequence of consistency, is symmetry. To mathematicians and physicists, symmetry considerations are often important in guiding the search for a solution to a problem; if the problem exhibits a certain symmetry, a candidate solution will be rejected if it does not satisfy that symmetry. Such was the situation here: since the structure's specification is symmetric with respect to the two possible directions of traversal, so too should the feature design be. <br/>
|
||||
The third lesson is also well-known in mathematics and physics: the usefulness of looking at limit cases. To check that a design is sound it is often useful to examine what becomes of it when it is applied to extreme situations - in particular, as was done in this example, empty structures. <br/>
|
||||
Finally, the only way to make delicate design decisions is to express the issues clearly through assertions, most notably invariants. To analyze the properties under discussion, and weigh the various alternatives, we need the precision of mathematical logic. Once again note that without assertions it would be impossible to build a good library; we would have no way to know precisely what we are talking about.
|
||||
|
||||
=Sequences And Chains=
|
||||
|
||||
Still deferred, classes [[ref:libraries/base/reference/sequence_chart|SEQUENCE]] and[[ref:libraries/base/reference/chain_chart|CHAIN]] provide the basis for all list and chain classes, as well as for many trees and for dispensers. <br/>
|
||||
SEQUENCE is constructed with the full extent of the technique described in the discussion of the taxonomy: using multiple inheritance to combine one class each from the access, traversal and storage hierarchy. [[ref:libraries/base/reference/sequence_chart|SEQUENCE]] indeed has three parents:
|
||||
* [[ref:libraries/base/reference/active_chart|ACTIVE]] gives the access properties. A sequence is an active structure with a notion of current item. Remember that active structures are a special case of bags.
|
||||
* [[ref:libraries/base/reference/bilinear_chart|BILINEAR]] , as studied above, indicates that a sequence may be traversed both ways.
|
||||
* 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 )</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.
|
||||
|
||||
Other procedures work on the 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>.
|
||||
|
||||
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==
|
||||
|
||||
Chains are sequences with a few more properties: items may be accessed through their indices, and it is possible to define cursor objects attached to individual items. <br/>
|
||||
Class [[ref:libraries/base/reference/chain_chart|CHAIN]] is an heir of [[ref:libraries/base/reference/sequence_chart|SEQUENCE]] . It gets its access properties from [[ref:libraries/base/reference/cursor_structure_chart|CURSOR_STRUCTURE]] (which adds the notion of cursor to the features of [[ref:libraries/base/reference/active_chart|ACTIVE]] , already present in [[ref:libraries/base/reference/sequence_chart|SEQUENCE]] ) and is also an heir of [[ref:libraries/base/reference/indexable_chart|INDEXABLE]] . This ancestry implies in particular the presence of the following features:
|
||||
* <eiffel>cursor</eiffel>, from [[ref:libraries/base/reference/cursor_structure_chart|CURSOR_STRUCTURE]] , which makes it possible to keep a reference to an item of the structure.
|
||||
* <eiffel>i_th</eiffel> and <eiffel>put_i_th</eiffel> from [[ref:libraries/base/reference/table_chart|TABLE]] , via [[ref:libraries/base/reference/indexable_chart|INDEXABLE]] , which make it possible to access and replace the value of an item given by its integer index.
|
||||
|
||||
These features were called <eiffel>item</eiffel> and <eiffel>put</eiffel> in [[ref:libraries/base/reference/table_chart|TABLE]] , but are renamed here to remove the conflict with homonymous features from [[ref:libraries/base/reference/sequence_chart|SEQUENCE]] . <br/>
|
||||
Procedure <eiffel>put</eiffel> for chains is the version obtained from [[ref:libraries/base/reference/cursor_structure_chart|CURSOR_STRUCTURE]] , which has the same effect as <eiffel>replace</eiffel> - replacing the value of the item at cursor position. The <eiffel>put</eiffel> procedure from [[ref:libraries/base/reference/sequence_chart|SEQUENCE]] is renamed <eiffel>sequence_ put</eiffel>. This feature is not exported by [[ref:libraries/base/reference/chain_chart|CHAIN]] , however, since its effect (adding an item at the end) may be obtained through the simpler name <eiffel>extend</eiffel>.
|
||||
|
||||
==Dynamic chains==
|
||||
|
||||
By default, chains can only be extended at the end, through <eiffel>extend</eiffel> and <eiffel>sequence_put</eiffel>. Of particular interest are those chains where clients can insert and remove items at any position. Such chains are said to be dynamic, and described by [[ref:libraries/base/reference/chain_chart|CHAIN]] 's heir [[ref:libraries/base/reference/dynamic_chain_chart|DYNAMIC_CHAIN]] . The new features are predictable:
|
||||
* 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>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.
|
||||
|
||||
=Lists And Circular Structures=
|
||||
|
||||
A chain is a finite sequential structure. This property means that items are arranged in a linear order and may be traversed from the first to the last. To do this you may use a loop of the form shown above, based on procedures <eiffel>start</eiffel> and <eiffel>forth</eiffel>. <br/>
|
||||
This property leaves room for several variants. In particular chains may be straight or circular.
|
||||
* A straight chain, which from now on will be called a list, has a beginning and an end.
|
||||
* A circular chain, as represented by class [[ref:libraries/base/reference/circular_chart|CIRCULAR]] and its descendants, has a much more flexible notion of first item. It is organized so that every item has a successor.
|
||||
|
||||
This representation is conceptual only; in fact the implementations of circular chains found in the Base libraries are based on lists, implemented in one of the ways described below (in particular linked and arrayed). <br/>
|
||||
The major originality of circular chains is that unless the structure is empty procedure <eiffel>forth</eiffel> is always applicable: it will cycle past the last item, coming back to the <br/>
|
||||
first. The symmetric property applies to <eiffel>back</eiffel>. The cyclic nature of <eiffel>forth</eiffel> and <eiffel>back</eiffel> for circular chains is expressed precisely by the assertions. The version of <eiffel>forth</eiffel> for class [[ref:libraries/base/reference/chain_chart|CHAIN]] , which comes from [[ref:libraries/base/reference/linear_chart|LINEAR]] , has precondition
|
||||
<code>not after</code>
|
||||
|
||||
Similarly, the precondition for back is
|
||||
<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>
|
||||
|
||||
For a non-empty circular chain, then, you can circle forever around the items, using forth or back.
|
||||
|
||||
==Choosing the first item==
|
||||
|
||||
For a list, the first and last items are fixed, and correspond to specific places in the physical representation. <br/>
|
||||
A circular chain also needs a notion of first item, if only to enable a client to initiate a traversal through procedure start. Similarly, there is a last item - the one just before the first in a cyclic traversal. (If the chain has just one item, it is both first and last.) <br/>
|
||||
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>
|
||||
|
||||
==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>
|
||||
|
||||
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>
|
||||
|
||||
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.
|
||||
|
||||
==Dynamic structures==
|
||||
|
||||
For both lists and circular chains, the most flexible variants, said to be dynamic, allow insertions and deletions at any position. <br/>
|
||||
The corresponding classes are descendants of [[ref:libraries/base/reference/dynamic_list_chart|DYNAMIC_LIST]] and [[ref:libraries/base/reference/dynamic_circular_chart|DYNAMIC_CIRCULAR]] , themselves heirs of [[ref:libraries/base/reference/dynamic_chain_chart|DYNAMIC_CHAIN]] studied above. <br/>
|
||||
|
||||
|
||||
==Infinite sequences==
|
||||
|
||||
Class [[ref:libraries/base/reference/countable_sequence_chart|COUNTABLE_SEQUENCES]] , built by inheritance from [[ref:libraries/base/reference/countable_chart|COUNTABLE]] , [[ref:libraries/base/reference/linear_chart|LINEAR]] and [[ref:libraries/base/reference/active_chart|ACTIVE]] , is similar to [[ref:libraries/base/reference/sequence_chart|SEQUENCE]] but describes infinite rather than finite sequences.
|
||||
|
||||
=Implementations=
|
||||
|
||||
We have by now seen the concepts underlying the linear structures of the Base libraries, especially lists and circular chains. Let us look at the techniques used to implement them.
|
||||
|
||||
==Linked and arrayed implementations==
|
||||
|
||||
Most of the implementations belong to one of four general categories, better described <br/>
|
||||
as two categories with two subcategories each:
|
||||
* Linked implementations, which may be one-way or two-way.
|
||||
* Arrayed implementations, which may be resizable or fixed.
|
||||
|
||||
A linked implementation uses linked cells, each containing an item and a reference to the next cell. One-way structures are described by classes whose names begin with LINKED_, for example [[ref:libraries/base/reference/linked_list_chart|LINKED_LIST]] . Two-way structures use cells which, in addition to the reference to the next cell, also include a reference to the previous one. Their names begin with TWO_WAY_. <br/>
|
||||
An arrayed implementation uses an array to represent a linear structure. If the array is resizable, the corresponding class name begins with ARRAYED_, for example <br/>
|
||||
[[ref:libraries/base/reference/arrayed_list_chart|ARRAYED_LIST]] ; if not, the prefix is FIXED_.
|
||||
|
||||
==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]]
|
||||
|
||||
|
||||
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>
|
||||
|
||||
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]]
|
||||
|
||||
|
||||
==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.
|
||||
|
||||
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.
|
||||
|
||||
{{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>
|
||||
|
||||
using the <eiffel>item</eiffel> feature of [[ref:libraries/base/reference/linkable_chart|LINKABLE]] , applied to <eiffel>active</eiffel>.
|
||||
|
||||
==One-way and two-way linked chains==
|
||||
|
||||
If you look at the interfaces of one-way and two-way linked structures, you will notice that they are almost identical. This is because it is possible to implement features such as <eiffel>back</eiffel> for one-way structures such as described by [[ref:libraries/base/reference/linked_list_chart|LINKED_LIST]] and [[ref:libraries/base/reference/linked_circular_chart|LINKED_CIRCULAR]] . A simple implementation of <eiffel>back</eiffel> stores away a reference to the current active item, executes <eiffel>start</eiffel>, and then performs <eiffel>forth</eiffel> until the item to the right of the cursor position is the previous <eiffel>active</eiffel>. <br/>
|
||||
Although correct, such an implementation is of course rather inefficient since it requires a traversal of the list. In terms of algorithmic complexity, it is in O (<eiffel>count</eiffel>), meaning that its execution time is on the average proportional to the number of items in the structure. In contrast, <eiffel>forth</eiffel> is O (1), that is to say, takes an execution time bounded by a constant.
|
||||
|
||||
{{caution|As a consequence, you should not use one-way linked structures if you need to execute more than occasional <eiffel>back</eiffel> operations (and other operations requiring access to previous items, such as <eiffel>remove_left</eiffel>). }}
|
||||
|
||||
Two-way linked structures, such as those described by [[ref:libraries/base/reference/two_way_list_chart|TWO_WAY_LIST]] and [[ref:libraries/base/reference/two_way_circular_chart|TWO_WAY_CIRCULAR]] , treat the two directions symmetrically, so that <eiffel>back</eiffel> will be just as efficient as <eiffel>forth</eiffel>. Hence the following important advice: If you need to traverse a linked structure both ways, not just left to right, use the <eiffel>TWO_WAY_</eiffel> classes, not the <eiffel>LINKED_</eiffel> versions. The <eiffel>TWO_WAY_</eiffel> structures will take up more space, since they use [[ref:libraries/base/reference/bi_linkable_chart|BI_LINKABLE]] rather than [[ref:libraries/base/reference/linkable_chart|LINKABLE]] cells, but for most applications this space penalty is justified by the considerable gains in time that will result if right-to-left operations are frequently needed.
|
||||
|
||||
==Arrayed chains==
|
||||
|
||||
Arrayed structures as described by [[ref:libraries/base/reference/arrayed_list_chart|ARRAYED_LIST]] , [[ref:libraries/base/reference/fixed_list_chart|FIXED_LIST]] and [[ref:libraries/base/reference/arrayed_circular_chart|ARRAYED_CIRCULAR]] use arrays for their implementations. A list or circular chain of <eiffel>count</eiffel> items may be stored in positions 1 to <eiffel>count</eiffel> of an array of <eiffel>capacity</eiffel> items, where <eiffel>capacity</eiffel> >= <eiffel>count</eiffel>. <br/>
|
||||
An instance of [[ref:libraries/base/reference/fixed_list_chart|FIXED_LIST]] , as the name suggests, has a fixed number of items. In particular:
|
||||
* Query extendible has value false for [[ref:libraries/base/reference/fixed_list_chart|FIXED_LIST]] : you may replace existing items, but not add any, even at the end. A [[ref:libraries/base/reference/fixed_list_chart|FIXED_LIST]] is created with a certain number of items and retains that number.
|
||||
* As a result, [[ref:libraries/base/reference/fixed_list_chart|FIXED_LIST]] joins the deferred feature count of [[ref:libraries/base/reference/list_chart|LIST]] with the feature count of ARRAY, which satisfies the property <eiffel>count</eiffel> = <eiffel>capacity</eiffel>.
|
||||
* Query <eiffel>prunable </eiffel>has value false too: it is not possible to remove an item from a fixed list.
|
||||
|
||||
In contrast, [[ref:libraries/base/reference/arrayed_list_chart|ARRAYED_LIST]] has almost the same interface as [[ref:libraries/base/reference/linked_list_chart|LINKED_LIST]] . In particular, it is possible to add items at the end using procedure <eiffel>extend</eiffel>; if the call causes the list to grow beyond the current array's capacity, it will trigger a resizing. This is achieved by using the procedure <eiffel>force</eiffel> of class ARRAY to implement <eiffel>extend</eiffel>. [[ref:libraries/base/reference/arrayed_list_chart|ARRAYED_LIST]] even has the insertion procedures (<eiffel>put_front</eiffel>, <eiffel>put_left</eiffel>, <eiffel>put_right</eiffel>) and removal procedures (<eiffel>prune</eiffel>, <eiffel>remove</eiffel>, <eiffel>remove_left</eiffel>, <eiffel>remove_right</eiffel>) that apply to arbitrary positions and appear in the linked implementations. These procedures, however, are rather inefficient, since they usually require moving a whole set of array items, an O (count) operation. (Procedure <eiffel>extend</eiffel> does not suffer from this problem, since it is easy to add an item to the end of an array, especially if there is still room so that no resizing is necessary.)
|
||||
|
||||
{{caution|The situation of these features in [[ref:libraries/base/reference/arrayed_list_chart|ARRAYED_LIST]] is similar to the situation of <eiffel>back</eiffel> in classes describing one-way linked structures: it is convenient to include them because they may be needed once in a while and an implementation exists; but using them more than occasionally may result in serious inefficiencies. If you do need to perform arbitrary insertions and removal, use linked structures, not arrayed ones. }}
|
||||
|
||||
Arrayed structures, however, use up less space than linked representations. So they are appropriate for chains on which, except possibly for insertions at the end, few insertion and removal operations or none at all are expected after creation. [[ref:libraries/base/reference/fixed_list_chart|FIXED_LIST]] offers few advantages over [[ref:libraries/base/reference/arrayed_list_chart|ARRAYED_LIST]] . [[ref:libraries/base/reference/fixed_list_chart|FIXED_LIST]] may be useful, however, for cases in which the fixed number of items is part of the specification, and any attempt to add more items must be treated as an error. For circular chains only one variant is available, [[ref:libraries/base/reference/arrayed_circular_chart|ARRAYED_CIRCULAR]] , although writing a <eiffel>FIXED_</eiffel> version would be a simple exercise.
|
||||
|
||||
==Multi-arrayed lists==
|
||||
|
||||
For lists one more variant is available, combining some of the advantages of arrayed and linked implementations: [[ref:libraries/base/reference/multi_array_list_chart|MULTI_ARRAY_LIST]] . With this implementation a list is <br/>
|
||||
divided into a number of blocks. Each block is an array, but the successive arrays are linked.
|
||||
|
||||
=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>
|
||||
|
||||
As indicated by the constrained generic parameter it describes bilinear structures whose items may be compared by a total order relation.
|
||||
|
||||
{{caution|The class name COMPARABLE_STRUCT, chosen for brevity's sake, is slightly misleading: it is not the structures that are comparable but their items. }}
|
||||
|
||||
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>
|
||||
|
||||
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]] .
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
[[Property:modification_date|Fri, 14 Sep 2018 22:39:40 GMT]]
|
||||
[[Property:publication_date|Fri, 14 Sep 2018 22:39:40 GMT]]
|
||||
[[Property:title|EiffelBase, Dispensers]]
|
||||
[[Property:weight|2]]
|
||||
[[Property:uuid|4f65d62b-940b-c3c2-557e-d709a2a1bcaf]]
|
||||
A dispenser is called that way because of the image of a vending machine (a dispenser) of a rather primitive nature, in which there is only one button. If you press the button and the dispenser is not empty, you get one of its items in the exit tray at the bottom, but you do not choose that item: the machine does. There is also an input slot at the top, into which you may deposit new items; but you have no control over the order in which successive button press operations will retrieve these items.
|
||||
|
||||
The deferred class [[ref:libraries/base/reference/dispenser_chart|DISPENSER]] provides the facilities which will be shared by all specialized classes. In fact, the interface of all dispenser classes is nearly identical, with the exception of a few extra possibilities offered by priority queues. Many kinds of dispenser are possible, each defined by the relation that the machine defines between the order in which items are inserted and the order in which they are returned. The Base libraries support three important categories - stacks, queues, and priority queues:
|
||||
* A stack is a dispenser with a last-in, first-out (LIFO) internal policy: items come out in the reverse order of their insertion. Each button press returns the last deposited item.
|
||||
* A queue is a dispenser with a first-in, first-out (FIFO) internal policy: items come out in the order of their insertion. Each button press returns the oldest item deposited and not yet removed.
|
||||
* In a priority queue, items have an associated notion of order; the element that comes out at any given time is the largest of those which are in the dispenser.
|
||||
|
||||
==Stacks==
|
||||
|
||||
Stacks - dispensers with a LIFO retrieval policy - are a ubiquitous structure in software development. Their most famous application is to parsing (syntactic analysis), but many other types of systems use one or more stacks. Class STACK describes general stacks, without commitment to a representation. This is a deferred class which may not be directly instantiated. The fundamental operations are put (add an element to the top of the stack), item (retrieve the element from the top, non-destructively), remove (remove the element from the top), is_empty (test for an empty stack). <br/>
|
||||
Three effective heirs are provided:
|
||||
* [[ref:libraries/base/reference/linked_stack_chart|LINKED_STACK]] : stacks implemented as linked lists, with no limit on the number of items (<eiffel>count</eiffel>).
|
||||
* [[ref:libraries/base/reference/bounded_stack_chart|BOUNDED_STACK]] : stacks implemented as arrays. For such stacks, the maximum number of items (<eiffel>capacity</eiffel>) is set at creation time.
|
||||
* [[ref:libraries/base/reference/arrayed_stack_chart|ARRAYED_STACK]] : also implemented as arrays, but in this case there is no limit on the number of items; the interface is the same as [[ref:libraries/base/reference/linked_stack_chart|LINKED_STACK]] except for the creation procedure. If the number of elements exceeds the initially allocated capacity, the array will simply be resized.
|
||||
|
||||
==Queues==
|
||||
|
||||
Class [[ref:libraries/base/reference/queue_chart|QUEUE]] describes general queues, without commitment to a representation. This is a deferred class which may not be directly instantiated. Three non-deferred heirs are also provided, distinguished by the same properties as their stack counterparts:
|
||||
* [[ref:libraries/base/reference/linked_queue_chart|LINKED_QUEUE]]
|
||||
* [[ref:libraries/base/reference/bounded_queue_chart|BOUNDED_QUEUE]]
|
||||
* [[ref:libraries/base/reference/arrayed_queue_chart|ARRAYED_QUEUE]]
|
||||
|
||||
==Priority Queues==
|
||||
|
||||
In a priority queue, each item has an associated priority value, and there is an order relation on these values. The item returned by item or removed by remove is the element with the highest priority.The most general class is [[ref:libraries/base/reference/priority_queue_chart|PRIORITY_QUEUE]] , which is deferred. Two effective variants are provided:
|
||||
* [[ref:libraries/base/reference/linked_priority_queue_chart|LINKED_PRIORITY_QUEUE]] , a linked list implementation.
|
||||
* [[ref:libraries/base/reference/heap_priority_queue_chart|HEAP_PRIORITY_QUEUE]] which is more efficient and is to be preferred in most cases. A heap is organized like a binary tree, although physically stored in an array; elements with a high priority percolate towards the root.
|
||||
|
||||
Because it must be possible to compare priorities, the type of the items must conform to [[ref:libraries/base/reference/part_comparable_chart|PART_COMPARABLE]] . Constrained genericity ensures this; all the priority queue classes have a formal generic parameter constrained by [[ref:libraries/base/reference/part_comparable_chart|PART_COMPARABLE]] .
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,535 @@
|
||||
[[Property:modification_date|Wed, 12 Sep 2018 17:55:43 GMT]]
|
||||
[[Property:publication_date|Wed, 12 Sep 2018 13:40:39 GMT]]
|
||||
[[Property:title|EiffelBase, Iteration]]
|
||||
[[Property:weight|6]]
|
||||
[[Property:uuid|9c0313bf-571d-0c8d-5c49-8bd99f86bed5]]
|
||||
The classes of the Iteration cluster encapsulate control structures representing common traversal operations.
|
||||
|
||||
=Iterators and Agents=
|
||||
|
||||
Eiffel's agent mechanism offers an attractive alternative to the <eiffel>Iterator</eiffel> cluster of EiffelBase.
|
||||
|
||||
=The Notion of iterator=
|
||||
|
||||
Let us first explore the role of iterators in the architecture of a system.
|
||||
|
||||
==Iterating over data structures==
|
||||
|
||||
Client software that uses data structures of a certain type, for example lists or trees, often needs to traverse a data structure of that type in a predetermined order so as to apply a certain action to all the items of the structure, or to all items that satisfy a certain criterion. Such a systematic traversal is called an iteration. <br/>
|
||||
Cases of iteration can be found in almost any system. Here are a few typical examples:
|
||||
* A text processing system may maintain a list of paragraphs. In response to a user command, such as a request to resize the column width, the system will iterate over the entire list so as to update all paragraphs.
|
||||
* A business system may maintain a list of customers. If the company decides that a special promotion will target all customers satisfying a certain criterion (for example all customers that have bought at least one product over the past six months), the system will iterate over the list, generating a mailing for every list item that satisfies the criterion.
|
||||
* An interactive development environment for a programming language may maintain a syntax tree. In response to a program change, the system will traverse the tree to determine what nodes are affected by the change and update them.
|
||||
|
||||
These examples illustrate the general properties of iteration. An iteration involves a data structure of a known general type and a particular ordering of the structure's items. For some structures, more than one ordering will be available; for example a tree iteration may use preorder, postorder or breadth-first (as defined below). The iteration involves an operation, say <eiffel>item_action</eiffel>, to be applied to the selected items. It may also involve a boolean-valued query, say <eiffel>item_test</eiffel>, applicable to candidate items. Finally, it involves a certain policy, usually based on <eiffel>item_test</eiffel>, as to which items should be subjected to <eiffel>item_action</eiffel>. Typical example policies are:
|
||||
* Apply <eiffel>item_action</eiffel> to all the items in the structure. (In this case item_test is not relevant).
|
||||
* Apply <eiffel>item_action</eiffel> to all items that satisfy <eiffel>item_test</eiffel>.
|
||||
* Apply <eiffel>item_action</eiffel> to all items up to the first one that satisfies <eiffel>item_test</eiffel>.
|
||||
|
||||
The Iteration library provides many more, covering in particular all the standard control structures.
|
||||
|
||||
==Iterations and control structures==
|
||||
|
||||
You can perform iterations without any special iteration classes. For example if customers is declared as
|
||||
<code>customers: LIST [CUSTOMER]</code>
|
||||
|
||||
then a class <eiffel>SPECIAL_PROMOTION</eiffel> of a text processing system may include in one of its routines a loop of the form
|
||||
<code>
|
||||
from
|
||||
customers.start
|
||||
until
|
||||
customers.exhausted
|
||||
loop
|
||||
if recent_purchases.has (customers.item) then
|
||||
target_list.put (customers.item)
|
||||
end
|
||||
customers.forth
|
||||
end</code>
|
||||
|
||||
Such schemes are quite common. But it is precisely because they occur frequently that it is useful to rely on library classes to handle them. One of the principal tasks of object-oriented software development is to identify recurring patterns and build reusable classes that encapsulate them, so that future developers will be able to rely on ready-made solutions. <br/>
|
||||
The classes of the Iteration library address this need. Using them offers two benefits:
|
||||
* You avoid writing loops, in which the definition of sub-components such as exit conditions, variants and invariants is often delicate or error-prone.
|
||||
* You can more easily adapt the resulting features in descendant classes. The rest of this chapter shows how to obtain these benefits.
|
||||
|
||||
=Simple Examples=
|
||||
|
||||
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|LINEAR_ITERATOR]] , the class defining iteration mechanisms on linear (sequential) structures.
|
||||
<code>
|
||||
until_do (action: PROCEDURE [G]; test: FUNCTION [G, BOOLEAN])
|
||||
-- Apply `action' to every item of `target' up to
|
||||
-- but excluding first one satisfying `test'.
|
||||
-- (Apply to full list if no item satisfies `test'.)
|
||||
do
|
||||
start
|
||||
until_continue (action, test)
|
||||
ensure then
|
||||
achieved: not exhausted implies test.item ([target.item])
|
||||
end
|
||||
|
||||
until_continue (action: PROCEDURE [G]; test: FUNCTION [G, BOOLEAN])
|
||||
-- Apply `action' to every item of `target' from current
|
||||
-- position, up to but excluding first one satisfying `test'.
|
||||
require
|
||||
invariant_satisfied: invariant_value
|
||||
do
|
||||
from
|
||||
invariant
|
||||
invariant_value
|
||||
until
|
||||
exhausted or else test.item ([target.item])
|
||||
loop
|
||||
action.call ([item])
|
||||
forth
|
||||
end
|
||||
ensure
|
||||
achieved: exhausted or else test.item ([target.item])
|
||||
invariant_satisfied: invariant_value
|
||||
end
|
||||
</code>
|
||||
|
||||
The precise form of the procedure in the class relies on a call to another procedure, until_continue, and on inherited assertions. Here the routines are shown as they are found in the current implementation of the class [[ref:libraries/base/reference/linear_iterator_chart|LINEAR_ITERATOR]]. <br/>
|
||||
This procedure will traverse the linear structure identified by [[ref:libraries/base/linear_iterator_flatshort.html#f_target|target]] and apply the procedure called action on every item up to but excluding the first one satisfying test. <br/>
|
||||
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. Both routines are used trough the Eiffel's agent mechanism; here is an example of a <eiffel>test</eiffel>
|
||||
function intended to be used with iteration over a data structure whose elements are <code>STRING</code>s.
|
||||
<code>
|
||||
test (a_item: STRING): BOOLEAN
|
||||
-- Test to be applied to a_item
|
||||
do
|
||||
Result := a_item.count > 0
|
||||
end
|
||||
</code>
|
||||
|
||||
This indicates that the value of the boolean function <eiffel>test</eiffel> will be obtained by verifying that <eiffel>a_item</eiffel> is an empty string or not.
|
||||
|
||||
==An example use of iteration==
|
||||
|
||||
Here now is an example illustrating the use of these mechanisms. The result will enable us to resize all the paragraphs of a text up to the first one that has been modified - as we might need to do, in a text processing system, to process an interactive user request. Assume a class <eiffel>TEXT</eiffel> that describes lists of paragraphs with certain additional features. The example will also assume a class <eiffel>PARAGRAPH</eiffel> with a procedure <eiffel>resize</eiffel>, and a boolean-valued attribute <eiffel>modified</eiffel> which indicates whether a paragraph has been modified. Class <eiffel>TEXT</eiffel> inherits from [[ref:libraries/base/reference/linked_list_chart|LINKED_LIST]] and so is a descendant of [[ref:libraries/base/reference/linear_chart|LINEAR]] :
|
||||
<code>
|
||||
class
|
||||
TEXT
|
||||
|
||||
inherit
|
||||
LINKED_LIST [PARAGRAPH]
|
||||
...
|
||||
feature
|
||||
...
|
||||
end</code>
|
||||
|
||||
In a class <eiffel>TEXT_PROCESSOR</eiffel>, you can use an iteration procedure to write a very simple procedure <eiffel>resize_ paragraphs</eiffel> that will resize all paragraphs up to but excluding the first one that has been modified:
|
||||
<code>
|
||||
class
|
||||
TEXT_PROCESSOR
|
||||
|
||||
inherit
|
||||
LINEAR_ITERATOR [PARAGRAPH]
|
||||
|
||||
feature
|
||||
|
||||
resize_paragraphs (t: TEXT)
|
||||
-- Resize all the paragraphs of t up to but excluding
|
||||
-- the first one that has been modified.
|
||||
do
|
||||
set (t)
|
||||
until_do (agent item_action, agent item_test)
|
||||
end
|
||||
|
||||
feature {NONE}
|
||||
|
||||
item_test (p: PARAGRAPH): BOOLEAN
|
||||
-- Has p been modified?
|
||||
do
|
||||
Result := p.modified
|
||||
end
|
||||
|
||||
item_action (p: PARAGRAPH)
|
||||
-- Resize p.
|
||||
do
|
||||
p.resize
|
||||
end
|
||||
...
|
||||
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.)
|
||||
* Then it simply calls <eiffel>until_do</eiffel> as defined above.
|
||||
|
||||
Procedure <eiffel>item_action</eiffel> is defined to describe the operation to be performed on each successive element. Function <eiffel>item_test</eiffel> is defined to describe the exit test. <br/>
|
||||
As presented so far, the mechanism seems to limit every descendant of an iteration class to just one form of iteration. As shown later in this chapter, it is in fact easy to generalize the technique to allow a class to use an arbitrary number of iteration schemes. <br/>
|
||||
What is interesting here is that the definitions of <eiffel>item_test</eiffel> and <eiffel>item_action</eiffel> take care of all the details. There is no need to write any loop or other control structure. We are at the very heart of the object-oriented method, enjoying the ability to encapsulate useful and common software schemes so that client developers will only need to fill in what is specific to their application.
|
||||
|
||||
=Using the Iteration Library=
|
||||
|
||||
Let us now explore the classes of the Iteration library and the different ways of using them.
|
||||
|
||||
==Overview of the classes==
|
||||
|
||||
There are only four Iteration classes, whose simple inheritance structure appeared at the beginning of this chapter.
|
||||
* [[ref:libraries/base/reference/iterator_chart|ITERATOR]] , a deferred class which describes the most general notion.
|
||||
* [[ref:libraries/base/reference/linear_iterator_chart|LINEAR_ITERATOR]] , for iterating over linear structures and chains.
|
||||
* [[ref:libraries/base/reference/two_way_chain_iterator_chart|TWO_WAY_CHAIN_ITERATOR]] , a repeated heir of [[ref:libraries/base/reference/linear_iterator_chart|LINEAR_ITERATOR]] , for iterating in either direction over a bilinear structure.
|
||||
* [[ref:libraries/base/reference/cursor_tree_iterator_chart|CURSOR_TREE_ITERATOR]] , for iterating over trees.
|
||||
|
||||
As you will remember from the [[EiffelBase, Abstract Container Structures: The Taxonomy|presentation]] of the abstract overall taxonomy, the traversal hierarchy describes how data structures can be traversed; its most general class is [[ref:libraries/base/reference/two_way_list_chart|TRAVERSABLE]] . <br/>
|
||||
Each one of the iterator classes is paired with a traversal class (or two in one case):
|
||||
|
||||
|
||||
{| border="1"
|
||||
|-
|
||||
| [[ref:libraries/base/reference/iterator_chart|ITERATOR]]
|
||||
| [[ref:libraries/base/reference/two_way_list_chart|TRAVERSABLE]]
|
||||
|-
|
||||
| [[ref:libraries/base/reference/linear_iterator_chart|LINEAR_ITERATOR]]
|
||||
| [[ref:libraries/base/reference/linear_chart|LINEAR]]
|
||||
|-
|
||||
| [[ref:libraries/base/reference/two_way_chain_iterator_chart|TWO_WAY_CHAIN_ITERATOR]]
|
||||
| [[ref:libraries/base/reference/two_way_list_chart|TWO_WAY_LIST]]
|
||||
|-
|
||||
| [[ref:libraries/base/reference/two_way_chain_iterator_chart|TWO_WAY_CHAIN_ITERATOR]]
|
||||
| [[ref:libraries/base/reference/two_way_list_chart|TWO_WAY_LIST]] , [[ref:libraries/base/reference/two_way_circular_chart|TWO_WAY_CIRCULAR]]
|
||||
|-
|
||||
| [[ref:libraries/base/reference/cursor_tree_iterator_chart|CURSOR_TREE_ITERATOR]]
|
||||
| [[ref:libraries/base/reference/cursor_tree_chart|CURSOR_TREE]]
|
||||
|}
|
||||
|
||||
|
||||
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/>
|
||||
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)
|
||||
-- Make s the new target of iterations.
|
||||
require
|
||||
s /= Void
|
||||
do
|
||||
target := s
|
||||
ensure
|
||||
target = s
|
||||
target /= Void
|
||||
end</code>
|
||||
|
||||
Another feature introduced in [[ref:libraries/base/reference/iterator_chart|ITERATOR]] is the query <eiffel>invariant_value</eiffel>, describing invariant properties that must be ensured at the beginning of any iteration and preserved by every iteration step. As declared in [[ref:libraries/base/reference/iterator_chart|ITERATOR]] this query always returns true, but proper descendants can redefine it to describe more interesting invariant properties. <br/>
|
||||
Finally, [[ref:libraries/base/reference/iterator_chart|ITERATOR]] introduces in deferred form the general iteration routines applicable to all iteration variants. They include two queries corresponding to the quantifiers of first-order predicate calculus:
|
||||
* <eiffel>for_all</eiffel> will return true if all items of the target structure satisfy test.
|
||||
* <eiffel>there_exists</eiffel> will return true if at least one item satisfies test.
|
||||
|
||||
The other routines are commands which will traverse the target structure and apply action to items selected through test:
|
||||
* <eiffel>do_all</eiffel> applies <eiffel>action</eiffel> to all items.
|
||||
* <eiffel>do_if</eiffel>, to those items which satisfy test.
|
||||
* <eiffel>until_do</eiffel>, to all items up to but excluding the first one that satisfies test.
|
||||
* <eiffel>do_until</eiffel>, to all items up to and including the first one that satisfies test.
|
||||
* <eiffel>while_do</eiffel> and <eiffel>do_while</eiffel>, to all items up to the first one that does not satisfy test. (This can also be achieved with <eiffel>until_do</eiffel> or <eiffel>do_until </eiffel> by choosing the opposite test.)
|
||||
|
||||
Some of these features and most of the other iteration features introduced in proper descendants of [[ref:libraries/base/reference/iterator_chart|ITERATOR]] and described next, have either an <eiffel>action</eiffel> argument that must be of type <eiffel>PROCEDURE [G]</eiffel> or an argument <eiffel>test</eiffel> of type <eiffel>FUNCTION [G, BOOLEAN]</eiffel>. Some have both. Information about the target of the iterations comes from feature <eiffel>target</eiffel>, set by procedure <eiffel>set</eiffel>; information about what needs to be done for each item of the target structure comes from the argument <eiffel>action</eiffel> passed to the routines referenced above.
|
||||
|
||||
==Linear and chain iteration==
|
||||
|
||||
[[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]] . [[#An example iterator routine|Here]] for example is the effecting of <code>until_do</code>.<br/>
|
||||
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 position of target off?
|
||||
require
|
||||
traversable_exists: target /= Void
|
||||
do
|
||||
Result := target.off
|
||||
end</code>
|
||||
|
||||
and similarly for the others. <br/>
|
||||
In addition to effecting the general iteration features from [[ref:libraries/base/reference/iterator_chart|ITERATOR]] , class [[ref:libraries/base/reference/linear_iterator_chart|LINEAR_ITERATOR]] introduces iteration features that apply to the specific case of linear structures:
|
||||
* <eiffel>search (test: FUNCTION [G, BOOLEAN]; b: BOOLEAN)</eiffel> moves the iteration to the first position satisfying <code>test</code> if both <code>test</code> and <code>b</code> have the same value (both <eiffel>True</eiffel> or both <eiffel>False</eiffel>).
|
||||
* With a linear structure we can implement an iteration corresponding to the 'for' loop of traditional programming languages, defined by three integers: the starting position, the number of items to be traversed, and the step between consecutive items. This is provided by procedure <eiffel>do_for</eiffel> <code> (action: PROCEDURE [G]; starting , number , step: INTEGER). </code>
|
||||
* Since with a linear target the iterator can advance the cursor step by step, the basic iteration operations are complemented by variants which pick up from the position reached by the last call: <eiffel>continue_until</eiffel>, <eiffel>until_continue</eiffel>, <eiffel>continue_while</eiffel>, <eiffel>while_continue</eiffel>, <eiffel>continue_search</eiffel>, <eiffel>continue_for</eiffel>.
|
||||
|
||||
==Two-way iteration==
|
||||
|
||||
Class [[ref:libraries/base/reference/two_way_chain_iterator_chart|TWO_WAY_CHAIN_ITERATOR]] has all the features of [[ref:libraries/base/reference/linear_iterator_chart|LINEAR_ITERATOR]] , to which it adds features for iterating backward as well as forward. <br/>
|
||||
The class introduces commands <eiffel>finish</eiffel> and <eiffel>back</eiffel>, applying the corresponding operations to the two-way target. It also has a backward variant for every iteration feature. The name of each such variant is the name of the forward feature followed by ''_back'': <eiffel>do_all_back</eiffel>, <eiffel>until_do_back</eiffel> and so on. <br/>
|
||||
An alternative design would have kept just one set of features and added two features: a command reverse to reverse the direction of future iteration operations, and a query backward to find out the direction currently in force. <br/>
|
||||
Contrary to what one might at first imagine, class [[ref:libraries/base/reference/two_way_chain_iterator_chart|TWO_WAY_CHAIN_ITERATOR]] is extremely short and simple; its <code> Feature </code> clause only contains the declarations of two features, <eiffel>finish</eiffel> and <eiffel>back</eiffel>. <br/>
|
||||
The trick is to use repeated inheritance. [[ref:libraries/base/reference/two_way_chain_iterator_chart|TWO_WAY_CHAIN_ITERATOR]] inherits twice from [[ref:libraries/base/reference/linear_iterator_chart|LINEAR_ITERATOR]] ; the first inheritance branch yields the forward iteration features, the second yields those for backward iteration. There is no need for any explicit declaration or redeclaration of iteration features. Here is the entire class text that yields this result:
|
||||
<code>
|
||||
class TWO_WAY_CHAIN_ITERATOR [G] inherit
|
||||
|
||||
LINEAR_ITERATOR [G]
|
||||
redefine
|
||||
target
|
||||
select
|
||||
start,
|
||||
forth,
|
||||
do_all,
|
||||
until_do,
|
||||
do_until,
|
||||
while_do,
|
||||
do_while,
|
||||
do_if,
|
||||
do_for,
|
||||
search,
|
||||
for_all,
|
||||
there_exists,
|
||||
until_continue,
|
||||
continue_until,
|
||||
while_continue,
|
||||
continue_while,
|
||||
continue_for,
|
||||
continue_search
|
||||
end
|
||||
|
||||
LINEAR_ITERATOR [G]
|
||||
rename
|
||||
start as finish,
|
||||
forth as back,
|
||||
do_all as do_all_back,
|
||||
until_do as until_do_back,
|
||||
do_until as do_until_back,
|
||||
do_while as do_while_back,
|
||||
while_do as while_do_back,
|
||||
do_if as do_if_back,
|
||||
do_for as do_for_back,
|
||||
search as search_back,
|
||||
for_all as for_all_back,
|
||||
there_exists as there_exists_back,
|
||||
until_continue as until_continue_back,
|
||||
continue_until as continue_until_back,
|
||||
while_continue as while_continue_back,
|
||||
continue_while as continue_while_back,
|
||||
continue_for as continue_for_back,
|
||||
continue_search as continue_search_back
|
||||
redefine
|
||||
back, finish, target
|
||||
end
|
||||
|
||||
create
|
||||
set
|
||||
|
||||
feature -- Access
|
||||
|
||||
target: CHAIN [G]
|
||||
|
||||
feature -- Cursor movement
|
||||
|
||||
finish
|
||||
-- Move cursor of `target' to last position.
|
||||
do
|
||||
target.finish
|
||||
end
|
||||
|
||||
back
|
||||
-- Move cursor of `target' backward one position.
|
||||
do
|
||||
target.back
|
||||
end
|
||||
|
||||
end
|
||||
</code>
|
||||
|
||||
This class provides a good example of the economy of expression that the full inheritance mechanism affords through the combination of renaming, redefinition, repeated inheritance rules and selection, without sacrificing clarity and maintainability.
|
||||
|
||||
==Tree iteration==
|
||||
|
||||
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.
|
||||
|
||||
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=
|
||||
|
||||
To conclude this discussion, let us now put together the various mechanisms studied so far, to see how authors of client software can use the Iteration library to perform possibly complex iterations on various data structures without ever writing a single loop or test. The basic ideas were sketched above but now we have all the elements for the full view. <br/>
|
||||
An application class may use one of the iteration classes in either of two ways: as a descendant (single or repeated) or as a client. The descendant technique is extremely simple but less versatile.
|
||||
|
||||
==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 <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.
|
||||
|
||||
==Using repeated inheritance==
|
||||
|
||||
One way to obtain several iteration schemes is a simple extension to the single descendant technique. You can use repeated inheritance to provide two or more variants. We have in fact already encountered the technique when studying how to derive [[ref:libraries/base/reference/two_way_chain_iterator_chart|TWO_WAY_CHAIN_ITERATOR]] and [[ref:libraries/base/reference/cursor_tree_iterator_chart|CURSOR_TREE_ITERATOR]] from [[ref:libraries/base/reference/linear_iterator_chart|LINEAR_ITERATOR]] . The general pattern, applied here to just two iteration schemes but easily generalized to more, is straightforward:
|
||||
<code>
|
||||
class
|
||||
DUAL_PROCESSOR
|
||||
|
||||
inherit
|
||||
LINEAR_ITERATOR [SOME_TYPE]
|
||||
rename
|
||||
item_action as action1,
|
||||
item_test as test1,
|
||||
do_if as do_if1,
|
||||
redefine
|
||||
action1, test1
|
||||
select
|
||||
action1, test1
|
||||
end
|
||||
|
||||
LINEAR_ITERATOR [SOME_TYPE]
|
||||
rename
|
||||
item_action as action2,
|
||||
item_test as test2,
|
||||
do_if as do_if2,
|
||||
redefine
|
||||
action2, test2
|
||||
end
|
||||
|
||||
feature
|
||||
|
||||
action1
|
||||
-- Action for the first scheme
|
||||
do
|
||||
...
|
||||
end
|
||||
|
||||
test1: BOOLEAN
|
||||
-- Test for the first scheme
|
||||
do
|
||||
...
|
||||
end
|
||||
|
||||
action2
|
||||
-- Action for the second scheme
|
||||
do
|
||||
...
|
||||
end
|
||||
|
||||
test2: BOOLEAN
|
||||
-- Test for the second scheme
|
||||
do
|
||||
...
|
||||
end
|
||||
|
||||
iterate1
|
||||
-- Execute iteration of first kind.
|
||||
do
|
||||
set (...)
|
||||
do_if1
|
||||
end
|
||||
|
||||
iterate2
|
||||
-- Execute iteration of second kind.
|
||||
do
|
||||
set (...)
|
||||
do_if2
|
||||
end
|
||||
|
||||
...
|
||||
end
|
||||
</code>
|
||||
|
||||
The repeated inheritance machinery takes care of the rest.
|
||||
|
||||
==Using explicit iterator objects==
|
||||
|
||||
To obtain maximum flexibility, classes that need iteration facilities should be clients rather than descendants of the iteration classes. The resulting scheme is completely dynamic: to perform iterations you use iterator objects as discussed earlier. <br/>
|
||||
The following example illustrates the technique. Consider a deferred class <eiffel>FIGURE</eiffel> describing the notion of graphical figure, with many effective descendants ( <eiffel>POLYGON</eiffel>, <eiffel>CIRCLE</eiffel> and so on). It is useful to define <eiffel>COMPLEX_FIGURE</eiffel>, describing figures that are recursively composed of sub-figures. This is a remarkable example of multiple inheritance:
|
||||
<code>
|
||||
class
|
||||
COMPLEX_FIGURE
|
||||
|
||||
inherit
|
||||
FIGURE,
|
||||
LINKED_LIST [FIGURE]
|
||||
|
||||
feature
|
||||
...
|
||||
end
|
||||
</code>
|
||||
|
||||
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
|
||||
-- Recursively display all components of the complex
|
||||
-- figure
|
||||
do
|
||||
from
|
||||
start
|
||||
until
|
||||
exhausted
|
||||
loop
|
||||
item.display
|
||||
forth
|
||||
end
|
||||
end
|
||||
</code>
|
||||
|
||||
Although acceptable and even elegant, this scheme will cause significant duplication: all the <eiffel>FIGURE</eiffel> features - not just <eiffel>display</eiffel> but also <eiffel>hide</eiffel>, <eiffel>rotate</eiffel>, <eiffel>move</eiffel> and others - will have the same structure, with a loop. We can use iterators to avoid this duplication. The repeated inheritance technique would work, but given the large number of <eiffel>FIGURE</eiffel> features the amount of repeated inheritance that would be needed seems unwieldy. It is also not very desirable to have to change the inheritance structure of the system just to add a new feature to <eiffel>FIGURE</eiffel>. The more dynamic approach using iterator objects seems preferable. <br/>
|
||||
To implement this approach, define a class for iterating on complex figures:
|
||||
<code>
|
||||
class
|
||||
COMPLEX_FIGURE_ITERATOR
|
||||
|
||||
inherit
|
||||
LINEAR_ITERATOR
|
||||
redefine
|
||||
target
|
||||
end
|
||||
|
||||
create
|
||||
set
|
||||
|
||||
feature
|
||||
|
||||
target: COMPLEX_FIGURE
|
||||
|
||||
end
|
||||
</code>
|
||||
|
||||
Then for each operation to be iterated define a small class. For example:
|
||||
<code>
|
||||
class
|
||||
FIGURE_DISPLAYER
|
||||
|
||||
inherit
|
||||
COMPLEX_FIGURE_ITERATOR
|
||||
redefine
|
||||
item_action
|
||||
end
|
||||
|
||||
create
|
||||
set
|
||||
|
||||
feature
|
||||
|
||||
item_action (f: FIGURE)
|
||||
-- Action to be applied to each figure: display
|
||||
-- it.
|
||||
do
|
||||
f.display
|
||||
end
|
||||
end
|
||||
</code>
|
||||
|
||||
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
|
||||
-- Recursively display all components of the complex
|
||||
-- figure
|
||||
local
|
||||
disp: FIGURE_DISPLAYER
|
||||
do
|
||||
create disp.set (Current)
|
||||
disp.do_all
|
||||
end
|
||||
</code>
|
||||
|
||||
and similarly for all the others. <br/>
|
||||
Note the use of <eiffel>set</eiffel> as creation procedure, which is more convenient than requiring the clients first to create an iterator object and then to call <eiffel>set</eiffel>. This is also safer, since with <eiffel>set</eiffel> as a creation procedure the client cannot forget to initialize the target. (If a class <eiffel>C</eiffel> has a creation clause, the creation instruction <code>create </code> <eiffel>C</eiffel>.)
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
[[Property:title|EiffelBase, Sets]]
|
||||
[[Property:weight|4]]
|
||||
[[Property:uuid|44d33f46-cfa9-2aca-6b4f-2d9d91723d85]]
|
||||
Sets are containers where successive occurrences of the same item are not distinguished: inserting the same item twice has the same observable effect as inserting it once.
|
||||
|
||||
==Deferred classes==
|
||||
|
||||
The most general class describing sets is [[ref:libraries/base/reference/set_chart|SET]] . The usual operations of set theory such as union and intersection have been relegated to [[ref:libraries/base/reference/subset_chart|SUBSET]] , an heir of [[ref:libraries/base/reference/set_chart|SET]] . This enables a class to inherit from [[ref:libraries/base/reference/set_chart|SET]] without having to effect these operations if it satisfies the basic set property but has no convenient implementation of the subset operations.
|
||||
|
||||
==Sets without a notion of order==
|
||||
|
||||
[[ref:libraries/base/reference/linked_set_chart|LINKED_SET]] provides a basic implementation of [[ref:libraries/base/reference/set_chart|SET]] by linked lists.
|
||||
|
||||
==Sets of comparable elements and sorted sets==
|
||||
|
||||
The deferred class [[ref:libraries/base/reference/comparable_set_chart|COMPARABLE_SET]] , declared as
|
||||
<code>
|
||||
deferred class
|
||||
COMPARABLE_SET [G -> COMPARABLE]
|
||||
|
||||
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 PART_SORTED_SET [G -> PART_COMPARABLE]], which uses a two-way sorted list implementation.
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
[[Property:title|EiffelBase, Tables]]
|
||||
[[Property:weight|5]]
|
||||
[[Property:uuid|194a63a2-e440-18dc-c9d5-6959dbe169fb]]
|
||||
Hash tables are a convenient mechanism tostore and retrieve objects identified by unique keys.
|
||||
==Why use hash tables?==
|
||||
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>
|
||||
|
||||
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:
|
||||
* '''A1'''. For each object there must be an associated integer, which for the purpose of this discussion we may call the object's index (since it will serve as index for the object in the array.)
|
||||
* '''A2'''. No two objects may have the same index.
|
||||
* '''A3'''. If we want to avoid wasting huge amount of storage, all the indices must lie in a contiguous or almost contiguous range.
|
||||
|
||||
Hash tables may be viewed as a rehabilitation mechanism for objects that do not naturally possess these three properties. If we are unable to find a natural index, we can sometimes devise an artificial one. To do so we must be able to find a key. Each key must uniquely identify the corresponding object; this is the same as property '''A2''', making keys similar to indices. But keys are not necessarily integers (violating property '''A1'''), although it must be possible to associate an integer with each key. The mechanism that maps keys to integers is called the hashing function. <br/>
|
||||
Thanks to the hashing mechanism we will indeed be able to store suitable objects into arrays, approaching the optimal efficiency of this data structure. The efficiency will not be quite as good, however, for two reasons:
|
||||
* We must pay the price of computing the hash function whenever we store or retrieve an object.
|
||||
* 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)
|
||||
x := h.item (k)</code>
|
||||
|
||||
where <eiffel>h</eiffel> is a hash-table and <eiffel>k</eiffel> is a key (for example a string) as conceptually equivalent to 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]] .
|
||||
|
||||
==When hash tables are appropriate==
|
||||
|
||||
You may keep objects in a hash table if for each one of these objects you can find a key that uniquely identifies it. The objects and their keys may be of many possible kinds:
|
||||
* '''H1'''. In a simple example, the objects are integers; each integer serves as its own key. (More precisely we will use its absolute value, since it is convenient to have non-negative keys only.) This case is of more than theoretical interest, since it makes hash tables appropriate for storing a set of integers with widely scattered values, for which simple array storage would be a waste of space (see requirement '''A3''' above).
|
||||
* '''H2'''. Frequently, the objects will be composite, that is to say, instances of a developer-defined class, and one of the attributes of that class, of type [[ref:libraries/base/reference/string_8_chart|STRING]] , can serve as the key. For example if you were writing an Eiffel compiler you would probably need to keep a data structure that includes information about classes of the system. Each class is represented by an object with several fields describing the properties of the class; one of these fields, the class name, corresponding to an attribute of type [[ref:libraries/base/reference/string_8_chart|STRING]] , will serve as key.
|
||||
* '''H3'''. Instead of being the full object (as in case '''H1''') or one of the object's fields (as in case '''H2'''), the key may have to be computed through a function of the generating class, which will take into account several attributes of the class (that is to say, for each object, several fields).
|
||||
|
||||
What this practically means is that in all cases you will need, in the generating class of the objects to be stored, a query (attribute or function) that gives the key. The type of the key is highly variable but must in all cases be a descendant of [[ref:libraries/base/reference/hashable_chart|HASHABLE]] . This is true of both [[ref:libraries/base/reference/integer_32_chart|INTEGER]] (case '''H1''') and [[ref:libraries/base/reference/string_8_chart|STRING]] (case '''H2'''). The requirements for being a [[ref:libraries/base/reference/hashable_chart|HASHABLE]] are not harsh: all you need is a function hash_code that returns a non-negative integer.>
|
||||
|
||||
==Using hash tables==
|
||||
|
||||
Class [[ref:libraries/base/reference/hash_table_chart|HASH_TABLE]] takes two generic parameters:
|
||||
<code>
|
||||
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>
|
||||
|
||||
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.
|
||||
* If the number of items in the table grows beyond the initial allocation, the table will automatically be resized.
|
||||
|
||||
It is useful, however, to use a reasonable upon creation: not too large to avoid wasting space, but not too small to avoid frequent applications of resizing, an expensive operation.
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,128 @@
|
||||
[[Property:title|EiffelBase, Trees]]
|
||||
[[Property:weight|3]]
|
||||
[[Property:uuid|8357fc1a-a089-c846-aee8-18b8fb26c288]]
|
||||
Trees and their immediate generalization, forests, are useful for any system that manipulates hierarchically organized information. The range of applications is broad, from abstract syntax trees in compilers through document structures in text processing systems to company organization charts in business software. <br/>
|
||||
Trees, in particular binary trees and their variants, also provide convenient implementations of container data structures.
|
||||
|
||||
==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]]
|
||||
|
||||
|
||||
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.
|
||||
|
||||
The descendants of a node are the node itself and, recursively, the descendants of its children. The ancestors of a node are the node itself and, recursively, the ancestors of its parent. For example the descendants of ''A'' are all the tree's nodes; and the ancestors of ''I'' are ''A'', ''D'' and ''I''.
|
||||
|
||||
To obtain a tree rather than a more general kind of graph, there is an extra requirement: in we start from an arbitrary node, go to the parent, to the parent's parent and so on (for as long as there is a parent), we will never find the starting node again. In more precise terms the condition is that the 'parent' relation must not have any cycles. (That relation is in fact a partial function, since every node has zero or one parent.) If we consider infinite as well as finite trees the above condition must be restated to express that if we start from an arbitrary node and repeatedly go to the parent we will eventually hit a root. This discussion, however, will only consider finite trees (those with a finite number of nodes), for which the two statements are equivalent.
|
||||
|
||||
The definition given so far properly defines forests rather than trees. ''A'' tree is a forest with at most one root (that is to say, with exactly one root unless it is empty), such as the example on the last figure. The discussion and the library classes will handle trees rather than forest, but this is not a very important difference since by using an obvious device you can treat any forest as a tree: simply add an extra node, and make it the parent of all the forest's roots.
|
||||
|
||||
Another important observation is that there is a one-to-one correspondence between trees and nodes. To any node N of a tree T we can associate a tree: take T, remove all nodes that are not descendants of N, and use N as the new root. Conversely, to any tree we can associate a node - its root. This correspondence enables us to treat the two concepts of tree and node as essentially the same.
|
||||
|
||||
==Recursive Trees==
|
||||
|
||||
The closeness of the notions of tree and node yields an elegant definition of trees in terms of lists. If we look at trees or, equivalently, at tree nodes, we can consider each node as being both:
|
||||
* A list: the list of its children.
|
||||
* A list cell (similar to a [[ref:libraries/base/reference/linkable_chart|LINKABLE ]] or [[ref:libraries/base/reference/bi_linkable_chart|BI_LINKABLE]] for one-way and two-way linked lists), paired with the node's siblings.
|
||||
|
||||
This yields a simple definition of trees by multiple inheritance from [[ref:libraries/base/reference/list_chart|LIST]] and [[ref:libraries/base/reference/cell_chart|CELL]] .
|
||||
|
||||
===Dynamic recursive trees===
|
||||
|
||||
An example of dynamic tree structure is provided by class [[ref:libraries/base/reference/two_way_tree_chart|TWO_WAY_TREE]] , an heir of both [[ref:libraries/base/reference/two_way_list_chart|TWO_WAY_LIST]] and [[ref:libraries/base/reference/bi_linkable_chart|BI_LINKABLE]] . There is also [[ref:libraries/base/reference/linked_tree_chart|LINKED_TREE]] , which inherits from [[ref:libraries/base/reference/linked_list_chart|LINKED_LIST]] and [[ref:libraries/base/reference/linkable_chart|LINKABLE]] , but [[ref:libraries/base/reference/two_way_tree_chart|TWO_WA Y_TREE]] is generally preferable since children of a node often needs to be traversed both ways; the notion of order is usually less significant here than for lists. Such a form of definition is a particularly effective way of conveying the profoundly recursive nature of trees. The corresponding classes are useful in many different areas such as graphics, text processing and compilation. To create a one-way or two-way linked tree, use
|
||||
<code>create my_tree.make (root_value)</code>
|
||||
|
||||
This will attach my_tree to a new one-node tree, with root_value at the root node. Here my_tree must be declared of type [[ref:libraries/base/reference/two_way_tree_chart|TWO_WAY_TREE]] [MY_TYPE] for some type MY_TYPE, and root_value must be of type MY_TYPE. <br/>
|
||||
A class with a similar interface but using arrays rather than lists to represent nodes is also available: [[ref:libraries/base/reference/arrayed_tree_chart|ARRAYED_TREE ]] . This class is more efficient in both time and space for trees whose nodes have many children that are accessed randomly, if relatively few child insertions occur after node creation. Here the creation procedure indicates the initial number of children:
|
||||
<code>create my_tree.make (max_estimated_children, root_value)</code>
|
||||
|
||||
The integer argument max_estimated_children only serves for the initial allocation; the array will be resized if the number of children grows beyond the initial size. As with the previous kinds of tree, the newly created node initially has no children.
|
||||
|
||||
===Fixed trees===
|
||||
|
||||
[[ref:libraries/base/reference/two_way_tree_chart|TWO_WAY_TREE]] is useful for fully dynamic trees, in which a node may get new children at any time. For some applications, the number of children of a tree, although still arbitrary, is set for each node when the node is created, and will not change after that. Of course, some children may be absent; the corresponding entries will be void references. Class [[ref:libraries/base/reference/fixed_tree_chart|FIXED_TREE]] provides the basic mechanism; as you may have guessed, the implementation associates with each node an array of its children, arrays usually being the right structure when a collection of objects is known not to change size after creation. To create a fixed tree, use
|
||||
<code>create my_tree.make (how_many_children, root_value)</code>
|
||||
|
||||
The root will have the associated value root value and will have how_many_ children children, all initially void. Unlike the argument max_estimated_children for the creation procedure of [[ref:libraries/base/reference/arrayed_tree_chart|ARRAYED_TREE]] , the value of how_many_children is the final arity of the newly created node; since it set separately for each node on creation, the various nodes of a tree can have different arities, as illustrated on the above figure.
|
||||
|
||||
===Properties of recursive trees===
|
||||
|
||||
Whether fixed or dynamic, recursive trees fully enjoy their dual origin. This means in particular that each node is viewed as a list of its children, and can apply to this list the features inherited from [[ref:libraries/base/reference/list_chart|LIST]] , appropriately renamed; for example: <br/>
|
||||
[[ref:libraries/base/reference/dynamic_tree_chart|child_put_left]] <br/>
|
||||
[[ref:libraries/base/reference/dynamic_tree_chart|child_forth]] <br/>
|
||||
[[ref:libraries/base/reference/dynamic_tree_chart|child_put]] <br/>
|
||||
and so on. Feature [[ref:libraries/base/reference/dynamic_tree_chart|count]] , inherited from [[ref:libraries/base/reference/list_chart|LIST]] , indicates the number of children; it is renamed arity to conform to accepted tree terminology. (The word is a substantived form of the 'ary' adjective ending, as in 'ternary', 'quaternary' and so on, which yielded the expression 'n-ary'.)
|
||||
|
||||
==Binary Trees==
|
||||
|
||||
Binary trees are a special case of fixed trees in which nodes always have two children, although either or both of these children may be void.
|
||||
|
||||
===Basic binary trees===
|
||||
|
||||
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===
|
||||
|
||||
For any ordinary tree, there exists a standard representation as a binary tree, which is useful in some applications. The correspondence is one-to-one, so that the original tree may be reconstructed without ambiguity. It actually applies to forests rather than trees and works as follows, ''fr'' being the first root in a forest: the binary tree's root corresponds to ''fr''; its left subtree is obtained recursively from the forest made by the subtrees of ''fr''; and its right subtree is obtained recursively from the original forest deprived of the tree rooted at ''fr''. If you start from a tree rather than a forest the binary tree's root will have no right child. <br/>
|
||||
Function [[ref:libraries/base/reference/tree_chart|binary_representation]] , in [[ref:libraries/base/reference/tree_chart|TREE]] , creates a binary tree representation (the function's result) obtained from the current tree. <br/>
|
||||
Procedure [[ref:libraries/base/reference/dynamic_tree_chart|fill_ from_binary]] , in [[ref:libraries/base/reference/dynamic_tree_chart|DYNAMIC_TREE]] , reconstructs a tree from a binary tree representation passed as argument.
|
||||
|
||||
===Binary search trees===
|
||||
|
||||
Class [[ref:libraries/base/reference/binary_search_tree_chart|BINARY_SEARCH_TREE]] describes binary search trees, an implementation ofbags which is appropriate for comparable items. <br/>
|
||||
Binary search trees rely for insertion on a policy whereby any item less than the root is inserted (recursively) into the left subtree, and any item greater than the root into the right subtree. So if the insertion order is reasonably random the items will distribute evenly among the various branches, This means that the average height of the tree will be not much more than the optimal: [log2 n] where n is the number of nodes and [x], for any x, is the integer part of x. <br/>
|
||||
Since search operations will follow the same principle (search left if smaller than the root, and so on), the time to find an item or ascertain that it is not there is proportional to the average height. In normal cases this means about [log2 n] basic operations, rather than n with a linear structure such as a list, and hence much better performance for large n.
|
||||
|
||||
==Cursor Trees==
|
||||
|
||||
Recursive trees, as described so far, are not active data structures: even though each node has its own cursor to traverse the list of its children, there is no global cursor on the tree as a whole. It is not hard to see that the notion of recursive tree is in fact incompatible with the presence of a global cursor. In situations where you need such a cursor, enabling you to move freely from a node to its children, siblings and parents, you may use class [[ref:libraries/base/reference/cursor_tree_chart|CURSOR_TREE]] and its descendants.
|
||||
|
||||
===The conceptual model===
|
||||
|
||||
With cursor trees the model is different from what we have seen earlier in this chapter: there is a clear distinction between the nodes and the tree itself. The manipulated object is a tree, and the notion of node is merely implicit. In the various operations presented below and illustrated on the following figure, 'up' means towards the root and 'down' towards the leaves. This, of course, is the reverse of the properties of trees of the other kind - those which grow towards the sun and serve to print books about software.
|
||||
|
||||
===Operations on cursor trees===
|
||||
|
||||
The cursor supported by instances of [[ref:libraries/base/reference/cursor_tree_chart|CURSOR_TREE]] has a position referring to a node of the tree, which is then considered to be the active node, or is off the tree. The different off positions are: [[ref:libraries/base/reference/cursor_tree_chart|above]] (above the root), [[ref:libraries/base/reference/cursor_tree_chart|below]] (below a leaf), [[ref:libraries/base/reference/cursor_tree_chart|before]] (before a leftmost sibling), [[ref:libraries/base/reference/cursor_tree_chart|after]] (after a rightmost sibling.) As with linear structures, fictitious sentinel elements are assumed to be present to the left, right, top and bottom. <br/>
|
||||
Various procedures are available to move the cursor in all directions:
|
||||
* [[ref:libraries/base/reference/cursor_tree_chart|down (i)]] moves the cursor down to the <code> i </code>-th child of the active node. If <code> i </code> is equal to 0 the cursor ends up before; if <code> i </code> is equal to the arity of the current parent plus 1, the cursor ends up [[ref:libraries/base/reference/cursor_tree_chart|after]] . Calling [[ref:libraries/base/reference/cursor_tree_chart|down (i)]] when the cursor is on a leaf node results in setting [[ref:libraries/base/reference/cursor_tree_chart|below]] and [[ref:libraries/base/reference/cursor_tree_chart|before]] to true if <code> i </code> is equal to 0, or [[ref:libraries/base/reference/cursor_tree_chart|below]] and [[ref:libraries/base/reference/cursor_tree_chart|after]] to true if is equal to arity+1.
|
||||
* [[ref:libraries/base/reference/cursor_tree_chart|forth]] and [[ref:libraries/base/reference/cursor_tree_chart|back]] move the cursor forward and backward between siblings and can cause the cursor to end up [[ref:libraries/base/reference/cursor_tree_chart|after]] or [[ref:libraries/base/reference/cursor_tree_chart|before]] .
|
||||
* [[ref:libraries/base/reference/cursor_tree_chart|up]] moves the cursor up one level. The call may be made even when the cursor is [[ref:libraries/base/reference/cursor_tree_chart|after]] or [[ref:libraries/base/reference/cursor_tree_chart|before]] . If the cursor is on the root of the tree or below in an empty tree, the cursor ends up [[ref:libraries/base/reference/cursor_tree_chart|above]] .
|
||||
|
||||
You can move the cursor in any one direction ( [[ref:libraries/base/reference/cursor_tree_chart|up]] , [[ref:libraries/base/reference/cursor_tree_chart|down]] , [[ref:libraries/base/reference/cursor_tree_chart|forth]] , [[ref:libraries/base/reference/cursor_tree_chart|back]] ), repeatedly, until it is [[ref:libraries/base/reference/cursor_tree_chart|off]] ( [[ref:libraries/base/reference/cursor_tree_chart|above]] , [[ref:libraries/base/reference/cursor_tree_chart|below,]] [[ref:libraries/base/reference/cursor_tree_chart|after]] , [[ref:libraries/base/reference/cursor_tree_chart|before]] respectively), but once it is [[ref:libraries/base/reference/cursor_tree_chart|off]] , further movement in the same direction is prohibited. For example the precondition of [[ref:libraries/base/reference/cursor_tree_chart|put_left]] requires [[ref:libraries/base/reference/cursor_tree_chart|before]] to be false, and the precondition of [[ref:libraries/base/reference/cursor_tree_chart|put_right]] requires [[ref:libraries/base/reference/cursor_tree_chart|after]] to be false. <br/>
|
||||
It is possible to move down from an [[ref:libraries/base/reference/cursor_tree_chart|above]] position; in an empty tree this brings the cursor [[ref:libraries/base/reference/cursor_tree_chart|below]] . Similarly, it is possible to move up from [[ref:libraries/base/reference/cursor_tree_chart|below]] , left from [[ref:libraries/base/reference/cursor_tree_chart|after]] , right from [[ref:libraries/base/reference/cursor_tree_chart|before]] . <br/>
|
||||
The sentinel element above the tree's root is considered to be the root of a forest containing just one tree. This view justifies the convention for the result of arity when the cursor is above: 0 if the tree [[ref:libraries/base/reference/cursor_tree_chart|is_empty]] , 1 if it has a root (viewed as the child of the fictitious sentinel element).
|
||||
|
||||
===Manipulating the cursor explicitly===
|
||||
|
||||
The cursor attached to a cursor tree is not just a conceptual notion but an actual object, of type [[ref:libraries/base/reference/cursor_chart|CURSOR]] . You may use the query cursor to obtain a reference to the current cursor. Procedure [[ref:libraries/base/reference/cursor_tree_chart|go_to]] takes a cursor as argument and brings the tree's cursor to the node identified by the value of that argument.
|
||||
|
||||
===Traversals===
|
||||
|
||||
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]]
|
||||
|
||||
|
||||
* 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''.
|
||||
|
||||
For each of the traversals, procedures are available to move the cursor accordingly, for example [[ref:libraries/base/reference/cursor_tree_chart|breadth_start]] and [[ref:libraries/base/reference/cursor_tree_chart|breadth_ forth]] for breadth-first, and similar names for the others.
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
[[Property:title|EiffelBase Data Structures Overview]]
|
||||
[[Property:weight|1]]
|
||||
[[Property:uuid|195085fd-0b2f-8211-3d70-81c32cc13566]]
|
||||
The data structures cluster of EiffelBase includes classes that cover a wide range of data structures classes for you to reuse in your systems. The cluster is divided into a number of subclusters. Each subcluster contains one or more '''deferred''' classes, which provide the general high-level abstractions; other classes in the cluster inherit from the deferred ones.
|
||||
|
||||
The highest-level class is [[ref:libraries/base/reference/container_chart| CONTAINER]] , with the following heirs:
|
||||
* [[ref:libraries/base/reference/collection_chart|COLLECTION]] , which describes containers through their access properties (defining how to access a container's items, forexample through an index or according to a last-in, first-out policy).
|
||||
* [[ref:libraries/base/reference/traversable_chart|TRAVERSABLE]] , which considers their traversal properties, such as sequential or hierarchical.
|
||||
* [[ref:libraries/base/reference/box_chart|BOX]] , which describes their storage properties, such as being bounded or unbounded.
|
||||
|
||||
The documentation further discusses:
|
||||
|
||||
@@ -0,0 +1,62 @@
|
||||
[[Property:uuid|3E1CF220-F2C6-458D-8ED9-92FA7FD4CBAF]]
|
||||
[[Property:weight|4]]
|
||||
[[Property:title|Access to internal properties]]
|
||||
In some applications, you may need to fine-tune the exception handling and memory management mechanisms. You may also need a simple way to access command-line arguments. In less common cases, you may require low-level access to internal properties of objects.
|
||||
|
||||
==Exception handling==
|
||||
|
||||
Class [[ref:libraries/base/reference/exceptions_chart|EXCEPTIONS]] enables you to control the handling of exceptions. [[ref:libraries/base/reference/unix_signals_chart|UNIX_SIGNALS]] , discussed next, complements it for the special case of fine-grain signal handling on Unix or Unix-like platforms. Both are meant to be inherited by any class that needs their facilities.
|
||||
|
||||
The basic exception mechanism treats all exceptions in the same way. In some cases, it may be useful to discriminate in a Rescue clause between the various possible causes.
|
||||
|
||||
Class [[ref:libraries/base/reference/exceptions_chart|EXCEPTIONS]] provides the features to do this. Each kind of exception has an integer code, which you can use through several features:
|
||||
* The integer-valued query exception which gives the code of the latest exception.
|
||||
* Queries which determine the general nature of the latest exception: <eiffel>is_signal</eiffel> which determines whether the exception was an operating system signal; <eiffel>is_developer_exception</eiffel> which determines whether it was explicitly caused by a raise, as explained next; <eiffel>assertion_violation</eiffel>.
|
||||
* Query<eiffel> recipient_name</eiffel> which gives the name of the exception's recipient - the routine that was interrupted by the exception.
|
||||
|
||||
The class also provides a set of constant integer-valued attributes which denote the various possible codes, such as <eiffel>No_more_memory</eiffel>, <eiffel>Routine_ failure</eiffel> and <eiffel>Precondition_violation</eiffel>. So you can test the value of exception against these codes if you need to ascertain the precise nature of an exception. To keep [[ref:libraries/base/reference/exceptions_chart|EXCEPTIONS]] simple these constant attributes are declared in a class EXCEP_CONST, of which [[ref:libraries/base/reference/exceptions_chart|EXCEPTIONS]] is an heir.
|
||||
|
||||
Another occasional requirement is for a mechanism to trigger an exception explicitly. Procedure raise answers this needs; the argument, a string, is the tag chosen for the exception. The code in this case is <eiffel>Developer_exception</eiffel>; the query <eiffel>is_developer_exception</eiffel> will return true; and the tag is accessible through feature <eiffel>tag_name</eiffel>.
|
||||
|
||||
You will notice in the interface specification for [[ref:libraries/base/reference/exceptions_chart|EXCEPTIONS]] that for some properties of the latest exception there are two features, one with a name such as exception or recipient_name as seen above and the other with a name prefixed by original_: <eiffel>original_exception</eiffel>, <eiffel>original_recipient_name</eiffel>.
|
||||
|
||||
{{caution|The reason for the presence of these pairs is that the immediately visible cause of a routine interruption may not be the real one. Assume that routine <eiffel>r</eiffel> from class <eiffel>C</eiffel>, which has a Rescue clause, calls <eiffel>s</eiffel> from <eiffel>D</eiffel> with no Rescue clause, and that some call executed by <eiffel>s</eiffel> causes a precondition violation. Because <eiffel>s</eiffel> has no Rescue clause of its own, <eiffel>s</eiffel> will fail. Up the call chain, the first routine that has a Rescue clause - <eiffel>r</eiffel> itself, or one of its own direct or indirect callers - may process the exception; but if it examines the exception code through attribute exception it will get the value of <eiffel>Routine_failure</eiffel>. This may be what you want; but to handle the situation in a finer way you will usually need to examine the code for the original exception, the one that interrupted <eiffel>s</eiffel>. This code will be accessible through the attribute original_exception, which in this case will have the value of <eiffel>Precondition</eiffel>, the exception code for precondition violations. So you have the choice between exploring the properties of the original exception, or those of the resulting routine failures. Just make sure you know what you are looking for. }}
|
||||
|
||||
As you will see from the header comments in the flat-short form of class [[ref:libraries/base/reference/exceptions_chart|EXCEPTIONS]] , the queries that return detailed information about an exception, such as <eiffel>assertion_violation</eiffel>, all give an answer determined by <eiffel>original_exception</eiffel> rather than <eiffel>exception</eiffel>, since when the two are different (that is to say, when you handle the exception in a routine other than the original recipient) the value of exception is always <eiffel>Routine_failure</eiffel> and there is nothing more to say about it.
|
||||
|
||||
==Signal handling==
|
||||
|
||||
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.
|
||||
|
||||
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.
|
||||
|
||||
Query <eiffel>is_defined </eiffel> <code>(some_signal)</code>, where <code>some_signal</code> is an integer code, will determine whether some_signal is supported on the platform.
|
||||
|
||||
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.
|
||||
|
||||
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.
|
||||
|
||||
==Memory management==
|
||||
|
||||
Class [[ref:libraries/base/reference/memory_chart|MEMORY]] , like [[ref:libraries/base/reference/exceptions_chart|EXCEPTIONS]] , is meant to be used as an ancestor by classes that need its facilities. It offers a number of features for controlling memory management and fine-tuning the garbage collection mechanism, a key component of the Eiffel Software environment.
|
||||
|
||||
One of the most useful features in this class is <e>dispose</e>. This procedure describes actions to be applied to an unreachable object just before the garbage collector reclaims it. By default, as declared in [[ref:libraries/base/reference/memory_chart|MEMORY]] , the procedure does nothing; but you may redefine it in a proper descendant of [[ref:libraries/base/reference/memory_chart|MEMORY]] to describe dispose actions. Normally such actions will involve freeing external resources: for example a class describing file descriptors may redefine <e>dispose</e> so that whenever a descriptor object is garbage-collected the corresponding file will be closed.
|
||||
|
||||
{{caution|This example is typical of proper uses of <e>dispose</e>. In a dispose <e>procedure</e>, you should not include any instruction that could modify the Eiffel object structure, especially if some objects in that structure may themselves have become unreachable: these instructions could conflict with the garbage collector's operations and cause catastrophic behavior. The legitimate use of <e>dispose</e> redefinition is for disposing of non-Eiffel resources. }}
|
||||
|
||||
Other features of [[ref:libraries/base/reference/memory_chart|MEMORY]] provide direct control over the operation of the garbage collector. You can in particular stop garbage collection through a call to <eiffel>collection_off</eiffel>, and restart it through a call to <eiffel>collection_on</eiffel>. By default, garbage collection is always on (a testimony to its authors' trust in its efficiency). Garbage collection is normally incremental, so as not to disrupt the application in a perceptible way. To start a complete garbage collection mechanism - reclaiming all unused objects - call procedure <eiffel>full_collect</eiffel>. The remaining features of [[ref:libraries/base/reference/memory_chart|MEMORY]] enable finer control of the collection mechanism and are useful in special cases only. You will even find a free procedure providing brave (and competent) developers with a mechanism for reclaiming individual objects manually.
|
||||
|
||||
MEM_INFO, the result type for query <eiffel>memory_statistics</eiffel> in [[ref:libraries/base/reference/memory_chart|MEMORY]] , describes objects containing information collected about memory usage. The features of [[ref:libraries/base/reference/gc_info_chart|GC_INFO]] provide statistics about the garbage collector's operation.
|
||||
|
||||
==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>
|
||||
|
||||
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.
|
||||
|
||||
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 <code>ARRAY [STRING]</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.
|
||||
|
||||
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.
|
||||
@@ -0,0 +1,35 @@
|
||||
[[Property:uuid|37099E33-657D-44CF-923C-1F10BE09AAFB]]
|
||||
[[Property:weight|2]]
|
||||
[[Property:title|Files, input, output]]
|
||||
A few classes of the Kernel Library support file manipulation, input and output: [[ref:libraries/base/reference/std_files_chart|STD_FILES]] , FILE, [[ref:libraries/base/reference/directory_chart|DIRECTORY]] and [[ref:libraries/base/reference/unix_file_info_chart|UNIX_FILE_INFO]] . For simple applications it suffices to use [[ref:libraries/base/reference/std_files_chart|STD_FILES]] , but to understand the concepts better it is preferable to look first at the other two.
|
||||
|
||||
==General files==
|
||||
|
||||
FILE describes the notion of sequential file viewed as a data structure which fits in the general taxonomy of EiffelBase.
|
||||
|
||||
The class declaration defines files as unbounded sequences of characters. This means that you will find in FILE all the operations on sequential data structures that you have come to know and love by reading this documentation - at least, all that apply. Just as stacks and linked lists, files have <eiffel>put</eiffel>, <eiffel>extend</eiffel>, <eiffel>has</eiffel>, <eiffel>item</eiffel> and so on. More specific to files are the typed input and output operations. For output, you will find <eiffel>put_character</eiffel>, <eiffel>put_integer</eiffel>, <eiffel>put_real</eiffel>, <eiffel>put_double</eiffel> and <eiffel>put_string</eiffel>, as well as <eiffel>new_line</eiffel>. For input you will find <eiffel>read_integer</eiffel> and its co-conspirators.
|
||||
|
||||
{{caution|Note the application to input features of the command-query separation principle. <br/>
|
||||
The input features such as read_integer do not by themselves return a result; they set the values of queries such as last_integer. So the normal way to read is through two operations:<br/>
|
||||
my_file.read_integer<br/>
|
||||
new_value := my_file.last_integer}}
|
||||
|
||||
Queries are available to determine the status of a file, in particular <eiffel>exists</eiffel>, <eiffel>is_readable</eiffel>, <eiffel>is_executable</eiffel>, <eiffel>is_writable</eiffel>, <eiffel>is_creatable</eiffel>, <eiffel>is_closed</eiffel>, <eiffel>is_open_read</eiffel> and so on.
|
||||
|
||||
{{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/>
|
||||
<eiffel>if my_file.exists and then my_file.is_readable then ...</eiffel>}}
|
||||
|
||||
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.
|
||||
|
||||
[[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.
|
||||
|
||||
The class [[ref:libraries/base/reference/directory_chart|DIRECTORY]] describes those files which are directories - nodes in the tree describing the file structure.
|
||||
|
||||
==Basic input and output==
|
||||
|
||||
Regardless of the operating system that you use, for simple input and output [[ref:libraries/base/reference/std_files_chart|STD_FILES]] is sufficient. You may inherit from that class to gain direct access to its features; or you may declare an entity of type [[ref:libraries/base/reference/std_files_chart|STD_FILES]] . But remember that a feature of this type is always available: io, from class [[ref:libraries/base/reference/any_chart|ANY]] . Thanks to this feature you may include simple input and output in any class, with instructions such as
|
||||
<code>io.put_string ("My message")</code>
|
||||
|
||||
[[ref:libraries/base/reference/std_files_chart|STD_FILES]] defines three default files through features <eiffel>input</eiffel>, <eiffel>output</eiffel> and <eiffel>error</eiffel>. These features are Once functions, so that the first reference to any one of them will automatically create the corresponding file descriptor and open the associated file.
|
||||
|
||||
To simplify the writing of common input and output operations, the most frequently used features of class FILE - for reading and writing integers, reals and so on, as discussed next - have been repeated in [[ref:libraries/base/reference/std_files_chart|STD_FILES]] so as to apply to the default input and output. Procedure put_string in the example at the beginning of this section is typical: it writes its output on the standard output. More generally, [[ref:libraries/base/reference/std_files_chart|STD_FILES]] has all the <eiffel>put_xxx</eiffel>, <eiffel>read_xxx</eiffel> and <eiffel>last_xxx</eiffel> features of FILE.
|
||||
@@ -0,0 +1,186 @@
|
||||
[[Property:uuid|9BEE6EE3-5652-4C2E-BD2E-E6A50787508E]]
|
||||
[[Property:weight|1]]
|
||||
[[Property:title|Language-related facilities]]
|
||||
A number of classes offer facilities which are very close to the language level. Here too the book ''[[Eiffel: The Language]]'' covers the classes in detail, so we can satisfy ourselves with a quick summary; the flat-short forms appear in part C.
|
||||
|
||||
==Basic types==
|
||||
|
||||
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.
|
||||
|
||||
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
|
||||
</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.
|
||||
|
||||
==Arrays==
|
||||
|
||||
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.
|
||||
|
||||
To create an instance of [[ref:libraries/base/reference/array_chart|ARRAY]] , use the creation instruction
|
||||
<code>
|
||||
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>.
|
||||
|
||||
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)
|
||||
</code>
|
||||
|
||||
Function item has a bracket alias, so that you may also write the first assignment above more concisely as
|
||||
<code>
|
||||
x := my_array [i]
|
||||
</code>
|
||||
|
||||
Features <eiffel>item</eiffel> 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]
|
||||
</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
|
||||
-- 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.
|
||||
|
||||
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==
|
||||
|
||||
A new Kernel Library class is introduced: [[ref:libraries/base/reference/tuple_chart|TUPLE]] .
|
||||
|
||||
Alone among all classes, class TUPLE has a variable number of generic parameters. <code>TUPLE, TUPLE [X], TUPLE [X, Y], TUPLE [X, Y, Z]</code> and so on are all valid types, assuming valid types <code>X, Y, Z</code> and so on.
|
||||
|
||||
Conformance rules:
|
||||
<code>[CONF1]
|
||||
For n >= 0
|
||||
TUPLE [U1, U2, ..., Un, Un+1] conforms to
|
||||
TUPLE [U1, U2, ..., Un]</code>
|
||||
|
||||
(and hence to <code>TUPLE [T1, T2, ..., Tn]</code> if each of the Ui conforms to each of the Ti for <code>1 <= i <= n</code>.)
|
||||
|
||||
In particular all tuple types conform to [[ref:libraries/base/reference/tuple_chart|TUPLE]] , with no parameter.
|
||||
<code>[CONF2]
|
||||
For n >= 0 If *every* one of the types T1, T2, ..., Tn conforms
|
||||
to a type T, then TUPLE [T1, T2, ..., Tn] conforms
|
||||
to ARRAY [T]</code>
|
||||
|
||||
|
||||
{{Definition|Tuple Type|A "tuple type" is any type based on class [[ref:libraries/base/reference/tuple_chart|TUPLE]] , i.e. any type of the form <code>TUPLE [T1, T2, ..., Tn]</code> for any n (including 0, for which there is no generic parameter). }}
|
||||
|
||||
|
||||
{{note|CONF1 should be understood in terms of the underlying mathematical model. <br/> Mathematically, <code>TUPLE [T1, T2, ..., Tn]</code> is the set TUPLE n of all partial functions f from N+ (the set of non-negative integers) to <code>T1 U T2 U ... Tn</code>, such that:<br/>The domain of f contains the interval 1..n (in other words, f is defined for any i such that 1 <nowiki><</nowiki>= i <nowiki><</nowiki>= n). <br/>For 1 <nowiki><</nowiki>= i <nowiki><</nowiki>= n, f (i) is a member of Ti. }}
|
||||
|
||||
With this definition, TUPLE <code>n</code> is indeed a subset of TUPLE <code>n+1</code>, and in particular TUPLE <code>0</code>, the empty set, is a subset of TUPLE <code>n</code> for any n.)
|
||||
|
||||
Semantics: an instance of <code>TUPLE [T1, T2, ..., Tn]</code> is a tuple whose first element is an instance of <code>T1</code>, the second element being an instance of <code>T2</code> etc. (The precise definition is the mathematical one given in note 1.) Note that there can be more than n elements to the tuple: for example a tuple with first element 5 and second element "FOO" is an instance of all of the following tuple types: <code>TUPLE; TUPLE [INTEGER]; TUPLE [INTEGER, STRING]</code>.
|
||||
|
||||
It may seem restrictive at first to permit only one class, [[ref:libraries/base/reference/tuple_chart|TUPLE]] , to have an arbitrary number of actual generic parameters. Why not have a general mechanism for declaring any class <code>C</code> so that all of <code>C [X], C [X, Y]</code> etc. are valid? But in fact this is not really a restriction. To obtain this effect without any complicated language convention, just declare <code>C</code> as
|
||||
<code>C [G -> TUPLE]</code> and then use the generic derivations
|
||||
<code>C [TUPLE [X]]
|
||||
C [TUPLE [X, Y]]
|
||||
</code>
|
||||
and so on. This also makes it possible to have the effect of some fixed parameters and some variable ones, as in <code>C [G, H, I -> TUPLE]</code> so we have all the necessary flexibility.)
|
||||
|
||||
==Tuple expressions==
|
||||
|
||||
Let e1, e2, ..., en be expressions of respective types T1, T2, ..., Tn. Then the expression <code>[e1, e2, ..., en]</code> denotes an instance of <code>TUPLE [T1, T2, ..., Tn]</code>, whose first element is e1, the second element being e2, etc.
|
||||
|
||||
Tuple expressions can be nested: whereas <code>[1, 2, 3]</code> is a tuple with three elements (representing an instance of <code>TUPLE [INTEGER, INTEGER, INTEGER]), [1, [2, 3]]</code> is a tuple with two elements, the second one itself a tuple; the overall expression represents an instance of <code>TUPLE [INTEGER, TUPLE [INTEGER, INTEGER]]</code>.
|
||||
|
||||
As a special case of tuple expression syntax, the delimiters <code>[</code> and <code>]</code> are replaced by parentheses for the tuple representing the actual argument list of a routine call (see section 4).
|
||||
|
||||
==Tuple features==
|
||||
|
||||
The exact specification of class [[ref:libraries/base/reference/tuple_chart|TUPLE]] will be described in an addition to ELKS. The principal features are:
|
||||
* [[ref:libraries/base/reference/tuple_chart|count]] (number of significant elements)
|
||||
* [[ref:libraries/base/reference/tuple_chart|item]] (i), with the obvious precondition: the i-th element, of type [[ref:libraries/base/reference/any_chart|ANY]] (since the value of i is not known at compile time); also first, second, third, fourth and fifth, of the appropriate types.
|
||||
* [[ref:libraries/base/reference/tuple_chart|put]] (x, i), with the obvious precondition: replace i-th element with x. If argument x is not of the appropriate type T <code>i</code> there is no effect.
|
||||
* [[ref:libraries/base/reference/tuple_chart|is_equal]] : redefined to consider only the first n elements, where n is the smaller length.
|
||||
|
||||
Other features under consideration include:
|
||||
* stripped (i): a tuple of type <code>TUPLE [T1, T2, Ti-1, Ti+1, ..., Tn]</code>, derived from the current one by removing the i-th component, again with the obvious precondition.
|
||||
* wedged (x, i): a tuple with one more element, inserted at position i.
|
||||
* '''infix''' "+": tuple concatenation
|
||||
* '''infix''' "++": element concatenation; t ++ x is the same thing as t.wedged (x, t.count + 1).
|
||||
|
||||
==What have we gained?==
|
||||
|
||||
First we have solved the only case in the Eiffel programming language in which an expression has no precisely defined type: polymorphic manifest arrays. We don't have manifest arrays any more, but manifest tuples, with a precisely defined type. No incompatibility is introduced thanks to rule CONF2. The original syntax for manifest arrays, <code>Result := <<e1, e2, ..., en>></code>, will continue to be supported.
|
||||
Second, we can define functions that return multiple results. This is a quite significant increase in expressive power. No common language has that. (You have to go to Lisp and functional languages.) Just define <code>TUPLE [...]</code> as the result type; in the function, you will write things like
|
||||
<code>
|
||||
Result := [e1, e2, ..., en]
|
||||
</code>
|
||||
Also, from a theoretical viewpoint, feature calls are simpler and more homogeneous: every feature takes exactly one tuple as argument and returns exactly one tuple as a result. (Either of these tuples may be empty: the first for a feature with no argument, the second for a procedure.) The syntax for a call becomes
|
||||
<code>Feature Arguments</code>
|
||||
|
||||
with Arguments defined as
|
||||
<code>Tuple_expression</code>
|
||||
|
||||
where the Tuple_expression uses the form given in section 2 but with the outermost <code>[</code> and <code>]</code> delimiters replaced by parentheses to conform to usual practice. So the call
|
||||
<code>f (a, b, c)</code>
|
||||
|
||||
which we continue to think of as having three arguments a, b and c, formally has only one tuple argument <code>[a, b, c]</code>. This is of course not to be confused with a call of the form
|
||||
<code>g ([a, b, c])</code>
|
||||
|
||||
which has one argument (a tuple with three elements) in both the ordinary and the formal sense.
|
||||
|
||||
==Active, iterators, numerical applications, introspection==
|
||||
|
||||
For a set of important applications of tuples see the book chapter on [[EiffelBase, Iteration|agents and iterators]] which also covers aspects of numerical software and related topics following from the tuple mechanism.
|
||||
|
||||
==Temporary limitations==
|
||||
|
||||
The implementation of tuples has the following limitations:
|
||||
* Conformance of [[ref:libraries/base/reference/array_chart|ARRAY]] types to [[ref:libraries/base/reference/tuple_chart|TUPLE]] types is not yet fully supported.
|
||||
* Class [[ref:libraries/base/reference/tuple_chart|TUPLE]] does not have features such as first and second. You must use item and, in most cases, an object test.
|
||||
|
||||
==Strings==
|
||||
|
||||
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>
|
||||
|
||||
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.
|
||||
|
||||
==String descriptor==
|
||||
|
||||
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 := 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. 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
|
||||
|
||||
[[ref:libraries/base/reference/basic_routines_chart|BASIC_ROUTINES]] provides a number of conversion functions, such as charconv.
|
||||
@@ -0,0 +1,173 @@
|
||||
[[Property:uuid|EA781CE6-3452-4EEF-BF05-47D94FC88A3D]]
|
||||
[[Property:weight|3]]
|
||||
[[Property:title|Persistence, storage, and retrieval]]
|
||||
Most object-oriented applications need the ability to store object structures on persistent storage for later retrieval, and to transfer such object structures to other applications. Eiffel offers a full persistence mechanisms serving these needs.
|
||||
|
||||
=Persistence completeness=
|
||||
|
||||
A fundamental requirement on object persistence mechanisms is the ''Persistence Completeness'' rule, stated as follows in ''[[Eiffel: The Language]]'':
|
||||
|
||||
<blockquote>Whenever an object is stored into an external file, the stored content contains all the dependents of that object. Conversely, retrieving a previously stored object also retrieves all its dependents.
|
||||
</blockquote>
|
||||
<blockquote>
|
||||
Storing an object just by itself would usually result in wrong semantics: most objects contain references to other objects, which must also be stored and retrieved with it. The persistence completeness rule ensures that this is always the case. It also means, of course, that features used for storing and retrieving objects must do much more than simple input and output; they must perform complete traversals of object structures.
|
||||
</blockquote>
|
||||
|
||||
The Eiffel persistence mechanism applies Persistence Completeness.
|
||||
|
||||
=Varieties of store operations=
|
||||
|
||||
Different variants of the store operation are available: '''session''', '''basic''' and '''independent''' store
|
||||
|
||||
* '''Session''' store produces the most compact structure in the resulting files; but the resulting structure is dependent on the current execution of the system which executes the store operation (''System'' is taken here, as elsewhere in this documentation, in its Eiffel sense of an executable assembly of classes, compiled together with the help of a configuration file.)
|
||||
|
||||
* '''Basic''' store is like session store with the difference that the resulting structure is dependent on the system which executes the store operation (i.e. only the system creating the persistent version can retrieve it.)
|
||||
|
||||
* On the other hand, '''independent''' store allows a system running on a computer with a certain architecture to retrieve, without any explicit conversion operation, object structures stored by a system running on a machine of a completely different architecture. In addition, independent store lets you retrieve an old version of the object that was saved (see more details in the recoverable section below.)
|
||||
|
||||
=Using the storage and retrieval facilities=
|
||||
|
||||
Historically, the persistence mechanism was offered via the helper class [[ref:libraries/base/reference/storable_chart|STORABLE]] which you could use as an ancestor whenever you wanted to store and retrieve objects. Via this class you would have access to <eiffel>basic_store</eiffel> and <eiffel>independent_store</eiffel> to store an object, and <eiffel>retrieved</eiffel> to retrieve one. However this was not necessary and the persistence mechanism could be used directly from any descendants of the [[ref:libraries/base/reference/io_medium_chart|IO_MEDIUM]] using routines with the same names. This manner of storing and retrieving objects is called the '''C''' persistence mechanism since it was completely written in C and is included as part of the Eiffel runtime.
|
||||
|
||||
Today, we recommend using the '''SED''' ('''SE'''rialization '''D'''eserialization) persistence mechanism, entirely written in Eiffel. It is very flexible, since it lets you control the format of the stored structure, but in most cases it suffices to rely on the simple helper class [[ref:libraries/base/reference/sed_storable_facilities_chart|SED_STORABLE_FACILITIES]]. This class offers <eiffel>session_store</eiffel>, <eiffel>basic_store</eiffel>, and <eiffel>store</eiffel> (the de-facto independent store), as well as <eiffel>retrieved</eiffel>.
|
||||
|
||||
In both cases, you only need to be aware of the difference between the various storing mechanism (session, basic and independent) at storage time. The stored structure will always be available through feature retrieved; this feature will figure out, from the format of the stored structure how it was stored and will decode it accordingly.
|
||||
|
||||
{{Caution|This is only true when using just the '''C''' or '''SED''' persistence format. If the structure was stored using the '''C''' format, you need to use the '''C''' retrieved feature. Conversely, if it was stored using '''SED''', you need to use the '''SED''' retrieval mechanism.}}
|
||||
|
||||
Regardless of the mechanism used, the feature <eiffel>retrieved</eiffel> returns a result of type [[ref:libraries/base/reference/any_chart|detachable ANY]] and is typically used through an object test.
|
||||
|
||||
==With the C persistence format==
|
||||
|
||||
The example below will show you how to store an object using the C persistence format. It uses <eiffel>independent_store</eiffel> but you could also use in-place <eiffel>basic_store</eiffel> instead:
|
||||
|
||||
<code>
|
||||
store_object (o: ANY; p: PATH)
|
||||
-- Store object `o` into file located in `p`.
|
||||
local
|
||||
f: RAW_FILE
|
||||
do
|
||||
create f.make_with_path (p)
|
||||
f.open_write
|
||||
f.independent_store (o)
|
||||
f.close
|
||||
end
|
||||
</code>
|
||||
|
||||
Then to retrieve the object you would write the following:
|
||||
|
||||
<code>
|
||||
retrieve (p: PATH)
|
||||
-- Retrieve object stored in file located in `p`.
|
||||
local
|
||||
f: RAW_FILE
|
||||
do
|
||||
create f.make_with_path (p)
|
||||
f.open_read
|
||||
if attached {MY_TYPE} io_medium.retrieved as data then
|
||||
-- Retrieved result is of expected type.
|
||||
-- Proceed with processing of result,
|
||||
-- typically with calls of the form `data.some_feature'.
|
||||
else
|
||||
-- Result was not of expected type MY_TYPE.
|
||||
end
|
||||
f.close
|
||||
end
|
||||
</code>
|
||||
|
||||
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>.)
|
||||
|
||||
==With the SED persistence format==
|
||||
|
||||
The example below will show you how to store an object using the SED mechanism assuming the current class is a descendant of [[ref:libraries/base/reference/sed_storable_facilities_chart|SED_STORABLE_FACILITIES]]. It uses the <eiffel>store</eiffel> feature but you could also use <eiffel>session_store</eiffel> or <eiffel>basic_store</eiffel> too.
|
||||
|
||||
<code>
|
||||
store_object (o: ANY; p: PATH)
|
||||
-- Store object `o` into file located in `p`.
|
||||
local
|
||||
f: RAW_FILE
|
||||
writer: SED_MEDIUM_READER_WRITER
|
||||
do
|
||||
create f.make_with_path (p)
|
||||
f.open_write
|
||||
create writer.make_for_writing (l_file)
|
||||
store (o, writer)
|
||||
f.close
|
||||
end
|
||||
</code>
|
||||
|
||||
Then to retrieve the object you would write the following:
|
||||
|
||||
<code>
|
||||
retrieve (p: PATH)
|
||||
-- Retrieve object stored in file located in `p`.
|
||||
local
|
||||
f: RAW_FILE
|
||||
reader: SED_MEDIUM_READER_WRITER
|
||||
do
|
||||
create f.make_with_path (p)
|
||||
f.open_read
|
||||
create reader.make_for_reading (f)
|
||||
if attached {MY_TYPE} retrieved (reader, False) as data then
|
||||
-- Retrieved result is of expected type.
|
||||
-- Proceed with processing of result,
|
||||
-- typically with calls of the form `data.some_feature'.
|
||||
else
|
||||
-- Result was not of expected type MY_TYPE.
|
||||
end
|
||||
f.close
|
||||
end
|
||||
</code>
|
||||
|
||||
In case of an error during retrieval, no objects will be returned and instead the query <eiffel>retrieved_errors</eiffel> will list all the errors it encountered.
|
||||
|
||||
=Recoverable format=
|
||||
|
||||
Sometimes you will be in a position where the definition of a class (its "schema") will have changed between the time you stored your object and the time you are trying to retrieve it. Such schema changes include:
|
||||
|
||||
* Renaming the class.
|
||||
* Adding attributes.
|
||||
* Removing attributes.
|
||||
* Renaming attributes.
|
||||
* Changing the types of attributes.
|
||||
|
||||
The persistence mechanism allows you to retrieve the old version of the object only if it was saved using the <eiffel>independent_store</eiffel> facility.
|
||||
|
||||
The mechanism lets you define precisely what will happen in the case of a scheme change, or "mismatch". Each time you retrieve an object of a certain base class whose schema has changed, the feature <eiffel>correct_mismatch</eiffel> will be called. This feature is defined in [[ref:libraries/base/reference/any_chart|ANY]] and has the following effect:
|
||||
* Its default version causes an exception. This is the proper behavior since the old object might not make sense with the new schema (for example, it might violate the invariant), and you do not want to continue the computation, without warning, with wrong objects.
|
||||
* To specify otherwise, and avoid the exception, just redefine <eiffel>correct_mismatch</eiffel> in the class whose schema has been changed.
|
||||
For example, the important EiffelBase library class [[ref:libraries/base/reference/hash_table_chart|HASH_TABLE]] changed between version 5.1 and version 5.2 to use <eiffel> SPECIAL</eiffel> rather than [[ref:libraries/base/reference/array_chart|ARRAY]] for its internal data storage. To retrieve a 5.1 version of [[ref:libraries/base/reference/hash_table_chart|HASH_TABLE]], you can redefine <eiffel>correct_mismatch</eiffel> as follows:
|
||||
<code>
|
||||
correct_mismatch
|
||||
-- Attempt to correct object mismatch during retrieve using `mismatch_information'.
|
||||
do
|
||||
-- In version 5.1 and earlier, `content', `keys' and `deleted_marks'
|
||||
-- were of base class ARRAY. In 5.2 we changed it to be a SPECIAL for
|
||||
-- efficiency reasons. In order to retrieve an old HASH_TABLE we
|
||||
-- need to convert those ARRAY instances into SPECIAL instances.
|
||||
|
||||
-- Convert `content' from ARRAY to SPECIAL.
|
||||
if attached {ARRAY [G]} mismatch_information.item ("content") as l_temp then
|
||||
content := l_temp.area
|
||||
end
|
||||
|
||||
-- Convert `keys' from ARRAY to SPECIAL.
|
||||
if attached {ARRAY [H]} mismatch_information.item ("keys") as l_temp then
|
||||
keys := l_temp.area
|
||||
end
|
||||
|
||||
-- Convert `deleted_marks' from ARRAY to SPECIAL.
|
||||
if attached {ARRAY [BOOLEAN]} mismatch_information.item ("deleted_marks") as l_temp then
|
||||
deleted_marks := l_temp.area
|
||||
end
|
||||
|
||||
if content = Void or keys = Void or deleted_marks = Void then
|
||||
-- Could not retrieve old version of HASH_TABLE. We throw an exception.
|
||||
Precursor {TABLE}
|
||||
end
|
||||
end
|
||||
</code>
|
||||
|
||||
Note the use of <eiffel>mismatch_information</eiffel>. This is a once query of [[ref:libraries/base/reference/any_chart|ANY]] of type <eiffel>MISMATCH_INFORMATION</eiffel> which behaves like a [[ref:libraries/base/reference/hash_table_chart|HASH_TABLE]]. The keys of the table are the names of the attributes on which a mismatch occurred, and the values are the corresponding object fields as they were originally stored. In this particular case of [[ref:libraries/base/reference/hash_table_chart|HASH_TABLE]] we know that the previous version was an [[ref:libraries/base/reference/array_chart|ARRAY]], so we do an object test and if it succeeds we assign its <eiffel>area</eiffel> to the corresponding attribute of [[ref:libraries/base/reference/hash_table_chart|HASH_TABLE]].
|
||||
|
||||
If a class name changed, you should create an instance of <eiffel>CLASS_NAME_TRANSLATIONS</eiffel>, it behaves like a [[ref:libraries/base/reference/hash_table_chart|HASH_TABLE]] where the keys represent the old name, and the value the new name. This instance needs to be created before the call to retrieved.
|
||||
@@ -0,0 +1,93 @@
|
||||
[[Property:uuid|419E822A-646B-4817-810C-B30F973A2D70]]
|
||||
[[Property:weight|0]]
|
||||
[[Property:title|Universal class and its features]]
|
||||
The Eiffel inheritance mechanism is set up in such a way that every class is a descendant of a Kernel Library class called [[ref:libraries/base/reference/any_chart|ANY]] . The features of this class provide a number of generally applicable facilities covering such needs as comparison, copying and rudimentary input and output.
|
||||
|
||||
=The structure of universal classes=
|
||||
|
||||
Every class which has no inheritance clause is understood to have an inheritance clause of the form
|
||||
<code>
|
||||
inherit
|
||||
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.
|
||||
|
||||
=Using the universal classes=
|
||||
|
||||
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
|
||||
inherit
|
||||
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
|
||||
</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.
|
||||
|
||||
=Input and output features=
|
||||
|
||||
Some of the features of [[ref:libraries/base/reference/any_chart|ANY]] cover common input and output needs.
|
||||
|
||||
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.
|
||||
|
||||
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.
|
||||
|
||||
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)
|
||||
</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 <code>twin</code>. For non-void source, the assignment
|
||||
<code>
|
||||
target := source.twin
|
||||
</code>
|
||||
starts by creating a new object, then populates its fields to be identical to <code>source</code>.
|
||||
|
||||
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.
|
||||
|
||||
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>.
|
||||
|
||||
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.
|
||||
|
||||
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=
|
||||
|
||||
The string-valued query <eiffel>generator</eiffel>, applied to any object, returns the name of the object's generating class: the class of which it is an instance. The boolean function <eiffel>conforms_to</eiffel> makes it possible to test dynamically whether the type of an object conforms to that of another - that is to say whether the first one's generator is a descendant of the second one's.
|
||||
|
||||
These two features enable clients to ascertain the dynamic type of an entity at runtime. They are only useful for low-level components; the normal mechanism for type-dependent operations is dynamic binding.
|
||||
|
||||
=Miscellaneous=
|
||||
|
||||
The query Void, of type <eiffel>NONE</eiffel>, denotes a reference that is always void - not attached to any object.
|
||||
|
||||
Procedure <eiffel>do_nothing</eiffel> does what its name implies.
|
||||
|
||||
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.
|
||||
@@ -0,0 +1,20 @@
|
||||
[[Property:title|EiffelBase, The Kernel]]
|
||||
[[Property:weight|0]]
|
||||
[[Property:uuid|A26C5BCA-20A5-4EE6-9710-F4E8B72DBA10]]
|
||||
In addition to basic concepts close to the language level, the Kernel covers such common needs as '''input''' and '''output''', '''storage''' and '''retrieval''' of objects on persistent storage, fine control over '''exception handling''' and '''memory management''', and access to '''operating system facilities'''. The kernel can be divided into 5 logical clusters of classes:
|
||||
# [[Universal class and its features|The first cluster]] contains the universal class defining facilities accessible to all other classes: [[ref:libraries/base/reference/any_chart|ANY]] . Every developer-defined class is a descendant of this class.
|
||||
# [[Language-related facilities|The second cluster]] includes classes whose facilities are directly related to language concepts:
|
||||
** Classes describing 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]]
|
||||
** Arrays: class [[ref:libraries/base/reference/array_chart|ARRAY]]
|
||||
** Tuples: class [[ref:libraries/base/reference/tuple_chart|TUPLE]]
|
||||
** Strings: class [[ref:libraries/base/reference/string_8_chart|STRING]]
|
||||
** Basic facilities: class [[ref:libraries/base/reference/basic_routines_chart|BASIC_ROUTINES]]
|
||||
# [[Files, input, output|The third cluster]] provides input and output facilities:
|
||||
** [[ref:libraries/base/reference/std_files_chart|STD_FILES]] offers basic mechanisms, sufficient for simple input and output.
|
||||
** [[ref:libraries/base/reference/file_chart|FILE]] describes the notion of sequential file, viewed as a sequence of characters and fully integrated in the data structure library.
|
||||
** [[ref:libraries/base/reference/directory_chart|DIRECTORY]] gives properties of directories (files serving as collections of other files).
|
||||
# [[Persistence, storage, and retrieval|The fourth cluster]] makes it possible to store object structures on persistent storage and retrieve them later. This facility can also be used to transmit object structures through pipes or over a network.
|
||||
# [[Access to internal properties|The last cluster]] provides access to internal properties of the compiler and environment, useful for applications that need some fine-tuning of the basic mechanisms:
|
||||
** Class [[ref:libraries/base/reference/exceptions_chart|EXCEPTIONS]] (complemented by [[ref:libraries/base/reference/unix_signals_chart|UNIX_SIGNALS]] for Unix-type platforms) provides control over the exception handling mechanism, in particular for applications that need to handle different types of exception in different ways.
|
||||
** Similarly, classes [[ref:libraries/base/reference/memory_chart|MEMORY]] and [[ref:libraries/base/reference/gc_info_chart|GC_INFO]] provide ways to control the garbage collector and tailor it to specific needs.
|
||||
** Class ARGUMENTS gives access to the command-line arguments.
|
||||
@@ -0,0 +1,31 @@
|
||||
[[Property:title|EiffelBase Support Cluster]]
|
||||
[[Property:weight|2]]
|
||||
[[Property:uuid|f9c2a003-9836-6688-db1f-5aa31313b317]]
|
||||
The support cluster offers some commonly needed functionality that do not belong to the kernel.
|
||||
|
||||
==Conversions, mathematical properties and ASCII characters==
|
||||
|
||||
A few utility classes complement the [[EiffelBase, The Kernel|kernel]] facilities. [[ref:libraries/base/reference/primes_chart|PRIMES]] , [[ref:libraries/base/reference/random_chart|RANDOM]] and [[ref:libraries/base/reference/fibonacci_chart|FIBONACCI]] are part of the data structure taxonomy; the others are meant to be used as ancestors by classes needing their features.
|
||||
|
||||
Two classes provide basic mathematical functions such as logarithms and trigonometric functions: [[ref:libraries/base/reference/single_math_chart|SINGLE_MATH]] for single precision and [[ref:libraries/base/reference/double_math_chart|DOUBLE_MATH]] for the double-precision variants. [[ref:libraries/base/reference/math_const_chart|MATH_CONST]] contains mathematical constants: <code> p </code>, the square root of two, Euler's constant <code> e </code>.
|
||||
|
||||
[[ref:libraries/base/reference/primes_chart|PRIMES]] , [[ref:libraries/base/reference/random_chart|RANDOM]] and [[ref:libraries/base/reference/fibonacci_chart|FIBONACCI]] are data structure classes - heirs of [[ref:libraries/base/reference/countable_sequence_chart|COUNTABLE_SEQUENCE]] . In all of these classes function i_th takes an integer argument <code> i </code> and will return the i-th element of the sequence under consideration - prime numbers, pseudo-random numbers or Fibonacci numbers. These sequences are active structures, on which forth will advance the current position and item will return the value at the current position. A few other features are specific to each case: for example [[ref:libraries/base/reference/random_flatshort|higher_prime]] will yield the smallest prime greater than or equal to a certain value, and [[ref:libraries/base/reference/random_flatshort|set_seed]] will define the seed to be used for starting the pseudo-random sequence.
|
||||
|
||||
==Internal object structures==
|
||||
|
||||
Class [[ref:libraries/base/reference/internal_chart|INTERNAL]] provides low-level access to internal object structures. It, too, is meant to be used as ancestor by classes needing its features. <br/>
|
||||
Here are some of the most useful calls and what they yield, <eiffel>obj</eiffel> being an entity attached to an object <eiffel>O</eiffel> and <eiffel>i</eiffel> an integer:
|
||||
* <eiffel>class_name (obj)</eiffel> : the name of the generator class for O.
|
||||
* <eiffel>dynamic_type (obj) </eiffel> : the integer code for the type of <eiffel>O</eiffel>, where each type in a system is identified by a unique code.
|
||||
* <eiffel>field_count (obj) </eiffel> : the number of fields in <eiffel>O</eiffel>.
|
||||
* <eiffel>physical_size (obj) </eiffel> : the space occupied by <eiffel>O</eiffel>, in bytes.
|
||||
* <eiffel>field_xx (i, obj) </eiffel> where <eiffel>xx</eiffel> is name or offset: name or offset of the i-th field of <eiffel>O</eiffel>.
|
||||
* <eiffel>field (i, obj) </eiffel> : the value of the i-th field of , if a reference; declared of type ANY in the class.
|
||||
* <eiffel>yy_field (i, obj) </eiffel> where <eiffel>yy</eiffel> is boolean, character, integer, real or double: the value of the i-th field of <eiffel>O</eiffel>, if of the corresponding type; each declared of the appropriate type in the class.
|
||||
* <eiffel>is_special (obj) </eiffel> : a boolean query which indicates whether <eiffel>O</eiffel> is a special object (the sequence of values representing the elements of an array or the characters of a string).
|
||||
|
||||
{{warning|Only very special cases justify the use of the class [[ref:libraries/base/reference/internal_chart|INTERNAL]]. Unless you are writing the lowest level of an interface between an Eiffel application and external tools (such as a database management system), and this requires passing to those tools information about the internals of Eiffel objects, you almost certainly should not use [[ref:libraries/base/reference/internal_chart|INTERNAL]] . }}
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
[[Property:title|EiffelBase Tutorial]]
|
||||
[[Property:weight|-2]]
|
||||
[[Property:uuid|d540615d-802b-8e12-af74-4d01d1fc4760]]
|
||||
|
||||
Learn about the EiffelBase library.
|
||||
|
||||
@@ -0,0 +1,108 @@
|
||||
[[Property:title|IFELL]]
|
||||
[[Property:weight|4]]
|
||||
[[Property:uuid|34079f13-741d-a937-e492-9ccfe235efeb]]
|
||||
This license describes the terms applicable to the open-source EiffelBase libraries. For the EiffelStudio and other libraries license terms, please refer to the Eiffel Software end-user license.
|
||||
|
||||
On August 4, 1998 -- as part of the Eiffel Summit at TOOLS USA '98 -- Interactive Software Engineering (Eiffel Software) officially made its flagship [[EiffelBase|EiffelBase library ]] available for free to any organization that wants to use it, for either commercial or non-profit purposes.
|
||||
|
||||
The terms of the new EiffelBase license are given below. Changes since the original version are recorded in the [[#changes|changes section]] .
|
||||
|
||||
We hope that others will be inspired by this example to release their own reusable Eiffel components to the public.
|
||||
==Preamble==
|
||||
(This Preamble is not part of the license.)
|
||||
|
||||
EiffelBase is one of the principal contributions of Eiffel: a library of fundamental structures and algorithms covering the basics of computing, and resulting from a "Linnaean" effort at a general-purpose taxonomy of computing structures. We believe that EiffelBase is one of the most carefully designed and extensively used libraries in the object-oriented industry. The library and its design principles are described in detail in the book [http://www.eiffel.com/services/training/books.html ''Reusable Software: The Base Object-Oriented Component Libraries'' ] .
|
||||
|
||||
EiffelBase is at the center of Eiffel Software's Eiffel and has been among the major factors attracting users to the technology. In spite of this competitive advantage, Eiffel Software officially announced in June of 1998 that it would release the library to the public in source form, under the terms of the license below. There are two main reasons for this initiative:
|
||||
* As the software industry seems poised to adopt the idea of reusable components, we feel that EiffelBase provides an ideal basis and are happy to contribute it to the community.
|
||||
* We are impressed by the quality of some public-domain software and the effectiveness of the "open source" movement. We hope that the public availability of EiffelBase in source form will encourage many people to take a close look at the classes of EiffelBase, criticize its weaker points, provide improvements, and add their own high-quality contributions.
|
||||
|
||||
===About the license===
|
||||
|
||||
After considering existing license contracts for publicly available software, we felt that the GPL (Gnu Public License) as well as LGPL, its variant for libraries, while useful and interesting, were not adequate since they are too restrictive.
|
||||
<span id="contamination"></span>
|
||||
In particular a crucial requirement was that the license should allow commercial companies to use EiffelBase (and other libraries that may use this license in the future) in for-profit developments without any fear (justified or not) that GPL-like requirements could cause proprietary elements of their products to come under the open source umbrella themselves.
|
||||
|
||||
The closest model was that of the Perl Artistic License, which significantly influenced the terms of the agreement as shown below. It too, however, was not applicable verbatim. EiffelBase was initially designed as a commercial product and then released as free software, a different situation from that of software that was never commercial.
|
||||
|
||||
You will note that there are few restrictions on the use of EiffelBase in the license given below. You can use EiffelBase in your academic work and re-publish the source; you can produce a commercial software product using EiffelBase and not owe a cent to Eiffel Software.
|
||||
|
||||
===Ownership===
|
||||
|
||||
EiffelBase remains an Eiffel Software product and will continue to be distributed as such to Eiffel Software customers. This means in particular that there is no o lessening of our maintenance and support responsibility to our customers under maintenance. In fact, the increased scrutiny and outside contributions will mean an increase of our commitment to develop EiffelBase and of the quality of the product.
|
||||
|
||||
==The License Text==
|
||||
|
||||
This is the official license text.
|
||||
|
||||
===Definitions===
|
||||
"Eiffel Software" refers to Interactive Software Engineering Inc.
|
||||
"The Library" refers to the collection of software texts in the Eiffel programming language, originally written by Eiffel Software, and constituting the EiffelBase library of fundamental computing science structures, as well as any derivatives of these texts as may be created through textual modification (by people) or through translation to lower-level languages (by programs such as compilers).
|
||||
|
||||
"Standard Version" refers to the version of the Library maintained by Eiffel Software and distributed by Eiffel Software to its customers under maintenance.
|
||||
|
||||
"You" means any person interested in using, modifying or distributing the Library.
|
||||
|
||||
"Source form" means the text of the Library's classes, in the Eiffel programming language.
|
||||
|
||||
"Object form" means the translation, as performed by a compiler or other program, of the Library or part of the Library into another, usually lower-level code (such as C, assembly language or machine language) intended for execution by a machine.
|
||||
<span id="reference_implementation"></span>
|
||||
"Reference implementation" means the latest officially released version of Eiffel Software's implementation of the Eiffel programming language.
|
||||
|
||||
===Statement of copyright and ownership===
|
||||
|
||||
'''1.''' The Library is the property of Eiffel Software and bears Eiffel Software's copyright. Any use you make of the Library must respect Eiffel Software's proprietary rights.
|
||||
|
||||
===Right of use===
|
||||
'''2.''' Eiffel Software hereby grants you permission to do any of the following without payment to Eiffel Software, under the conditions of sections 3 to 7 of this Agreement:
|
||||
* '''2.1.''' Use the Library, in source form or object form, as part of any software you develop and distribute to other parties.
|
||||
* '''2.2.''' Distribute the source of the Library to other parties.
|
||||
|
||||
===Conditions===
|
||||
<span id="encourage_bug_reports"></span>
|
||||
'''3.''' If you use (2.1) or distribute (2.2) the Library, you are encouraged, but not required,to provide to Eiffel Software and the rest of the Library's user community any bug fixes or other improvements that you make to the existing classes of the Library.
|
||||
|
||||
'''4.'''If you produce new classes based on the Library or extending the Library, you are encouraged, but not required, to make them available in the same way.
|
||||
|
||||
'''5.''' If you use the Library (2.1) in source or object form for producing software that you distribute, you are encouraged, but not required, to include the following mention (or its translation into the language of the rest of the distribution, if other than English) in the documentation of your software, including textual documentation as well as online documentation such as "About..." entries:
|
||||
* ''This product includes EiffelBase software from Interactive Software Engineering, used according to the terms of the Eiffel Software Free Eiffel Library License (IFELL). ''See [http://eiffel.com/ http://eiffel.com] and, for the license terms, [http://eiffel.com/products/base/license.html http://eiffel.com/products/base/license.html] .
|
||||
|
||||
|
||||
'''6.''' If you distribute the source code of part or all of the Library (2.2) you must:
|
||||
* Distribute entire classes, not just extracts.
|
||||
* Retain, in the distributed version, the entire copyright notice of these classes.
|
||||
* If you need to make any modification to the classes, mark those modifications clearly in the class texts.
|
||||
* <span id="valid_eiffel"></span> Ensure that the text remains valid Eiffel. Note that this license does not authorize you to distribute versions of the Library restated in other programming, analysis or design languages, except if mechanically translated from the source form by an Eiffel compiler, in which case they fall under "object form". For any non-mechanical adaptation to other languages you must obtain authorization from Eiffel Software. "Valid Eiffel" includes the language described in the latest edition or printing of the book ''[[Eiffel: The Language]]'' (Prentice Hall), plus any changes or extensions that have been approved by the Nonprofit International Consortium for Eiffel (NICE) or have been accepted for discussion by the Language Committee of NICE, provided they are accepted by the reference implementation.
|
||||
* <span id="obsolete_version"></span> If Eiffel Software releases a new version of the Library, you must, no later than six months after the release of that version, either: (A) update your distribution to the new version; (B) add the following notice to all files in your distribution:" ''This is an obsolete version of EiffelBase. The latest version is available from http://eiffel.com''"; or (C) cease distributing your version.
|
||||
|
||||
|
||||
You may not charge any fee for the distribution of the Library (source or binary), although you may charge for software that includes the Library in whole or in part as per the other provisions of this Agreement.
|
||||
|
||||
'''7.''' You may not use the name of Eiffel Software to endorse or promote products derived from the Library without specific prior written permission.
|
||||
|
||||
'''8.''' THIS PACKAGE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
|
||||
<span id="changes"></span>
|
||||
==Changes==
|
||||
(This list of changes is not part of the license.)
|
||||
'''General note on changes''': as usage of free EiffelBase grows and more people contribute to it, some terms of the license text may occasionally have to be changed in light of this accumulated experience and of comments received from users and contributors. It is our intention to ensure that
|
||||
* (1) Any such changes will be minor, affecting the details of the licensing rather than the spirit of IFELL.
|
||||
* (2) If they affect the substance of the license in any way, they go in the general direction of extending, not restricting, the rights of users of EiffelBase.
|
||||
|
||||
|
||||
Should a question arise regarding use of EiffelBase, the terms to be applied are those in effect at the time of use, disregarding any later change.
|
||||
|
||||
'''28 June 2000''': in response to comments by Joachim Durchholz through Patrick Schoenbach, replacement of most occurrences of the word "EiffelBase" by"the Library" to facilitate application to other libraries.
|
||||
|
||||
'''7 May 1999''': in response to comments by David Broadfoot on the Eiffel Software user list, rewording of the [[#contamination|explanation]] of why we are applying more liberal terms than GPL-style licenses. regarding fears of [[#contamination|contamination]] with the Gnu Public License.
|
||||
|
||||
'''6 May 1999''': following comments by Ed Avis and Alexander Kjeldaas, we now permit distribution maintainers to continue distributing an old version as long as they [[#obsolete_version|mention clearly that it's obsolete ]] .
|
||||
|
||||
'''7 April 1999''': following comments by Loryn Jenkins and Ed Avis, removal of the requirement that bug fixes and improvements be provided back to the maintainers of the library. This requirement has been changed to a mere [[#encourage_bug_reports|encouragement ]] , facilitating use of the library. Of course it is in everyone's interest to report bugs and improvements.
|
||||
|
||||
'''7 April 1999''': following comments by Loryn Jenkins, loosening of the [[#valid_eiffel|definition of "valid Eiffel"]] . This change also caused addition of the notion of [[#reference_implementation|reference implementation]] . The purpose is to avoid crippling EiffelBase by preventing it from utilizing innovative language constructs and corrections in the language contributions. Such language extensions are OK as long as they have been accepted for discussion by NICE and are supported by the reference implementation.
|
||||
|
||||
'''1 April 1999''': following some comments by Richard Stallman,addition of a qualification (" ''justified or not''") to the notes regarding fears of [[#contamination|contamination ]] with the Gnu Public License. Our intention is not to criticize the GPL but simply to note that the GPL does cause some fears, "justified or not", among commercial developers.
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
[[Property:title|EiffelBase]]
|
||||
[[Property:weight|1]]
|
||||
[[Property:uuid|0153c1de-bf88-fa0d-52a5-e50ffcc4e8c8]]
|
||||
==The EiffelBase Library==
|
||||
|
||||
Type: Library <br/>
|
||||
Platform: Any <br/>
|
||||
Availability: Open Source, Eiffel Forum License version 2 (EFLv2)
|
||||
|
||||
The EiffelBase class library, covered by the open-source Eiffel Forum License version 2 (EFLv2), is one of the principal contributions of Eiffel: a library of fundamental structures and algorithms covering the basics of computing, and resulting from a "Linnaean" effort at a general-purpose taxonomy of computing structures. EiffelBase is one of the most carefully designed and extensively used libraries in the object-oriented industry.
|
||||
|
||||
Reference in New Issue
Block a user