Question regarding Incomplete array assignments

Hi;

Let’s say that I guessed that I need ten elements for my array.

But while generating values for that array, I only used, say, nine of them.

When compiling would I get a warning about undefined value(s)?

In Perl (!!) I could ask if an array index is undefined and use that to carefully only use the part of the array I needed.

Here’s my dilemma:

I want to generate a list of all factors of a positive integer and preserve it in an array.

I’m still trying to find a method of determining the correct size of the array, but haven’t found it yet.

So, if my array is too small, I fail to generate (and preserve) all of the factors.

If my array is too big, then I easily could run into a run-time error (or worse!) by accessing undefined data.

One way to do this is for me to place negative integers in the part of the array that I don’t use, but that defeats the purpose of defining the array only to have positive integers.

I must be missing something obvious here.

Thanks,
Retired Build Engineer

Perhaps I should create an array of Natural numbers and define all elements to the value 0. Then the parts I use will have Positive values and I can ignore any elements containing zeros. Then, apart from determining an exact way to declare the array of the proper size, I will be safe.

Better ways to proceed?

I’m going to try choosing (n^(1/3) + 1) as my upper array index and see if that works along with default assignment of zero (array type is Natural).

You could use a vector to hold them. It grows dynamically as you add more elements. It is indexible just like an array. Take a look at the package Ada.Containers.Vectors

1 Like

Here’s an example with a vector:

with Ada.Text_IO; use Ada.Text_IO;
with Ada.Containers.Vectors;

procedure jdoodle is
    package Positive_Vectors is new Ada.Containers.Vectors
        (Index_Type   => Positive,
         Element_Type => Positive);
         
    Factors : Positive_Vectors.Vector;
    
begin
    Put_Line("Number of Elements:" & Factors.Length'Image);
    
    Put_Line("Adding elements ...");
    Factors.Append(2);
    Factors.Append(3);
    Put_Line("Number of Elements:" & Factors.Length'Image);
    
    Put_Line("Indexing the vector like an array ...");
    Put_Line("Factors(1) =" & Positive'Image(Factors(1)));
    Put_Line("Factors(2) =" & Positive'Image(Factors(2)));
    
    Put_Line("Iterating over the vector like an array ...");
    for Factor of Factors loop
        Put_Line("Factor of" & Factor'Image);
    end loop;
    
end jdoodle;

Output:

Number of Elements: 0
Adding elements ...
Number of Elements: 2
Indexing the vector like an array ...
Factors(1) = 2
Factors(2) = 3
Iterating over the vector like an array ...
Factor of 2
Factor of 3

gcc -c jdoodle.adb
gnatbind -x jdoodle.ali
gnatlink jdoodle.ali -o jdoodle

Thank you very much. This is much better than trying to guess how large the array should be, guessing too small is bad, guessing too big is bad, and there doesn’t seem to be a good way to know for sure in advance exactly how large it should be, and using “sentinel values” (a default assignment of zero) to an array that is bigger than needed is not good either.

Appreciate not only the clue to use the Ada.Containers.Vectors to replace the array but also some example code on how to use it.

Retired Build Engineer

1 Like

In addition to using vectors, you could use a recursive function to build up your list. Something like

type Factor_List is array (Positive range <>) of Positive;
...
function List_For (Number : in Positive) return Factor_List is
   function List_For (Number : in Positive; From : in Positive) return Factor_List is
      Factor : Positive;
      Exists : Boolean;
   begin
      Find_Next_Factor (Number => Number, From => From, Factor => Factor, Exists => Exists);

      if Exists then
         return Factor & List_For (Number, Factor);
      end if;

      return (1 .. 0 => 1);
   end List_For;
begin
   return List_For (Number, 1);
end List_For;
1 Like

Hm, I would suggest that the problem is you’re not using unconstrained arrays.

Type Sequence is Array(Positive range <>) of Natural;
Null_Sequence : Constant Sequence := (2..1 => <>);
Type Factorization(Negative : Boolean; Length : Natural) is record
  Factors : Sequence( 1..Length ) := (others => 0);
end record;


Function Factors(Value : Natural) return Factorization is
   Function Factorize( X       : Natural  := abs Value; 
                       Working : Sequence := Null_Sequennce
                     ) return Sequence is
   Begin
    case Value is
      when 0 | 1  => return Working;
      when 2      => return Sequence'( 1 => 1 );
      when 3      => return Sequence'( 1 => 0, 2 => 1 );
      when others =>
         if Working'Length not in 1..Sqrt(Value)-1 then
            Return Working;
         else
            Declare
               This    : Positive renames Xth_Prime( X );
               Remain  : Constant Natural := X mod This;
               Divides : Constant Boolean := Remain = 0;
               Value   : Constant Natural := (if Divides then X div This else X);
            Begin
               Return Factorize(Value, Working & Remain);
            End;
         end if;
    end case;
   End Factorize;

   Temp : Sequence renames Factorize;
Begin
   Return Result : Constant Factorization:=
      (Length   => Temp'Length,
       Negative => Value not in Natural,
       Factors  => Temp
      );
End Factors;

Um, I think…
I just typed it out; not compiled or anything.
(More a “how to use unconstrained arrays and variant-records” example.)

1 Like