In Ada, when you declare a array, you can do something like
Work_Day : constant array (Day) of Boolean := (Mon .. Fri => True, Sat | Sun => False);
which utilizes the .. and | symbols to account for multiple elements of the array instead of having to reference each array.
I want to do something similar, but with a array of arrays, and I dont want to do it at the declaration, but instead later in the program, when I might have to redefine the initial value. I am attempting to do something like
-- Set elements 2 through 5 of row 1 to asdf
Array_of_Arrays(1)(2..5) := "asdf";
or
-- Set elements 1 and 3 of row 1 to asdf2
Array_of_Arrays(1)(1 | 3) := "asdf2"
But neither seem to be the correct syntax. Is there a way of doing this?
Thanks
The syntax
Array_of_Arrays(1)(2..5) := "asdf";
is legal, assuming that what you have is really an array of arrays. However, I'm guessing that you don't. Unlike some languages (C, Java, etc.), Ada makes a distinction between multi-dimensional arrays and "arrays of arrays". A two-dimensional array is declared something like
type Array_Type is array (1 .. 10, 1 .. 5) of Character;
Array_2D : Array_Type;
(and similarly for 3- or higher-dimensional arrays). When you declare your array type like this, you use indexes separated by commas to refer to a single element:
Array_2D (2, 3) := 'x';
and not
Array_2D (2) (3) := 'x'; -- wrong!! will not compile
You can't use a slice for multi-dimensional arrays:
Array_2D (1, 2..5) := "asdf";
The language designers just didn't allow that.
An array of arrays would be declared like:
type Array_Row is array (1 .. 5) of Character;
type Array_Of_Array_Type is array (1 .. 10) of Array_Row;
Array_Of_Arrays : Array_Of_Array_Type;
Now, since the array row type is separate, you don't use the multi-dimensional array syntax. To get at one character, you'd use something like
Array_Of_Arrays (2) (3) := 'x';
and it's legal to do something like
Array_Of_Arrays (1) (2..5) := "asdf";
but not
Array_Of_Arrays (1, 2..5) := "asdf"; -- syntax error
The key is to remember that in this case, each "row" of the array is a separate array object with its own array type; while in the multi-dimensional case, rows don't have their own types.
You can use either one; there are some cases where an array of arrays may be more appropriate, and some where a multi-dimensional array is more appropriate.
Since String is an array type, this also is an array of arrays:
type Array_Of_Strings is array (1 .. 10) of String(1..5);
and this is legal:
A : Array_Of_Strings;
A (3) (4) := 'y';
but not
A (3, 4) := 'y'; -- illegal
Related
I am currently working on programming this (https://rosettacode.org/wiki/Cartesian_product_of_two_or_more_lists) in Ada, I am trying to do a Cartesian product between different sets. I need help figuring it out, the main issue is how would I be able to declare empty Pairs and calculate that if it's empty, then the result should be empty. Thank you!
Numbers I am trying to use:
{1, 2} × {3, 4}
{3, 4} × {1, 2}
{1, 2} × {}
{} × {1, 2}
My code:
with Ada.Text_IO; use Ada.Text_IO; -- Basically importing the functions that will be used to print out the results.
procedure Hello is -- Procedure is where variables & functions are declared!
type T_Pair is array(1..2) of Integer; -- Declare a type of array that contains a set of 2 numbers (Used for final result!).
type T_Result is array(1..4) of T_Pair; -- Declare a type of array that contains a set of T_Pair(s), used to hold the result.
Demo1 : T_Result;
Demo1A : T_Pair := (1, 2);
Demo1B : T_Pair := (3, 4);
function Fun_CartProd(p1: T_Pair; p2: T_Pair) return T_Result is
results: T_Result;
i: integer := 1;
begin
for j in 1..2 loop
for h in 1..2 loop
results(i) := (p1(j), p2(h));
i := i + 1;
end loop;
end loop;
return results;
end Fun_CartProd;
begin -- This is where the statements go
Demo1 := Fun_CartProd(Demo1A, Demo1B);
for K in 1 .. Demo1'length loop
Put(" [");
for B in 1 .. Demo1(K)'length loop
Put(Integer'Image(Demo1(K)(B)));
if Demo1(K)'length /= B then
Put(",");
end if;
end loop;
Put("]");
if Demo1'length /= K then
Put(",");
end if;
end loop;
Put_Line(""); -- Create a new line.
end Hello;
Since each set of integers can be any length, including empty, I would start with a type that can handle all those situations:
type Integer_Set is array(Positive range <>) of Integer; -- Input type
type Integer_Tuple is array(Positive range <>_ of Integer; -- Output type
and you can represent the empty sets and tuples this way:
Empty_Set : constant Integer_Set(1..0) := (others => <>);
Empty_Tuple : constant Integer_Tuple(1..0) := (others => <>);
The other problem isn't just how many elements are in each set, but how many sets you will be finding the product of. For this I would recommend some kind of container. An array won't work here because each of the individual sets can have different sizes (including empty), but Ada has a variety of "indefinite" containers that can handle that. Here is an example using vectors:
package Set_Vectors is new Ada.Containers.Indefinite_Vectors
(Index_Type => Positive,
Element_Type => Integer_Set);
package Tuple_Vectors is new Ada.Containers.Indefinite_Vectors
(Index_Type => Positive,
Element_Type => Integer_Tuple);
and you can then represent an empty result as:
Empty_Tuple_Vector : constant Tuple_Vectors.Vector := Tupler_Vectors.Empty_Vector;
Now you can create a function that takes in a vector of sets and returns a Cartesian product which will also be a vector of sets:
function Cartesian_Product(Inputs : Set_Vectors.Vector) return Tuple_Vectors.Vector;
If one of the input sets is empty, you return an Empty_Tuple_Vector. You can check if one of the input sets are empty by checking their Length attribute result. It will be 0 if they are empty. Additionally if the input vector is completely empty, you can decide to either return Empty_Tuple_Vector or raise an exception. For example:
if Inputs'Length = 0 then
return Empty_Tuple_Vector; -- or raise an exception, etc.
end if;
for Set of Inputs loop
if Set'Length = 0 then
return Empty_Tuple_Vector;
end if;
-- Maybe do other stuff here if you need
end loop;
Note that the logic you presented assumes only pairs of inputs. I don't have enough experience to convert your logic to account for variable inputs, but perhaps someone can comment on that if you need it.
Also note as Flyx commented, this does not semantically check if a set is a set or not on the inputs (I.E. no duplicate values).
I am trying create a subtype which takes certain enumerations of a type for example,
type Integers_Type is (1,2,3,4,5,6,7,8,9,10);
I want something like this,
subtype Odd_Numbers_Type is Integers_Type (1,3,5,7,9);
I understand that when we use the keyword subtype of a type we need to use range, but the problem is the enumeration is not in a series.
For this kind of values filtering, I would use subtype predicates.
In your case, modifying your enumeration according to what Keith said :
type Integers_Type is (ONE,TWO,THREE,FOUR,FIVE,SIX,SEVEN,EIGHT,NINE,TEN);
subtype Odd_Numbers_Type is Integers_Type
with Static_Predicate => Odd_Numbers_Type in ONE | THREE | FIVE | SEVEN | NINE;
If you want to use numeric types instead of enumeration, use the following
type Integers_Type is range 1 .. 10;
subtype Odd_Numbers_Type is Integers_Type
with Dynamic_Predicate => Odd_Numbers_Type mod 2 /= 0;
For more information, you can read the rationale
EDIT :
For Enumeration types, the following compiles using gnatmake -gnata -gnatVa test_enum.adb but warns about the affectation line 14 and fails at execution because of the assert linked to the static predicate.
with Ada.Text_IO; use Ada.Text_IO;
Procedure Test_Enum is
type Integers_Type is (ONE,TWO,THREE,FOUR,FIVE,SIX,SEVEN,EIGHT,NINE,TEN);
subtype Odd_Numbers_Type is Integers_Type
with Static_Predicate => Odd_Numbers_Type in ONE | THREE | FIVE | SEVEN | NINE;
Test_I : Integers_Type := TWO;
Test_O : Odd_Numbers_Type := ONE;
begin
Put_Line("Test_I=" & Integers_Type'Image (Test_I));
Put_Line ("Test_O=" & Odd_Numbers_Type'Image (Test_O));
Test_O := Test_I;
Put_Line ("Test_O=" & Odd_Numbers_Type'Image (Test_O));
end Test_Enum;
For integer types, using the gnatmake -gnata -gnatVa test_int.adb compilation command, the compiler warns that the check will fail at runtime which is the case as the assertion is triggered.
with Ada.Text_IO; use Ada.Text_IO;
Procedure Test_Int is
type Integers_Type is range 1 .. 10;
subtype Odd_Numbers_Type is Integers_Type
with Dynamic_Predicate => Odd_Numbers_Type mod 2 /= 0;
Test_I : Integers_Type := 2;
Test_O : Odd_Numbers_Type := 1;
begin
Put_Line("Test_I=" & Integers_Type'Image (Test_I));
Put_Line ("Test_O=" & Odd_Numbers_Type'Image (Test_O));
Test_O := Test_I;
Put_Line ("Test_O=" & Odd_Numbers_Type'Image (Test_O));
end Test_Int;
In both cases, removing the -gnata flag will make the program working without taking care of the predicate as asserts are desactivated.
You're confusing enumerations and integers, this is a big difference if you're coming from something like C: in Ada enumerations are not merely labels for integers.
The answer given by Frédéric Praca is absolutely correct, but (a) you can use the Static_Predicate on numeric-types; and (b) there's a way to do what you ask in the question's title which is subtly different than what you're asking in the question-body.
The problem with using subtype-predicates is that you lose the ability to use certain
(a) — Static_Predicate
Type Digit_Range is 0..9;
Subtype Odd_Digit is Digit_Range
with Static_Predicate => 1|3|5|7|9;
(b) — Positions of an Enumeration
In Ada there are several attributes, among these are Pos & Val, and Pred & Succ. Given an enumeration (Type Example is (A,B,C,D,E);, you can say Example'Pos(D) which will return 3, and you can say Example'Val(1) which will return B.
You could then combine things together to create multiple-types, though this is heavy-handed and not normally what you want to do.
Type Base_Numeric_Range is range 0..9;
Type Odds is range 1..Base_Numeric_Range'Pos(Base_Numeric_Range'Last)) - Base_Numeric_Range'Pos(Base_Numeric_Range'First)) / 2;
-- This is from memory, and untested; so probably wrong in the details.
Function Convert(X : Base_Numeric_Range) return Odds is
( Odds'Val((Base_Numeric_Range'Pos(X) / 2) + 1) );
Function Convert(X : Odds) return Base_Numeric_Range is
( Base_Numeric_Range'Val(Odds'Pos(X) * 2 - 1) );
I have the syntax wrong but can't seem to find a informative page on the correct syntax of my question. I have a function I'm working on that takes in an array and I want to do some calculations on a slice of that array, so I thought to just create an array of a slice from the array passed in, specifically those indices. Could someone help me out on the syntax here cause this one keeps telling me missing ','
p1: UnboundArray(1 .. 15);
p1DataBits: UnboundArray(1 .. 7);
begin
p1DataBits := (p1(3 | 5 | 7 | 9 | 11 | 13 | 15));
end;
Another solution without expensive concatenation is to assign a direct, explicit array expression:
procedure Odds is
type UnboundArray is array (Integer range <>) of Integer;
p1: UnboundArray(1 .. 15);
p1DataBits: UnboundArray(1 .. 7);
begin
p1DataBits := (p1(3), p1(5), p1(7), p1(9), p1(11), p1(13), p1(15));
end;
It looks like you want to copy some elements of one array to another using slices. Because none of the elements are consecutive, you would likely be better off aggregating, shown here, or concatenating the desired elements.
p1DataBits : UnboundArray(1 .. 7) := p1(3) & p1(5) & p1(7) …;
You may have misread the syntax for a discrete_range; in this context the | symbol "separates alternative items" rather the representing a literal delimiter.
Can I have a vector filled with both, type A and B? I will fill it only with one type, so I am always sure, what I will get out of it. But it would make many things very easy for me, if I can define the vector once.
type T is tagged null record;
type A is new T with
record
x : Integer;
end record;
type B is new T with
record
y : Integer;
end record;
package Some_Vector is new Ada.Containers.Indefinite_Vectors (
Index_Type => Positive,
Element_Type => T <- ???
);
You can say:
package Some_Vector is new Ada.Containers.Indefinite_Vectors (
Index_Type => Positive,
Element_Type => T'Class
);
Some_Vector is now able to hold elements of type T or any type derived from it, including A or B. There's no requirement that all the elements have to be of the same type, as long as they're all derived from T; so if you don't really care whether this property is enforced, the above should work. If you really want the compiler to enforce that all elements are the same type, then you should simply declare two packages, A_Vector and B_Vector, for vectors of the two types; but then there's no way to write a "class-wide" type name that could refer to either an A_Vector or a B_Vector.
If you really want to combine both--have a vector type that could refer either to a vector of A or a vector of B, but still enforce that all elements of the vector have the same type, then I think this could be done if you define your own vector type and perform the needed check at run time, but it could get complicated. This compiles, but I haven't tested it:
generic
type Elem is new T with private;
package Sub_Vectors is
type Sub_Vector is new Some_Vector.Vector with null record;
overriding
procedure Insert (Container : in out Sub_Vector;
Before : in Some_Vector.Extended_Index;
New_Item : in T'Class;
Count : in Ada.Containers.Count_Type := 1)
with Pre => New_Item in Elem;
end Sub_Vectors;
package body Sub_Vectors is
procedure Insert (Container : in out Sub_Vector;
Before : in Some_Vector.Extended_Index;
New_Item : in T'Class;
Count : in Ada.Containers.Count_Type := 1) is
begin
Some_Vector.Insert
(Some_Vector.Vector(Container), Before, New_Item, Count);
end Insert;
end Sub_Vectors;
Unfortunately, you'd have to override every Insert and Replace_Element operation that could put an element into the vector. After you do all this, though, you can instantiate Sub_Vectors with Elem => A and with Elem => B, and the class Some_Vector.Vector'Class would be a class-wide type that would include both Sub_Vector types in the instance packages.
If you really want the compiler to enforce that all elements are the
same type, then you should simply declare two packages, A_Vector and
B_Vector, for vectors of the two types; but then there's no way to
write a "class-wide" type name that could refer to either an A_Vector
or a B_Vector.
You can, however, have a vector that points only to the A or B subtrees of the hierarchy:
type T is tagged null record;
type A is new T with null record;
type B is new T with null record;
type C is new T with null record;
type TAB is access all T'Class
with Dynamic_Predicate =>
TAB = null or else
(TAB.all in A'Class or TAB.all in B'Class);
Above yields the TAB Type which must be a [pointer to] an A'Class or B'Class, which you should be able to use in your vector. -- The only problem I've run into is you have to use GNAT's 'Unchecked_Access to get the access values of objects (due, I think, to my quick and dirty testing).
I want to declare an array with element-type of a variant record.
Something like this:
type myStruct (theType : vehicleType) is
record
...
when car => numOfWheels : Positive;
when others => null;
end record;
myArray : array (Positive range <>) of myStruct;
But in this case I get error.
It only allows this:
myArray : array (1.100) of myStruct(Car); //such as
So how to solve the index-problem and how to describe an array of a variant record's type without giving it's discriminant?
The example above will not compile. Here is a correct version (I changed mystruct to Integer for simplicity):
procedure test_array is
subtype Vehicle_Array_Index is Integer range 1..100; --// Maximum size is 100
type Arr_T is array (Vehicle_Array_Index range <>) of Integer;
type Vehicle_Array (Size: Vehicle_Array_Index := 1) is record
Vehicles : Arr_T(1..Size);
end record;
begin
null;
end;
One of the errors was that you cannot have anonymous arrays inside records, and second, you should use the discriminant to constrain the array inside.
As mentioned above this is not a good solution for varying-length arrays as most likely you will get a maximum sized array anyway. If you want arrays with dynamically determined sizes you can use blocks for that.
declare
a: array(1..n) of integer; -- n can be a variable here
begin
--some code using a
end;
It also works in the declaration parts of procedures and functions where n can be a parameter passed to the subprogram (one of the advantages Ada has over C/C++). And of course you can just allocate arrays dynamically on the heap using allocators.
If you want to be able to create objects of a discriminated type and change (or figure out) what the type is at runtime, you have to give the discriminant a default value in the declaration.
The reason for this is that Ada doesn't want to have to worry about dealing with uninitialized discriminated objects that it can't even figure out the size and valid record fields of.
For this reason, and some reasons I go into a bit in the comments, Ada discriminated records aren't actually very useful in mixed language programming (eg: exactly duplicating a C union). They can be handy on their own terms though.
For the example you give, you'd do the following (warning: Not compiled):
type myStruct (theType : vehicleType := car) is
record
...
when car => numOfWheels : Positive;
when others => null;
end record;
Then you could set one of the array values at runtime thusly:
myArray(20) := (theType => car,
field1 => myArray(20).field1,
... , --// Fill in the rest of the fields by name
numberOfWheels => 4);
As for this part:
myArray : array (Positive range <>) of myStruct;
You cannot declare actual array objects with an indeterminate range like this. You could declare a type that way, but an object has to have an actual size. If you want an array of varying length, you can once again use a variant record. (Again, not compiled)
subtype Vehicle_Array_Index is Integer range 1..100; --// Maximum size is 100
type Vehicle_Array (Vehicle_Array_Index : Size := 1) is record
Vehicles : array (Vehicle_Array_Index range <>) of myStruct;
end record;
Oh, and one more thing. You aren't doing this in your example, but if you ever want to use your discriminant to size an array like above, beware. When you declare objects of that type (again, assuming you used a default for the discriminant), the compiler will try to reserve enough space for the largest possible size you could ever feed it a value for. That makes it a Very Bad Idea to make a discriminated array indexed by something with a huge range like Integer or Positive. I know computers are bigger these days, but still most folks don't have 4 gig to spare for one silly little array. So if you are using your discriminant as an array index, it would be a good idea to create a smaller subtype of Integer to use for the type of the discriminant.