Ada Programming/Types/array

  • 1.1 Basic syntax
  • 1.2 With known subrange
  • 1.3 With unknown subrange
  • 1.4 With aliased elements
  • 1.5 Arrays with more than one dimension
  • 2.1 Assignment
  • 2.2 Slicing
  • 2.3 Concatenate
  • 2.4 Array Attributes
  • 2.5 Empty or Null Arrays
  • 3.1 Wikibook
  • 3.2 Ada 95 Reference Manual
  • 3.3 Ada 2005 Reference Manual
  • 3.4 Ada Quality and Style Guide

An array is a collection of elements which can be accessed by one or more index values. In Ada any definite type is allowed as element and any discrete type, i.e. Range , Modular or Enumeration , can be used as an index.

Declaring arrays [ edit | edit source ]

Ada's arrays are quite powerful and so there are quite a few syntax variations, which are presented below.

Basic syntax [ edit | edit source ]

The basic form of an Ada array is:

where Index_Range is a range of values within a discrete index type, and Element_Type is a definite subtype. The array consists of one element of "Element_Type" for each possible value in the given range. If you for example want to count how often a specific letter appears inside a text, you could use:

Very often, the index does not have semantic contents by itself, it is just used a means to identify elements for instance in a list. Thus, as a general advice, do not use negative indices in these cases. It is also a good style when using numeric indices, to define them starting in 1 instead of 0, since it is more intuitive for humans and avoids off-by-one errors .

There are, however, cases, where negative indices make sense. So use indices adapted to the problem at hand. Imagine you are a chemist doing some experiments depending on the temperature:

With known subrange [ edit | edit source ]

Often you don't need an array of all possible values of the index type. In this case you can subtype your index type to the actually needed range.

Since this may involve a lot of typing and you may also run out of useful names for new subtypes , the array declaration allows for a shortcut:

Since First and Last are expressions of Index_Type , a simpler form of the above is:

Note that if First and Last are numeric literals, this implies the index type Integer .

If in the example above the character counter should only count upper case characters and discard all other characters, you can use the following array type:

With unknown subrange [ edit | edit source ]

Sometimes the range actually needed is not known until runtime or you need objects of different lengths. In some languages you would resort to pointers to element types. Not with Ada. Here we have the box '<>', which allows us to declare indefinite arrays:

When you declare objects of such a type, the bounds must of course be given and the object is constrained to them.

The predefined type String is such a type. It is defined as

You define objects of such an unconstrained type in several ways (the extrapolation to other arrays than String should be obvious):

(These declarations additionally define anonymous subtypes of String.) In the first example, the range of indices is explicitly given. In the second example, the range is implicitly defined from the initial expression, which here could be via a function reading data from some file. Both objects are constrained to their ranges, i.e. they cannot grow nor shrink.

With aliased elements [ edit | edit source ]

If you come from C / C++ , you are probably used to the fact that every element of an array has an address. The C / C++ standards actually demand that.

In Ada, this is not true. Consider the following array:

Since we have packed the array, the compiler will use as little storage as possible. And in most cases this will mean that 8 boolean values will fit into one byte.

So Ada knows about arrays where more than one element shares one address. So what if you need to address each single element. Just not using pragma Pack is not enough. If the CPU has very fast bit access, the compiler might pack the array without being told. You need to tell the compiler that you need to address each element via an access.

Arrays with more than one dimension [ edit | edit source ]

Arrays can have more than one index. Consider the following 2-dimensional array:

This type permits declaring rectangular arrays of characters. Example:

Or, stating some index values explicitly,

The index values of the second dimension, those indexing the characters in each row, are in 1 .. 5 here. By choosing a different second range, we could change these to be in 11 .. 15:

By adding more dimensions to an array type, we could have squares, cubes (or « bricks »), etc., of homogenous data items.

Finally, an array of characters is a string (see Ada Programming/Strings ). Therefore, Magic_Square may simply be declared like this:

Using arrays [ edit | edit source ]

Assignment [ edit | edit source ].

When accessing elements, the index is specified in parentheses. It is also possible to access slices in this way:

Note that the index range slides in this example: After the assignment, Vector_A (1) = Vector_B (3) and similarly for the other indices.

Also note that slice assignments are done in one go, not in a loop character by character, so that assignments with overlapping ranges work as expected:

The result is "Lady Lady Ada" (and not "Lady Lady Lad").

Slicing [ edit | edit source ]

As has been shown above, in slice assignments index ranges slide. Also subtype conversions make index ranges slide:

The result of Str_1_8 (Name (6 .. 13)) has the new bounds 1 and 8 and contents "Lady Ada" and is not a copy. This is the best way to change the bounds of an array or of parts thereof.

Concatenate [ edit | edit source ]

The operator "&" can be used to concatenate arrays:

In both cases, if the resulting array does not fit in the destination array, Constraint_Error is raised.

If you try to access an existing element by indexing outside the array bounds, Constraint_Error is raised (unless checks are suppressed).

Array Attributes [ edit | edit source ]

There are four Attributes which are important for arrays: ' First , ' Last , ' Length and ' Range . Lets look at them with an example. Say we have the following three strings:

Then the four attributes will have the following values:

