Once again Ada provides the same facilities as other languages, plus a few extra. Records can have aggregates, discriminants which allow for variant records, variable sized records and initialized variables.
In general the declaration of an record has this form:
type record name is record field name 1: type A; field name 2: type B; ... field name n: type N; end record;
For example:
type bicycle is record frame :construction; maker :manufacturer; front_brake :brake_type; rear_brake :brake_type; end record;
Accessing fields of a record is identical to that in pascal and C, i.e. the name of the variable is followed by a dot and then the field name.
expensive_bike :bicycle; expensive_bike.frame := aluminium; expensive_bike.manufacturer := cannondale; expensive_bike.front_brake := cantilever; expensive_bike.rear_brake := cantilever; if expensive_bike.frame = aluminium then ...
As with arrays records can be assigned an aggregate, a complete set of values for all record elements.
expensive_bike:=(aluminium,cannondale,canitlever,cantilever);
Alternatively the aggregate can use positional notation where each element of the record is named.
expensive_bike := ( frame => aluminium, manufacturer => cannondale, front_brake => cantilever, rear_brake => cantilever );
Both notations can be mixed in the one aggregate with the proviso that all positional values precede named values.
Aggregates can also be used in declarations in exactly the same manner, using either notation.
expensive_bike :bicycle := (aluminium,cannondale,cantilever,cantilever);
The same value can be assigned to different fields by using the '|' character.
expensive_bike := (
frame => aluminium,
manufacturer => cannondale,
front_brake | rear_brake => cantilever
);
Fields in records can be given default values that are used whenever a record of that type is created (unless it is initialised with different values).
type bicycle is record frame:construction := CromeMolyebdenum; maker :manufacturer; front_brake :brake_type := cantilever; rear_brake :brake_type := cantilever; end record;
Just like normal variables constant records can be created. In this case all fields should be initialised through the use of an aggregate or default field values.
my_bicycle :constant bicycle := ( hi_tensile_steel, unknown, front_brake => side_pull, rear_brake => side_pull);
The fields in a constant record cannot be assigned a value, nor can the record be reassigned a new value.
Ada allows records to contain discriminants. These extra fields help to customise the record further. They give an extra level of abstraction when modelling data; records can be of the same type yet still be different in size or the number of fields.
One of the uses of discriminants allows for variant records. In this case the record contains some fields whose existance is dependent upon the value of the discriminant.
For example the discriminant in the following record is the variable vehicle_type
type vehicle is (bicycle, car, truck, scooter); type transport (vehicle_type: vehicle:= car) is record owner :string(1..10); description: string(1..10); case vehicle_type is when car => petrol_consumption:float; when truck => diesel_consumption:float; tare:real; net:real; when others => null; end case; end record;
The discriminant can be supplied when a record is being declared.
my_car:transport(car); my_bicycle:transport (vehicle_type => bicycle);
When the discriminant has the value car, the record contains the fields owner, description and petrol_consumption. Attempting to access fields such as tare and net is illegal and will cause a constraint error to be generated (this can be caught and handled using exceptions).
The records above are said to be constrained, that is the discriminant can never change. The my_bicycle record does not have the fields tare, net petrol_consumption etc. nor does the compiler allocate room for them.
We can declare the records without setting an initial value for the discriminant, in which case the record is said to be unconstrained. In this case the discriminant can take on any value throughout its life. Obviously in this case the compiler has to allocate enough room for the largest record possible.
Aggregates for records with discriminants must include the value of the discriminant as the first value.
my_transport:transport; begin my_transport := (car,"dale ","escort ",30.0);
We can create records that do not have a fixed discriminant, it can change throughout the life of the record. However to do this the discriminant of the record must have a default value.
type accounts is (cheque, savings); type account (account_type: accounts:= savings) is record account_no :positive; title :string(1..10); case account_type is when savings => interest_rate; when cheque => null; end case; end record;
Here the account record discriminant has a default value of savings.
We can declare a record
household_account:account;
which is created as a savings account. We can change the record type later...
household_account:= (cheque,123_456,"household ");
As well as being used as a case selector in a record, discriminants can also be used to specify the length of arrays that are components of the record.
type text (length:positive:=20) is record value:string(1..length); end record;
In this case the length of the array is dependent upon the value of the discriminant. As described above the record can be declared constrained or unconstrained.
This text record is the usual implementation used for variable length strings and text processing.