dynamic array size determined at runtime in ada - ada

Is it possible to have an array with size that is determined at runtime like so,
Procedure prog is
type myArray is array(Integer range <>) of Float;
arraySize : Integer := 0;
theArray : myArray(0..arraySize);
Begin
-- Get Array size from user.
put_line("How big would you like the array?");
get(arraySize);
For I in 0..arraySize Loop
theArray(I) := 1.2 * I;
End Loop;
End prog;
Is there a way to achieve this result other than using dynamically Linked Lists or another similar structure? Or is there a simple built in data structure that would be simpler than using dynamically linked lists?

Sure, declare it in a block as follows:
procedure prog is
arraySize : Integer := 0;
type myArray is array(Integer range <>) of Float;
begin
-- Get Array size from user.
put_line("How big would you like the array?");
get(arraySize);
declare
theArray : myArray(0..arraySize);
begin
for I in 0..arraySize Loop
theArray(I) := 1.2 * I;
end Loop;
end;
end prog;
or pass the arraySize as an argument into a subprogram and declare and operate on it in that subprogram:
procedure Process_Array (arraySize : Integer) is
theArray : myArray(0..arraySize);
begin
for I in arraySize'Range Loop
theArray(I) := 1.2 * I;
end Loop;
end;
This is just illustrative (and not compiled :-), as you need to deal with things like an invalid array size and such.

Yes, you can defer the declaration of a constrained object until you know the size. In this example, the array Candidates can be allocated in a nested block (introduced by the keyword declare) or on the heap (using the keyword new). In this related example, Line has a different size each time through the loop, depending on what Get_Line finds.

Related

How to Declare VARRAY with Integer variable as Number of Elements in Oracle19c

Trying to declare a PL SQL VARRAY with the 'Number of Elements' as a previously declared variable but I get this error:
PLS-00325: non-integral numeric literal NUM_ENV_COUNT is inappropriate in this context
Here is what I tried:
Declare
num_Env_Count INTEGER := 3;
type arr_text is VARRAY(num_Env_Count) of VARCHAR2(2000);
arr_Env_Type arr_text := ('D','V','P');
Begin
for i in 1..num_Env_Count loop
DBMS_OUTPUT.PUT_LINE(arr_Env_Type(i));
end loop;
end;
No. Have you tried it? If you do/did then you get a PLS-00320 indication that the expression is malformed. I understand the desire to avoid repeating the litteral variable. You can achieve the same wit a nested table.
Declare
type arr_text is table of VARCHAR2(2000) ;
arr_Env_Type arr_text := arr_text('D','V','P');
Begin
for i in 1..arr_Env_Type.count loop
DBMS_OUTPUT.PUT_LINE(arr_Env_Type(i));
end loop;
end;

Printing arrays elements using recursion

