# Ada Programming/Containers


What follows is a simple demo of some of the container types. It does
not cover everything, but should get you started.

#### First Example: Maps

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;`\
\
` Regional `\
\
`   `` Language_ID `` (DE, EL, EN, ES, FR, NL);`\
`   `\
\
`   `` Hello_Text `` `` `` Wide_String;`\
`   `\
\
\
`   `` ID_Hashed (id: Language_ID) `` Hash_Type;`\
`   `\
\
`   `` Phrases `` `` Ada.Containers.Hashed_Maps`\
`     (Key_Type => Language_ID,`\
`      Element_Type => Hello_Text,`\
`      Hash => ID_Hashed,`\
`      Equivalent_Keys => "=");`\
\
` Regional;`

Here is the program, details will be explained later.



` Regional; `` Regional;`\
` ``; `` Ada;`\
\
` Hello_World_Extended `\
\
`   `\
\
`   greetings: Phrases.Map;`\
`   `\
\
` `\
\
`   Phrases.Insert(greetings,`\
`                  Key => EN,`\
`                  New_Item => `` Wide_String'("Hello, World!"));`\
\
`   `\
`   greetings.Insert(DE, `` Wide_String'("Hallo, Welt!"));`\
`   greetings.Insert(NL, `` Wide_String'("Hallo, Wereld!"));`\
`   greetings.Insert(ES, `` Wide_String'("¡Hola mundo!")); `\
`   greetings.Insert(FR, `` Wide_String'("Bonjour, Monde!"));`\
`   greetings.Insert(EL, `` Wide_String'("Γεια σου κόσμε"));`\
\
`   `\
`      `` Phrases;`\
\
`      speaker: Cursor := First(greetings);`\
`   `\
`      `` Has_Element(speaker) `\
`         Wide_Text_IO.Put_Line( Element(speaker).`` );`\
`         Next(speaker);`\
`      `` ``;`\
`   ``;`\
\
` Hello_World_Extended;`

The first of the `Insert` statements is written in an Ada 95 style:

`  Phrases.Insert(greetings,`\
`                 Key => EN,`\
`                 New_Item => `` 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 (
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, `` 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:

1.  `Has_Element`, for checking whether the cursor is at an element
2.  `Element`, to get the element and
3.  `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
.

#### 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.



` Regional; `` Regional;`\
` Ada.Wide_Text_IO; `` Ada;`\
\
` Hello_World_Pick `\
\
`  ... as before ...`\
\
` `\
`     `` Phrases;`\
` `\
`     `` Lang_IO `` `` Wide_Text_IO.Enumeration_IO(Language_ID);`\
`     lang: Language_ID;`\
`  `\
`     Lang_IO.Get(lang);`\
`     Wide_Text_IO.Put_Line( greetings.Element(lang).`` );`\
`  ``;`\
` `\
` 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.

#### Second Example: Vectors and Maps

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):



` `\
\
` Beans `\
\
`   `` Bean_Color `` ``R`` G`` W`\
`   `\
\
`   `` Grams `` `` 0``01 `` 7`\
`   `\
`   `\
\
`   `` Bean `\
`   `\
`      `\
`         kind`` Bean_Color`\
`         weight`` Grams`\
`      `` `\
\
`   `` Bean_Count `` Positive `` 1 `` 1_000`\
`   `\
\
`   `` Bean_Vecs `` `` Ada``Containers``Vectors`\
`     ``Element_Type => Bean`\
`      Index_Type => Bean_Count`\
\
` 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:

` Beans``   `` Beans`\
\
` average_weight`\
`  ``buffer`` Bean_Vecs``Vector`` desired_color`` Bean_Color`` `` Grams`\
``{{Ada/comment | scan `buffer` for all beans that have `desired_color`. Compute the}}``{=mediawiki}\
``{{Ada/comment | mean of their `.weight` components}}``{=mediawiki}

Then the average value is printed for beans of each color and the
program stops.



` Beans`\
` average_weight`\
` `\
\
` bean_counting `\
`   `` Beans`` Ada`\
\
`   buffer`` Bean_Vecs``Vector`\
\
`   `` read_input``buf`` `` `` Bean_Vecs``Vector`` `` `\
`   ```{{Ada/comment | collect information from a series of bean measurements into `buf`}}``{=mediawiki}\
\
\
` `\
\
`   read_input``buffer`\
\
`   `\
`   ```{{Ada/comment | For every bean color in `Bean_Color`, the function `average_weight`}}``{=mediawiki}\
`   ```{{Ada/comment | will scan `buffer` once, and accumulate statistical data from}}``{=mediawiki}\
`   `\
\
`   `` kind `` Bean_Color `\
`      Wide_Text_IO``Put_Line`\
`        ``Bean_Color``Wide_Image``kind`` `\
`         " ø =" `` Grams``Wide_Image`` average_weight``buffer`` kind`` `\
`   `` `\
\
` 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.



` Beans``  `` Beans``Bean_Vecs`\
\
` average_weight`\
`  ``buffer`` Bean_Vecs``Vector`` desired_color`` Bean_Color`` `` Grams`\
\
`   total`` Grams := 0``0`\
`   ```{{Ada/-- | weight of all beans in `buffer` having `desired_color`}}``{=mediawiki}\
\
`   number`` Natural := 0`\
`   ```{{Ada/-- | number of beans in `buffer` having `desired_color`}}``{=mediawiki}\
\
`   `` accumulate``c`` Cursor`` `\
`      ```{{Ada/-- | if the element at `c` has the `desired_color`, measure it}}``{=mediawiki}\
`   `\
`      `` Element``c``kind = desired_color `\
`         number := number `` 1`\
`         total := total `` Element``c``weight`\
`      `` `\
`   `` accumulate`\
\
` `\
\
`   Iterate``buffer`` accumulate``Access`\
\
`   `` number `` 0 `\
`      `` total `` number`\
`   `\
`      `` 0``0`\
`   `` `\
\
` 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`.

`   ...`\
`   `` Bean_Pots `` ``Bean_Color`` `` 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).



` gather_into_pots``buffer`` Bean_Vecs``Vector`` pots`` `` `` Bean_Pots`` `\
`   `` Bean_Vecs`\
\
`   `` put_into_right_pot``c`` Cursor`` `\
`      ```{{Ada/comment | select the proper bowl for the bean at `c` and «append»}}``{=mediawiki}\
`      `\
`   `\
`      Append``pots``Element``c``kind`` Element``c`\
`   `` put_into_right_pot`\
\
`  `\
`   Iterate``buffer`` put_into_right_pot``Access`\
` gather_into_pots`

Everything is in place now.



` Beans`\
` average_weight`\
` gather_into_pots`\
` Ada``Wide_Text_IO`\
\
` bean_counting `\
`   `` Beans`` Ada`\
\
`   buffer`` Bean_Vecs``Vector`\
`   bowls`` Bean_Pots`\
\
`   `` read_input``buf`` `` `` Bean_Vecs``Vector`` `` `\
`   ```{{Ada/-- | collect information from a series of bean measurements into `buf`}}``{=mediawiki}\
\
\
` `\
\
`   read_input``buffer`\
\
`   `\
`   `\
`   `\
\
`   gather_into_pots``buffer`` bowls`\
\
`   `` color `` Bean_Color `\
`      Wide_Text_IO``Put_Line`\
`        ``Bean_Color``Wide_Image``color`\
`         & " ø ="`\
`         & Grams``Wide_Image``average_weight``bowls``color`` color`\
`   `` `\
\
` 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
 iteration mechanism.

The Beans package in this example instantiates another generic library
unit, . Where
the  require a
hashing function,
 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, `"<"`.

`   ...`\
`   `` "<"``a`` b`` Bean`` `` Boolean`\
`   `\
\
`   `` Bean_Statistics`\
`     `\
`     `\
`   `` `` Ada``Containers``Ordered_Maps`\
`     ``Element_Type => Natural`\
`      Key_Type => Bean`\
`   ...`

Where the previous examples have ed
subprograms, this variation on `bean_counting` packs them all as local
subprograms.



` Beans`\
` Ada``Wide_Text_IO`\
\
` bean_counting `\
`   `` Beans`` Ada`\
\
`   buffer`` Bean_Vecs``Vector`\
`   stats_cw`` Bean_Statistics``Map`\
`   `\
`   `\
\
`   `` read_input``buf`` `` `` Bean_Vecs``Vector`` `` `\
`   ```{{Ada/-- | collect information from a series of bean measurements into `buf`}}``{=mediawiki}\
\
`   `` add_bean_info``specimen`` `` Bean`\
`   ```{{Ada/-- | insert bean `specimen` as a key into the `stats_cw` table unless}}``{=mediawiki}\
`   `\
`   `\
\
`   `` add_bean_info``specimen`` `` Bean`` `\
\
`      `` one_more``b`` `` Bean`` n`` `` `` Natural`` `\
`       `\
`      `\
`         n := n `` 1`\
`      `` one_more`\
\
`      c `` Bean_Statistics``Cursor`\
`      inserted`` Boolean`\
`   `\
`      stats_cw``Insert``specimen`` 0`` c`` inserted`\
`      Bean_Statistics``Update_Element``c`` one_more``Access`\
`   `` add_bean_info`\
\
` `\
\
`   read_input``buffer`\
\
`   ```{{Ada/-- | next, for all beans in the vector `buffer` just filled, store}}``{=mediawiki}\
`   ```{{Ada/-- | information about each bean in the `stats_cw` table.}}``{=mediawiki}\
\
`   `\
`      `` Bean_Vecs`\
\
`      `` count_bean``c`` Cursor`` `\
`      `\
`         add_bean_info``Element``c`\
`      `` count_bean`\
`   `\
`      Iterate``buffer`` count_bean``Access`\
`   `\
\
`   `\
`   `\
`   ```{{Ada/-- | The `First`, and `Ceiling` functions will find cursors}}``{=mediawiki}\
`   `\
\
\
`   `\
`      `` Bean_Statistics`\
\
`      `\
\
`      q_sum`` Grams`\
`      q_count`` Natural`\
\
`      `` q_stats``lo`` hi`` Cursor`\
`      ```{{Ada/-- | `q_stats` will update the `q_sum` and `q_count` globals with}}``{=mediawiki}\
`      ```{{Ada/-- | the sum of the key weights and their number, respectively. `lo`}}``{=mediawiki}\
`      ```{{Ada/-- | (included) and `hi` (excluded)  mark the interval of keys}}``{=mediawiki}\
`      `\
\
`      `` q_stats``lo`` hi`` Cursor`` `\
`         k`` Cursor := lo`\
`      `\
`         q_count := 0`` q_sum := 0``0`\
`         `\
`            `` `` k = hi`\
`            q_count := q_count `` Element``k`\
`            q_sum := q_sum `` Key``k``weight `` Element``k`\
`            Next``k`\
`         `` `\
`      `` q_stats`\
\
\
`      `\
`      `` assert`` Is_Empty``stats_cw`` "container is empty"`\
\
`      lower`` upper`` Cursor := First``stats_cw`\
`      `\
`      `\
\
`   `\
\
`      `\
\
`      Wide_Text_IO``Put_Line``"Summary:"`\
\
`      `` color `` Bean_Color `\
`         lower := upper`\
`         `` color = Bean_Color``Last `\
`            upper := No_Element`\
`         `\
`            upper := Ceiling``stats_cw`` Bean``Bean_Color``Succ``color`` 0``0`\
`         `` `\
\
`         q_stats``lower`` upper`\
\
`         `` q_count `` 0 `\
`            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`\
`         `` `\
`      `` `\
`   `\
\
` 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
.

## See also

### Wikibook

-   Ada Programming
-   Ada
    Programming/Libraries/Ada.Containers

### Ada 2005 Reference Manual

-   
Programming}}\|Containers
