Using multidimensional array in Ada - ada

In this code, I need help writing a multidimensional array with a range between 2020-01-01 to 2119-12-31.
My code works but as you see there are no arrays in it. How can I write this code with only arrays?
with Ada.Text_IO; use Ada.Text_IO;
with Ada.Integer_Text_IO; use Ada.Integer_Text_IO;
Procedure Date is
type date_type is record
Str : string (1..8);
Length : Natural := 0; end record;
A: date_type;
begin
loop
Put ("Enter a date between 2020-01-01 to 2119-12-31 : ");
Get_Line (A.Str, A.Length);
exit when A.Length = 8;
Put_Line ("Wrong input. Try again.");
end loop;
Put_Line (A.Str (1 .. 4) & "-" & A.Str (5 .. 6) & "-" & A.Str (7 .. 8));
end Date;

Perhaps, rather than a multi-dimensional array you should consider using a record such as
type Year_Number is range 1900..3000;
type Month_Number is range 1..12;
type Day_Number is range 1..31;
type Date_Rec is record
Year : Year_Number;
Month : Month_Number;
Day : Day_Number;
end record;
subtype Year_String is string (1..4);
subtype Month_String is string (1..2);
subtype Day_String is string (1..2);
function To_Date (Yr : Year_String; Mnth : Month_String; Dy : Day_String)
return Date_Rec is
Result : Date_Rec;
begin
Result.Year := Year_Number'Value (Yr);
Result.Month := Month_Number'Value (Mnth);
Result.Day := Day_Number'Value (Dy);
return Result;
end To_Date;
You can now pass around instances of Date_
Rec doing whatever you want with the date.
If you go this far then you might want to consider using the Time type described in Ada Language Reference Manual sections 9.6 and 9.6.1.

You haven't asked a reasonable question here because "I want to use arrays" is not a good reason to use an array.
"This problem can be best solved with an array ... but how do I deal with ... ?" would be a reasonable question, but you haven't stated a problem,let alone one that needs an array.
This is important because "using an array" is thinking in the solution domain, like "using a chisel". It's not the way to think about programming in Ada, (or IMO in any language).
Try thinking in the problem domain first : instead of "I want to use a chisel", I think "I want to recess this hinge so the door fits precisely" and a chisel is the neatest way of doing the job.
Then "How can I best validate a date?" would be one reasonable question, or "how can I store events that happen on each day for 100 years?"
The answer to the first question is probably in the Ada.Calendar package. Possibly the Value function in Ada.Calendar.Formatting, with an exception handler to catch incomprehensible strings and make the user try again.
with Ada.Text_IO; use Ada.Text_IO;
with Ada.Calendar;
with Ada.Calendar.Formatting;
procedure date is
Date : Ada.Calendar.Time;
Done : Boolean;
begin
loop
Put ("Enter a date between 2020-01-01 to 2119-12-31 : ");
Done := TRUE;
declare
A : String := Get_Line & " 12:00:00";
begin
Date := Ada.Calendar.Formatting.Value(A); -- validate it's a date
Done := Ada.Calendar.Year(Date) >= 2020
and Ada.Calendar.Year(Date) < 2120; -- validate correct range
exception
when Constraint_Error => Done := False; -- Formatting.Value failed
end;
exit when Done;
Put("Try Again : ");
end loop;
end date;
The answer to the second is probably a 1-dimensional array indexed by Day_Count from Ada.Calendar.Arithmetic but let's use the wrong tool : a 3D array indexed by your range of years, Month_Number and Day_Number from the Ada.Calendar base package.
with Ada.Text_IO; use Ada.Text_IO;
with Ada.Calendar; use Ada.Calendar;
with Ada.Calendar.Formatting;
with Ada.Strings.Unbounded; use Ada.Strings.Unbounded;
procedure date is
Date : Ada.Calendar.Time;
Done : Boolean := TRUE;
Event_Array : array(2020 .. 2119,
Ada.Calendar.Month_Number,
Ada.Calendar.Day_Number) of Unbounded_String;
begin
Event_Array := (others => (others => (others => Null_Unbounded_String)));
Event_Array(2020,11,3) := To_Unbounded_String("nothing much");
loop
Put ("Enter a date between 2020-01-01 to 2119-12-31 : ");
Done := TRUE;
declare
A : String := Get_Line & " 12:00:00";
begin
Date := Ada.Calendar.Formatting.Value(A);
Done := Ada.Calendar.Year(Date) >= 2020
and Ada.Calendar.Year(Date) < 2120;
exception
when Constraint_Error => Done := False;
end;
exit when Done;
Put("Try Again : ");
end loop;
Put_Line("Today " & Ada.Calendar.Formatting.Image(Date) & " : " &
To_String(Event_Array(Year(Date), Month(Date), Day(Date))) & " happened");
end date;
Test with the string 2020-11-03

