Introduction
This article describes what an active iterator is, and how to use a
factory method to create one.
There are also examples of using System.Address_To_Access_Conversions to
get around the limitation of not having in out parameter modes for
functions.
Iterators: Active vs Passive
Ordered data structures allow you to access a single item, for example
the top of a stack, or the front of a queue, or the head of a list. An
iterator effectively extends the behavior of the abstraction, by
allowing you to visit all the items in a data structure.
There are two kinds of iterators: passive and active. A passive
iterator controls the actual movement within the data structure, and all
a client has to do is supply a procedure to receive each item in turn.
There was an example of a passive iterator in my posts about the visitor
pattern:
generic
with procedure Process
(Item : access Root_Equipment'Class);
procedure For_Every_Item
(Composite : access Composite_Equipment'Class);
You can use this to visit each piece of equipment in the composite
object. The composite itself uses it to implement its destructor:
procedure Do_Free
(Composite : access Composite_Equipment) is
procedure Free_Item
(Item : access Root_Equipment'Class) is
begin
Do_Free (Item);
end;
procedure Free_Items is
new For_Every_Item (Free_Item);
begin
Free_Items (Composite);
end Do_Free;
An active iterator moves the resposibility for movement onto the client.
Unlike a passive iterator, which is essentially just a generic
subprogram, an active iterator is an actual type, with primitive
operations for retrieving the current item and for moving to the next
item in the sequence.
You often see active iterators implemented as a limited private type,
with an access discriminant:
type Stack_Iterator
(Stack : access Stack_Type) is limited private;
function Done
(Iterator : Stack_Iterator) return Boolean;
function Get_Item
(Iterator : Stack_Iterator)
return Item_Type;
procedure Advance
(Iterator : in out Stack_Iterator);
You would typically use this in a loop, to process each item:
Stack : aliased Stack_Type;
...
declare
Iter : Stack_Iterator (Stack'Access);
begin
while not Done (Iter) loop
... process Get_Item (Iter) ...
Advance (Iter);
end loop;
end;
Factory Methods
However, there is a potential problem with the formulation above. There
are times when you'd like to be able to invoke a constructor for the
iterator during its declaration, as in:
declare
Iter : Iterator_Type := Initialize (Stack);
begin
but this is not possible, because the type is limited.
Here's an example of a time when you'd need to do this. Each specific
type in a data structure hierarchy needs its own iterator, because the
iterator needs to have access to the representation of the type. But
suppose you want to iterate over a stack (say) whose type is class-wide,
something like:
procedure Print (Stack : in Root_Stack'Class) is
Iter : ...; <-- ???
begin
How to you get an iterator for the specific type, if you have only a
class-wide object?
The answer is that you ask the stack to give you an iterator that will
allow you to iterate over that stack. Here's where the factory method
comes in.
A "factory method" is just a fancy name for a dispatching constructor
that returns a class-wide object. A factory method for iterators would
look something like:
function Initialize
(Stack : Root_Stack)
return Root_Iterator'Class;
Note carefully the types in this declaration. Initialize is a primitive
operation of stack, and therefore takes the specific stack type as a
parameter, but its return type is a class-wide iterator.
We can implement the Print operation above using a factory method
constructor for iterators:
procedure Print (Stack : in Root_Stack'Class) is
Iter : Root_Iterator'Class := Initialize (Stack);
begin
We then call iterator operations (Get_Item, Advance) that dispatch
according to the tag of the iterator:
while not Done (Iter) loop
Put_Line (Get_Item (Iter));
Advance (Iter);
end loop;
end Print;
This is also an example of double dispatching: dispatch an interator
constructor according to the tag of Stack, and then dispatch iterator
operations according to the tag of the iterator returned by the
constructor.
The declaration of a class-wide object requires that the object be given
an initial value, because a class-wide type is indefinate. (In our case,
the initial value is supplied by the factory method constructor.) This
means we need assignment for the type, and therefore the iterator must
be declared as non-limited.
Discussion of Examples
I've chosen a stack as the data structure to illustrate iterators and
factory methods, because it presents an interesting problem: How do you
copy one stack into another, without the copy being in reverse order?
In a naive implementation, we would iterate over the source stack from
top to bottom, and push each item onto the target stack. But this is
wrong, because the target would be backwards (the top of the target
stack contains the bottom of the source stack).
There are two ways to solve this problem:
1) Iterate over the source stack in bottom-to-top order, and push each
item in turn onto the target.
2) Iterate over the source stack in top-to-bottom order, but populate
the target stack backwards (push the item on bottom of the stack).
There are implementation issues associated with each of these solutions:
1) The iterator for the source stack has to be able to traverse that
stack in bottom-to-top order. This will require that the source stack
be implemented using a doubly-linked list. If you only needed
top-to-bottom traversal, then a singly-linked list implementation would
suffice.
The benefit of this approach is that, if you do have a bottom-to-top
iterator, then you only have to implement the Copy operation once,
because it uses the standard Push operation of the target.
2) In order to populate the target stack backwards, you have to have
access to the representation of the stack, because you're doing a very
special kind of push. This will require that the populate operation
(here, Copy) be a primitive operation of the target stack.
For a stack hierarchy, this requires that each different stack type
implement its own version of Copy (because each target type has a
different representation). If your source stack doesn't have a
bottom-to-top iterator, this is your only choice.
For this example, I've implemented Copy using the first approach (see
the body of Stacks):
procedure Copy
(From : in Root_Stack'Class;
To : in out Root_Stack) is
Iter : Root_Iterator'Class :=
Start_At_Bottom (From);
begin
...
Like we saw earlier in the Print operation, this operation starts off by
declaring an class-wide iterator object, initialized by calling a
factory method, Start_At_Bottom.
Since this stack is implemented using a doubly-linked list, it permits
iteration in both top-to-bottom and bottom-to-top order. That means the
client needs to state where she wants to start, either at the top of the
stack or the bottom.
There are therefore two constructors for this iterator class:
function Start_At_Top
(Stack : Root_Stack)
return Root_Iterator'Class is abstract;
function Start_At_Bottom
(Stack : Root_Stack)
return Root_Iterator'Class is abstract;
(For subtle reasons, I decided not to implement one constructor that
takes an enumeration value indicating which end to start at.)
There are also two modifiers to move the iterator, either towards the
bottom (Advance) or towards the top (Backup):
procedure Advance
(Iter : in out Root_Iterator) is abstract;
procedure Backup
(Iter : in out Root_Iterator) is abstract;
The remainder of the Copy operation follows the same pattern as we've
seen before: call Get_Item to return the current item designated by the
iterator, do something with the item (here, push it on the target
stack), and then move the iterator (Backup) in order to designate the
next item:
begin
...
for I in 1 .. Get_Length (From) loop
Push (Get_Item (Iter), On => Root_Stack'Class (To));
Backup (Iter);
end loop;
end Copy;
The next example of iterator and factory method combined is in the
implementation of equality. Stacks are equal if they have the same
values in the same order, so we should be able to compare stacks just by
using iterators to compare items. Furthermore, we should only need to
write this operation once, so that we can compare any stack type to any
other stack type in the same class.
Equality starts out by using a constructor to create iterators for both
arguments:
function "="
(L : Root_Stack;
R : Root_Stack'Class)
return Boolean is
L_Iter : Root_Iterator'Class :=
Start_At_Top (Root_Stack'Class (L));
R_Iter : Root_Iterator'Class :=
Start_At_Top (R);
...
It doesn't really make any difference where we start, so here I decided
to just start at the top of the stacks, and advance towards the bottom.
The rest of the body of equality looks like this:
begin
if L_Len /= R_Len then
return False;
end if;
for I in 1 .. L_Len loop
if Get_Item (L_Iter) /= Get_Item (R_Iter) then
return False;
end if;
Advance (L_Iter);
Advance (R_Iter);
end loop;
return True;
end "=";
We use the iterators to compare the items of each stack in turn. As
soon as we get a mismatch, we know that stacks aren't equal, and so we
just return. Otherwise, we advance the iterators (move towards the
bottom), and then compare the next pair of items. This continues until
either a mismatch occurs, or we've compared all the items.
In Out Parameters For Functions
Although Ada doesn't officially have in out parameters for functions,
you can still give a function behavior that amounts to the same thing.
One useful thing to be able to do is to modify the item on top of the
stack, without actually copying a new item to the top. This doesn't
mean replacing the item on the top with another item that has a
different value -- it means modifying the actual item, in place.
What we do is to return a reference to the top item. Ada doesn't have
reference types like you have in C++, so we use access types instead,
which give you the same thing with only a small amount of additional
syntactic overhead.
The operation Set_Top is _function_ that allows the client to modify the
item on top of the stack, by returning a pointer to the item on top:
type Item_Access is
access all Item_Type;
function Set_Top
(Stack : Root_Stack)
return Item_Access is abstract;
You use it as follows:
Set_Top (Stack).all := ;
or, if the Item_Type is a record:
Set_Top (Stack). := ;
or, passing it to a modifier:
Do_Something (To_The_Object => Set_Top (Stack).all);
For example, suppose a stack of integers has the following value (top to
bottom):
3 2 1
If I say
Set_Top (Stack).all := 4;
then the stack now looks like
4 2 1
If we combine the ability to modify an item in place with iterators, it
becomes even more powerful, because we can then change any item in the
stack, not just the top item. The iterator modifier looks like:
function Set_Item
(Iter : Root_Iterator)
return Item_Access is abstract;
which allows us to change the current item designated by the iterator.
Let's say we want to change our stack above, by adding 1 to every value
in the stack. Here's the code:
declare
Iter : Root_Iterator'Class := Start_At_Top (S);
begin
while not Done (Iter) loop
Set_Item (Iter).all := Get_Item (Iter) + 1;
Advance (Iter);
end loop;
Print (S);
end;
So our stack went from the value
4 2 1
to the value
5 3 2
The package System.Address_To_Access_Conversions provides the magic that
makes this behavior possible.
The Stack argument of function Set_Top provides only a "constant view"
of that object, because its mode is in. However, by using
Address_To_Access_Conversions, we can get a "variable view," which will
allow us to modify the item. Here's how:
function Set_Top
(Stack : Bounded_Stack)
return Item_Access is
SA : constant Object_Pointer :=
To_Pointer (Stack'Address);
...
Address_To_Access_Conversions provides an operation, To_Pointer, which
converts an address into an access-to-variable type, Object_Pointer.
Now that we have an access-to-variable view of the stack, we can then
return a pointer to the top item:
return SA.Items (Top)'Access;
end Set_Top;
Taking the address of tagged type is more-or-less well-defined, per RM95
13.3 (16), because a tagged type passed as a subprogram argument
provides an "aliased view" of that object. Don't try to use this
technique if your type isn't a by-reference type.
The implementation of iterator operation Set_Item is similar, except
that you're converting an access-to-constant pointer to an
access-to-variable pointer:
function Set_Item
(Iter : Stack_Iterator)
return Item_Access is
SA : constant Object_Pointer :=
To_Pointer (Iter.Stack.all'Address);
begin
return SA.Items (Iter.Index)'Access;
end;
(These examples of Address_To_Access_Conversions are only needed for the
bounded version of the stack; see the file stacks-bounded_g.adb.)
Happy iterating,
Matt
P.S. Ada95 Tip O' The Day: Remember, tagged types passed as subprogram
arguments are implicitly aliased; that's why you can do this:
type T is tagged private;
type Iterator (O : access T) is limited private;
procedure Op (O : in out T) is
Iter : Iterator (O'Access);
begin
...
You can't officially do this for non-tagged types, although as we've
seen, if you have a pass-by-reference type, you can then safely use
System.Address_To_Access_Conversions. (In addition to all tagged types,
a limited type whose full view is limited is also passed by reference.)
The sources below are in a format suitable for use with gnatchop.
I recommend you enable assertion checks when building this code. If
you're using gnat, use this command:
gnatmake -gnata test_unbounded
gnatmake -gnata test_bounded
gnatmake -gnata test_stacks
I prefer to test for error conditions (such as trying to pop an empty
stack) by using pragma Assert. That way, I can turn off assertion
checks when I'm satisfied everything's working.
--STX
with Stacks.Bounded_G;
package Integer_Stacks.Bounded is
new Integer_Stacks.Bounded_G (Size => 5);
with Stacks.Stack_IO_G;
package Integer_Stacks.Stack_IO is
new Integer_Stacks.Stack_IO_G (Integer'Image);
with Stacks.Unbounded_G;
package Integer_Stacks.Unbounded is
new Integer_Stacks.Unbounded_G;
with Stacks;
package Integer_Stacks is
new Stacks (Integer);
with System.Address_To_Access_Conversions;
package body Stacks.Bounded_G is
package Access_Conversions is
new System.Address_To_Access_Conversions (Bounded_Stack);
use Access_Conversions;
procedure Clear
(Stack : in out Bounded_Stack) is
begin
Stack.Top := 0;
end Clear;
procedure Push
(Item : in Item_Type;
On : in out Bounded_Stack) is
Top : Natural renames On.Top;
begin
pragma Assert (Top < Size);
Top := Top + 1;
On.Items (Top) := Item;
end Push;
procedure Pop
(Stack : in out Bounded_Stack) is
Top : Natural renames Stack.Top;
begin
pragma Assert (Top /= 0);
Top := Top - 1;
end;
function Get_Top
(Stack : Bounded_Stack)
return Item_Type is
Top : Natural renames Stack.Top;
begin
pragma Assert (Top /= 0);
return Stack.Items (Top);
end;
function Set_Top
(Stack : Bounded_Stack)
return Item_Access is
Top : Natural renames Stack.Top;
SA : constant Object_Pointer :=
To_Pointer (Stack'Address);
begin
pragma Assert (Top /= 0);
return SA.Items (Top)'Access;
end;
function Get_Length
(Stack : Bounded_Stack)
return Natural is
begin
return Stack.Top;
end;
function Is_Full
(Stack : Bounded_Stack)
return Boolean is
begin
return Stack.Top = Size;
end;
function Start_At_Top
(Stack : Bounded_Stack)
return Root_Iterator'Class is
Iter : constant Stack_Iterator :=
(Stack => Stack'Unchecked_Access,
Index => Stack.Top);
begin
return Iter;
end;
function Start_At_Bottom
(Stack : Bounded_Stack)
return Root_Iterator'Class is
Iter : Stack_Iterator :=
(Stack => Stack'Unchecked_Access,
Index => 1);
begin
return Iter;
end;
function Done
(Iter : Stack_Iterator)
return Boolean is
begin
return
Iter.Index = 0 or
Iter.Index > Iter.Stack.Top;
end;
function Get_Item
(Iter : Stack_Iterator)
return Item_Type is
begin
pragma Assert (Iter.Index in 1 .. Iter.Stack.Top);
return Iter.Stack.Items (Iter.Index);
end;
function Set_Item
(Iter : Stack_Iterator)
return Item_Access is
SA : constant Object_Pointer :=
To_Pointer (Iter.Stack.all'Address);
begin
pragma Assert (Iter.Index in 1 .. Iter.Stack.Top);
return SA.Items (Iter.Index)'Access;
end;
procedure Advance
(Iter : in out Stack_Iterator) is
begin
pragma Assert (Iter.Index > 0);
Iter.Index := Iter.Index - 1;
end;
procedure Backup
(Iter : in out Stack_Iterator) is
begin
pragma Assert (Iter.Index <= Iter.Stack.Top);
Iter.Index := Iter.Index + 1;
end;
end Stacks.Bounded_G;
generic
Size : Positive;
package Stacks.Bounded_G is
pragma Preelaborate;
type Bounded_Stack is
new Root_Stack with private;
procedure Clear
(Stack : in out Bounded_Stack);
procedure Push
(Item : in Item_Type;
On : in out Bounded_Stack);
procedure Pop
(Stack : in out Bounded_Stack);
function Get_Top
(Stack : Bounded_Stack)
return Item_Type;
function Set_Top
(Stack : Bounded_Stack)
return Item_Access;
function Get_Length
(Stack : Bounded_Stack)
return Natural;
function Is_Full
(Stack : Bounded_Stack)
return Boolean;
function Start_At_Top
(Stack : Bounded_Stack)
return Root_Iterator'Class;
function Start_At_Bottom
(Stack : Bounded_Stack)
return Root_Iterator'Class;
private
type Item_Array is
array (Positive range 1 .. Size) of aliased Item_Type;
type Bounded_Stack is
new Root_Stack with record
Items : Item_Array;
Top : Natural := 0;
end record;
type Stack_Access is
access constant Bounded_Stack;
type Stack_Iterator is
new Root_Iterator with record
Stack : Stack_Access;
Index : Natural;
end record;
function Done
(Iter : Stack_Iterator)
return Boolean;
function Get_Item
(Iter : Stack_Iterator)
return Item_Type;
function Set_Item
(Iter : Stack_Iterator)
return Item_Access;
procedure Advance
(Iter : in out Stack_Iterator);
procedure Backup
(Iter : in out Stack_Iterator);
end Stacks.Bounded_G;
with Ada.Text_IO; use Ada.Text_IO;
package body Stacks.Stack_IO_G is
procedure Print (Stack : in Root_Stack'Class) is
Iter : Root_Iterator'Class :=
Start_At_Top (Stack);
begin
if Is_Empty (Stack) then
Put_Line ("");
return;
end if;
for I in 1 .. Get_Length (Stack) loop
Put (Image (Get_Item (Iter)));
Advance (Iter);
end loop;
New_Line;
end Print;
end Stacks.Stack_IO_G;
generic
with function Image
(Item : Item_Type) return String;
package Stacks.Stack_IO_G is
procedure Print (Stack : in Root_Stack'Class);
end Stacks.Stack_IO_G;
with Ada.Unchecked_Deallocation;
package body Stacks.Unbounded_G is
procedure Finalize
(Control : in out Stack_Control) is
begin
Clear (Control.Stack.all);
end Finalize;
procedure Free is
new Ada.Unchecked_Deallocation (Node, Node_Access);
procedure Clear
(Stack : in out Unbounded_Stack) is
Top : Node_Access renames Stack.Top;
Node : Node_Access;
begin
for I in 1 .. Get_Length (Stack) loop
Node := Top;
Top := Top.Next;
Free (Node);
end loop;
pragma Assert (Stack.Top = null);
Stack.Bottom := null;
Stack.Length := 0;
end Clear;
procedure Push
(Item : in Item_Type;
On : in out Unbounded_Stack) is
Stack : Unbounded_Stack renames On;
New_Node : constant Node_Access :=
new Node;
begin
New_Node.Item := Item;
if Stack.Length = 0 then
Stack.Top := New_Node;
Stack.Bottom := New_Node;
Stack.Length := 1;
else
New_Node.Next := Stack.Top;
Stack.Top.Prev := New_Node;
Stack.Top := New_Node;
Stack.Length := Stack.Length + 1;
end if;
end Push;
procedure Pop
(Stack : in out Unbounded_Stack) is
Node : Node_Access := Stack.Top;
begin
pragma Assert (Stack.Length /= 0);
if Stack.Length = 1 then
Stack.Top := null;
Stack.Bottom := null;
Stack.Length := 0;
else
Stack.Top := Stack.Top.Next;
Stack.Top.Prev := null;
Stack.Length := Stack.Length - 1;
end if;
Free (Node);
end Pop;
function Get_Top
(Stack : Unbounded_Stack)
return Item_Type is
begin
pragma Assert (Stack.Length /= 0);
return Stack.Top.Item;
end;
function Set_Top
(Stack : Unbounded_Stack)
return Item_Access is
begin
pragma Assert (Stack.Length /= 0);
return Stack.Top.Item'Access;
end;
function Get_Length
(Stack : Unbounded_Stack)
return Natural is
begin
return Stack.Length;
end;
function Start_At_Top
(Stack : Unbounded_Stack)
return Root_Iterator'Class is
Iter : constant Stack_Iterator :=
(Node => Stack.Top);
begin
return Iter;
end;
function Start_At_Bottom
(Stack : Unbounded_Stack)
return Root_Iterator'Class is
Iter : Stack_Iterator :=
(Node => Stack.Bottom);
begin
return Iter;
end;
function Done
(Iter : Stack_Iterator)
return Boolean is
begin
return Iter.Node = null;
end;
function Get_Item
(Iter : Stack_Iterator)
return Item_Type is
begin
return Iter.Node.Item;
end;
function Set_Item
(Iter : Stack_Iterator)
return Item_Access is
begin
return Iter.Node.Item'Access;
end;
procedure Advance
(Iter : in out Stack_Iterator) is
begin
Iter.Node := Iter.Node.Next;
end;
procedure Backup
(Iter : in out Stack_Iterator) is
begin
Iter.Node := Iter.Node.Prev;
end;
end Stacks.Unbounded_G;
with Ada.Finalization;
generic
package Stacks.Unbounded_G is
pragma Preelaborate;
type Unbounded_Stack is
new Root_Stack with private;
procedure Clear
(Stack : in out Unbounded_Stack);
procedure Push
(Item : in Item_Type;
On : in out Unbounded_Stack);
procedure Pop
(Stack : in out Unbounded_Stack);
function Get_Top
(Stack : Unbounded_Stack)
return Item_Type;
function Set_Top
(Stack : Unbounded_Stack)
return Item_Access;
function Get_Length
(Stack : Unbounded_Stack)
return Natural;
function Start_At_Top
(Stack : Unbounded_Stack)
return Root_Iterator'Class;
function Start_At_Bottom
(Stack : Unbounded_Stack)
return Root_Iterator'Class;
private
type Node;
type Node_Access is access Node;
type Node is
limited record
Item : aliased Item_Type;
Next : Node_Access;
Prev : Node_Access;
end record;
type Stack_Control (Stack : access Unbounded_Stack) is
new Ada.Finalization.Limited_Controlled with null record;
procedure Finalize
(Control : in out Stack_Control);
type Unbounded_Stack is
new Root_Stack with record
Top : Node_Access;
Bottom : Node_Access;
Length : Natural := 0;
Control : Stack_Control (Unbounded_Stack'Access);
end record;
type Stack_Iterator is
new Root_Iterator with record
Node : Node_Access;
end record;
function Done
(Iter : Stack_Iterator)
return Boolean;
function Get_Item
(Iter : Stack_Iterator)
return Item_Type;
function Set_Item
(Iter : Stack_Iterator)
return Item_Access;
procedure Advance
(Iter : in out Stack_Iterator);
procedure Backup
(Iter : in out Stack_Iterator);
end Stacks.Unbounded_G;
with System; use type System.Address;
package body Stacks is
procedure Copy
(From : in Root_Stack'Class;
To : in out Root_Stack) is
Iter : Root_Iterator'Class :=
Start_At_Bottom (From);
begin
if From'Address = To'Address then
return;
end if;
Clear (Root_Stack'Class (To));
for I in 1 .. Get_Length (From) loop
Push (Get_Item (Iter), On => Root_Stack'Class (To));
Backup (Iter);
end loop;
end Copy;
function "="
(L : Root_Stack;
R : Root_Stack'Class)
return Boolean is
L_Iter : Root_Iterator'Class :=
Start_At_Top (Root_Stack'Class (L));
R_Iter : Root_Iterator'Class :=
Start_At_Top (R);
L_Len : constant Natural :=
Get_Length (Root_Stack'Class (L));
R_Len : constant Natural :=
Get_Length (R);
begin
if L_Len /= R_Len then
return False;
end if;
for I in 1 .. L_Len loop
if Get_Item (L_Iter) /= Get_Item (R_Iter) then
return False;
end if;
Advance (L_Iter);
Advance (R_Iter);
end loop;
return True;
end "=";
function Is_Empty
(Stack : Root_Stack) return Boolean is
begin
return Get_Length (Root_Stack'Class (Stack)) = 0;
end;
end Stacks;
generic
type Item_Type is private;
with function "="
(L, R : Item_Type)
return Boolean is <>;
package Stacks is
pragma Preelaborate;
type Root_Stack is
abstract tagged limited null record;
procedure Copy
(From : in Root_Stack'Class;
To : in out Root_Stack);
procedure Clear
(Stack : in out Root_Stack) is abstract;
procedure Push
(Item : in Item_Type;
On : in out Root_Stack) is abstract;
procedure Pop
(Stack : in out Root_Stack) is abstract;
function "="
(L : Root_Stack;
R : Root_Stack'Class)
return Boolean;
function Get_Top
(Stack : Root_Stack)
return Item_Type is abstract;
type Item_Access is
access all Item_Type;
function Set_Top
(Stack : Root_Stack)
return Item_Access is abstract;
function Get_Length
(Stack : Root_Stack)
return Natural is abstract;
function Is_Empty
(Stack : Root_Stack)
return Boolean;
type Root_Iterator is
abstract tagged null record;
function Start_At_Top
(Stack : Root_Stack)
return Root_Iterator'Class is abstract;
function Start_At_Bottom
(Stack : Root_Stack)
return Root_Iterator'Class is abstract;
function Done
(Iter : Root_Iterator)
return Boolean is abstract;
function Get_Item
(Iter : Root_Iterator)
return Item_Type is abstract;
function Set_Item
(Iter : Root_Iterator)
return Item_Access is abstract;
procedure Advance
(Iter : in out Root_Iterator) is abstract;
procedure Backup
(Iter : in out Root_Iterator) is abstract;
end Stacks;
with Integer_Stacks.Bounded; use Integer_Stacks.Bounded;
with Integer_Stacks.Stack_IO; use Integer_Stacks.Stack_IO;
with Ada.Text_IO; use Ada.Text_IO;
procedure Test_Bounded is
S : Bounded_Stack;
S2 : Bounded_Stack;
use Integer_Stacks;
begin
Print (S);
Push (1, S);
Print (S);
Pop (S);
Print (S);
Put_Line
("Stacks are equal: " &
Boolean'Image (S = S2));
Push (1, S);
Put ("Len:" & Integer'Image (Get_Length (S)));
Put (" Top:" & Integer'Image (Get_Top (S)));
New_Line;
Push (2, S);
Put ("Len:" & Integer'Image (Get_Length (S)));
Put (" Top:" & Integer'Image (Get_Top (S)));
New_Line;
Push (3, S);
Put ("Len:" & Integer'Image (Get_Length (S)));
Put (" Top:" & Integer'Image (Get_Top (S)));
New_Line;
Put ("S:"); Print (S);
Copy (From => S, To => S2);
Put ("S2:"); Print (S2);
Put_Line
("Stacks are equal: " &
Boolean'Image (S = S2));
Set_Top (S).all := 4;
Put ("S:"); Print (S);
declare
Iter : Root_Iterator'Class := Start_At_Top (S);
begin
while not Done (Iter) loop
Set_Item (Iter).all := Get_Item (Iter) + 1;
Advance (Iter);
end loop;
Put ("S:"); Print (S);
end;
end Test_Bounded;
with Integer_Stacks.Bounded; use Integer_Stacks.Bounded;
with Integer_Stacks.Unbounded; use Integer_Stacks.Unbounded;
with Integer_Stacks.Stack_IO; use Integer_Stacks.Stack_IO;
with Ada.Text_IO; use Ada.Text_IO;
procedure Test_Stacks is
B : Bounded_Stack;
U : Unbounded_Stack;
begin
Print (B);
Push (1, B);
Print (B);
Pop (B);
Print (B);
Put_Line ("Compare empty B to empty U");
Put_Line
("Stacks are equal: " &
Boolean'Image (B = U));
Put_Line
("Stacks are equal: " &
Boolean'Image (U = B));
New_Line;
Put_Line ("Compare non-empty B to empty U");
Push (1, B);
Push (2, B);
Push (3, B);
Put ("B:"); Print (B);
Put ("U:"); Print (U);
Put_Line
("Stacks are equal: " &
Boolean'Image (B = U));
Put_Line
("Stacks are equal: " &
Boolean'Image (U = B));
New_Line;
Put_Line ("Copy B to U");
Copy (From => B, To => U);
Put ("U:"); Print (U);
Put_Line
("Stacks are equal: " &
Boolean'Image (B = U));
Put_Line
("Stacks are equal: " &
Boolean'Image (U = B));
New_Line;
Put_Line ("Copy U to itself");
Copy (From => U, To => U);
Put ("U:"); Print (U);
New_Line;
Put_Line ("Copy U to B");
Push (5, U);
Push (6, U);
Put ("U:"); Print (U);
Put ("B:"); Print (B);
Copy (From => U, To => B);
Put ("B:"); Print (B);
Put_Line
("Stacks are equal: " &
Boolean'Image (B = U));
Put_Line
("Stacks are equal: " &
Boolean'Image (U = B));
New_Line;
Put_Line ("Clear U, then copy to B");
Clear (U);
Put ("U:"); Print (U);
Put ("B:"); Print (B);
Copy (From => U, To => B);
Put ("B:"); Print (B);
Put_Line
("Stacks are equal: " &
Boolean'Image (B = U));
Put_Line
("Stacks are equal: " &
Boolean'Image (U = B));
end Test_Stacks;
with Integer_Stacks.Unbounded; use Integer_Stacks.Unbounded;
with Integer_Stacks.Stack_IO; use Integer_Stacks.Stack_IO;
with Ada.Text_IO; use Ada.Text_IO;
procedure Test_Unbounded is
S : Unbounded_Stack;
S2 : Unbounded_Stack;
use Integer_Stacks;
begin
Print (S);
Push (1, S);
Print (S);
Pop (S);
Print (S);
Put_Line
("Stacks are equal: " &
Boolean'Image (S = S2));
Push (1, S);
Put ("Len:" & Integer'Image (Get_Length (S)));
Put (" Top:" & Integer'Image (Get_Top (S)));
New_Line;
Push (2, S);
Put ("Len:" & Integer'Image (Get_Length (S)));
Put (" Top:" & Integer'Image (Get_Top (S)));
New_Line;
Push (3, S);
Put ("Len:" & Integer'Image (Get_Length (S)));
Put (" Top:" & Integer'Image (Get_Top (S)));
New_Line;
Put ("S:"); Print (S);
Copy (From => S, To => S2);
Put ("S2:"); Print (S2);
Put_Line
("Stacks are equal: " &
Boolean'Image (S = S2));
Set_Top (S).all := 4;
Put ("S:"); Print (S);
declare
Iter : Root_Iterator'Class := Start_At_Top (S);
begin
while not Done (Iter) loop
Set_Item (Iter).all := Get_Item (Iter) + 1;
Advance (Iter);
end loop;
Put ("S:"); Print (S);
end;
end Test_Unbounded;
|