3

Im working on a problem where I need to make a set of boxes according to an input number where each and every box has a unique name. I've managed to create the boxes but I've only managed to insert one name on all of them as my names are overwritten in the name collecting procedure.

here is the code https://pastebin.com/FBMvvrn4

with Ada.Text_IO;       use Ada.Text_IO;
with Ada.Float_Text_IO;     use Ada.Float_Text_IO;
with Ada.Integer_Text_IO;   use Ada.Integer_Text_IO;

procedure exercise is

  N : Integer;
  Names : String(1..10);
  L : Integer;

  procedure Objectcatcha (N: out Integer) is

  begin

  Put("Enter amount of objects: ");
  Get(N);

  end Objectcatcha;

  procedure Namescatcha (Names: out string; L: out integer) is

  begin
 for I in 1..N loop
    Get_Line(Names, L);
 end loop;

  end Namescatcha;

  procedure SpaceBox(Names: in String; L: in Integer; N : in integer) is

 begin

 for I in 1..N loop
    Put("+-----------+     ");
 end loop;
 New_Line;

 for I in 1..N loop
    Put("! ");
    Put(Names(1..L));
    for J in (L+1)..10 loop
       Put(" ");
    end loop;
    Put("!");

    if I = N then
       Put("");
    else
       Put("<>---");
    end if;
 end loop;
 New_Line;

 for I in 1..N loop
    Put("+-----------+     ");
 end loop;

  end SpaceBox;

  begin

  Objectcatcha(N);

  Put("Enter the name of the objects: ");
  Namescatcha(Names, L);

  SpaceBox(Names,L, N);

  end exercise;

I've been sitting around a lot with this and Id be very glad if someone could help me find a way to name each box individually.

Thanks in advance!

  • 3
    An obvious issue is that `Names : String(1..10);` is a single string of length 10, and you need an array of N strings at some point to store the N entered names, then to display those N names. – Zerte May 11 '20 at 06:40

4 Answers4

4

Where you can, (and you can here), just declare a variable of the exact size to hold the name you are using. This can be done by declaring it as an indefinite array, and initialising it with the correct name.

