Computing » Computer Science » Computer Programming » Ada Programming
This language feature is only available from Ada 2005 on.
Containers is an Ada (2005 or later) specification that provides facilities for storing collections of elements. There are several types of containers defined as child packages of Ada.Containers, each one with different features. All container packages are generic units, thus it is recommended to read Generics section of this Wikibook.
The following table list all containers available in the Containers package. The array type is provided by the language itself and is also listed for comparative purposes. The table describes the following features: the package or the container name; if the elements are associated to a specific index; if the elements are stored in a specific order.
| Package/Name | Indexed | Ordered |
|---|---|---|
| array | By scalar type | No |
| Ada.Containers.Vectors | By scalar type | No |
| Ada.Containers.Doubly_Linked_Lists | No | No |
| Maps | ||
| Ada.Containers.Hashed_Maps | By key type | No |
| Ada.Containers.Ordered_Maps | By key type | Yes |
| Sets | ||
| Ada.Containers.Hashed_Sets | No | No |
| Ada.Containers.Ordered_Sets | No | Yes |
| Trees | ||
| Ada.Containers.Multiway_Trees | ||
| Indefinites | ||
| Ada.Containers.Indefinite_Vectors | By scalar type | No |
| Ada.Containers.Indefinite_Doubly_Linked_Lists | No | No |
| Ada.Containers.Indefinite_Hashed_Maps | ||
| Ada.Containers.Indefinite_Ordered_Maps | By key type | Yes |
| Ada.Containers.Indefinite_Hashed_Sets | No | No |
| Ada.Containers.Indefinite_Ordered_Sets | No | Yes |
| Ada.Containers.Indefinite_Multiway_Trees | ||
| Ada.Containers.Indefinite_Holders | ||
| Bounded | ||
| Ada.Containers.Bounded_Vectors | By scalar type | No |
| Ada.Containers.Bounded_Doubly_Linked_Lists | No | No |
| Ada.Containers.Bounded_Hashed_Maps | By key type | No |
| Ada.Containers.Bounded_Ordered_Maps | By key type | Yes |
| Ada.Containers.Bounded_Hashed_Sets | No | No |
| Ada.Containers.Bounded_Ordered_Sets | No | Yes |
| Ada.Containers.Bounded_Multiway_Trees | ||
| Array Sorting | ||
| Ada.Containers.Synchronized_Queue_Interfaces | ||
| Ada.Containers.Unbounded_Synchronized_Queues | ||
| Ada.Containers.Bounded_Synchronized_Queues | ||
| Ada.Containers.Unbounded_Priority_Queues | ||
| Ada.Containers.Bounded_Priority_Queues |
Vectors
A vector container is similar to an array. The difference is that the vector can expand automatically its capacity in run-time. It has the following features:
- It can contain several elements of only one specific type.In order to store elements of different types in a container, a new type must be created to support each of the types. For instance, by using an variant record where each component store an element of each type.
- The index must be of scalar type. It can be a subtype defined by the user.
- The capacity is expanded on run-time.
- An element contained can be accessed in any indexed position (random access).
- An element can be inserted or deleted in any indexed position.
Specifically optimized for insertion/deletion at the high end.
- A Cursor type can be used to access the contained elements.
- A user-defined procedure can be used to iterate (and operate on) the contained elements.
- Find functions are provided.
- There is an "empty element" (a No_Element constant) for positions without an element assigned.
It is important to note that length and capacity are not the same. Length is the current amount of elements the containers possess. Its capacity is the maximum amount of elements it can currently contain. Once the capacity is exceeded, the vector expands it automatically.
Creating a new Vector
To create a new vector, first a new instance package must be created from the generic package Ada.Containers.Vectors. There are three generic parameters: Index_Type which defines the scalar type of the index, Element_Type which is the type of the items to store, and the "=" function used to find and determine if an element is present on the vector. The "=" function can be optional, using the default equal operation of the Element_Type. For instance, the following code creates a new vector package that uses Positive numbers as index, Unbounded_String as elements, and the default "=" from Unbounded_String:
- package MyVectors is new Ada.Containers.Vectors (
Index_Type => Positive,
Element_Type => Unbounded_String);
Now, a vector instance can be created in a variable declaration:
Motivational_Phrases : MyVectors.Vector;
Vector examples
A complete example using the codes shown above can be the following:
File: motivation.adb (view, plain text, download page, browse all)
with Ada.Containers.Vectors;
with Ada.Strings.Unbounded; use Ada.Strings.Unbounded;
with Ada.Text_IO; use Ada.Text_IO;
-- Create a new package instance of Ada.Containers.Vectors.
package MyVectors is new Ada.Containers.Vectors (
Index_Type => Positive,
Element_Type => Unbounded_String);
-- Create a new vector instance.
Motivational_Phrases : MyVectors.Vector;
begin
-- Add some nice phrases!
MyVectors.Append (Motivational_Phrases, To_Unbounded_String ("Yes! This is great!"));
MyVectors.Append (Motivational_Phrases, To_Unbounded_String ("I learn something new with Ada!"));
-- Loop on each element and print it on standard output.
for Elt of Motivational_Phrases loop
Put_Line (To_String (Elt));
end loop;
-- Print the second phrase again!
Put_Line (To_String (MyVectors.Element (Motivational_Phrases, 2)));
end Motivation;
Maps
Maps, sometimes called "associations" or "dictionaries", establish a relation between items of one type with items of another. One item is called the "key", and the associated item is the "value".
One key is limited to be associated to one value. If the developer requires to associate a key to different values, then it must encapsulate these into a data type such as an array, vector, or set. Thus, a key is associated to a container instance, which in turn, it possess all the values.
There are two types of maps: the ordered and hashed maps. The first one uses the function "<" to insert the keys in an order fashion, along with their values. The second one, uses a hash function to create a shorter or simpler hash value from the key. The hash value must be the same for the same key.
When inserting the same key twice with Insert, a Constraint_Error is raised. Instead, use the Replace procedure, which removes the last element associated to the key, and store the new one.
Map creation
To create an ordered map, the generic Ada.Containers.Ordered_Maps package requires the following parameters: Key_Type, Element_Type, the "<" function for the key, and "=" function for the element. If the last two paramaters are not provided, then the default are used for the corresponding type.
package MyMaps is new Ada.Containers.Ordered_Maps (
Key_Type => Unbounded_String,
Element_Type => Unbounded_String);
To create a hashed map package, the generic Ada.Containers.Hashed_Maps package requires the following parameters: Key_Type, Element_Type, Hash, Equivalent_Keys.
package MyMaps is new Ada.Containers.Hashed_Maps (
Key_Type => Unbounded_String,
Element_Type => Unbounded_String,
Hash => Ada.Strings.Unbounded.Hash,
Equivalent_Keys => "=");
Map examples
A complete ordered map example using the code above.
with Ada.Text_IO; use Ada.Text_IO;
with Ada.Containers.Ordered_Maps;
with Ada.Strings.Unbounded; use Ada.Strings.Unbounded;
procedure Favourite_Books is
package MyMaps is new Ada.Containers.Ordered_Maps (
Key_Type => Unbounded_String,
Element_Type => Unbounded_String);
function U (Item : String) return Unbounded_String
renames Ada.Strings.Unbounded.To_Unbounded_String;
-- The U function is easier to call.
MyBooks : MyMaps.Map;
begin
MyBooks.Insert (U ("Fantasy"), U ("Eragon"));
MyBooks.Insert (U ("Terror"), U ("It"));
MyBooks.Insert (U ("Short stories"), U ("El Aleph"));
MyBooks.Insert (U ("Science fiction"), U ("I, Robot"));
-- The following will raise a Constraint_Error.
-- MyBooks.Insert (U ("Fantasy"), U("Lord of the Rings"));
Put_Line (To_String (MyBooks.Element (U ("Fantasy"))));
end Favourite_Books;
A complete hashed map example.
with Ada.Text_IO; use Ada.Text_IO;
with Ada.Containers.Hashed_Maps;
with Ada.Strings.Unbounded; use Ada.Strings.Unbounded;
with Ada.Strings.Unbounded.Hash;
procedure Favourite_Books is
package MyMaps is new Ada.Containers.Hashed_Maps (
Key_Type => Unbounded_String,
Element_Type => Unbounded_String,
Hash => Ada.Strings.Unbounded.Hash,
Equivalent_Keys => "=");
function U (Item : String) return Unbounded_String
renames Ada.Strings.Unbounded.To_Unbounded_String;
-- The U function is easier to call.
MyBooks : MyMaps.Map;
begin
MyBooks.Insert (U ("Fantasy"), U ("Eragon"));
MyBooks.Insert (U ("Terror"), U ("It"));
MyBooks.Insert (U ("Short stories"), U ("El Aleph"));
MyBooks.Insert (U ("Science fiction"), U ("I, Robot"));
-- The following will raise a Constraint_Error.
-- MyBooks.Insert (U ("Fantasy"), U("Lord of the Rings"));
Put_Line (To_String (MyBooks.Element (U ("Fantasy"))));
end Favourite_Books;
Sets
The set containers store elements of a specific type, but avoid storing duplicated elements. Therefore, it uses an equivalent function or operation of the element type to identify if it is already contained.
There is no index.
- The capacity is expanded on run-time.
- An element contained can be accessed with the Cursor instance.
- An element can be inserted or removed from the set. No index is used in this procedures.
- A user-defined procedure can be used to iterate (and operate on) the contained elements.
- Find and contains functions are provided.
- Empty_Set and No_Element are provided. The latter is used for the Cursor instance as initial value if an element was not assigned to it.
There are two types of sets: the ordered set, and the hashed set. The orderer set maintain its elements sorted in the container. While inserting new elemnets, it search for the proper place with the "<" operation.
In any case, inserting the same element into a set without checking its existence previously will raise a Constraint_Error exception.
Creating a new set
Similarly as vectors, the Ada.Containers.Ordered_Sets is a generic package. The parameters varies depending on the set used. The ordered set defines the Element_Type parameter, the "<" function to sort the elements, and the "=" function to detect if an element is already present. The hashed set defines the Element_Type, the hash function, the Equivalent_Elements function, and the "=" function.
The code to create an instace of an ordered set and hashed set is:
package MyNumberSet is new Ada.Containers.Ordered_Sets (
Element_Type => Natural);
package MyStrings is new Ada.Containers.Hashed_Sets (
Element_Type => Unbounded_String,
Hash => Ada.Strings.Unbounded.Hash,
Equivalent_Elements => "=");
Set examples
The following example is the complete code that uses an ordered set. After executing the program, the output is all the phrases sorted alphabetically.
File: motivation_set.adb (view, plain text, download page, browse all)
- with Ada.Text_IO; use Ada.Text_IO;
with Ada.Strings.Unbounded; use Ada.Strings.Unbounded;
with Ada.Containers.Ordered_Sets;
procedure Motivation_Set is
package MySets is new Ada.Containers.Ordered_Sets (
Element_Type => Unbounded_String);
Motivational_Phrases : MySets.Set;
begin
MySets.Insert (Motivational_Phrases, To_Unbounded_String ("Yes! This is great!"));
MySets.Insert (Motivational_Phrases, To_Unbounded_String ("I learn something new with Ada!"));
MySets.Insert (Motivational_Phrases, To_Unbounded_String ("Another success!"));
MySets.Insert (Motivational_Phrases, To_Unbounded_String ("Never surrender!"));
MySets.Insert (Motivational_Phrases, To_Unbounded_String ("I'll try again!"));
-- The following will raise a Constraint_Error:
-- MySets.Insert (Motivational_Phrases, To_Unbounded_String ("Yes! This is great!"));
-- Items are sorted alphabetically. The output will be printed in the same way.
for Elt of Motivational_Phrases loop
Put_Line (To_String (Elt));
end loop;
end Motivation_Set;
A hashed set complete example. The output in this case is not sorted.
with Ada.Text_IO; use Ada.Text_IO;
with Ada.Strings.Unbounded; use Ada.Strings.Unbounded;
with Ada.Strings.Unbounded.Hash;
with Ada.Containers.Hashed_Sets;
procedure Hashset_Example is
package MySets is new Ada.Containers.Hashed_Sets (
Hash => Ada.Strings.Unbounded.Hash,
Equivalent_Elements => "=",
Element_Type => Unbounded_String);
Cities : MySets.Set;
begin
Cities.Insert (To_Unbounded_String ("Madrid"));
Cities.Insert (To_Unbounded_String ("Paris"));
Cities.Insert (To_Unbounded_String ("Lima"));
Cities.Insert (To_Unbounded_String ("Hong Kong"));
Cities.Insert (To_Unbounded_String ("Tokyo"));
Cities.Insert (To_Unbounded_String ("Washington"));
Cities.Insert (To_Unbounded_String ("Mexico"));
Cities.Insert (To_Unbounded_String ("Berlin"));
Cities.Insert (To_Unbounded_String ("Moscow"));
for City of Cities loop
Put_Line (To_String (City));
end loop;
end Hashset_Example;
Iterating
Sometimes it is required to execute code on all elements of a container. There are several ways to do this:
By using for ... of This language feature has been introduced in Ada 2012..
- By retrieving a Cursor instance from the container and using the Next and Previous procedures on it.
- By defining a procedure with the code, and pass it to the container with the Iterate procedure.
Iterating with for-of loops
This language feature has been introduced in Ada 2012.
Arrays and containers can be iterated with for-of loops.
The following example is a simple program to sort city names alphabetically. First, it insert city names to an Ordered_Set instance. This type of set maintains the order of the items contained, therefore internally, each element is inserted in the proper place. Second, it uses the for-of loop to print the city name on screen.
The Ordered_Set uses two functions named "<" and "=". When not provided, it uses the functions with the same name specified by the Element_Type. In this case, it uses the one defined for the Unbounded_String type. See Ada.Strings.Unbounded for declarations of these functions.
In this case, the Insert function uses the Ada 2005 style. These lines can be changed to support the Ada 95 style by transcribing them to the Insert (Cities, ...) notation.
File: for_of.adb (view, plain text, download page, browse all)
- with Ada.Text_IO; use Ada.Text_IO;
with Ada.Strings.Unbounded; use Ada.Strings.Unbounded;
with Ada.Containers.Ordered_Sets;
procedure For_Of is
package MySets is new Ada.Containers.Ordered_Sets (
Element_Type => Unbounded_String);
Cities : MySets.Set;
begin
Cities.Insert (To_Unbounded_String ("Madrid"));
Cities.Insert (To_Unbounded_String ("Paris"));
Cities.Insert (To_Unbounded_String ("Lima"));
Cities.Insert (To_Unbounded_String ("Hong Kong"));
Cities.Insert (To_Unbounded_String ("Tokyo"));
Cities.Insert (To_Unbounded_String ("Washington"));
Cities.Insert (To_Unbounded_String ("Mexico"));
Cities.Insert (To_Unbounded_String ("Berlin"));
Cities.Insert (To_Unbounded_String ("Moscow"));
for City of Cities loop
Put_Line (To_String (City));
end loop;
end For_Of;
Demos
What follows are demos of some of the container types.
Maps: Greetings
The program below prints greetings to the world
in a number of human languages. The greetings are stored
in a table, or hashed map. The map associates every
greeting (a value) with a language code (a key).
That is, you can use language codes as keys
to find greeting values in the table.
The elements in the map are constant strings of international
characters, or really, pointers to such constant strings.
A package Regional is used to set up both
the language IDs and an instance of
Ada.Containers.Hashed_Maps.
File: regional.ads (view, plain text, download page, browse all)
with Ada.Containers.Hashed_Maps; use Ada.Containers;
type Language_ID is (DE, EL, EN, ES, FR, NL);
-- a selection from the two-letter codes for human languages
type Hello_Text is access constant Wide_String;
-- objects will contain a «hello»-string in some language
function ID_Hashed (id: Language_ID) return Hash_Type;
-- you need to provide this to every hashed container
package Phrases is new Ada.Containers.Hashed_Maps
(Key_Type => Language_ID,
Element_Type => Hello_Text,
Hash => ID_Hashed,
Equivalent_Keys => "=");
end Regional;
Here is the program, details will be explained later.
File: hello_world_extended.ads (view, plain text, download page, browse all)
with Regional; use Regional;
with Ada.Wide_Text_IO; use Ada;
procedure Hello_World_Extended is
-- print greetings in different spoken languages
Greetings : Phrases.Map;
-- the dictionary of greetings
begin -- Hello_World_Extended
Phrases.Insert (Greetings,
Key => EN,
New_Item => new Wide_String'("Hello, World!"));
-- or, shorter,
Greetings.Insert (DE, new Wide_String'("Hallo, Welt!"));
Greetings.Insert (NL, new Wide_String'("Hallo, Wereld!"));
Greetings.Insert (ES, new Wide_String'("¡Hola mundo!"));
Greetings.Insert (FR, new Wide_String'("Bonjour, Monde!"));
Greetings.Insert (EL, new Wide_String'("Γεια σου κόσμε"));
Speaker : Cursor := First (Greetings);
begin
while Has_Element (Speaker) loop
Wide_Text_IO.Put_Line( Element (Speaker).all );
Next (speaker);
end loop;
end;
end Hello_World_Extended;
The first of the Insert statements
is written in an Ada 95 style:
Phrases.Insert (Greetings,
Key => EN,
New_Item => new Wide_String'("Hello, World!"));
The next insertions
use so called distinguished receiver notation which you
can use in Ada 2005. (It's O-O parlance. While
the Insert call involves all of: a Container object (Greetings),
a Key object (EN),
and a New_Item object (new Wide_String'("Hello, World!")),
the Container object is distinguished
from the others in that the Insert call provides it (and only it) with the other
objects. In this case the Container object will be modified by the
call, using arguments named Key and New_Item for the modification.)
Greetings.Insert (ES, new Wide_String'("¡Hola mundo!"));
After the table is set up, the program goes on to print all the greetings
contained in the table. It does so employing a cursor that runs
along the elements in the table in some order. The typical scheme
is to obtain a cursor, here using First, and then
to iterate the following calls:
Has_Element, for checking whether the cursor is at an element
- Element, to get the element and
- Next, to move the cursor to another element
When there is no more element left, the cursor will have the
special value No_Element.
Actually, this is an iteration scheme that can be used with all
containers in child packages of Ada.Containers.
A slight variation: picking an element
The next program shows how to pick a value from the map,
given a key. Actually, you will provide the key.
The program is like the previous one, except that it doesn't
just print all the elements in the map, but picks one
based on a Language_ID value that it reads from standard
input.
File: hello_world_pick.adb (view, plain text, download page, browse all)
-- ... as before ...
package Lang_IO is new Wide_Text_IO.Enumeration_IO (Language_ID);
Lang : Language_ID;
begin
Lang_IO.Get (Lang);
Wide_Text_IO.Put_Line (Greetings.Element (Lang).all );
end;
end Hello_World_Pick;
This time the Element function consumes a Key (Lang)
not a Cursor. Actually, it consumes two values, the other value being
Greetings, in distinguished receiver notation.
Vectors and Maps: Bean Counting
Let's take bean counting literally. Red beans, green beans, and white
beans. (Yes, white beans really do exist.) Your job will be to collect a number
of beans, weigh them, and then determine the average weight of red,
green, and white beans, respectively. Here is one approach.
Again, we need a package, this time for storing vegetable
related information. Introducing the Beans package
(the Grams type doesn't belong in a vegetable package, but it's there
to keep things simple):
File: 1/beans.ads (view, plain text, download page, browse all)
type Bean_Color is (R, G, W);
-- red, green, and white beans
type Grams is delta 0.01 digits 7;
-- enough to weigh things as light as beans but also as heavy as
-- many of them
type Bean is
-- info about a single bean
record
Kind : Bean_Color;
Weight : Grams;
end record;
subtype Bean_Count is Positive range 1 .. 1_000;
-- numbers of beans to count (how many does Cinderella have to count?)
package Bean_Vecs is new Ada.Containers.Vectors
(Element_Type => Bean,
Index_Type => Bean_Count);
end Beans;
The Vectors instance offers a data structure similar to an array
that can change its size at run time. It is called Vector.
Each bean that is read will be appended to a Bean_Vecs.Vector object.
The following program first calls read_input to
fill a buffer with beans. Next, it calls a function that computes
the average weight of beans having the same color. This
function:
function Average_Weight
(Buffer : Bean_Vecs.Vector; Desired_Color : Bean_Color ) return Grams;
-- scan buffer for all beans that have desired_color. Compute the
-- mean of their .weight components
Then the average value is printed for beans of each color and
the program stops.
File: 1/bean_counting.adb (view, plain text, download page, browse all)
with Beans;
with Average_Weight;
with Ada.Wide_Text_IO;
procedure Bean_Counting is
use Beans, Ada;
Buffer : Bean_Vecs.Vector;
procedure Read_Input (Buf : in out Bean_Vecs.Vector) is separate;
-- collect information from a series of bean measurements into buf
begin -- bean_counting
Read_Input (Buffer);
-- now everything is set up for computing some statistical data.
-- For every bean color in Bean_Color, the function Average_Weight
-- will scan Buffer once, and accumulate statistical data from
-- each element encountered.
for Kind in Bean_Color loop
Wide_Text_IO.Put_Line
(Bean_Color'Wide_Image (Kind) &
" ø =" & Grams'Wide_Image (Average_Weight (Buffer, Kind) ));
end loop;
end Bean_Counting;
All container operations take place in function average_weight.
To find the mean weight of beans of the same color, the function
is looking at all beans in order. If a bean has the right color,
average_weight adds its weight to the total weight, and
increases the number of beans counted by 1.
The computation visits all beans. The iteration that is necessary
for going from one bean to the next and then performing the above steps
is best left to the Iterate procedure
which is part of all container packages. To do so, wrap
the above steps inside some procedure and pass this procedure
to Iterate. The effect is that Iterate calls your
procedure for each element in the vector, passing a cursor
value to your procedure, one for each element.
Having the container machinery do the iteration can also be
faster than moving and checking the cursor yourself, as was
done in the Hello_World_Extended example.
File: average_weight.adb (view, plain text, download page, browse all)
with Beans; use Beans.Bean_Vecs;
function Average_Weight
(Buffer : Bean_Vecs.Vector; Desired_Color : Bean_Color) return Grams
is
Total : Grams := 0.0;
-- weight of all beans in buffer having desired_color
Number : Natural := 0;
-- number of beans in buffer having desired_color
procedure Accumulate (C : Cursor) is
-- if the element at C has the Desired_Color, measure it
begin
if Element(c).Kind = Desired_Color then
Number := Number + 1;
Total := Notal + Element (C).Weight;
end if;
end Accumulate;
begin -- average_weight
Iterate (Buffer, Accumulate'Access);
if Number > 0 then
return Total / Number;
else
return 0.0;
end if;
end Average_Weight;
This approach is straightforward. However, imagine larger vectors.
Average_Weight will visit all elements repeatedly
for each color. If there are M colors and N beans,
Average_Weight will be called M * N times, and with each new
color, N more calls are necessary. A possible alternative is to collect all
information about a bean once it is visited. However, this will
likely need more variables, and you will have to find a way to return more
than one result (one average for each color), etc. Try it!
A different approach might be better. One is to copy beans of different
colors to separate vector objects. (Remembering Cinderella.) Then
Average_Weight must visit each element only one time. The
following procedure does this, using a new type from Beans,
called Bean_Pots.
...
type Bean_Pots is array (Bean_Color) of Bean_Vecs.Vector;
...
Note how this plain array associates colors with Vectors.
The procedure for getting the beans into the right bowls uses
the bean color as array index for finding the right bowl (vector).
File: 2/gather_into_pots.adb (view, plain text, download page, browse all)
procedure Gather_Into_Pots (Buffer : Bean_Vecs.Vector; Pots : in out Bean_Pots) is
use Bean_Vecs;
procedure Put_Into_Right_Pot (C : Cursor) is
-- select the proper bowl for the bean at C and «append»
-- the bean to the selected bowl
begin
Append(Pots (Element (C).Kind), Element (C));
end Put_Into_Right_Pot;
begin -- gather_into_pots
Iterate (Buffer, Put_Into_Right_Pot'Access);
end Gather_Into_Pots;
Everything is in place now.
File: 2/bean_counting.adb (view, plain text, download page, browse all)
with Beans;
with Average_Weight;
with Gather_Into_Pots;
with Ada.Wide_Text_IO;
procedure Bean_Counting is
use Beans, Ada;
Buffer : Bean_Vecs.Vector;
Bowls : Bean_Pots;
procedure Read_Input (,Buf : in out Bean_Vecs.Vector) is separate;
-- collect information from a series of bean measurements into buf
begin -- Bean_Counting
Read_Input (Buffer);
-- now everything is set up for computing some statistical data.
-- Gather the beans into the right pot by color.
-- Then find the average weight of beans in each pot.
Gather_Into_Pots (Buffer, Bowls);
for Color in Bean_Color loop
Wide_Text_IO.Put_Line
(Bean_Color'Wide_Image (Color)
& " ø ="
& Grams'Wide_Image(Average_Weight (Bowls (Color), Color)));
end loop;
end Bean_Counting;
As a side effect of having chosen one vector per color, we can determine
the number of beans in each vector by calling the Length function.
But Average_Weight, too, computes the number of elements in the vector.
Hence, a summing function might replace Average_Weight here.
All In Just One Map!
The following program first calls read_input to fill a buffer
with beans. Then, information about these beans is stored in a table,
mapping bean properties to numbers of occurrence. The processing that
starts at Iterate uses chained procedure calls typical of the
Ada.Containers iteration mechanism.
The Beans package in this example instantiates another
generic library unit, Ada.Containers.Ordered_Maps.
Where the Ada.Containers.Hashed_Maps require a hashing
function, Ada.Containers.Ordered_Maps require a comparison
function. We provide one, "<", which sorts beans first by color,
then by weight. It will automatically be associated with the corresponding
generic formal function, as its name, "<", matches that of the generic formal
function, "<".
...
function "<" (A, B: Bean) return Boolean;
-- order beans, first by color, then by weight
package Bean_Statistics
-- instances will map beans of a particular color and weight to the
-- number of times they have been inserted.
is new Ada.Containers.Ordered_Maps
(Element_Type => Natural,
Key_Type => Bean);
...
Where the previous examples have withed subprograms,
this variation on bean_counting packs them all as local
subprograms.
File: 3/bean_counting.adb (view, plain text, download page, browse all)
with Beans;
with Ada.Wide_Text_IO;
procedure Bean_Counting is
use Beans, Ada;
Buffer : Bean_Vecs.Vector;
Stats_CW : Bean_Statistics.Map;
-- maps beans to numbers of occurrences, grouped by color, ordered by
-- weight
procedure Read_Input (Buf : in out Bean_Vecs.Vector) is separate;
-- collect information from a series of bean measurements into Buf
procedure Add_Bean_Info (Specimen : in Bean);
-- insert bean Specimen as a key into the Stats_CW table unless
-- present. In any case, increase the count associated with this key
-- by 1. That is, count the number of equal beans.
procedure Add_Bean_Info (Specimen: in Bean) is
procedure One_More (B : in Bean; N : in out Natural) is
-- increase the count associated with this kind of bean
begin
N := N + 1;
end One_More;
C : Bean_Statistics.Cursor;
Inserted : Boolean;
begin
Stats_CW.Insert (Specimen, 0, C, Inserted);
Bean_Statistics.Update_Element (C, One_More'Access);
end add_bean_info;
begin -- Bean_Counting
Read_Input (Buffer);
-- next, for all beans in the vector buffer just filled, store
-- information about each bean in the Stats_CW table.
procedure Count_Bean (C : Cursor) is
begin
Add_Bean_Info (Element (C));
end Count_Bean;
begin
Iterate (Buffer, Count_Bean'Access);
end;
-- now everything is set up for computing some statistical data. The
-- keys of the map, i.e. beans, are ordered by color and then weight.
-- The First, and Ceiling functions will find cursors
-- denoting the ends of a group.
-- statistics is computed groupwise:
Q_Sum : Grams;
Q_Count : Natural;
procedure Q_Stats(lo, hi: Cursor);
-- Q_Stats will update the Q_Sum and Q_Count globals with
-- the sum of the key weights and their number, respectively. lo
-- (included) and hi (excluded) mark the interval of keys
-- to use from the map.
procedure Q_Stats (Lo , Hi : Cursor) is
K : Cursor := Lo;
begin
Q_Count := 0; Q_Sum := 0.0;
loop
exit when K = Hi;
Q_Count := Q_Count + Element (K);
Q_Sum := Q_Sum + Key (K).Weight * Element (K);
Next (K);
end loop;
end Q_Stats;
-- precondition
pragma assert (not Is_Empty (Stats_CW), "container is empty");
Lower, Upper : Cursor := First (Stats_CW);
-- denoting the first key of a group, and the first key of a
-- following group, respectively
-- start reporting and trigger the computations
Wide_Text_IO.Put_Line ("Summary:");
for Color in Bean_Color loop
Lower := Upper;
if Color = Bean_Color'Last then
Upper := No_Element;
else
Upper := Ceiling (Stats_CW, Bean'(Bean_Color'Succ (Color), 0.0));
end if;
Q_Stats (Lower, Upper);
if Q_Count > 0 then
Wide_Text_IO.Put_Line
(Bean_Color'Wide_Image (Color) & " group:" &
" ø =" & Grams'Wide_Image (Q_Sum / Q_Count) &
", # =" & Natural'Wide_Image (Q_Count) &
", Σ =" & Grams'Wide_Image (Q_Sum));
end if;
end loop;
end;
end Bean_Counting;
Like in the greetings example, you can pick values from the table.
This time the values tell the number of occurrences of beans with
certain properties. The Stats_CW table is ordered by key, that
is by bean properties. Given particular properties, you can use the
Floor and Ceiling functions to approximate the bean in
the table that most closely matches the desired properties.
It is now easy to print a histogram showing the frequency with which
each kind of bean has occurred. If N is the number of beans of a kind,
then print N characters on a line, or draw a graphical bar of length
N, etc. A histogram showing the number of beans per color can be drawn
after computing the sum of beans of this color, using groups like in the
previous example. You can delete beans of a color from the table using
the same technique.
Finally, think of marshalling the beans in order starting at the least
frequently occurring kind. That is, construct a vector appending first
beans that have occurred just once, followed by beans that have occurred
twice, if any, and so on. Starting from the table is possible, but be
sure to have a look at the sorting functions of
Ada.Containers.
See also
Wikibook
Ada 2005 Reference Manual
Where this page came from
This page was imported from Wikibooks. From “Ada Programming” on Wikibooks, by its contributors, under CC BY-SA 4.0. Changed here: set as a page; navigation and edit links left out; each image under its own licence, credited in its caption.
Nobody has written it yet — it is the source material at a new address, which is why search engines are asked to skip it and why no one earns from it. It is up for grabs: take it on, and it is yours to rewrite and to earn from.
In these publicationsAda Programming
Licence: CC BY-SA 4.0 · Adapted from en.wikibooks.org
1
0
0
0

Comments