Related

Changing a certain index in a string to a preferred character

When I try creating a subprogram that receives a string and a character and I for instance want to change the first index in the string to the character then return the result I'm not getting it to work...
This is how I tried doing it:
with Ada.Text_IO; use Ada.Text_IO;
with Ada.Integer_Text_IO; use Ada.Integer_Text_IO;
procedure Test is
function Change_Index_In_String(
Str : in String;
Char : in Character
) return String is
begin
Str(Str'First) := C
return Str;
end Change_Index_In_String
Str : String(1..5);
Char : Character;
begin
Put("Type a string (5 characters): ");
Get(String);
Put("Type a character: ");
Get(Character);
Put("Your new string is: ");
Put(Change_Index_In_String(Str, Char))
end Test;
How am I supposed to do this? I'm getting errors in my complier like this:
assignment to "in" mode parameter not allowed
expected type "Standard.Character"
found type "Standard.String"
I am aware on how to return the a certain index in a string as a character, like doing return Str(Str'Last) etc but what about changing a certain index and then returning it?
In the terminal if I type the string Stack and the character W it should return and print out Wtack
One of the key design decisions to make is whether your subprogram is modifying state (a procedure), or returning a new value (a function).
If you want to modify an argument to a subprogram, then A) it needs to be marked out or in out, and B) it should probably be a procedure.
If you want to write a function, you should work on a copy of the input data, rather than trying to modify the argument passed into the function.
Consider the below example of both approaches in one program.
with Ada.Text_IO; use Ada.Text_IO;
procedure Test is
function Change_First(s : in String; c : in Character) return String is
s2 : String(s'range) := s;
begin
s2(s2'First) := c;
return s2;
end Change_First;
procedure Sub_First(s : in out String; c : in Character) is
begin
s(s'First) := c;
end Sub_First;
s : String(1..5) := "Hello";
begin
Put_line(Change_First(s, 'W'));
Sub_First(s, 'Y');
Put_Line(s);
end Test;
This gives us as output:
Wello
Yello
Much is undefined for your function. What should happen if the String is null ('Length = 0)? What should the bounds of the returned String be? One simple solution for one set of answers is
function F (S : in String; C : in Character) return String is
(C & S (S'First + 1 .. S'Last) )
with Pre => S'Length > 0 and S'First < Integer'Last,
Post => (F'Result'First = 1 and
F'Result'Length = S'Length) and then
(F'Result (F'Result'First) = C and
F'Result (F'Result'First + 1 .. F'Result'Last) = S (S'First + 1 .. S'Last) );

Ada error: invalid use of subtype mark in expression or call

I'm stuck here with an error in my Ada program. There is a lot of code and I don't want to copy all of it here, so I hope that the part that I'm sharing is the part from where the problem comes.
task type Producent is
entry Start(Jedzenie: in Typ_Jedzenia; Czas_Produkcji: in Integer);
end Producent;
task type Buffer is
entry Zamow(Jedzenie: in Typ_Jedzenia; Numer: in Integer; Czy_Zatwierdzono: out Boolean);
entry Dostarcz(Zamowienie: in Typ_Zestawu; Numer: out Integer);
end Buffer;
task body Producent is
package Losowa_Produkcja is new
Ada.Numerics.Discrete_Random(Zakres_Czasu_Produkcji);
Generator: Losowa_Produkcja.Generator;
Index_Jedzenia: Integer;
Nr_Produkcji_Jedzenia: Integer := 1;
Produkcja: Integer;
Zatwierdzono: Boolean := False;
begin
accept Start (Jedzenie : in Typ_Jedzenia; Czas_Produkcji : in Integer) do
Losowa_Produkcja.Reset(Generator);
Index_Jedzenia := Jedzenie;
Produkcja := Czas_Produkcji;
end Start;
loop
delay Duration(Losowa_Produkcja.Random(Generator));
Put_Line("Przygotowano " & Nazwa_Jedzenia(Index_Jedzenia) & " numer " & Integer'Image(Nr_Produkcji_Jedzenia));
loop
Buffer.Zamow(Index_Jedzenia, Nr_Produkcji_Jedzenia, Zatwierdzono); <-------- ERROR
if Zatwierdzono = False then
Put_Line("Brak miejsca w kuchni dla " & Nazwa_Jedzenia(Index_Jedzenia) & ". Wstrzymanie");
delay Duration(3.0);
else
Nr_Produkcji_Jedzenia := Nr_Produkcji_Jedzenia + 1;
end if;
exit;
end loop;
end loop;
end Producent;
task body Buffer is
begin
Put_Line("Jestesmy u Buffera");
loop
select
accept Zamow(Jedzenie: in Typ_Jedzenia; Numer: in Integer; Czy_Zatwierdzono: out Boolean) do
Put_Line("Trwa zamawianie...");
end Zamow;
end select;
end loop;
end Buffer;
From my attempts I understand that when I want to call entry Buffer.Zamow(Index_Jedzenia, Nr_Produkcji_Jedzenia, Zatwierdzono); (which is in task Producent) there is an error with 'Zatwierdzono' argument. When I removed this argument from declarations and definitions Zamow() entry worked.
Full error: invalid use of subtype mark in expression or call
What should I change or where is the problem with this boolean Zatwierdzono variable?
Zatwierdzono means Accepted in this case.
Thanks for any ideas.
You have two problems:
Index_Jedzenia := Jedzenie;
In your Start entry is trying to implicitly convert Jedzenie from its type, Typ_Jedzenia, to Integer, the type of Index_Jedzenia. You need some way to convert this.
Additionally on the line you are seeing the error on, the first parameter of that entry is of type Typ_Jedzenia but you are passing in an Integer (Index_Jedzenia is an integer). Again, you can't implicitly convert types like that.
If Typ_Jedzenia is actually an integer, you can explicitly convert them. Otherwise you need to make a conversion function of some type and use that before passing in or assigning to different types.

Ada - How do you read an array from a single line of input?

My question is pretty simple, I have input that looks like this...
0 0 0 1 1 1 -1 -1 -1 1
And I need to store these values into an array but I can't figure it out. This is what I have so far...
with Ada.Text_IO; use Ada.Text_IO;
procedure Main is
type arr is array(1..10) of Integer;
Data : arr;
begin
for I in 1..arr'Length loop
Data(I) := Integer'Value(Get_Line);
end loop;
end Main;
I know this wrong and it's pretty obvious why this isn't working. I'm trying to store multiple values into a single integer, I need a way to iterate over the input or load all the values at once. How would you do this in Ada?
You can use Get_Line to get the whole line as a string and then Ada.Integer_Text_IO to parse the string:
with Ada.Text_IO; use Ada.Text_IO;
with Ada.Integer_Text_IO; use Ada.Integer_Text_IO;
procedure Hello is
Line : String := Get_Line;
Value : Integer;
Last : Positive := 1;
begin
while Last < Line'Last loop
Get(Line(Last..Line'Last),Value,Last);
Put_Line(Value'Image); -- Save the value to an array here instead
Last := Last + 1; -- Needed to move to the next part of the string
end loop;
end Hello;
After that, you can load the values into an array in the loop or however you like.
Example output:
$gnatmake -o hello *.adb
gcc -c hello.adb
gnatbind -x hello.ali
gnatlink hello.ali -o hello
$hello
0
0
0
1
1
1
-1
-1
-1
1
EDIT: Adding a recursive option that is more general. This will read a line from STDIN and recursively concatenate the values into an array. It uses the secondary stack to do so in GNAT.
with Ada.Text_IO; use Ada.Text_IO;
with Ada.Integer_Text_IO; use Ada.Integer_Text_Io;
procedure Hello is
-- Need an array return type
type Integer_Array is array (Positive range <>) of Integer;
-- Recursive function
function Get_Ints return Integer_Array is
Value : Integer;
begin
-- Read from STDIN using Integer_Text_IO;
Get(Value);
-- Concatinate recursively
return Integer_Array'(1 => Value) & Get_Ints;
exception
-- I found different exceptions with different versions
-- of GNAT, so using "others" to cover all versions
when others =>
-- Using Ada2012 syntax here. If not using Ada2012
-- then just declare the Empty variable somewhere
-- and then return it here
return Empty : Integer_Array(1..0);
end Get_Ints;
Result : Integer_Array := Get_Ints;
begin
Put_Line("Hello, world!");
Put_Line(Integer'Image(Result'Length));
for E of Result loop
Put(Integer'Image(E) & " ");
end loop;
end Hello;
If you know that you have 10 elements to read, it can be done a little more simply like this:
with Ada.Text_IO; use Ada.Text_IO;
with Ada.Integer_Text_IO; use Ada.Integer_Text_IO;
procedure Hello is
A: array (1..10) of Integer;
begin
for V of A loop
Get(V);
end loop;
for I in A'Range loop
Put(I, 5);
Put(": ");
Put(A(I), 5);
New_Line;
end loop;
end Hello;
If you don't actually know how many elements to read in advance, please update the question.
Even though this already was answered, I'd like to add a couple improvements to Jere's answer.
It is more Ada-like to terminate the recursion using End_Of_File rather than an exception. Plus, it makes the program clearer.
Also, using tail-call recursion instead of normal recursion allows the compiler to perform some optimization.
function Get_Ints(input : in File_Type) return Integer_Array is
function Get_Ints_Rec(accumulator : in Integer_Array) return Integer_Array is
value : Integer;
begin
if End_Of_File(input) then
return accumulator;
else
begin
Get(input, value);
exception
when Data_Error => -- problem when reading
if not End_Of_Line(input) then
Skip_Line(input);
end if;
return Get_Ints_Rec(acc);
end;
return Get_Ints_Rec(accumulator & (1 => value));
end if;
end Get_Ints_Rec;
acc : constant Integer_Array(1 .. 0) := (others => 0);
begin
return Get_Ints_Rec(acc);
end Get_Ints;

Ada linker not letting me call my procedure

Here is my code:
procedure String_To_Int(str: String) is
str_length : Integer := str'Size / 8;
ASCII_Values_Array: array (Integer range 1 .. str_length) of Integer;
begin
Text_Io.Put_Line(str & " has a length of " & natural'image(str_length));
for x in 1 .. str_length loop
ASCII_Values_Array(x) := Character'Pos(str(x));
Text_Io.Put_Line(natural'image(ASCII_Values_Array(x)));
end loop;
end String_To_Int;
and I am trying to call it with:
String_To_Int(str => "abcdefghijklmnopqrstuvwxyz");
but the compiler is telling me:
Saw '(', expected: , :
And I have no clue what is wrong about how I am calling my procedure. I have looked at many other examples of procedure calls and this looks exactly the same. Any help is appreciated!
There's something you aren't telling us.
./test_sti;
abcdefghijklmnopqrstuvwxyz has a length of 26
97
98
99
...
121
122
Now you don't say what you're actually doing, but here's what I did.
I called String_To_Int from another procedure, in a file test_sti.adb.
with String_To_Int;
procedure test_sti is
begin
String_To_Int(str => "abcdefghijklmnopqrstuvwxyz");
end;
Note that String_To_Int is a separate procedure, in its own file, so the with clause tells the compiler to look for it. I could have declared it locally, i.e. between "is" and "begin" and saved both the files below, but separation is probably better design.
Now anything "with"ed will have both a specification and a body - in this case, specification string_to_int.ads :
procedure String_To_Int(str: String);
and body string_to_int.adb :
with Ada.Text_IO;
use Ada;
procedure String_To_Int(str: String) is
str_length : Integer := str'Size / 8;
ASCII_Values_Array: array (Integer range 1 .. str_length) of Integer;
begin
Text_Io.Put_Line(str & " has a length of " & natural'image(str_length));
for x in 1 .. str_length loop
ASCII_Values_Array(x) := Character'Pos(str(x));
Text_Io.Put_Line(natural'image(ASCII_Values_Array(x)));
end loop;
end String_To_Int;
and to build the lot, simply
gnatmake test_sti.adb
and the compiler works out its own dependencies, no Makefile necessary.
It's actually a bit odd (but perfectly legal) to have a separate "compilation unit" like this for just one procedure. More normally it would either be declared locally, or it would be a part of a package - either a collection of utilities like Ada.Text_IO, or something like a class if you are familiar with Java or C++.
Incidentally, String_To_Int is a very odd procedure : instead of declaring its variables as
str_length : Integer := str'Size / 8;
ASCII_Values_Array: array (Integer range 1 .. str_length) of Integer;
it's cleaner to use the attributes more consistently:
str_length : constant natural := str'Length;
ASCII_Values_Array: array (str'range) of Integer;
and express the loop condition as
for x in str'range loop

raised CONSTRAINT_ERROR : josephus.adb:50 index check failed

I'm trying to run this code, but something is going wrong with the line:
Soldiers (Number_Of_Soldiers) := Soldier_Type'(Name=>new String'(Line(1..Length)), Alive=>True);
Can someone help me, please?
Thank you so much!
--Josephus Problem
with Ada.Text_IO,Ada.Integer_Text_IO;
use Ada;
procedure Josephus is
type String_Pointer is access String;
type Soldier_Type is record
Name : String_Pointer;
Alive : Boolean;
end record;
Max_Number_Of_Soldiers: constant := 10;
Number_Of_Soldiers : Integer range 0..Max_Number_Of_Soldiers := 0;
-- start with 0 to facilitate modular arithmetic
Soldiers: array (0..Max_Number_Of_Soldiers-1) of Soldier_Type;
procedure Next (Index: in out Integer; Interval: Positive) is
begin
for I in 1..Interval loop
loop
Index := (Index + 1) mod Number_Of_Soldiers;
exit when Soldiers(Index).Alive;
end loop;
end loop;
end Next;
Interval : Integer;
Man : Integer := Soldiers'First;
begin
-- get interval from the standard input
Integer_Text_IO.Get (Interval);
Text_IO.Skip_Line;
Text_IO.Put ("Skip every ");
Integer_Text_IO.Put (Interval, Width=>1);
Text_IO.Put_Line (" soldiers.");
-- get names (one per line) from the standard input
declare
Line: String (1..10);
Length: Integer;
begin
while not (Text_IO.End_Of_File) loop
Text_IO.Get_Line (Line, Length);
Soldiers (Number_Of_Soldiers) := Soldier_Type'(Name=>new String'(Line(1..Length)), Alive=>True);
Number_Of_Soldiers := Number_Of_Soldiers + 1;
end loop;
end;
for I in 1..Number_Of_Soldiers-1 loop
Soldiers(Man).Alive := False;
Text_IO.Put (Soldiers(Man).Name.all);
Text_IO.Put_Line (" commits suicide.");
Next (Man, Interval);
end loop;
Text_IO.Put (Soldiers(Man).Name.all);
Text_IO.Put_Line (" is the last.");
end Josephus;
I think your problem is with the line
Max_Number_Of_Soldiers: constant := 10;
Obviously the number specified needs to be more than the maximum possible number of entries in your input!
The problem of unbounded input data sets is one reason to look at using Ada.Containers.Vectors instead of arrays.

Resources