I am learning Ada programming language and I can admit that it's strongly typed. In this code, I am trying to ask the user to enter 4 integers and then by using recursion, print out these numbers. However, I am facing trouble writing a recursion subprogram for it and wonder if I can get any suggestions on how to write it correctly?
with Ada.Text_IO; use Ada.Text_IO;
with Ada.Integer_Text_IO; use Ada.Integer_Text_IO;
procedure Dugga is
Y: Natural;
type arr is
array (1..4) of Natural;
A:arr;
function Recurs(Item: in Natural) return Natural is
begin
if Item >= 1 then
return Get(Item ); --Error: Context requires function call, found procedure name
end if;
end Recurs;
begin
Y:=Recurs(arr); --Error: expected type "Standard.Integer. Found type: arr
end Dugga;
Warnings: Program_error may be raised at run time
"return" statement missing following this statement
Error: Missing argument for parameter "Item" in call to get
You appear to be confused about what a recursive subprogram is. A recursive subprogram calls itself unless a termination condition is reached.
If your subprogram calls itself before printing the array element the array elements will be printed in reverse order. If your subprogram calls itself after printing the array element the array elements will be printed in the order they are entered.
with Ada.Text_Io; use Ada.Text_IO;
procedure Main is
subtype Idx is integer range 1..4;
type Arr is array (Idx) of Integer;
procedure Print(Nums : Arr; Index : Idx) is
begin
Put (Nums(Index)'Image);
if Index = Nums'Last then
New_Line;
else
Print(Nums, Index + 1);
end if;
end Print;
A : Arr := (1, 2, 3, 4);
begin
Print(A, A'First);
end Main;
The program above declares an array type and creates an instance of that array type.
The procedure Print is a recursive procedure that prints the value of an array at the specified index value. The procedure then checks the index value. If the index value is the last index value for the array the procedure calls New_Line, otherwise it calls Print with second parameter being Index + 1.
For any recursive algorithm you need to have some sort of control logic (IF, CASE, etc.) that decides between calling the function again with a different input (and returning that result with the potential result of this call) or otherwise returning a final value.
A couple of things.
You say our end game is to use recursion to "print" the values, so your recursive operation only needs to be a procedure. It doesn't need to return anything, it just needs to print something each time it runs. One way to do this is print the first element of the array and then recall the procedure with just the remaining elements. If there are no elements, don't print anything and leave the procedure.
In order to do this, I would recommend passing in the whole array instead of a single value (like your version does). Each time you recursively call the function, you shorten the array until there is no array left. To do this though, you need to adjust your array type to be any potential size, not just 1..4, so that you can use that to end the recursion.
Start with making an array type:
type Natural_Array is array (Positive range <>) of Natural;
Now change the specification of your function to a procedure that uses that type.
procedure Recurse(Numbers: in Natural_Array);
The last part is implementing the recursion:
procedure Recurse(Numbers: in Natural_Array) is
begin
-- See if the array has any elements first
if Numbers'Length > 0 then
-- There are elements in the array, so print the
-- first one. This is why we use Numbers'First
-- as the index
Put_Line(Numbers(Numbers'First)'Image);
-- Now that we printed the first number, lets
-- only pass in the remaining elements in the array
-- here. This is where the "recursion" happens
-- as it calls this function again with a reduced
-- input set. Eventually we'll pass in an
-- empty array once the last element is printed
-- above.
Recurse(Numbers(Numbers'First+1..Numbers'Last));
else
-- Here there weren't any eleemnts in the array
-- left, so lets just end the recursion by
-- doing nothing
null;
end if;
end Recurse;
Here's the full compilable example:
with Ada.Text_IO; use Ada.Text_IO;
procedure Hello is
type Natural_Array is array (Positive range <>) of Natural;
procedure Recurse(Numbers: in Natural_Array) is
begin
-- See if the array has any elements first
if Numbers'Length > 0 then
-- There are elements in the array, so print the
-- first one. This is why we use Numbers'First
-- as the index
Put_Line(Numbers(Numbers'First)'Image);
-- Now that we printed the first number, lets
-- only pass in the remaining elements in the array
-- here. This is where the "recursion" happens
-- as it calls this function again with a reduced
-- input set. Eventually we'll pass in an
-- empty array once the last element is printed
-- above.
Recurse(Numbers(Numbers'First+1..Numbers'Last));
else
-- Here there weren't any eleemnts in the array
-- left, so lets just end the recursion by
-- doing nothing
null;
end if;
end Recurse;
A : Natural_Array(1..4) := (1,2,3,4);
begin
Recurse(A);
end Hello;

How to delete elements by using recursion?

In this code, I have built a list of three integers (5, 10, 15) and what I need help with is that I need to ask the user which of these elements he/she wants to remove and then only return the element/elements that are left. I need to write a subprogram for this and just by using recursion, I need to remove the elements that the user does not need.
Main program:
with Ada.Text_IO; use Ada.Text_IO;
with Ada.Integer_Text_IO; use Ada.Integer_Text_IO;
with Linked_List; use Linked_List;
procedure Main_Remove is
I : Integer;
L : List_Type;
begin
Build_Test_List(L); -- builds a list of 3 integers (5 => 10 => 15)
Put(L);
Put("Which elements do you want to remove/delete ");
Get(I);
Remove(L, I);
Put(L);
end Main_Remove;
Package:
package Linked_List is
type List_Type is private;
procedure Put(Item : in List_Type);
procedure Build_Test_List(Item : out List_Type;
Base : in Integer := 5);
private
type E_Type;
type List_Type is access E_Type;
type E_Type is
record
Data : Integer;
Next : List_Type;
end record;
end Linked_List;
Pakage body:
with Ada.Text_IO; use Ada.Text_IO;
with Ada.Integer_Text_IO; use Ada.Integer_Text_IO;
with Ada.Unchecked_Deallocation;
package body Linked_List is
procedure Put(Item : in List_Type) is
P : List_Type := Item;
begin
Put("Listan: ");
while P /= null loop
if P /= Item then
Put(" -> ");
end if;
Put(P.Data, Width => 0);
P := P.Next;
end loop;
New_Line;
end Put;
procedure Insert_First(L : in out List_Type;
D : in Integer) is
begin
L := new E_Type'(Data => D, Next => L);
end Insert_First;
procedure Build_Test_List(Item : out List_Type;
Base : in Integer := 5) is
begin
for I in reverse 1..3 loop
Insert_First(Item, Base * I);
end loop;
end Build_Test_List;
end Linked_List;
Something like this will do, with reservations: specifically, there’s a memory leak.
procedure Remove (L : in out List_Type; Item : Integer) is
begin
The recursion has to be stopped when the list is empty.
if L = null then
return;
end if;
The list isn’t empty. What to do next depends on whether the current list element contains the value we’re looking for, or not.
if L.Data = Item then
This item needs to be removed from the list. Do this by altering the original pointer (which came from the list head, or the previous element) to skip over this element, and then process that element.
This is the point at which the memory leak has occurred. Obviously the cell being pointed to by the initial L needs to be freed, but you’re going to have to be careful about the order of operations.
L := L.Next;
Remove (L, Item);
else
The item stays in the list, go on to the next element.
Remove (L.Next, Item);
end if;
end Remove;

Issues assigning a string to a linked list in Ada

I'm trying to bring in a string and assign it , character by character, to a linked list, declared like so:
type lstring is private;
type lstring_node;
type lstring_access is access lstring_node;
type lstring_node is
record
Char : character;
Position : integer;
Next : lstring_access;
end record;
private
type lstring is
record
First : lstring_access := null;
end record;
The function to assign it goes like so:
function toLstring (Input : string) return lstring is
LStr : lstring;
Current : lstring_access;
begin
-- Set the first item of LStr as Current.
-- LStr will from now on inherit the items of Current.
LStr.First := new lstring_node;
Current := LStr.First;
-- Iterate through the string and add it to the lstring.
for I in 1..Input'Length loop
if I /= 1 then
Current := new lstring_node;
end if;
Current.Char := Input(I);
Ada.Text_IO.Put(Current.Char);
Current.Position := I;
Current := Current.Next;
end loop;
-- Return the new lstring.
return LStr;
end toLstring;
I know through debugging that the for loop is working just fine, and the elements are being assigned to Current just fine. But for some reason the items aren't being added to LStr. Do I need to declare something after the for loop to finish it up? I'm under the impression that because Current is assigned to LStr.First, LStr would inherit the rest of the appended list. Am I wrong there?
Thanks
At the end of the loop, you assign Current.Next (which is null at this point) to Current. This is a value copy. Changing the value of Current in the next iteration will not change the value of Next in the previous node. (Mind that Current.Char and Current.Next are implicit dereferences that will actually do Current.all.Char / Next, but Current := new lstring_node is not a dereference, because it changes the value of the reference.)
Instead, you should assign new lstring_node to Next and then advance your Current reference:
for I in Input'Range loop
Current.Char := Input(I);
Ada.Text_IO.Put(Current.Char);
Current.Position := I - Input'First + 1;
if I /= Input'Last then
Current.Next := new lstring_node;
Current := Current.Next;
end if;
end loop;
Note that I changed the loop range (Strings are not required to start at 1) and tweaked the position calculation so you'll end up with a 1-based position index in your list.

Oracle 11g : When declaring new TYPE as TABLE, must I add "INDEX BY PLS_INTEGER"?

What is the diffecence between adding INDEX BY PLS_INTEGER and not at end of declaration of new table type. Look at this example:
DECLARE
GC_BULK_LIMIT CONSTANT INTEGER := 500;
CURSOR CUR_CLIENTS IS SELECT C.ID, C.NAME FROM CLIENTS C;
TYPE RT_CLIENTS IS TABLE OF CUR_CLIENTS%ROWTYPE;
-- TYPE RT_CLIENTS IS TABLE OF CUR_CLIENTS%ROWTYPE INDEX BY PLS_INTEGER;
LT_CLIENTS RT_CLIENTS;
BEGIN
OPEN CUR_CLIENTS;
LOOP
FETCH CUR_CLIENTS BULK COLLECT INTO LT_CLIENTS LIMIT GC_BULK_LIMIT;
EXIT WHEN LT_CLIENTS.COUNT = 0;
FOR I IN 1..LT_CLIENTS.COUNT LOOP
-- ... SOME LOGIC
END LOOP;
END LOOP;
CLOSE CUR_CLIENTS;
END;
in response to "must i add". The short answer is NO.
the difference is that
TYPE RT_CLIENTS IS TABLE OF CUR_CLIENTS%ROWTYPE;
Is a nested table. This means that for a given variable of this type, we know that the subscripts are sequential. i.e. the subscript starts from 1 and goes up to the array length.
The following loop therefore, is the right way to access a nested table array:
FOR I IN 1..LT_CLIENTS.COUNT LOOP
This however, is called a associative array:
TYPE RT_CLIENTS IS TABLE OF CUR_CLIENTS%ROWTYPE INDEX BY PLS_INTEGER;
(you could also index by a varchar2 if you wanted). The difference is that the subscripts in this case do not have to be sequential, depending on how the array was populated. In your code, they would be (as bulk collect would do that), but its not always the case.
The safe way to access and loop through an index by array is :
v_subscript := t_arr.first;
while v_subscript is not null loop
dbms_output.put_line(v_subscript || ': ' || t_arr(v_subscript));
v_subscript := t_arr.next(v_subscript);
end loop;
where v_subscript is a variable of the same datatype of the index by part.
also with a nested table, you can populate the array quickly with:
declare
type myarr is table of number;
t_arr myarr;
v_subscript number;
begin
t_arr := myarr(1, 12, 44);
whereas with an index by array you'd have to have three lines there to populate it:
t_arr(1):= 1;
t_arr(2):= 12;
t_arr(3):= 44;
for your particular case, without the index by is perfectly fine.
further reading: http://docs.oracle.com/cd/E18283_01/appdev.112/e17126/composites.htm

Resources