The example was chosen to show a few common beginner's mistakes:

  • The assumption that strings begin with the index value 1 is wrong (cf. World'First = 7 on the second line).
  • The assumption (which follows from the first one) that X'Length = X'Last is wrong.
  • The assumption that X'Last >= X'First; this is not true for empty strings.

The index subtype of predefined type String is Positive, therefore excluding 0 or -17 etc. from the set of possible index values, by subtype constraint (of Positive). Also, 'A' or 2.17e+4 are excluded, since they are not of type Positive.

The attribute ' Range is a little special as it does not return a discrete value but an abstract description of the array. One might wonder what it is good for. The most common use is in the for loop on arrays . When looping over all elements of an array, you need not know the actual index range; by using the attribute, one of the most frequent errors, accessing elements out of the index range, can never occur:

' Range can also be used in declaring a name for the index subtype:

The Range attribute can be convenient when programming index checks:

Empty or Null Arrays [ edit | edit source ]

As you have seen in the section above, Ada allows for empty arrays. Any array whose last index value is lower than the first index value is empty. And — of course — you can have empty arrays of all sorts, not just String:

Note: If you give an initial expression to an empty array (which is a must for a constant), the expression in the aggregate will of course not be evaluated since there are no elements actually stored.

See also [ edit | edit source ]

Wikibook [ edit | edit source ].

  • Ada Programming
  • Ada Programming/Types
  • Data Structures
  • Data Structures/Arrays

Ada 95 Reference Manual [ edit | edit source ]

  • 3.6: Array Types [ Annotated ]

Ada 2005 Reference Manual [ edit | edit source ]

Ada quality and style guide [ edit | edit source ].

  • 10.5.7 Packed Boolean Array Shifts

ada array slice assignment

  • Book:Ada Programming

Navigation menu

Copyright 1980, 1982, 1983 owned by the United States Government. Direct reproduction and usage requests to the Ada Information Clearinghouse .

ada array slice assignment

  • Introduction to Ada
  • Standard library: Containers

Standard library: Containers 

In previous chapters, we've used arrays as the standard way to group multiple objects of a specific data type. In many cases, arrays are good enough for manipulating those objects. However, there are situations that require more flexibility and more advanced operations. For those cases, Ada provides support for containers — such as vectors and sets — in its standard library.

We present an introduction to containers here. For a list of all containers available in Ada, see Appendix B .

In the following sections, we present a general overview of vectors, including instantiation, initialization, and operations on vector elements and vectors.

Instantiation 

Here's an example showing the instantiation and declaration of a vector V :

Containers are based on generic packages, so we can't simply declare a vector as we would declare an array of a specific type:

Instead, we first need to instantiate one of those packages. We with the container package ( Ada . Containers . Vectors in this case) and instantiate it to create an instance of the generic package for the desired type. Only then can we declare the vector using the type from the instantiated package. This instantiation needs to be done for any container type from the standard library.

In the instantiation of Integer_Vectors , we indicate that the vector contains elements of Integer type by specifying it as the Element_Type . By setting Index_Type to Natural , we specify that the allowed range includes all natural numbers. We could have used a more restrictive range if desired.

Initialization 

One way to initialize a vector is from a concatenation of elements. We use the & operator, as shown in the following example:

We specify use Integer_Vectors , so we have direct access to the types and operations from the instantiated package. Also, the example introduces another operation on the vector: Length , which retrieves the number of elements in the vector. We can use the dot notation because Vector is a tagged type, allowing us to write either V . Length or Length ( V ) .

Appending and prepending elements 

You add elements to a vector using the Prepend and Append operations. As the names suggest, these operations add elements to the beginning or end of a vector, respectively. For example:

This example puts elements into the vector in the following sequence: (100, 40, 30, 20, 10, 0, 13).

The Reference Manual specifies that the worst-case complexity must be:

O(log N) for the Append operation, and

O(N log N) for the Prepend operation.

Accessing first and last elements 

We access the first and last elements of a vector using the First_Element and Last_Element functions. For example:

You can swap elements by calling the procedure Swap and retrieving a reference (a cursor ) to the first and last elements of the vector by calling First and Last . A cursor allows us to iterate over a container and process individual elements from it.

With these operations, we're able to write code to swap the first and last elements of a vector:

Iterating 

The easiest way to iterate over a container is to use a for E of Our_Container loop. This gives us a reference ( E ) to the element at the current position. We can then use E directly. For example:

This code displays each element from the vector V .

Because we're given a reference, we can display not only the value of an element but also modify it. For example, we could easily write a loop to add one to each element of vector V :

We can also use indices to access vector elements. The format is similar to a loop over array elements: we use a for I in < range > loop. The range is provided by V . First_Index and V . Last_Index . We can access the current element by using it as an array index: V ( I ) . For example:

Here, in addition to displaying the vector elements, we're also displaying each index, I , just like what we can do for array indices. Also, we can access the element by using either the short form V ( I ) or the longer form V . Element ( I ) but not V . I .

As mentioned in the previous section, you can use cursors to iterate over containers. For this, use the function Iterate , which retrieves a cursor for each position in the vector. The corresponding loop has the format for C in V . Iterate loop . Like the previous example using indices, you can again access the current element by using the cursor as an array index: V ( C ) . For example:

Instead of accessing an element in the loop using V ( C ) , we could also have used the longer form Element ( C ) . In this example, we're using the function To_Index to retrieve the index corresponding to the current cursor.

As shown in the comments after the loop, we could also use a while ... loop to iterate over the vector. In this case, we would start with a cursor for the first element (retrieved by calling V . First ) and then call Next ( C ) to retrieve a cursor for subsequent elements. Next ( C ) returns No_Element when the cursor reaches the end of the vector.

You can directly modify the elements using a reference. This is what it looks like when using both indices and cursors:

The Reference Manual requires that the worst-case complexity for accessing an element be O(log N).

Another way of modifying elements of a vector is using a process procedure , which takes an individual element and does some processing on it. You can call Update_Element and pass both a cursor and an access to the process procedure. For example:

Finding and changing elements 

You can locate a specific element in a vector by retrieving its index. Find_Index retrieves the index of the first element matching the value you're looking for. Alternatively, you can use Find to retrieve a cursor referencing that element. For example:

As we saw in the previous section, we can directly access vector elements by using either an index or cursor. However, an exception is raised if we try to access an element with an invalid index or cursor, so we must check whether the index or cursor is valid before using it to access an element. In our example, Find_Index or Find might not have found the element in the vector. We check for this possibility by comparing the index to No_Index or the cursor to No_Element . For example:

Instead of writing V ( C ) := 14 , we could use the longer form V . Replace_Element ( C , 14 ) .

Inserting elements 

In the previous sections, we've seen examples of how to add elements to a vector:

using the concatenation operator ( & ) at the vector declaration, or

calling the Prepend and Append procedures.

You may want to insert an element at a specific position, e.g. before a certain element in the vector. You do this by calling Insert . For example:

In this example, we're looking for an element with the value of 10. If we find it, we insert an element with the value of 9 before it.

Removing elements 

You can remove elements from a vector by passing either a valid index or cursor to the Delete procedure. If we combine this with the functions Find_Index and Find from the previous section, we can write a program that searches for a specific element and deletes it, if found:

We can extend this approach to delete all elements matching a certain value. We just need to keep searching for the element in a loop until we get an invalid index or cursor. For example:

In this example, we remove all elements with the value 10 from the vector by retrieving their index. Likewise, we remove all elements with the value 13 by retrieving their cursor.

Other Operations 

We've seen some operations on vector elements. Here, we'll see operations on the vector as a whole. The most prominent is the concatenation of multiple vectors, but we'll also see operations on vectors, such as sorting and sorted merging operations, that view the vector as a sequence of elements and operate on the vector considering the element's relations to each other.

We do vector concatenation using the & operator on vectors. Let's consider two vectors V1 and V2 . We can concatenate them by doing V := V1 & V2 . V contains the resulting vector.

The generic package Generic _ Sorting is a child package of Ada . Containers . Vectors . It contains sorting and merging operations. Because it's a generic package, you can't use it directly, but have to instantiate it. In order to use these operations on a vector of integer values ( Integer_Vectors , in our example), you need to instantiate it directly as a child of Integer_Vectors . The next example makes it clear how to do this.

After instantiating Generic _ Sorting , we make all the operations available to us with the use statement. We can then call Sort to sort the vector and Merge to merge one vector into another.

The following example presents code that manipulates three vectors ( V1 , V2 , V3 ) using the concatenation, sorting and merging operations:

The Reference Manual requires that the worst-case complexity of a call to Sort be O(N 2 ) and the average complexity be better than O(N 2 ).

Sets are another class of containers. While vectors allow duplicated elements to be inserted, sets ensure that no duplicated elements exist.

In the following sections, we'll see operations you can perform on sets. However, since many of the operations on vectors are similar to the ones used for sets, we'll cover them more quickly here. Please refer back to the section on vectors for a more detailed discussion.

Initialization and iteration 

To initialize a set, you can call the Insert procedure. However, if you do, you need to ensure no duplicate elements are being inserted: if you try to insert a duplicate, you'll get an exception. If you have less control over the elements to be inserted so that there may be duplicates, you can use another option instead:

a version of Insert that returns a Boolean value indicating whether the insertion was successful;

the Include procedure, which silently ignores any attempt to insert a duplicated element.

To iterate over a set, you can use a for E of S loop, as you saw for vectors. This gives you a reference to each element in the set.

Let's see an example:

Operations on elements 

In this section, we briefly explore the following operations on sets:

Delete and Exclude to remove elements;

Contains and Find to verify the existence of elements.

To delete elements, you call the procedure Delete . However, analogously to the Insert procedure above, Delete raises an exception if the element to be deleted isn't present in the set. If you want to permit the case where an element might not exist, you can call Exclude , which silently ignores any attempt to delete a non-existent element.

Contains returns a Boolean value indicating whether a value is contained in the set. Find also looks for an element in a set, but returns a cursor to the element or No_Element if the element doesn't exist. You can use either function to search for elements in a set.

Let's look at an example that makes use of these operations:

In addition to ordered sets used in the examples above, the standard library also offers hashed sets. The Reference Manual requires the following average complexity of each operation:

The previous sections mostly dealt with operations on individual elements of a set. But Ada also provides typical set operations: union, intersection, difference and symmetric difference. In contrast to some vector operations we've seen before (e.g. Merge ), here you can use built-in operators, such as - . The following table lists the operations and its associated operator:

The following example makes use of these operators:

Indefinite maps 

The previous sections presented containers for elements of definite types. Although most examples in those sections presented Integer types as element type of the containers, containers can also be used with indefinite types, an example of which is the String type. However, indefinite types require a different kind of containers designed specially for them.

We'll also be exploring a different class of containers: maps. They associate a key with a specific value. An example of a map is the one-to-one association between a person and their age. If we consider a person's name to be the key, the value is the person's age.

Hashed maps 

Hashed maps are maps that make use of a hash as a key. The hash itself is calculated by a function you provide.

In other languages

Hashed maps are similar to dictionaries in Python and hashes in Perl. One of the main differences is that these scripting languages allow using different types for the values contained in a single map, while in Ada, both the type of key and value are specified in the package instantiation and remains constant for that specific map. You can't have a map where two elements are of different types or two keys are of different types. If you want to use multiple types, you must create a different map for each and use only one type in each map.

When instantiating a hashed map from Ada . Containers . Indefinite_Hashed_Maps , we specify following elements:

Key_Type : type of the key

Element_Type : type of the element

Hash : hash function for the Key_Type

Equivalent_Keys : an equality operator (e.g. = ) that indicates whether two keys are to be considered equal.

If the type specified in Key_Type has a standard operator, you can use it, which you do by specifying that operator as the value of Equivalent_Keys .

In the next example, we'll use a string as a key type. We'll use the Hash function provided by the standard library for strings (in the Ada . Strings package) and the standard equality operator.

You add elements to a hashed map by calling Insert . If an element is already contained in a map M , you can access it directly by using its key. For example, you can change the value of an element by calling M ( "My_Key" ) := 10 . If the key is not found, an exception is raised. To verify if a key is available, use the function Contains (as we've seen above in the section on sets).

Ordered maps 

Ordered maps share many features with hashed maps. The main differences are:

A hash function isn't needed. Instead, you must provide an ordering function ( < operator), which the ordered map will use to order elements and allow fast access, O(log N), using a binary search.

If the type specified in Key_Type has a standard < operator, you can use it in a similar way as we did for Equivalent_Keys above for hashed maps.

You can see a great similarity between the examples above and from the previous section. In fact, since both kinds of maps share many operations, we didn't need to make extensive modifications when we changed our example to use ordered maps instead of hashed maps. The main difference is seen when we run the examples: the output of a hashed map is usually unordered, but the output of a ordered map is always ordered, as implied by its name.

Complexity 

Hashed maps are generally the fastest data structure available to you in Ada if you need to associate heterogeneous keys to values and search for them quickly. In most cases, they are slightly faster than ordered maps. So if you don't need ordering, use hashed maps.

The Reference Manual requires the following average complexity of operations:

10.5.7 Packed Boolean Array Shifts guideline When measured performance indicates, perform packed Boolean array shift operations by using slice assignments rather than repeated bit-wise assignment. example subtype Word_Range is Integer range 0 .. 15; type Flag_Word is array (Word_Range) of Boolean; pragma Pack (Flag_Word); Word : Flag_Word; ... -- Loop to shift by one bit for Index in 0 .. 14 loop Word (Index) := Word (Index + 1); end loop; Word (15) := False; -- Use slice assignment to shift by one bit Word (0 .. 14) := Word (1 .. 15); Word (15) := False; rationale Determine the impact of slice manipulation when shifting packed Boolean arrays. For Ada 83 implementations using packed Boolean arrays, shift operations may be much faster when slice assignments are used as opposed to for loop moving one component at a time. For Ada 95 implementations, consider using modular types instead (see Guideline 10.6.3 ).

3.6 Array Types

This Reference Manual output has not been verified, and may contain omissions or errors. Report any problems on the tracking issue

An array object is a composite object consisting of components which all have the same subtype. The name for a component of an array uses one or more index values belonging to specified discrete types. The value of an array object is a composite value consisting of the values of the components.

array _ type _ definition ::= unconstrained_array_definition | constrained_array_definition

unconstrained _ array _ definition ::= array ( index_subtype_definition { , index_subtype_definition } ) of component_definition

constrained _ array _ definition ::= array ( discrete_subtype_definition { , discrete_subtype_definition } ) of component_definition

discrete _ subtype _ definition ::= discrete _ subtype_indication | range

component _ definition ::= [ aliased ] subtype_indication | [ aliased ] access_definition

Name Resolution Rules ​

For a discrete_subtype_definition that is a range , the range shall resolve to be of some specific discrete type[; which discrete type shall be determined without using any context other than the bounds of the range itself (plus the preference for root _ integer — see 8.6 ).]

Legality Rules ​

Each index_subtype_definition or discrete_subtype_definition in an array_type_definition defines an index subtype ; its type (the index type ) shall be discrete.

An index is a discrete quantity used to select along a given dimension of an array. A component is selected by specifying corresponding values for each of the indices.

The subtype defined by the subtype_indication of a component_definition (the component subtype ) shall be a definite subtype.

This applies to all uses of component_definition , including in record_type_definition s and protected_definition s.

This paragraph was deleted.

Static Semantics ​

An array is characterized by the number of indices (the dimensionality of the array), the type and position of each index, the lower and upper bounds for each index, and the subtype of the components. The order of the indices is significant.

A one-dimensional array has a distinct component for each possible index value. A multidimensional array has a distinct component for each possible sequence of index values that can be formed by selecting one value for each index position (in the given order). The possible values for a given index are all the values between the lower and upper bounds, inclusive; this range of values is called the index range . The bounds of an array are the bounds of its index ranges. The length of a dimension of an array is the number of values of the index range of the dimension (zero for a null range). The length of a one-dimensional array is the length of its only dimension.

An array_type_definition defines an array type and its first subtype. For each object of this array type, the number of indices, the type and position of each index, and the subtype of the components are as in the type definition[; the values of the lower and upper bounds for each index belong to the corresponding index subtype of its type, except for null arrays (see 3.6.1 )].

An unconstrained_array_definition defines an array type with an unconstrained first subtype. Each index _ subtype _ definition defines the corresponding index subtype to be the subtype denoted by the subtype _ mark . [ The compound delimiter < > (called a box ) of an index_subtype_definition stands for an undefined range (different objects of the type can have different bounds).]

A constrained_array_definition defines an array type with a constrained first subtype. Each discrete _ subtype _ definition defines the corresponding index subtype, as well as the corresponding index range for the constrained first subtype. The constraint of the first subtype consists of the bounds of the index ranges.

Although there is no nameable unconstrained array subtype in this case, the predefined slicing and concatenation operations can operate on and yield values that do not necessarily belong to the first array subtype. This is also true for Ada 83.

The discrete subtype defined by a discrete _ subtype _ definition is either that defined by the subtype _ indication , or a subtype determined by the range as follows:

  • If the type of the range resolves to root _ integer , then the discrete_subtype_definition defines a subtype of the predefined type Integer with bounds given by a conversion to Integer of the bounds of the range ;

This ensures that indexing over the discrete subtype can be performed with regular Integers, rather than only universal _ integer s.

We considered doing this by simply creating a “preference” for Integer when resolving the range . However, this can introduce Beaujolais effects when the simple_expression s involve calls on functions visible due to use clauses.

  • Otherwise, the discrete_subtype_definition defines a subtype of the type of the range , with the bounds given by the range .

The component_definition of an array_type_definition defines the nominal subtype of the components. If the reserved word aliased appears in the component_definition , then each component of the array is aliased (see 3.10 ).

Dynamic Semantics ​

The elaboration of an array_type_definition creates the array type and its first subtype, and consists of the elaboration of any discrete _ subtype _ definition s and the component _ definition .

{ 8652/0002 } The elaboration of a discrete_subtype_definition that does not contain any per-object expressions creates the discrete subtype, and consists of the elaboration of the subtype _ indication or the evaluation of the range . The elaboration of a discrete_subtype_definition that contains one or more per-object expressions is defined in 3.8 . The elaboration of a component _ definition in an array _ type _ definition consists of the elaboration of the subtype _ indication or access_definition . The elaboration of any discrete _ subtype _ definition s and the elaboration of the component _ definition are performed in an arbitrary order.

For an array type with a scalar component type, the following language-defined representation aspect may be specified with an aspect_specification (see 13.1.1 ):

The part about requiring an explicit expression is to disallow omitting the value for this aspect, which would otherwise be allowed by the rules of 13.1.1 .

This is a representation attribute in order to disallow specifying it on a derived type that has inherited primitive subprograms; that is necessary as the sizes of out parameters could be different whether or not a Default _ Value is specified (see 6.4.1 ).

Aspect Description for Default _ Component _ Value: Default value for the components of an array-of-scalar subtype.

If a derived type inherits a boolean Default _ Component _ Value aspect, the aspect may be specified to have any value for the derived type.

This overrides the 13.1.1 rule that says that a boolean aspect with a value True cannot be changed.

The expected type for the expression specified for the Default _ Component _ Value aspect is the component type of the array type defined by the full_type_declaration on which it appears.

NOTE 1 All components of an array have the same subtype. In particular, for an array of components that are one-dimensional arrays, this means that all components have the same bounds and hence the same length.

NOTE 2 Each elaboration of an array_type_definition creates a distinct array type. A consequence of this is that each object whose object_declaration contains an array_type_definition is of its own unique type.

Examples of type declarations with unconstrained array definitions:

type Vector is array(Integer range < > ) of Real; type Matrix is array(Integer range < > , Integer range < > ) of Real; type Bit _ Vector is array(Integer range < > ) of Boolean; type Roman is array(Positive range < > ) of Roman _ Digit; -- see 3.5.2

Examples of type declarations with constrained array definitions:

type Table is array(1 .. 10) of Integer; type Schedule is array(Day) of Boolean; type Line is array(1 .. Max _ Line _ Size) of Character;

Examples of object declarations with array type definitions:

Grid : array(1 .. 80, 1 .. 100) of Boolean; Mix : array(Color range Red .. Green) of Boolean; Msg _ Table : constant array(Error _ Code) of access constant String := (Too _ Big = > new String'("Result too big"), Too _ Small = > ...); Page : array(Positive range < > ) of Line := -- an array of arrays (1 | 50 = > Line'(1 | Line'Last = > '+', others = > '-'), -- see 4.3.3 2 .. 49 = > Line'(1 | Line'Last = > '|', others = > ' ')); -- Page is constrained by its initial value to (1..50)

Extensions to Ada 83 ​

The syntax rule for component_definition is modified to allow the reserved word aliased .

The syntax rules for unconstrained_array_definition and constrained_array_definition are modified to use component_definition (instead of component _ subtype_indication ). The effect of this change is to allow the reserved word aliased before the component subtype_indication .

A range in a discrete_subtype_definition may use arbitrary universal expressions for each bound (e.g. –1 .. 3+5), rather than strictly "implicitly convertible" operands. The subtype defined will still be a subtype of Integer.

Wording Changes from Ada 83 ​

We introduce a new syntactic category, discrete_subtype_definition , as distinct from discrete_range . These two constructs have the same syntax, but their semantics are quite different (one defines a subtype, with a preference for Integer subtypes, while the other just selects a subrange of an existing subtype). We use this new syntactic category in for loops and entry families.

The syntax for index_constraint and discrete_range have been moved to their own subclause, since they are no longer used here.

The syntax rule for component_definition (formerly component _ subtype _ definition ) is moved here from RM83-3.7.

Extensions to Ada 95 ​

Array components can have an anonymous access type.

The prohibition against unconstrained discriminated aliased components has been lifted. It has been replaced by a prohibition against the actual troublemakers: general access discriminant constraints (see 3.7.1 ).

Wording Changes from Ada 95 ​

{ 8652/0002 } Corrigendum: Added wording to allow the elaboration of per-object constraints for constrained arrays.

Extensions to Ada 2005 ​

The new aspect Default _ Component _ Value allows defining implicit initial values (see 3.3.1 ) for arrays of scalar types.

3.6.1 Index Constraints and Discrete Ranges ​

An index_constraint determines the range of possible values for every index of an array subtype, and thereby the corresponding array bounds.

index _ constraint ::= ( discrete_range { , discrete_range } )

discrete _ range ::= discrete _ subtype_indication | range

The type of a discrete_range is the type of the subtype defined by the subtype_indication , or the type of the range . For an index_constraint , each discrete_range shall resolve to be of the type of the corresponding index.

In Ada 95, index_constraint s only appear in a subtype_indication ; they no longer appear in constrained_array_definition s.

An index_constraint shall appear only in a subtype_indication whose subtype_mark denotes either an unconstrained array subtype, or an unconstrained access subtype whose designated subtype is an unconstrained array subtype; in either case, the index_constraint shall provide a discrete_range for each index of the array type.

A discrete_range defines a range whose bounds are given by the range , or by the range of the subtype defined by the subtype_indication .

An index_constraint is compatible with an unconstrained array subtype if and only if the index range defined by each discrete_range is compatible (see 3.5 ) with the corresponding index subtype. If any of the discrete_range s defines a null range, any array thus constrained is a null array , having no components. An array value satisfies an index_constraint if at each index position the array value and the index_constraint have the same index bounds.

There is no need to define compatibility with a constrained array subtype, because one is not allowed to constrain it again.

The elaboration of an index_constraint consists of the evaluation of the discrete_range (s), in an arbitrary order. The evaluation of a discrete_range consists of the elaboration of the subtype_indication or the evaluation of the range .

NOTE 1 The elaboration of a subtype_indication consisting of a subtype_mark followed by an index_constraint checks the compatibility of the index_constraint with the subtype_mark (see 3.2.2 ).

NOTE 2 Even if an array value does not satisfy the index constraint of an array subtype, Constraint _ Error is not raised on conversion to the array subtype, so long as the length of each dimension of the array value and the array subtype match. See 4.6 .

Examples of array declarations including an index constraint:

Board : Matrix(1 .. 8, 1 .. 8); -- see 3.6 Rectangle : Matrix(1 .. 20, 1 .. 30); Inverse : Matrix(1 .. N, 1 .. N); -- N can be nonstatic 13/5

Filter : Bit _ Vector(0 .. 31); -- see 3.6

Example of array declaration with a constrained array subtype:

My _ Schedule : Schedule; -- all arrays of type Schedule have the same bounds

Example of record type with a component that is an array:

type Var _ Line(Length : Natural) is record Image : String(1 .. Length); end record; 18 Null _ Line : Var _ Line(0); -- Null _ Line.Image is a null array

We allow the declaration of a variable with a nominally unconstrained array subtype, so long as it has an initialization expression to determine its bounds.

We have moved the syntax for index_constraint and discrete_range here since they are no longer used in constrained_array_definition s. We therefore also no longer have to describe the (special) semantics of index_constraint s and discrete_range s that appear in constrained_array_definition s.

The rules given in RM83-3.6.1(5,7-10), which define the bounds of an array object, are redundant with rules given elsewhere, and so are not repeated here. RM83-3.6.1(6), which requires that the (nominal) subtype of an array variable be constrained, no longer applies, so long as the variable is explicitly initialized.

3.6.2 Operations of Array Types ​

[The argument N used in the attribute_designator s for the N-th dimension of an array shall be a static expression of some integer type.] The value of N shall be positive (nonzero) and no greater than the dimensionality of the array.

{ 8652/0006 } The following attributes are defined for a prefix A that is of an array type [(after any implicit dereference)], or denotes a constrained array subtype:

These attributes are not defined if A is a subtype-mark for an access-to-array subtype. They are defined (by implicit dereference) for access-to-array values.

Implementation Advice ​

An implementation should normally represent multidimensional arrays in row-major order, consistent with the notation used for multidimensional array aggregates (see 4.3.3 ). However, if convention Fortran is specified for a multidimensional array type, then column-major order should be used instead (see B.5 , “ Interfacing with Fortran ”).

Multidimensional arrays should be represented in row-major order, unless the array has convention Fortran.

NOTE 1 The attribute_reference s A'First and A'First(1) denote the same value. A similar relation exists for the attribute_reference s A'Last, A'Range, and A'Length. The following relation is satisfied (except for a null array) by the above attributes if the index type is an integer type:

A'Length(N) = A'Last(N) - A'First(N) + 1

NOTE 2 An array type is limited if its component type is limited (see 7.5 ).

NOTE 3 The predefined operations of an array type include the membership tests, qualification, and explicit conversion. If the array type is not limited, they also include assignment and the predefined equality operators. For a one-dimensional array type, they include the predefined concatenation operators (if nonlimited) and, if the component type is discrete, the predefined relational operators; if the component type is boolean, the predefined logical operators are also included.

NOTE 4 A component of an array can be named with an indexed_component . A value of an array type can be specified with an array_aggregate . For a one-dimensional array type, a slice of the array can be named; also, string literals are defined if the component type is a character type.

Examples (using arrays declared in the examples of 3.6.1 ):

-- Filter'First = 0 Filter'Last = 31 Filter'Length = 32 -- Rectangle'Last(1) = 20 Rectangle'Last(2) = 30

3.6.3 String Types ​

A one-dimensional array type whose component type is a character type is called a string type .

[There are three predefined string types, String, Wide _ String, and Wide _ Wide _ String, each indexed by values of the predefined subtype Positive; these are declared in the visible part of package Standard:]

[subtype Positive is Integer range 1 .. Integer'Last; 4/2

type String is array(Positive range < > ) of Character; type Wide _ String is array(Positive range < > ) of Wide _ Character; type Wide _ Wide _ String is array(Positive range < > ) of Wide _ Wide _ Character; ]

NOTE String literals (see 2.6 and 4.2 ) are defined for all string types. The concatenation operator & is predefined for string types, as for all nonlimited one-dimensional array types. The ordering operators < , < =, > , and > = are predefined for string types, as for all one-dimensional discrete array types; these ordering operators correspond to lexicographic order (see 4.5.2 ).

Examples of string objects:

Stars : String(1 .. 120) := (1 .. 120 = > ' * ' ); Question : constant String := "How many characters?"; -- Question'First = 1, Question'Last = 20 -- Question'Length = 20 (the number of characters) 8 Ask _ Twice : String := Question & Question; -- constrained to (1..40) Ninety _ Six : constant Roman := "XCVI"; -- see 3.5.2 and 3.6

Inconsistencies With Ada 83 ​

The declaration of Wide _ String in Standard hides a use-visible declaration with the same defining_identifier . In rare cases, this might result in an inconsistency between Ada 83 and Ada 95.

Incompatibilities With Ada 83 ​

Because both String and Wide _ String are always directly visible, an expression like

"a" < "bc"

is now ambiguous, whereas in Ada 83 both string literals could be resolved to type String.

The type Wide _ String is new (though it was approved by ARG for Ada 83 compilers as well).

We define the term string type as a natural analogy to the term character type .

Inconsistencies With Ada 95 ​

The declaration of Wide _ Wide _ String in Standard hides a use-visible declaration with the same defining_identifier . In the (very) unlikely event that an Ada 95 program had depended on such a use-visible declaration, and the program remains legal after the substitution of Standard.Wide _ Wide _ String, the meaning of the program will be different.

The type Wide _ Wide _ String is new.

  • 3.6.1 Index Constraints and Discrete Ranges
  • 3.6.2 Operations of Array Types
  • 3.6.3 String Types

COMMENTS

  1. Arrays

    Array slices One last feature of Ada arrays that we're going to cover is array slices. It is possible to take and use a slice of an array (a contiguous sequence of elements) as a name or a value. ... As we can see above, you can use a slice on the left side of an assignment, to replace only part of an array. A slice of an array is of the same ...

  2. Array slicing between different types in Ada

    An Ada assignment requires (usually) that the source and destination be of the same type, but they can be different subtypes. ... Assigning slices of an array to a new array. 0. Wrap-around Semantics for accessing Array Slices indexed by a Modular Type. Hot Network Questions

  3. Slices

    A slice denotes a one-dimensional array formed by the sequence of consecutive components of the array denoted by the prefix, corresponding to the range of values of the index given by the discrete_range. The type of the slice is that of the prefix. Its bounds are those defined by the discrete_range.

  4. Ada '83 Rationale, Sec 4.5: Array Types

    and write the slice assignment PLACE(1 .. 20) := HEADLINE(41 .. 60); Finally, we may want to compare a slice to a string literal or to another slice: if PLACE(1 .. ... Name equivalence, as explained in section 4.3, is used systematically for all types in Ada, and in particular for array types. As for other types, the main arguments in favor of ...

  5. Arrays

    Also, we can have three procedures for this array: Show_Indices, which presents the indices (days and hours) of the two-dimensional array; Show_Values, which presents the values stored in the array; and. Reset, which resets each value of the array. This is the complete code for this application: measurement_defs.ads.

  6. Ada Tutorial

    The program named e_c10_p2.ada contains several examples of the use of the slice in Ada, which is a portion of an array. You may wish to assign part of an array to part of another array in a single statement. This can be done with a slice. We begin by declaring an array type, MY_ARRAY, which is then used to declare two arrays, First and Second.

  7. 7.6 Assignment and Finalization

    For example, in a slice assignment, an anonymous object is not necessary if the slice is copied component-by-component in the right direction, since array types are not controlled (although their components may be). ... (for an overlapping array assignment), or not at all (for an assignment where the target and the source of the assignment are ...

  8. Type System

    Ada offers high-level operations for copying, slicing, and assigning values to arrays. We'll start with assignment. In C++ or Java, the assignment operator doesn't make a copy of the value of an array, but only copies the address or reference to the target variable. In Ada, the actual array contents are duplicated.

  9. 5.2 Assignment Statements

    4 Notes on the examples: Assignment_statement s are allowed even in the case of overlapping slices of the same array, ... The assignment is legal in Ada 95 (only the first Foo would be considered), and is ambiguous in Ada 2005. We made the change because we want limited types to be as similar to nonlimited types as possible.

  10. 7.6 Assignment and Finalization

    Reason: For example, in a slice assignment, an anonymous object is not necessary if the slice is copied component-by-component in the right direction, since array types are not controlled (although their components may be). Note that the direction, and even the fact that it's a slice assignment, can in general be determined only at run time.

  11. Ada Programming/Types/array

    The basic form of an Ada array is: array (Index_Range) of Element_Type. where Index_Range is a range of values within a discrete index type, and Element_Type is a definite subtype. The array consists of one element of "Element_Type" for each possible value in the given range. If you for example want to count how often a specific letter appears ...

  12. Ada 83 LRM, Sec 5.2: Assignment Statement

    Array assignment is defined even in the case of overlapping slices, because the expression on the right-hand side is evaluated before performing any component assignment. In the above example, an implementation yielding A(1 .. 12) = "tartartartar" would be incorrect.

  13. 4.1.2 Slices

    Annotated Ada Reference Manual (Ada 202y Draft 1) — Legal Information 4.1.2 Slices. 1 [A slice denotes a ... NOTE 2 For a one-dimensional array A, the slice A(N .. N) denotes an array that has only one component; its type is the type of A. On the other hand, A(N) denotes a component of the array A and has the corresponding component type. ...

  14. 5.2 Assignment Statements

    Assignment_statements are well-defined even in the case of overlapping slices of the same array, because the variable_name and expression are both evaluated before copying the value into the variable. In the above example, an implementation yielding A(1 .. ... The assignment is legal in Ada 95 (only the first Foo would be considered), and is ...

  15. Operations of Array Types

    NOTE 3 The predefined operations of an array type include the membership tests, qualification, and explicit conversion. If the array type is not limited, they also include assignment and the predefined equality operators. For a one-dimensional array type, they include the predefined concatenation operators (if nonlimited) and, if the component type is discrete, the predefined relational ...

  16. Standard library: Containers

    We with the container package ( Ada.Containers.Vectors in this case) and instantiate it to create an instance of the generic package for the desired type. Only then can we declare the vector using the type from the instantiated package. This instantiation needs to be done for any container type from the standard library.

  17. 10.5.7 Packed Boolean Array Shifts

    Determine the impact of slice manipulation when shifting packed Boolean arrays. For Ada 83 implementations using packed Boolean arrays, shift operations may be much faster when slice assignments are used as opposed to for loop moving one component at a time. For Ada 95 implementations, consider using modular types instead (see Guideline 10.6.3 ...

  18. 3.6 Array Types

    12. An array is characterized by the number of indices (the dimensionality of the array), the type and position of each index, the lower and upper bounds for each index, and the subtype of the components. The order of the indices is significant. 13. A one-dimensional array has a distinct component for each possible index value.

  19. Operations of Array Types

    50 {AI95-00287-01} A component of an array can be named with an indexed_component. A value of an array type can be specified with an array_aggregate, unless the array type is limited. For a one-dimensional array type, a slice of the array can be named; also, string literals are defined if the component type is a character type.

  20. CS 214 Lab 6: Ada

    For fixed-length arrays, the normal Ada assignment operator (:=) can be used to copy values between two arrays provided the arrays are the same length. Unfortunately, that is not the case here: parameter First_Part is an array whose size generally speaking different than the size of the substring we wish to assign to it.