Is it necessary to wrap shared array data in a protected type? - ada

I am aware that it is generally bad practice (and the ARM probably says that this is undefined behavior), but I am attempting to write a fast text parser containing many floating point numbers and it would be very expensive to wrap the loaded text into a protected type given that the data is examined character by character and may have up to a million floats or pass a slice on the stack.
Is it possible in Ada (GNAT) to "safely" divide up an unprotected array for consumption with multiple tasks given that the array is never written and only read?
As in:
Text : array (1..1_000_000) of Character := ...
begin
Task_1.Initialize (Start_Index => 1, End_Index => 10_000);
Task_2.Initialize (Start_Index => 10_001, End_Index => 20_000);
...

Yes. That is safe because there is no race condition associated with reading the data and there is no temporally overlapping write operation.
For example, the following code uses such a technique to perform parallel addition on an array of integers.
package Parallel_Addition is
type Data_Array is array(Integer range <>) of Integer;
type Data_Access is access all Data_Array;
function Sum(Item : in not null Data_Access) return Integer;
end Parallel_Addition;
package body Parallel_Addition is
---------
-- Sum --
---------
function Sum (Item : in not null Data_Access) return Integer is
task type Adder is
entry Set (Min : Integer; Max : Integer);
entry Report (Value : out Integer);
end Adder;
task body Adder is
Total : Integer := 0;
First : Integer;
Last : Integer;
begin
accept Set (Min : Integer; Max : Integer) do
First := Min;
Last := Max;
end Set;
for I in First .. Last loop
Total := Total + Item (I);
end loop;
accept Report (Value : out Integer) do
Value := Total;
end Report;
end Adder;
A1 : Adder;
A2 : Adder;
R1 : Integer;
R2 : Integer;
Mid : constant Integer := (Item'Length / 2) + Item'First;
begin
A1.Set (Min => Item'First, Max => Mid);
A2.Set (Min => Mid + 1, Max => Item'Last);
A1.Report (R1);
A2.Report (R2);
return R1 + R2;
end Sum;
end Parallel_Addition;
with Parallel_Addition; use Parallel_Addition;
with Ada.Text_IO; use Ada.Text_IO;
with Ada.Calendar; use Ada.Calendar;
procedure Parallel_Addition_Test is
The_Data : Data_Access := new Data_Array (1 .. Integer'Last / 5);
Start : Time;
Stop : Time;
The_Sum : Integer;
begin
The_Data.all := (others => 1);
Start := Clock;
The_Sum := Sum (The_Data);
Stop := Clock;
Put_Line ("The sum is: " & Integer'Image (The_Sum));
Put_Line
("Addition elapsed time is " &
Duration'Image (Stop - Start) &
" seconds.");
Put_Line
("Time per addition operation is " &
Float'Image(Float(Stop - Start) / Float(The_Data'Length)) &
" seconds.");
end Parallel_Addition_Test;

Related

Constraint Error running Heapify SiftDown

I am writing this code in Ada for a class where we have to teach ourselves the code. I understand heap sort, but the Ada syntax is really confusing me. I don't understand why I am getting a constraint error in this sort function.
Essentially we have to pass array "A" into this procedure, and it should organize it. I get the constraint error at siftDown(A(Start...A'Last));
Thank you in advance
Procedure sort_3(A : in out array_type) is
procedure swap(Left : in out Integer; Right : in out Integer) is
temp : Integer;
begin
temp := Left;
Left := Right;
Right := Temp;
end swap;
procedure siftDown(A : in out array_type) is
Count : Integer := 1;
root : Integer := Integer'Pos(A'First);
child : Integer := Integer'Pos(A'Last);
last : Integer := Integer'Pos(A'Last);
begin
while root * 2 + 1 <= last loop
child := root * 2 + 1;
if child + 1 <= last and then A(Integer'Val(child)) < A(Integer'Val(child + 1)) then
child := child + 1;
end if;
if A(Integer'Val(root)) < A(Integer'Val(child)) then
swap(A(Integer'Val(root)), A(Integer'Val(child)));
root := child;
else
exit;
end if;
end loop;
end siftDown;
procedure heapify(A : in out array_type) is
Count : Integer := 0;
First_Pos : Integer;
Last_Pos : Integer;
Start : Integer;
begin
First_Pos := A'First;
Last_Pos := A'Last;
Start := Integer'Val((Last_Pos - First_Pos + 1) / 2);
loop
siftDown(A(Start...A'Last));
if Start > Integer'First then
Start := Integer'Pred(Start);
else
exit;
end if;
end loop;
end heapify;
Last_Index : Integer := Integer'Last;
begin
heapify(A);
while Last_Index > Integer'First loop
swap(A(Last_Index), A(A'First));
Last_Index := Integer'Pred(Last_Index);
siftDown(A(A'First..Last_Index));
end loop;
end sort_3;
You have a syntax error in the code - an extra dot in A(Start...A'Last).
The syntax A(Start..A'Last) means a slice, part of array from Start to the last element. The Constraint_Error means that Start not in array bounds. Try to add
Ada.Text_IO.Put_Line (Start'Image);
before that line and you will see Start values and when it became out of the A'Range.
Your code has some references to Integer'First and Integer'Last, which are huge values that have nothing to do with the array A and its values. I'm pretty sure you should use A'First and A'Last instead.
Also a note on style: Using the same identifier, "A", for the parameter of the local (inner, nested) procedures as for the parameter "A" of the containing (outer) procedure, when these arrays can be different, invites confusion and errors. Better to use different identifiers.

Ada - Accessibility check raised within a procedure

I previously asked a question regarding accessibility checks being raised in Ada, which #Brian Drummond was kind enough to awnser. The accessibility check was in a function, now I have a similair problem within a procedure; any guidance as to why this is would be greatly appreciated.
The code I am working on has been taken from here: https://github.com/raph-amiard/ada-synth-lib
The code in main file below is from the the Simple_Sine example which can be found here:
https://github.com/raph-amiard/ada-synth-lib/blob/master/examples/simple_sine.adb
My main file looks like this:
with Write_To_Stdout;
with Command; use Command;
with Effects; use Effects;
with Sound_Gen_Interfaces; use Sound_Gen_Interfaces;
with Utils; use Utils;
procedure main is
pragma Suppress (Accessibility_Check);
BPM : Natural := 15;
Notes : Notes_Array :=
To_Seq_Notes ((C, G, F, G, C, G, F, A, C, G, F, G, C, G, F, G), 400, 4);
function Simple_Synth
(S : access Simple_Sequencer; Tune : Integer := 0; Decay : Integer)
return access Mixer
is
(Create_Mixer
((0 => (Create_Sine (Create_Pitch_Gen (Tune, S)), 0.5)),
Env => Create_ADSR (5, 50, Decay, 0.5, S)));
Volume : Float := 0.9;
Decay : Integer := 800;
Seq : access Simple_Sequencer;
Sine_Gen : access Mixer;
Main : constant access Mixer := Create_Mixer (No_Generators);
begin
for I in -3 .. 1 loop
Seq := Create_Sequencer (16, BPM, 1, Notes);
Sine_Gen := Simple_Synth (Seq, I * 12, Decay);
Main.Add_Generator (Sine_Gen, Volume);
BPM := BPM * 2;
Volume := Volume / 1.8;
Decay := Decay / 2;
end loop;
Write_To_Stdout (Main);
end main;
The error that's raised is this:
raised PROGRAM_ERROR : sound_gen_interfaces.adb:20 accessibility check failed
It is raised during a call to this procedure:
-- Register_Note_Generator --
-----------------------------
procedure Register_Simulation_Listener
(N : access I_Simulation_Listener'Class) is
begin
Simulation_Listeners (Simulation_Listeners_Nb) := N;
Simulation_Listeners_Nb := Simulation_Listeners_Nb + 1;
end Register_Simulation_Listener;
Which is line 20 of the code below:
with Ada.Containers.Vectors;
package body Sound_Gen_Interfaces is
package PA_Vectors
is new Ada.Containers.Vectors (Natural, Params_Scope);
Params_Aggregators : PA_Vectors.Vector;
function Current_FPA return Params_Scope is
(Params_Aggregators.Last_Element);
-----------------------------
-- Register_Note_Generator --
-----------------------------
procedure Register_Simulation_Listener
(N : access I_Simulation_Listener'Class) is
begin
Simulation_Listeners (Simulation_Listeners_Nb) := N;
Simulation_Listeners_Nb := Simulation_Listeners_Nb + 1;
end Register_Simulation_Listener;
---------------
-- Next_Step --
---------------
procedure Next_Steps is
begin
for I in 0 .. Simulation_Listeners_Nb - 1 loop
Simulation_Listeners (I).Next_Step;
end loop;
end Next_Steps;
----------------
-- Base_Reset --
----------------
procedure Base_Reset (Self : in out Generator) is
begin
null;
end Base_Reset;
--------------------
-- Reset_Not_Null --
--------------------
procedure Reset_Not_Null (Self : Generator_Access) is
begin
if Self /= null then
Self.Reset;
end if;
end Reset_Not_Null;
--------------------
-- Reset_Not_Null --
--------------------
procedure Reset_Not_Null (Self : Note_Generator_Access) is
begin
if Self /= null then
Self.Reset;
end if;
end Reset_Not_Null;
--------------------------
-- Compute_Fixed_Params --
--------------------------
procedure Compute_Params (Self : in out Generator) is
procedure Internal (Self : in out Generator'Class);
procedure Internal (Self : in out Generator'Class) is
begin
for C of Self.Children loop
if C /= null then
if C.Is_Param then
Add_To_Current (C);
end if;
Internal (C.all);
end if;
end loop;
end Internal;
begin
Self.Parameters := new Params_Scope_Type;
Enter (Self.Parameters);
Internal (Self);
Leave (Self.Parameters);
end Compute_Params;
-----------
-- Enter --
-----------
procedure Enter (F : Params_Scope) is
begin
Params_Aggregators.Append (F);
end Enter;
-----------
-- Leave --
-----------
procedure Leave (F : Params_Scope) is
begin
pragma Assert (F = Current_FPA);
Params_Aggregators.Delete_Last;
end Leave;
--------------------
-- Add_To_Current --
--------------------
procedure Add_To_Current (G : Generator_Access) is
use Ada.Containers;
begin
if Params_Aggregators.Length > 0 then
Current_FPA.Generators.Append (G);
end if;
end Add_To_Current;
------------------
-- All_Children --
------------------
function All_Children
(Self : in out Generator) return Generator_Array
is
function All_Children_Internal
(G : Generator_Access) return Generator_Array
is
(G.All_Children) with Inline_Always;
function Is_Null (G : Generator_Access) return Boolean
is (G /= null) with Inline_Always;
function Cat_Arrays
is new Generator_Arrays.Id_Flat_Map_Gen (All_Children_Internal);
function Filter_Null is new Generator_Arrays.Filter_Gen (Is_Null);
S : Generator'Class := Self;
use Generator_Arrays;
begin
return Filter_Null (S.Children & Cat_Arrays (Filter_Null (S.Children)));
end All_Children;
----------------
-- Get_Params --
----------------
function Get_Params
(Self : in out Generator) return Generator_Arrays.Array_Type
is
use Generator_Arrays;
function Internal
(G : Generator_Access) return Generator_Arrays.Array_Type
is
(if G.Parameters /= null
then Generator_Arrays.To_Array (G.Parameters.Generators)
else Generator_Arrays.Empty_Array) with Inline_Always;
function Cat_Arrays
is new Generator_Arrays.Id_Flat_Map_Gen (Internal);
begin
return Internal (Self'Unrestricted_Access)
& Cat_Arrays (Self.All_Children);
end Get_Params;
----------------------
-- Set_Scaled_Value --
----------------------
procedure Set_Scaled_Value
(Self : in out Generator'Class; I : Natural; Val : Scaled_Value_T)
is
V : Float :=
(if Self.Get_Scale (I) = Exp
then Exp8_Transfer (Float (Val)) else Float (Val));
Max : constant Float := Self.Get_Max_Value (I);
Min : constant Float := Self.Get_Min_Value (I);
begin
V := V * (Max - Min) + Min;
Self.Set_Value (I, V);
end Set_Scaled_Value;
end Sound_Gen_Interfaces;
Any help as to why this is happening would be greatly appreciated.
Thank you
What you're seeing here is the result of (over-)using anonymous access types (discussed in ARM 3.10.2, informally known as the “Heart of Darkness” amongst the maintainers of Ada).
I don't think there's a simple way around this (aside from using -gnatp, as we found earlier, to suppress all checks; though perhaps you might have luck with
pragma Suppress (Accessibility_Check);
in the units where there's a problem).
I managed to get a build without Program_Errors with a fairly brutal hack, changing the anonymous access I_Simulation_Listener'Class to the named Simulation_Listener_Access throughout and, for example,
function Create_Simple_Command
(On_Period, Off_Period : Sample_Period;
Note : Note_T) return access Simple_Command'Class
is
begin
return N : constant access Simple_Command'Class
:= new Simple_Command'(Note => Note,
Buffer => <>,
On_Period => On_Period,
Off_Period => Off_Period,
Current_P => 0)
do
Register_Simulation_Listener (N);
end return;
end Create_Simple_Command;
to
function Create_Simple_Command
(On_Period, Off_Period : Sample_Period;
Note : Note_T) return access Simple_Command'Class
is
Command : constant Simulation_Listener_Access
:= new Simple_Command'(Note => Note,
Buffer => <>,
On_Period => On_Period,
Off_Period => Off_Period,
Current_P => 0);
begin
Register_Simulation_Listener (Command);
return Simple_Command (Command.all)'Access;
end Create_Simple_Command;
Ideally I'd have thought about having Create_Simple_Command returning a named access type too.
You can see where I got to at Github.

Different scalar range in different cirumstance

How to represent data with complete scalar range in the first state then represent it as zero to one in the next state while using the same memory space?
Any approaches to the problem is appreciated, the example procedures does not have to be like that if solution requires them to change.
Example
Reading values from a file and then normalize it. Float_Array is for raw value with any range that comes directly from file.
Feature_Array is for normalized values.
type Float_Array is array (Integer range <>) of Float;
type Feature is new Float range 0.0 .. 1.0;
type Feature_Array is array (Integer range <>) of Feature;
The first step is to read floats into an Float_Array and finding max and min value.
procedure Read (Name : String; Result : out Float_Array; Last : out Integer; Min : out Float; Max : out Float) is
use Ada.Text_IO;
use Ada.Float_Text_IO;
F : File_Type;
begin
Open (F, In_File, Name);
for I in Result'Range loop
exit when End_Of_File (F);
Get (F, Result (I));
Min := Float'Min (Min, Result (I));
Max := Float'Max (Max, Result (I));
Last := I;
end loop;
Close (F);
end;
Float_Array is just temporarily being used to read and find min max. The next step is to normalize all values.
function Normalize (Value : Float; Min, Max : Float) return Float is
begin
return (Value - Min) / (Max - Min);
end;
procedure Normalize (Min : Float; Max : Float; Scale : Float; Result : in out Float_Array) is
begin
for E of Result loop
E := Normalize (E, Min, Max) * Scale;
end loop;
end;
After normalization I want the values to be represented as Feature_Array.
Bad solution that does no range check.
There is no range check so it is not a proper solution. Scaling the values from one to three does not yield range check error. So at this point there is no point to have Feature_Array if there is no range check.
Last : Integer;
Data : Float_Array (1 .. 100);
Min : Float := Float'First;
Max : Float := Float'Last;
begin
Read ("frequency.lines_of_float", Data, Last, Min, Max);
Normalize (Min, Max, 1.0, Data);
-- Normalize (Min, Max, 3.0, Data);
declare
The_Features : Feature_Array (Data'Range) with Address => Data'Address;
begin
Put (The_Features);
end;
I have tried attribute 'Valid on the array i.e. The_Features'Valid but it only works on scalar types. And using 'Valid for range check will involve extra code.
I think that I finally understand what is needed here. You want to have variable of normalized type and not of Floats. (in case of floats one would have to constantly do array overlays or have 2 variables pointing to the same address).
Last : Integer;
The_Features : Feature_Array (1 .. 100);
Min : Float := Float'First;
Max : Float := Float'Last;
begin
declare
Data : Float_Array (The_Features'Range) with Address => The_Features'Address;
begin
Read ("frequency.lines_of_float", Data, Last, Min, Max);
Normalize (Min, Max, 1.0, Data);
-- Normalize (Min, Max, 3.0, Data);
end;
Put (The_Features);
This should work but keep in mind that you have to ensure that the result of Normalize is valid.
It seems that manual range checking is the way to go. I can't find a way to use Ada range checking automatically.
To manually check an array of float is within a range
This uses Ada 2012 - conditional expressions.
This is needed sometimes when variables depends on address.
A := (for all E of Item (Item'First .. Last) => E'Valid);
Assert (A, "Elements of Item is not within range.");
Code
with Ada.Text_IO;
with Ada.Float_Text_IO;
procedure Main is
type Float_Array is array (Integer range <>) of Float;
type Feature is new Float range 0.0 .. 1.0;
type Feature_Array is array (Integer range <>) of Feature;
procedure Read (Name : String; Result : out Float_Array; Last : in out Integer; Min : in out Float; Max : in out Float) is
use Ada.Text_IO;
use Ada.Float_Text_IO;
F : File_Type;
begin
Open (F, In_File, Name);
loop
exit when End_Of_File (F);
Last := Last + 1;
Get (F, Result (Last));
Skip_Line (F);
Min := Float'Min (Min, Result (Last));
Max := Float'Max (Max, Result (Last));
exit when Last = Result'Last;
end loop;
Close (F);
end;
function Normalize (Value : Float; Min, Max : Float) return Float is ((Value - Min) / (Max - Min));
procedure Normalize (Min : Float; Max : Float; Scale : Float; Result : in out Float_Array) is
begin
for E of Result loop
E := Normalize (E, Min, Max) * Scale;
end loop;
end;
procedure Put (Item : Feature_Array) is
use Ada.Float_Text_IO;
use Ada.Text_IO;
begin
for E of Item loop
Put (Float (E), 3, 3, 0);
New_Line;
end loop;
end;
procedure Put (Item : Float_Array) is
use Ada.Float_Text_IO;
use Ada.Text_IO;
begin
for E of Item loop
Put (E, 3, 3, 0);
New_Line;
end loop;
end;
procedure Read (Item : out Feature_Array; Last : in out Integer) with
Pre => Feature_Array'Component_Size = Float_Array'Component_Size,
Post => (for all E of Item (Item'First .. Last) => E >= 0.0 and E <= 1.0);
procedure Read (Item : out Feature_Array; Last : in out Integer) is
Data : Float_Array (Item'Range) with Address => Item'Address;
Min : Float := Float'Last;
Max : Float := Float'First;
begin
Read ("f.ssv", Data, Last, Min, Max);
Ada.Text_IO.Put_Line ("Before normalization.");
Put (Data (Data'First .. Last));
Normalize (Min, Max, 1.0, Data (Data'First .. Last));
end;
F : Feature_Array (-5 .. 10);
Last : Integer := F'First - 1;
begin
Read (F, Last);
Ada.Text_IO.Put_Line ("After normalization.");
Put (F (F'First .. Last));
end;
f.ssv
0.1
11.0
-3.0
Result
Before normalization.
0.100
11.000
-3.000
After normalization.
0.221
1.000
0.000

GNATprove: "postcondition might fail" in simple function

I want to write a simple function that finds the biggest number in given Integer array. Here is specification:
package Maximum with SPARK_Mode is
type Vector is array(Integer range <>) of Integer;
function Maximum (A : in Vector) return Integer
with
SPARK_Mode,
Pre => A'Length > 0,
Post =>
(for all i in A'Range => A(i) <= Maximum'Result)
and then
(for some i in A'Range => A(i) = Maximum'Result);
end Maximum;
And here is function's body:
package body Maximum with SPARK_Mode is
function Maximum (A : in Vector) return Integer
is
Max : Integer := A (A'First);
begin
if (A'Length = 1) then
return Max;
end if;
for I in A'First + 1 .. A'Last loop
pragma Loop_Invariant
(for all Index in A'First .. I - 1 => Max >= A(Index));
if A (I) > Max then
Max := A (I);
end if;
end loop;
return Max;
end Maximum;
end Maximum;
And when I try to prove this function with SPARK, it says that postcondition might fail. I'm trying to understand this for like 5 hours now and I have no idea why it says so. It's really annoying, this function MUST work. Do you have any idea why SPARK behaves so strange? What is a data example for this function to not fullfil its postcondition? It always returns a value taken directly from given array and it is always maximal.
Your mistake is to make a loop invariant, which is weaker than the postcondition:
Specification:
package Maximum
with SPARK_Mode
is
type Vector is array (Integer range <>) of Integer;
function Maximum (A : in Vector) return Integer
with
Pre => A'Length > 0,
Post => (for all i in A'Range => A(i) <= Maximum'Result)
and
(for some i in A'Range => A(i) = Maximum'Result);
end Maximum;
Implementation:
package body Maximum with SPARK_Mode is
function Maximum (A : in Vector) return Integer
is
Max : Integer := A (A'First);
begin
if (A'Length = 1) then
return Max;
end if;
for K in A'First + 1 .. A'Last loop
pragma Loop_Invariant
((for all I in A'First .. K - 1 => A (I) <= Max)
and
(for some I in A'First .. K - 1 => A (I) = Max));
if A (K) > Max then
Max := A (K);
end if;
end loop;
return Max;
end Maximum;
end Maximum;
Project file:
project Maximum is
for Main use ("maximum");
end Maximum;

Ada Generic Averaging Function

I have a function which averages a certain numeric value from an array of records. This value is either a natural or an enumerated type delta. I have it summing up the values correctly but my question is this: how do I get the length of an array into a generic type, so that it can divide both integers and delta type numbers?
On your array-of-records use the 'Length attribute; this has the advantage of always working even if your bounds are somewhat odd, like -18..3, or an enumeration, like cheeses..fruits.
Something like:
Function Average( Input : In Array_of_Records ) Return float is
-- You say you already have a summation function, so...
Sum : Natural:= Summation( Input );
Begin
Return Sum / Input'Length;
End Average;
You may need to convert the numeric types, by saying Float(Sum) or the like, as Ada does no automatic type "promotions."
This has some flaws in it, but is this closer to what you wanted ?
NWS.
with Ada.Text_Io;
procedure Main is
generic
type Element_T is private;
Zero : Element_T;
One : Element_T;
type Vec_T is array (Integer range <>) of Element_T;
with function "+"(Left, Right : in Element_T) return Element_T is <>;
with function "/"(Left, Right : in Element_T) return Element_T is <>;
package Arrayops is
function Sum (Vec : in Vec_T) return Element_T;
function Count (Vec : in Vec_T) return Element_T;
function Average (Vec : in Vec_T) return Element_T;
end Arrayops;
package body Arrayops is
function Sum (Vec : in Vec_T) return Element_T is
S : Element_T := Zero;
begin
for I in Vec'First .. Vec'Last loop
S := S + Vec(I);
end loop;
return S;
end Sum;
function Count (Vec : in Vec_T) return Element_T is
C : Element_T := Zero;
begin
for I in Vec'First .. Vec'Last loop
C := C + One;
end loop;
return C;
end Count;
function Average (Vec : in Vec_T) return Element_T is
S : constant Element_T := Sum (Vec);
Len : constant Element_T := Count (Vec);
begin
return S / Len;
end Average;
end Arrayops;
type Fl_Arr_T is array (Integer range <>) of Float;
package Fl_Arr is new Arrayops (Element_T => Float,
Zero => 0.0,
One => 1.0,
Vec_T => Fl_Arr_T);
type Int_Arr_T is array (Integer range <>) of Integer;
package Int_Arr is new Arrayops (Element_T => Integer,
Zero => 0,
One => 1,
Vec_T => Int_Arr_T);
My_Ints : constant Int_Arr_T (1 .. 5) := (6,7,5,1,2);
My_Floats : constant Fl_Arr_T (1 .. 7) := (6.1,7.2,5.3,1.4,2.5,8.7,9.7);
Int_Sum : constant Integer := Int_Arr.Sum (My_Ints);
Int_Count : constant Integer := Int_Arr.Count (My_Ints);
Int_Avg : constant Integer := Int_Arr.Average (My_Ints);
Float_Sum : constant Float := Fl_Arr.Sum (My_Floats);
Float_Count : constant Float := Fl_Arr.Count (My_Floats);
Float_Avg : constant Float := Fl_Arr.Average (My_Floats);
begin
Ada.Text_Io.Put_Line ("Integers => Sum: " & Integer'Image (Int_Sum) & ", Count: " & Integer'Image (Int_Count) & ", Avg: " & Integer'Image (Int_Avg));
Ada.Text_Io.Put_Line ("Floats => Sum: " & Float'Image (Float_Sum) & ", Count: " & Float'Image (Float_Count) & ", Avg: " & Float'Image (Float_Avg));
end Main;
Result :
Integers => Sum: 21, Count: 5, Avg: 4
Floats => Sum: 4.09000E+01, Count: 7.00000E+00, Avg: 5.84286E+00
Expanding on Shark8 a bit here...
Ada allows you to declare array types as unconstrained. Something like
type Array_of_Records is array (Natural range <>) of My_Record;
Gives you a type that can be used for arrays of records with starting and ending array indices that could be anywhere in the range of Natural.
One of the nifty things I can do with such a type is use it as a subroutine parameter, like so:
function Sum (Vector : in Array_of_Records) return Natural;
OK, so inside that routine, how do I know where the array bounds are? By using attributes, like so:
for index in Vector'first..Vector'last loop
or
for index in Vector'range loop
Of course for this to work, you must pass in a perfectly-sized array to your Sum routine. Supppose that isn't what you have. Suppose you instead have a huge array (kind of a buffer) and not all of the values are valid? Well, you keep track of what are the valid values, and pass in only those by using a slice.
Rec_Buffer : Array_of_Records (1..10_000);
Last_Valid_Rec : Natural := 0;
....
--// Rec_Buffer gets loaded with 2,128 values or something. We pass it into Sum
--// like so:
Ada.Text_IO ("Sum of vector is " &
natural'image(Sum (Rec_Buffer (1..Last_Valid_Rec));
(warning - uncompiled code)

Resources