Skip to content

Commit

Permalink
Merge pull request #1128 from gusthoff/content/advanced_ada/new_conte…
Browse files Browse the repository at this point in the history
…nt/arrays/array_derived_types_subtypes/20241020

Adding section on derived array types and array subtypes
  • Loading branch information
gusthoff authored Oct 25, 2024
2 parents 3d30338 + ef2e1e7 commit e650b3a
Showing 1 changed file with 245 additions and 19 deletions.
264 changes: 245 additions & 19 deletions content/courses/advanced-ada/parts/data_types/arrays.rst
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,33 @@ Arrays

.. include:: ../../../global.txt

Unconstrained Arrays
--------------------

.. _Adv_Ada_Array_Constraints:

Array constraints
-----------------

Array constraints are important in the declaration of an array because they
define the total size of the array. In fact, arrays must always be constrained.
In this section, we start our discussion with unconstrained array types, and
then continue with constrained arrays and arrays types. Finally, we discuss
the differences between unconstrained arrays and vectors.

.. admonition:: In the Ada Reference Manual

- :arm22:`3.6 Array Types <3-6>`


Unconstrained array types
~~~~~~~~~~~~~~~~~~~~~~~~~

In the
:ref:`Introduction to Ada course <Intro_Ada_Unconstrained_Array_Types>`,
we've seen that we can declare array types whose bounds are not fixed: in that
case, the bounds are provided when creating objects of those types. For
example:

.. code:: ada run_button project=Courses.Advanced_Ada.Data_Types.Arrays.Unconstrained_Arrays.Unconstrained_Array_Example
.. code:: ada run_button project=Courses.Advanced_Ada.Data_Types.Arrays.Array_Constraints.Unconstrained_Array_Type

package Measurement_Defs is

Expand Down Expand Up @@ -40,8 +57,12 @@ In this example, the :ada:`Measurements` array type from the
:ada:`Show_Measurements` procedure, we declare a constrained object (:ada:`M`)
of this type.


Constrained arrays
~~~~~~~~~~~~~~~~~~

The :ref:`Introduction to Ada course <Intro_Ada_Unconstrained_Array_Type_Instance_Bound>`
also highlights the fact that the bounds are fixed once an object is declared:
highlights the fact that the bounds are fixed once an object is declared:

Although different instances of the same unconstrained array type can
have different bounds, a specific instance has the same bounds
Expand All @@ -52,21 +73,59 @@ also highlights the fact that the bounds are fixed once an object is declared:
In the :ada:`Show_Measurements` procedure above, once we declare :ada:`M`, its
bounds are fixed for the whole lifetime of :ada:`M`. We cannot *add* another
component to this array. In other words, :ada:`M` will have 10 components for
its whole lifetime.
its whole lifetime:

.. admonition:: In the Ada Reference Manual
.. code-block:: ada

- :arm22:`3.6 Array Types <3-6>`
M : Measurements (1 .. 10);
-- ^^^^^^^
-- Bounds cannot be changed!


.. _Adv_Ada_Constrained_Array_Type:

Constrained array types
~~~~~~~~~~~~~~~~~~~~~~~

Note that we could declare constrained array types. Let's rework the previous
example:

.. code:: ada run_button project=Courses.Advanced_Ada.Data_Types.Arrays.Array_Constraints.Constrained_Array_Type

package Measurement_Defs is

type Measurements is
array (1 .. 10) of Float;
-- ^ Bounds are of known and fixed.

end Measurement_Defs;

with Ada.Text_IO; use Ada.Text_IO;

with Measurement_Defs; use Measurement_Defs;

procedure Show_Measurements is
M : Measurements;
-- ^ We cannot change the
-- bounds here!
begin
Put_Line ("First index: " & M'First'Image);
Put_Line ("Last index: " & M'Last'Image);
end Show_Measurements;

In this case, the bounds of the :ada:`Measurements` type are fixed. Now, we
cannot specify the bounds (or change them) in the declaration of the :ada:`M`
array, as they have already been defined in the type declaration.


Unconstrained Arrays vs. Vectors
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

If you need, however, the flexibility of increasing the length of an array, you
could use vectors instead. This is how we could rewrite the previous example
using vectors:
could use the language-defined :ada:`Vector` type instead. This is how we could
rewrite the previous example using vectors:

.. code:: ada run_button project=Courses.Advanced_Ada.Data_Types.Arrays.Unconstrained_Arrays.Unconstrained_Array_Example
.. code:: ada run_button project=Courses.Advanced_Ada.Data_Types.Arrays.Array_Constraints.Unconstrained_Array_Type_Vs_Vector

with Ada.Containers; use Ada.Containers;
with Ada.Containers.Vectors;
Expand Down Expand Up @@ -327,6 +386,7 @@ to highlight the following aspects:
.. admonition:: In the Ada Reference Manual

- :arm22:`3.6 Array Types <3-6>`
- :arm22:`3.6.2 Operations of Array Types <3-6-2>`


Unconstrained Multidimensional Arrays
Expand Down Expand Up @@ -525,16 +585,182 @@ we'd like to highlight the following aspects:
form: :ada:`(others => (others => 0.0))`.


..
TO BE DONE:
Derived array types and array subtypes
--------------------------------------

Array Subtypes
--------------
.. _Adv_Ada_Derived_Array_Types:

.. admonition:: In the Ada Reference Manual
Derived array types
~~~~~~~~~~~~~~~~~~~

