Duplication of code in main program solution - ada

with Ada.Text_IO; use Ada.Text_IO;
procedure TF is
function Character_Check return Boolean is
Check : Character;
begin
Get(Check);
if Check = 'T' then
return True;
else
return False;
end if;
end Character_Check;
begin
Put("Type F or T (For False or True): ");
if Character_Check then
Put("You typed True");
else
Put("You typed False");
end if;
end TF;
I need to get a character in my actual function and I have a hard time not duplicating my code. You can see a very obvious duplication where it says "You typed " twice. I can fix it by typing the text in my function but that's not allowed so I wonder if theres anyway to fix this?

You could exchange this lines into one long:
Put("You typed " & (if Character_Check then "True" else "False"));
or
Put("You typed " & Boolean'Image(Character_Check));
Just the second solution will make text capitalized (TRUE and FALSE).

The answer provided by #thindil provides a good example of using a conditional expression to achieve the result you want. You can also substantially streamline your Character_Check function by simply returning the boolean value already generated.
Rather than:
function Character_Check return Boolean is
Check : Character;
begin
Get(Check);
if Check = 'T' then
return True;
else
return False;
end if;
end Character_Check;
Simply use:
function Character_Check return Boolean is
Check : Character;
begin
Get(Check);
return Check = 'T';
end Character_Check;
This is what Jim Lewis was trying to point you to 2 days ago in comments on a previous question.

For Character_Check, what about
function Character_Check return Boolean is
Check : Character;
begin
Get(Check);
return (case Check is
when 'F' => False,
when 'T' => True,
when others =>
raise Constraint_Error with "F or T, doofus");
end Character_Check;

Related

Function and procedure subprogram

I need to only use one function and one procedure and unfortunately I didn't manage to solve this problem.
The problem goes as following:
Create a subprogram that has three parameters. One string and two sign.
The subroutine must pick the first and last character in the string. The main program will then print these two characters.
Create a subprogram that retrieves a character, from the keyboard, which represents a truth value (see driving example).
The subprogram must send back the corresponding truth value to the main program.
In the terminal it should look like:
Type a string containing 6 characters: Overfl
First character is: O
Last character is : l
Type F or T (for False or True): T
You typed True
This is how my code looks like:
with Ada.Text_IO; use Ada.Text_IO;
procedure Test is
procedure String_Check(
S : in String;
Char_1, Char_2 : out Character
) is
begin
Char_1 := S(S'First);
Char_2 := S(S'Last);
end String_Check;
function Character_Check return Boolean is
Check : Integer;
begin
Put("Type F or T (For False or True): ");
Get(Check)
if Check = 'T' then
return True;
else
return False;
end if;
end Character_Check;
Char_1, Char_2 : Character;
Check : Character;
S : String(1 .. 6);
begin
Put("Type a string containing 6 characters: ");
Get(S);
String_Check(S, Char_1, Char_2);
Put("First character is: ");
Put(Char_1);
New_Line;
Put("Last character is: ");
Put(Char_2);
Skip_Line;
New_Line(2);
Put("You typed ");
if Character_Check then
Put("True");
else
Put("False");
end if;
end Test;
My code runs but it doesn't fulfill the given requirements. I guess I can't use a function in my second subprogram because the instruction says "create a subprogram that receives a character from the keyboard". How am I supposed to do Get in a function? But if I make my second program to a procedure I have to make my first program to a function. How am I supposed to do return for two values at the same time?

Compare a string input to an Enumerated type

I am looking to do a comparison of a string to and enumeration. I have written a sample code of what I am attempting. Since a String and and Enumerated type are different, how do I go about doing this properly in Ada?
WITH Ada.Text_IO; USE Ada.Text_IO;
PROCEDURE ColorTest IS
TYPE StopLightColor IS (red, yellow, green);
response : String (1 .. 10);
N : Integer;
BEGIN
Put("What color do you see on the stoplight? ");
Get_Line (response, N);
IF response IN StopLightColor THEN
Put_Line ("The stoplight is " & response(1..n));
END IF;
END ColorTest;
First instantiate Enumeration_IO for StopLightColor:
package Color_IO is new Ada.Text_IO.Enumeration_IO(StopLightColor);
Then you can do either of the following:
Use Color_IO.Get to read the value, catching any Data_Error that arises, as shown here for a similar instance of Enumeration_IO.
Use Color_IO.Put to obtain a String for comparison to response.
As an aside, Stoplight_Color might be a more consistent style for the enumerated type's identifier.
Another possibility:
Get_Line (response, N);
declare
Color : StopLightColor;
begin
Color := StopLightColor'Value(response(1..N));
-- if you get here, the string is a valid color
Put_Line ("The stoplight is " & response(1..N));
exception
when Constraint_Error =>
-- if you get here, the string is not a valid color (also could
-- be raised if N is out of range, which it won't be here)
null;
end;
Answering your actual question:
Ada doesn't allow you to compare values of different types directly, but luckily there is a way to convert an enumerated type to a string, which always works.
For any enumerated type T there exists a function:
function T'Image (Item : in T) return String;
which returns a string representation of the enumerated object passed to it.
Using that, you can declare a function, which compares a string and an enumerated type:
function "=" (Left : in String;
Right : in Enumerated_Type) return Boolean is
begin
return Left = Enumerated_Type'Image (Right);
end "=";
If you want to do a case-insensitive comparison, you could map both strings to lower case before comparing them:
function "=" (Left : in String;
Right : in Enumerated_Type) return Boolean is
use Ada.Characters.Handling;
begin
return To_Lower (Left) = To_Lower (Enumerated_Type'Image (Right));
end "=";

Ada 95 Check if enter was pressed

I am new to Ada.
How can I check if enter was pressed?
The while loop should be able to check whether the input character is a white space or an enter key.
Furthermore, how can I check the user input type, like the type() or typeof() function in other languages?
FUNCTION ReadValue RETURN Unbounded_String IS
ValueChar : Character;
Result : Unbounded_String := To_Unbounded_String("NULL");
BEGIN
Get(ValueChar);
Skip_Line;
WHILE ValueChar /= ';'LOOP
Get(ValueChar);
IF IsValidNameInput(ValueChar) THEN
Result := Result & ValueChar;
ELSE
exit;
END IF;
END LOOP;
ValueIntegerFlag := CheckValue(Value);
RETURN Result;
END ReadValue;
Read the characters one-at-a-time without special ENTER handling using Get_Immediate instead of Get - ARM A.10.7(9).
You can do checks on the class of the character you’ve just read using Ada.Characters.Handling - ARM A.3.2 - something like
function Is_Valid_Name_Input (Ch : Character) return Boolean is
begin
return Ada.Characters.Handling.Is_Graphic (Ch)
and then not Ada.Characters.Handling.Is_Space (Ch);
end Is_Valid_Name_Input;
(probably not quite what you want, since it makes &*^$$^ a valid name!)
Ada.Characters.Handling.Is_Line_Terminator detects ENTER (on Unix; probably on Windows too).
You can check whether a string corresponds to an integer by trying the conversion and catching the exception when it fails:
function Check_Integer_Value (Str : Unbounded_String) return Boolean is
Dummy : Integer;
begin
Dummy := Integer'Value (To_String (Str));
return True;
exception
when Constraint_Error =>
return False;
end Check_Integer_Value;
With regard to ReadValue:
Don’t initialize Result - it starts off as the empty string (and you really don’t want to start with the string ”NULL”).
It skips the first character input.
What’s that Skip_Line for?
Try something like
function Read_Value return Unbounded_String is
Value_Char : Character;
Result : Unbounded_String;
begin
loop
Get_Immediate (Value_Char);
exit when Value_Char = ';';
if Is_Valid_Name_Input (Value_Char) then
Result := Result & Value_Char;
end if;
end loop;
return Result;
end Read_Value;

Ada actual for "S" must be a variable

So here is a piece of my body file. I am getting the error "words.adb:75:42: actual for "S" must be a variable".
procedure Remove_Character(S : in out Ustring; C : in Character; Successful : out Boolean) is
begin
for I in 1..length(S) loop
if Element(S, I) = C then
Delete(S, I, I);
Successful := true;
return;
end if;
end loop;
Successful := false;
end Remove_Character;
function Is_Subset(Subset : Ustring; S : Ustring) return Boolean is
Could_Remove : Boolean;
begin
for I in 1..length(Subset) loop
Remove_Character(S , Element(Subset, I), Could_Remove);
if Could_Remove = false then
return false;
end if;
end loop;
return True;
end Is_Subset;
I understand where my error is coming from. Remove_Character uses S : in out Ustring while function Is_Subset uses S : in Ustring.
My question is how do I change the variable from Remove_Character into only an in Ustring?
Sorry if this is a tad jumbled, I'm fairly new to both programming and the site.
You can't, at least not directly.
I don't know what a UString is, but I presume the Delete procedure modifies it. If you changed the declaration of S in Remove_Character to S: in Ustring, you'd presumably get an error on the call to Delete.
The simplest approach I can think of would be to make a copy of S in Is_Subset:
Copy_Of_S: UString := S;
and then pass the (modifiable) copy to Remove_Character.
By "simplest", I mean it makes the smallest change to your existing code. But you should probably consider reorganizing it. Determining whether one UString is a subset of another by modifying one of the strings doesn't seem like the best approach; I'm sure there's a more efficient way to do it.
A minor and irrelevant point: this:
if Could_Remove = false then
is better written as:
if not Could_Remove then

How to change the range of the range type?

Lets say I have
function x return boolean is
type range0 is range 1..1;
begin
canse x is
when 4=> range0:=firstArray'range;
when 5=> range0:=secondArray'range;
when 6=> range0:=1..100;
end case;
end x;
Basically I would like to change the range of range0 on the go? How may I accomplish this without using the declare block?
Basically I would like to change the range of range0 on the go? How may I accomplish this without using the declare block?
Hm...
In Ada 2012 you can use if- and case-expressions, so you could have something like this:
Type Array_Type is Array(Positive Range <>) of Integer;
Array_1 : Array_Type(1..128);
Array_2 : Array_Type(33..63);
-- your variant-selector
Use_1 : constant Boolean:= True;
-- Your variant-range here:
Subtype Variant_Range is Positive Range
(if Use_1 then Array_1'First else Array_2'First)
..(if Use_1 then Array_1'Last else Array_2'Last);
Array_3 : Array_Type( Variant_Range );
All that said, this probably isn't the best way to go about it and using a declare-block is very likely going to be more easily maintained.
You could technically satisfy the stated requirements by converting the obvious way (declare block) into a local procedure :
function x return boolean is
procedure use_dynamic_range(first,last : in integer) is
type range0 is new integer range first .. last;
begin
null;
end use_dynamic_range;
begin
case z is
when 4=> use_dynamic_range(firstArray'first, firstArray'last);
when 5=> use_dynamic_range(secondArray'first, secondArray'last);
when 6=> use_dynamic_range(1,100);
end case;
end x;
Because it's a local procedure it executes in the same scope as the equivalent declare block, therefore it can access everything visible within X, so you don't need to pass it a huge parameter list.
What about something like :
function x return Boolean is
type Range_Info_Type is
record
First : Integer;
Last : Integer;
end record;
function Get_Range_Info_Type return Range_Info_Type is
begin
case z is
when 4=> return Range_Info_Type'(First => firstArray'First,
Last => FirstArray'Last);
when 5=> return Range_Info_Type'(First => secondArray'First,
Last => secondArray'Last);
when 6=> return Range_Info_Type'(First => 1,
Last => 100);
when others => return Range_Info_Type'(First => 1,
Last => 1);
end case;
end;
MyTypeInfo : constant Range_Info_Type := Get_Range_Info_Type;
-- Now declare the actual type I want to use.
type range0 is new Integer range MyTypeInfo.First .. MyTypeInfo.Last;
begin
return true;
end x;
A declare block might be easier to understand by this should do the trick.
Note that you cannot write type range0 is range <expr>..<expr> in your case since expr should be a static expression (see RM 3.5.4)
Another non declare-block answer from Ada 2012:
Minimum : Integer := Integer'First; --' SO highlight correction
Maximum : Integer := Integer'Last; --' *same*
Function In_Range(X : Integer) return Boolean is
( X in range Minimum..Maximum );
Subtype Variant_Range is Integer Range Integer
with Dynamic_Predicate => In_Range(Variant_Range);
WARNING: Though this should work, I have not tested it.

Resources