So your main program could be:

 Objectcatcha(N);    
 For I in 1 to N loop
   Put("Enter the name of the next object: ");
   Declare
     Name : String := Namescatcha;
   Begin
     SpaceBox(Name, Name'Length, N);  
   End;
 End loop;

Namescatcha is now a function returning just the right size of string:

function Namescatcha return String is
begin
   return Getline;
end Namescatcha;

and you should probably rewrite Spacebox without L (you can always use Name'Length to see the length of Name)

2

Brian Drummond already covered how to get back a variable length name and some means to work with them. In order to address your one name overwriting all the names problem, you have to consider that you are using one name variable to hold them all so it makes sense that one is overwriting the others. To store multiple names together, consider using an Indefinite_Vector to hold them. In your Objectcatcha procedure you get the capacity so use that to set the size of the vector

with Ada.Text_IO; use Ada.Text_IO;
with Ada.Containers.Indefinite_Vectors; use Ada.Containers;

-- Other stuff

package Name_Vectors is new Indefinite_Vectors
    (Index_Type   => Positive,
     Element_Type => String);

Names : Name_Vectors.Vector;

package Count_Type_IO is new Integer_IO(Count_Type);

procedure Objectcatcha is
    Number : Count_Type;
    Last : Positive; -- Placeholder for Get call
begin

    Put("Enter amount of objects: ");
    Count_Type_IO.Get
        (From => Get_Line,
         Item => Number,
         Last => Last);
    Names.Reserve_Capacity(Number);

end Objectcatcha;

procedure Namescatcha is
begin
    for Index in 1..Names.Capacity loop
        Names.Append(Get_Line);
    end loop;
end Namescatcha;

You will need to adjust your SpaceBox procedure to use the vector instead of the name or to only do one name at a time (your choice).

A few notes: 1. I changed your Get call to Get_Line for getting the number of names, you can change it back if you want. 2. When I stored the names in the vector, the last character stored might be the "new line" character, so you may have to strip it out. That's easy to do. Just use all the characters of the name except the last one. for example:

EDIT: Simon Wright indicated that this shouldn't be needed. My implementation does, so I'll leave this here if you have a setup similar to what I tested on and the new lines are copied.

declare
    Name : String := Names(Index);
begin
    Put(Name(1..Name'Length-1));
end;
Jere
  • 3,124
  • 7
  • 15
  • 1
    If you’re using Vectors, you don’t need to know how many names to expect; just Append them until the user puts in a terminator (e.g. a blank line). Get_Line doesn’t include the terminator. – Simon Wright May 11 '20 at 17:07
  • @SimonWright On the version of GNAT and platform I tested on, it did include the line terminator, so that's why put that edit at the end. You are correct that I don't need to know how many names ahead of time, but it saves on allocation overhead to preallocate if you already know how many (which the OP's scenario already did, so I just leveraged that). Thanks for the notes! – Jere May 11 '20 at 23:39
  • which GNAT/OS is that?! Terminator not included on macOS Mojave with either GNAT CE 2019 or FSF GCC 10.1.0. [AARM A.10.7(17.2)](http://www.ada-auth.org/standards/aarm12_w_tc1/html/AA-A-10-7.html#p17.2) isn’t totally clear, but the last sentence of the ramification in [(17.a)](http://www.ada-auth.org/standards/aarm12_w_tc1/html/AA-A-10-7.html#p17.a) gives a hint – Simon Wright May 12 '20 at 10:55
  • A variant of Red Hat 7. gnatmake --version returns GNATMAKE 7.1.1 20170622 (Red Hat 7.1.1-3), bash shell. If I take the code I suggested and paste it into a procedure and then do a loop over the vector calling Put (not Put_Line...I double checked), then it still prints out new lines for each element. – Jere May 12 '20 at 13:42
  • When I entered h e l l o RET Line’Length was 5 and Line (Line’Last) was o (FSF GCC 7.1.0) – Simon Wright May 12 '20 at 20:18
  • I get that. I'm just saying when my input is 3 (RET) a (RET) b (RET) c (RET) and I do "for Name of Names loop Put(Name); end loop;" it actually prints the new lines. Maybe a shell setup issue or whatever. It might be something similar to how using the GPS input shell and Get_Immediate still requires a new line (I.E. the shell is doing something weird). Not sure, but either way I noted your concern in the answer and left it as optional. I agree it is weird but it is happening. NOTE: (RET) represents hitting the return key. – Jere May 12 '20 at 21:32
1

Since your program seems to be a non-performance-critical application, I'd use variable-size strings to avoid storing the N different string lengths. In plain Ada variable-size strings are called Unbounded_String. Here, your exercise using an open-source package (hac_pack: spec, body) which facilitates things around variable-size strings.

with HAC_Pack;  use HAC_Pack;

procedure Names_in_Boxes is

  Max : constant := 100;
  type Names_Type is array (1 .. Max) of VString;

  procedure Objectcatcha (N: out Integer) is
  begin
    Put("Enter amount of objects: ");
    Get(N);
    Skip_Line;
  end Objectcatcha;

  procedure Namescatcha (Names: out Names_Type; N : in Integer) is
  begin
    for I in 1..N loop
      Put(+"Object " & I & ": ");
      Get_Line(Names (I));
    end loop;
  end Namescatcha;

  procedure SpaceBox(Names: in Names_Type; N : in Integer) is
  begin
    Put_Line (N * (+"+-----------+     "));
    for I in 1..N loop
       Put("! " & Names(I) & (10 - Length(Names(I))) * ' ' & "!");
       if I = N then
          Put("");
       else
          Put("<>---");
       end if;
    end loop;
    New_Line;
    Put_Line (N * (+"+-----------+     "));
  end SpaceBox;

  --  "Global" variables, unknown to
  --  Objectcatcha, Namescatcha, SpaceBox:
  N : Integer;
  Names : Names_Type;

begin
  Objectcatcha(N);
  Put_Line("Enter the name of the objects: ");
  Namescatcha(Names, N);
  SpaceBox(Names, N);
end Names_in_Boxes;
Zerte
  • 1,478
  • 10
  • 9
0

Thanks Zerte, Jere and Brian for your examples, it's much appreciated. Unfortunately I can't use third party packages so that rules out Zertes solution and as for Jere Im sorry but Im just a simple codemonkey with very shallow ADA knowledge and your example is just too complicated for me. Even if I got the exact code and it worked I still wouldnt learn it because it differs too much from what my school is teaching out. Like the procedures not having in/out parameters lets say for one. Maybe I misunderstand and its not that bad but at first glance it seems too complex for my level of ADA.

Brians I thought would work but what it does is, because it loops Spacebox N times, it creates N^2 amount of boxes, and on separate lines, when I only need N boxes on one line. Is there any way we could perhaps patch up the code to fix this because it seemed promising?

Thanks again for your time all of you!

with Ada.Text_IO;       use Ada.Text_IO;
with Ada.Float_Text_IO;     use Ada.Float_Text_IO;
with Ada.Integer_Text_IO;   use Ada.Integer_Text_IO;
with Ada.Strings.Unbounded;     use Ada.Strings.Unbounded;
with Ada.Strings.Unbounded.Text_IO; use Ada.Strings.Unbounded.Text_IO;


procedure exercise is

   Y : Ada.Text_IO.Count;
   N : Integer; 
   Names : Unbounded_string;
   type Names_Type is array (Positive range <>) of Unbounded_String;

   procedure Objectcatcha (N : out Integer) is

   begin

      Put("Enter amount of objects: ");
      Get(N);
      Skip_Line;

   end Objectcatcha;



   procedure Namescatcha (Names: out Names_Type; N : in Integer) is

   begin
      for I in Names'Range loop
     Get_Line(Names(I));
      end loop;

   end Namescatcha;

   procedure SpaceBox (Names: in Names_Type; N : in Integer) is

   begin

      for I in 1..N loop
     Put("+-----------+     ");
      end loop;
      New_Line;

      for I in Names'Range loop
     Put("! ");
     Put(Names(I));

     Y := Ada.Text_IO.Count(I);
     Set_Col(13+18*(Y-1));

     Put("!");

     if I = N then
        Put("");
     else
        Put("<>---");
     end if;

      end loop;
      New_Line;

      for I in 1..N loop
     Put("+-----------+     ");
      end loop;

   end SpaceBox;

begin

   Objectcatcha(N);
   declare
      A : Names_Type (1..N);
   begin
      Put("Enter the name of the objects: ");
      Namescatcha(A, N);
      SpaceBox(A, N);
   end;

end exercise;

This code works exactly the way I wanted it to, so I think it's finally solved: yay! :)

  • In the future, this would be better handled as comments on each person’s answer. Not only should answers be reserved for answers, but other contributors won’t get notifications when a new answer is posted, whereas they will if a comment is posted in response to one of their answers. – Jeremy Caney May 11 '20 at 16:13
  • Ah. I thought Spacebox only generated a box for the current Name (since you only passed in one name). You can simplify Spacebox to only generate a box for the current Name. But that would give each box on its own 3 lines. To pass a set of N names to Spacebox (calling it only once) Jere's solution using Ada.Containers would be best. –  May 11 '20 at 17:30
  • You could adjust Zerte's answer to use Ada.Strings.Unbounded rather than HAC_Pack. – Jeffrey R. Carter May 12 '20 at 10:26