- :arm:`3.7 Discriminants <3-7>`
As expected, we can derive from array types by declaring a new type. Let's see
a couple of examples based on the :ada:`Measurement_Defs` package from previous
sections:

.. code:: ada compile_button project=Courses.Advanced_Ada.Data_Types.Arrays.Derived_Arrays_And_Subtypes.Derived_Arrays

package Measurement_Defs is

type Measurements is
array (Positive range <>) of Float;

--
-- New array type:
--
type Measurements_Derived is
new Measurements;

--
-- New array type with
-- default component value:
--
type Measurements_Def30 is
new Measurements
with Default_Component_Value => 30.0;

--
-- New array type with constraints:
--
type Measurements_10 is
new Measurements (1 .. 10);

end Measurement_Defs;

In this example, we're deriving :ada:`Measurements_Derived` from the
:ada:`Measurements` type. In the case of the :ada:`Measurements_Def30` type,
we're not only deriving from the :ada:`Measurements` type, but also setting
the :ref:`default component value <Adv_Ada_Default_Component_Value>` to 30.0.
Finally, in the case of the :ada:`Measurements_10`, we're deriving from the
:ada:`Measurements` type and
:ref:`constraining the array type <Adv_Ada_Constrained_Array_Type>` in the
range from 1 to 10.

Let's use these types in a test application:

.. code:: ada run_button project=Courses.Advanced_Ada.Data_Types.Arrays.Derived_Arrays_And_Subtypes.Derived_Arrays

with Measurement_Defs; use Measurement_Defs;

procedure Show_Measurements is
M1, M2 : Measurements (1 .. 10)
:= (others => 0.0);

MD : Measurements_Derived (1 .. 10);
MD2 : Measurements_Derived (1 .. 40);
MD10 : Measurements_10;
begin
M1 := M2;
-- ^^^^^^
-- Assignment of arrays of
-- same type.

MD := Measurements_Derived (M1);
-- ^^^^^^^^^^^^^^^^^^^^^^^^^
-- Conversion to derived type for
-- the assignment.

MD10 := Measurements_10 (M1);
-- ^^^^^^^^^^^^^^^^^^^^
-- Conversion to derived type for
-- the assignment.

MD10 := Measurements_10 (MD);
MD10 := Measurements_10 (MD2 (1 .. 10));
end Show_Measurements;

As illustrated by this example, we can assign objects of different array types,
provided that we perform the appropriate type conversions and make sure that
the bounds match.


.. _Adv_Ada_Array_Subtypes:

Array subtypes
~~~~~~~~~~~~~~

Naturally, we can also declare subtypes of array types. For example:

.. code:: ada compile_button project=Courses.Advanced_Ada.Data_Types.Arrays.Derived_Arrays_And_Subtypes.Array_Subtypes

package Measurement_Defs is

type Measurements is
array (Positive range <>) of Float;

--
-- Simple subtype declaration:
--
subtype Measurements_Sub is Measurements;

--
-- Subtype with constraints:
--
subtype Measurements_10 is
Measurements (1 .. 10);

--
-- Subtype with dynamic predicate
-- (array can only have 20 components
-- at most):
--
subtype Measurements_Max_20 is Measurements
with Dynamic_Predicate =>
Measurements_Max_20'Length <= 20;

--
-- Subtype with constraints and
-- dynamic predicate (first element
-- must be 2.0).
--
subtype Measurements_First_Two is
Measurements (1 .. 10)
with Dynamic_Predicate =>
Measurements_First_Two (1) = 2.0;

end Measurement_Defs;

Here, we're declaring subtypes of the :ada:`Measurements` type. For example,
:ada:`Measurements_Sub` is a *simple* subtype of :ada:`Measurements` type. In
the case of the :ada:`Measurements_10` subtype, we're constraining the type to
a range from 1 to 10.

For the :ada:`Measurements_Max_20` subtype, we're specifying |mdash| via a
dynamic predicate |mdash| that arrays of this subtype can only have 20
components at most. Finally, for the :ada:`Measurements_First_Two` subtype,
we're constraining the type to a range from 1 to 10 and requiring that the
first component must have a value of 2.0.

Note that we cannot set the default component value for array subtypes |mdash|
only type declarations are allowed to use that facility.

Let's use these subtypes in a test application:

.. code:: ada run_button project=Courses.Advanced_Ada.Data_Types.Arrays.Derived_Arrays_And_Subtypes.Array_Subtypes
:class: ada-run-expect-failure

with Measurement_Defs; use Measurement_Defs;

procedure Show_Measurements is
M1, M2 : Measurements (1 .. 10)
:= (others => 0.0);
MS : Measurements_Sub (1 .. 10);
MD10 : Measurements_10;
M_Max20 : Measurements_Max_20 (1 .. 40);
M_F2 : Measurements_First_Two;
begin
MS := M1;
MD10 := M1;

M_Max20 := (others => 0.0); -- ERROR!

MD10 (1) := 4.0;
M_F2 := MD10; -- ERROR!
end Show_Measurements;

.. todo::
As expected, assignments to objects with different subtypes |mdash| but with
the same parent type |mdash| work fine without conversion. The assignment to
:ada:`M_Max_20` fails because of the predicate failure: the predicate requires
that the length be 20 at most, and it's 40 in this case. Also, the
assignment to :ada:`M_F2` fails because the predicate requires that the first
element must be set to :ada:`2.0`, and :ada:`MD10 (1)` has the value 4.0.

- Complete section!

0 comments on commit e650b3a

Please sign in to comment.