Skipping IF statement? - ada

I am so close to finishing one of the exercise in my book but I cannot fathom why this 'if' statement is being skipped. Everything else works, it compiles fine with no warnings. If I enter something over 1.00, it totally skips the part of the code that should handle that and returns the value 0.00. If I enter 24.00 or over (which are both handled within the same code block), it works fine and produces the expected output.
Here is my code, I have purposely marked the place where the code skips. I have been looking over it for over an hour and just do not know why it's not executing that part of the code, even though the value I pass to it is correct according to the condition. Really appreciate any help or advice you can give me.
Wishes Ada x
with Ada.Text_IO; use Ada.Text_IO;
with Ada.Integer_Text_IO; use Ada.Integer_Text_IO;
with Ada.Float_Text_IO; use Ada.Float_Text_IO;
-- procedure main - begins program execution
procedure main is
carTime : Float := 0.00;
EXIT_FLAG : constant Float := 999.00;
payment : Float;
-- function calculatePayment - calculates and returns the total charge for a
-- a car staying in the car park based on stay time
function calculateCharges(carTime : in Float; paye : out Float) return Float is
BASE_CHARGE : constant Float := 2.00;
DAY_CHARGE : constant Float := 10.00;
begin
-- standard charge if one hour or less
if carTime <= 1.00 then
paye := BASE_CHARGE;
end if;
-- full day charge
if carTime = 24.00 then
paye := DAY_CHARGE;
end if;
-- calculate extra payment if time exceeds given parameters
if carTime > 1.00 and carTime > 24.00 then
declare
EXTRA_CHARGE : constant FLoat := 0.50;
timeOverHour : Float := 0.00;
overPay : Float := 0.00;
begin
if carTime > 1.00 and carTime < 24.00 then <===== THIS NEVER EXECUTES
timeOverHour := carTime - 1.00;
overPay := timeOverHour * EXTRA_CHARGE;
paye := BASE_CHARGE + overPay;
end if;
if carTime > 24.00 then
timeOverHour := carTime - 24.00;
overPay := timeOverHour * EXTRA_CHARGE;
paye := DAY_CHARGE + overPay;
end if;
end;
end if;
return paye;
end calculateCharges;
begin
New_Line;
Put("Enter a stay time (enter 999.00 to quit): ");
carTime := float'value(Get_Line);
while carTime /= EXIT_FLAG loop
Put("Total charge is: ");
Put(calculateCharges(carTime, payment), Fore => 2, Aft => 2, Exp => 0);
New_Line;
Put("Enter a stay time (enter 999.00 to quit): ");
carTime := float'value(Get_Line);
end loop;
New_Line;
end main;

Related

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;

Struggling to collect and return DBMS_SQL.COLUMN_VALUE using User Defined Type

This is my first question on StackOverflow and I'm self taught so please be gentle.
My goal here is to be able to bulk collect headers/values from a dynamic query/cursor in a generated package via
SELECT *
BULK COLLECT INTO l_cur_val
FROM TABLE (CUSTOM.GET_REF_VAL(l_cursor));
I have this working successfully for column headers in a similar block of code to GET_REF_VAL (simply RETURN l_col_head before entering the /* COLUMN VALUES */ section).
The errors are coming when I'm trying to assign the return value of DBMS_SQL.COLUMN_VALUE into my t_col_val UDT. (Type definitions are in comments)
TYPES
CREATE OR REPLACE TYPE CUSTOM.r_col_val IS OBJECT (l_col_val VARCHAR2(250 byte));
CREATE OR REPLACE TYPE CUSTOM.t_col_val IS TABLE OF CUSTOM.r_col_val;
ERRORS
--l_val(n) := r_col_val(l_dum_val); --returns: ORA-06533: Subscript beyond count
--l_val(n) := l_dum_val; --returns: PLS-00382: expression is of wrong type
Table return function GET_REF_VAL
CREATE OR REPLACE FUNCTION
CUSTOM.GET_REF_VAL
(
p_cursor IN SYS_REFCURSOR
)
RETURN t_col_val
IS
l_val t_col_val := t_col_val();
l_col t_col_head := t_col_head();
n INTEGER := 0;
l_cursor SYS_REFCURSOR := p_cursor;
l_cursor_id INTEGER;
l_dummy INTEGER;
l_col_cnt INTEGER;
l_tab_rec DBMS_SQL.DESC_TAB2;
l_dum_val VARCHAR2(250);
BEGIN
l_cursor_id := DBMS_SQL.TO_CURSOR_NUMBER(l_cursor);
DBMS_SQL.DESCRIBE_COLUMNS2(l_cursor_id, l_col_cnt, l_tab_rec);
/* COLUMN HEADERS */
FOR r IN 1..l_col_cnt
LOOP
l_col.extend;
n := n + 1;
l_col(n) := r_col_head(l_tab_rec(r).col_name);
DBMS_SQL.DEFINE_COLUMN(l_cursor_id, r, l_dum_val, 4000);
END LOOP;
/* COLUMN VALUES */
LOOP
IF DBMS_SQL.FETCH_ROWS(l_cursor_id)> 0 THEN
FOR i IN 1 .. l_col_cnt
LOOP
l_val.extend;
n := n + 1;
DBMS_SQL.COLUMN_VALUE(l_cursor_id, i, l_dum_val);
DBMS_OUTPUT.PUT_LINE(l_dum_val); -- This return l_dum_val with no issues
--l_val(n) := r_col_val(l_dum_val); -- ORA-06533: Subscript beyond count
--l_val(n) := l_dum_val; --PLS-00382: expression is of wrong type
END LOOP;
ELSE
EXIT;
END IF;
END LOOP;
DBMS_SQL.CLOSE_CURSOR(l_cursor_id);
RETURN l_val;
END;
/
Execution block
DECLARE
l_sql_stmt VARCHAR(10000) :=
q'!
SELECT
SYS_CONTEXT('USERENV','OS_USER') AS OS_USER ,
SYS_CONTEXT('USERENV','SESSION_USER') AS SESSION_USER,
SYS_CONTEXT('USERENV','ISDBA') AS ISDBA,
SYS_CONTEXT('USERENV','SID') AS SID,
SYS_CONTEXT('USERENV','CURRENT_SQL') AS CURRENT_SQL,
SYS_CONTEXT('USERENV','DB_NAME') AS DB_NAME,
SYS_CONTEXT('USERENV','HOST') AS HOST,
SYS_CONTEXT('USERENV','IP_ADDRESS') AS IP_ADDRESS,
SYS_CONTEXT('USERENV','SERVICE_NAME') AS SERVICE_NAME
FROM
DUAL
!';
l_cursor SYS_REFCURSOR;
l_cursor_id INTEGER;
l_dummy VARCHAR2(50);
TYPE t_cur_head IS TABLE OF VARCHAR2(250) INDEX BY BINARY_INTEGER;
l_cur_head t_cur_head;
TYPE t_cur_val IS TABLE OF VARCHAR2(250) INDEX BY BINARY_INTEGER;
l_cur_val t_cur_val;
BEGIN
l_cursor := CUSTOM.GET_REF_CUR(l_sql_stmt);
IF l_cursor%ISOPEN
THEN
/* Header fetch works fine */
/*
SELECT *
BULK COLLECT INTO l_cur_head
FROM TABLE (CUSTOM.GET_REF_HEAD(l_cursor));
FOR i IN 1 .. l_cur_head.COUNT
LOOP
DBMS_OUTPUT.PUT_LINE(l_cur_head(i));
END LOOP;
*/
/* Values fetch fails */
SELECT *
BULK COLLECT INTO l_cur_val
FROM TABLE (CUSTOM.GET_REF_VAL(l_cursor));
FOR i IN 1 .. l_cur_val.COUNT
LOOP
DBMS_OUTPUT.PUT_LINE(l_cur_val(i));
END LOOP;
END IF;
END;
So I guess in summary what I want to know is
a) How to handle the return value of dbms_sql.column_value using a user defined type
b) How insert a VARCHAR2 value (l_dum_val) into a UDT object with VARCHAR2 records (l_col_val)
c) Any other obvious errors/bad practices in the code?
Thank you for your time an patience.
The first of your commented-out lines:
--l_val(n) := r_col_val(l_dum_val); -- ORA-06533: Subscript beyond count
gets that error because you are not resetting n to zero before the second loop. You don't really need that counter variable at all though, you can do use l_val.count instead (in both loops).
The second of your commented-out lines:
--l_val(n) := l_dum_val; --PLS-00382: expression is of wrong type
gets that error because the l_val(n) is pointing to an object, which has a string attribute; it isn't pointing directly to a string. So you can assign a new object via its constructor; which is what the first version was trying to do, but it should be:
l_val(l_val.count) := r_col_val(l_dum_val);
Once that object exists you can assign the attribute directly with:
l_val(some_index).l_col_val := r_col_val(l_dum_val);
but you have to create an object before you can access its attributes, and as you only have a default constructor, that probably isn't going to be much use to you in this case.
So with those changes (and some indentation, and refactoring slightly to get rid of the else) this now works:
CREATE OR REPLACE FUNCTION
GET_REF_VAL
(
p_cursor IN SYS_REFCURSOR
)
RETURN t_col_val
IS
l_val t_col_val := t_col_val();
l_col t_col_head := t_col_head();
l_cursor SYS_REFCURSOR := p_cursor;
l_cursor_id INTEGER;
l_dummy INTEGER;
l_col_cnt INTEGER;
l_tab_rec DBMS_SQL.DESC_TAB2;
l_dum_val VARCHAR2(250);
BEGIN
l_cursor_id := DBMS_SQL.TO_CURSOR_NUMBER(l_cursor);
DBMS_SQL.DESCRIBE_COLUMNS2(l_cursor_id, l_col_cnt, l_tab_rec);
/* COLUMN HEADERS */
FOR r IN 1..l_col_cnt
LOOP
l_col.extend;
l_col(l_col.count) := r_col_head(l_tab_rec(r).col_name);
DBMS_SQL.DEFINE_COLUMN(l_cursor_id, r, l_dum_val, 4000);
END LOOP;
/* COLUMN VALUES */
LOOP
IF DBMS_SQL.FETCH_ROWS(l_cursor_id) = 0 THEN
EXIT;
END IF;
FOR i IN 1 .. l_col_cnt
LOOP
l_val.extend;
DBMS_SQL.COLUMN_VALUE(l_cursor_id, i, l_dum_val);
DBMS_OUTPUT.PUT_LINE(l_dum_val);
l_val(l_val.count) := r_col_val(l_dum_val);
END LOOP;
END LOOP;
DBMS_SQL.CLOSE_CURSOR(l_cursor_id);
RETURN l_val;
END;
/
db<>fiddle
Your code suggests you have a separate function to get the headers, so you're duplicating code. You could simplify into one procedure with two out variables instead:
CREATE OR REPLACE PROCEDURE
GET_REF_HEAD_AND_VAL
(
p_cursor IN OUT SYS_REFCURSOR,
p_col OUT SYS.odcivarchar2list,
p_val OUT SYS.odcivarchar2list
)
IS
l_cursor_id INTEGER;
l_col_cnt INTEGER;
l_tab_rec DBMS_SQL.DESC_TAB3;
l_value VARCHAR2(250 byte);
BEGIN
l_cursor_id := DBMS_SQL.TO_CURSOR_NUMBER(p_cursor);
DBMS_SQL.DESCRIBE_COLUMNS3(l_cursor_id, l_col_cnt, l_tab_rec);
/* COLUMN HEADERS */
p_col := SYS.odcivarchar2list();
FOR r IN 1..l_col_cnt
LOOP
p_col.extend;
p_col(p_col.count) := l_tab_rec(r).col_name;
DBMS_SQL.DEFINE_COLUMN(l_cursor_id, r, l_value, 250);
END LOOP;
/* COLUMN VALUES */
p_val := SYS.odcivarchar2list();
LOOP
IF DBMS_SQL.FETCH_ROWS(l_cursor_id) = 0 THEN
EXIT;
END IF;
FOR i IN 1 .. l_col_cnt
LOOP
p_val.extend;
DBMS_SQL.COLUMN_VALUE(l_cursor_id, i, l_value);
--DBMS_OUTPUT.PUT_LINE(l_dum_val);
p_val(p_val.count) := l_value;
END LOOP;
END LOOP;
DBMS_SQL.CLOSE_CURSOR(l_cursor_id);
END;
/
This is using a built-in collection type rather than creating your own object/table types (though you could still create your own collection type; it doesn't need to used objects though).
db<>fiddle

Ada protected Put procedure doesn't write in the same line

My problem is that I created a protected Safe_Printer; but when I use it's procedure, it messes up the Put function. Every time it prints to a new line. How could I prevent this? Basically, I would like to print my matrix with it in a readable format.
Here is my code:
with Ada.Text_IO;
use Ada.Text_IO;
procedure main is
type Matrix is array(integer range <>, integer range <>) of integer;
protected Safe_Printer is
procedure Put(S: String);
procedure Put(M: Matrix);
end Safe_Printer;
protected body Safe_Printer is
procedure Put(S: String) is
begin
for I in S'range loop
Put(S(I));
end loop;
New_Line;
end Put;
procedure Put(M:Matrix) is
begin
for I in m'range(1) loop
for j in m'range(2) loop
put(integer'image(m(i,j)) & " ");
end loop;
new_line(1);
end loop;
end Put;
end Safe_Printer;
m:Matrix := ((1,2,3),
(4,5,6));
begin
Safe_Printer.Put(m);
for I in m'range(1) loop
for j in m'range(2) loop
put(integer'image(m(i,j)) & " ");
end loop;
new_line(1);
end loop;
end main;
And my output is :
1
2
3
4
5
6
1 2 3
4 5 6
Because the 'Image attribute returns a String, your implementation of Put(M : Matrix) invokes Safe_Printer.Put(S : String) in its inner loop; each matrix entry then gets a New_Line. One solution is to call the language defined Put explicitly in Safe_Printer.Put(M : Matrix):
procedure Put(M : Matrix) is
begin
for I in M'range(1) loop
for j in M'range(2) loop
Ada.Text_IO.Put(integer'image(M(i,j)) & " ");
end loop;
New_Line;
end loop;
end Put;
As tested:
with Ada.Text_IO;
use Ada.Text_IO;
procedure main is
type Matrix is array(integer range <>, integer range <>) of integer;
protected Safe_Printer is
procedure Put(S : String);
procedure Put(M: Matrix);
end Safe_Printer;
protected body Safe_Printer is
procedure Put(S : String) is
begin
for I in S'range loop
Put(S(I));
end loop;
New_Line;
end Put;
procedure Put(M : Matrix) is
begin
for I in M'range(1) loop
for j in M'range(2) loop
Ada.Text_IO.Put(integer'image(M(i,j)) & " ");
end loop;
New_Line;
end loop;
end Put;
end Safe_Printer;
M : Matrix := ((1,2,3),
(4,5,6));
begin
Safe_Printer.Put(M);
end main;
Console:
$ ./main
1 2 3
4 5 6
Alternatively, as #SimonWright comments, you could "remove the New_Line in Safe_Printer.Put(String)." The approach you choose will depend on the desired effect offered in the Safe_Printer specification. Also consider limiting the scope of the use clause.
You should not call a 'potentially blocking operation' from within a protected object. The call to Text_IO.Put is a potentially blocking operation. See the Ada Reference Manual section 9.5.1. I am surprised your compiler didn't complain, actually.
One thing you can do in this situation is to use a task instead of a protected object.
For example:
with Ada.Text_IO;
procedure Main is
type Matrix is array (Integer range <>, Integer range <>) of Integer;
task Safe_Printer is
entry Put (S: in String);
entry Put (M: in Matrix);
end Safe_Printer;
task body Safe_Printer is
S: access String;
M: access Matrix;
begin
loop
select
accept Put (S: in String) do
Safe_Printer.S := new String (S'Range);
Safe_Printer.S.all := S;
end;
Ada.Text_IO.Put_Line (S.all);
or
accept Put (M: in Matrix) do
Safe_Printer.M := new Matrix (M'Range (1), M'Range (2));
Safe_Printer.M.all := M;
end;
for I in M'Range (1) loop
for J in M'Range (2) loop
Ada.Text_IO.Put (Integer'Image (M (I, J)) & " ");
end loop;
Ada.Text_IO.New_Line;
end loop;
or
terminate;
end select;
end loop;
end Safe_Printer;
M: Matrix := ((1,2,3),
(4,5,6));
begin
Safe_Printer.Put (M);
end Main;
An added advantage of this approach is that the calls to Text_IO.Put[_Line] are actually executed in parallel with the main task. A disadvantage is that (to be safe) the string or matrix to be printed is copied.
A better version of this code might use Unchecked_Deallocation or Ada.Containers.Indefinite_Holders to clean up after itself nicely.
Bonus question: What do you think the array bounds of the matrix M in the Main procedure are?

Getting variables from a line from a file

I was wondering if any of you could answer a quick question for me. I am currently working with records right now and in my program I need it to understand what the line of a file that i'm importing contains. My problem lies in the fact that I don't know how to "split" the line into actual variables. For example the line is
22134.09 Kia Bernice
How do I make the program know that the first part, 22134.09 is the variable price, Kia is the variable company and Bernice is the variable model, and then sort them all into a record?
Such as
type PriceCompModel is record
price : Float range 1.0..99999.99;
company : String (1..CompNameLength);
Model : String (1..ModelNameLength);
Thanks.
edited code
with Ada.Text_IO; use Ada.Text_IO;
with Ada.Integer_Text_IO; use Ada.Integer_Text_IO;
with Ada.Float_Text_IO; use Ada.Float_Text_IO;
procedure Testexercise is
type Inventory is Record
CarPrice : Float := 0.0;
CarType : String (1..40);
-- CarType will include both the model and company
end record;
InventoryItem : Inventory;
ImportedFile : File_Type;
FileName : String := "Cars.txt";
WordsFromFile : String(1..40);
LengthofWords : Integer ;
PriceofCar : Float := 0.0;
LoopCount : Integer := 1;
type Cars is array (1..12) of Inventory;
begin
Open(File => ImportedFile, Mode => In_File, Name => FileName);
--for I in 1..12 loop
while LoopCount /= 12 loop
Get(File => ImportedFile, Item => InventoryItem.CarPrice);
Get_Line(File => ImportedFile, Item => WordsFromFile, Last=> LengthofWords);
Put (Integer(InventoryItem.CarPrice),1);
Put (WordsFromFile(1..LengthofWords));
New_Line;
LoopCount := LoopCount + 1;
InventoryItem.CarType := WordsFromFile;
end loop;
close(ImportedFile);
end Testexercise;
So i tried doing this within the loop
for I in 1..12 loop
Cars := Inventory;
end loop;
This ended up working for me after i set
Car : Cars;
for I in 1..12 loop
Car(I) := Inventory;
end loop;
You have many factors to consider when defining the record to contain your information.
It will be useful to create a named subtype of float, or a named floating point type so that the I/O routines can check the input values for the price component.
Fields of type String must be constrained to a predefined size. This means that all "company" strings must be the same size, and all "model" strings must be the same size, although model strings may be a different length than company strings. If the names of companies and/or models may vary then you should consider using either bounded strings (Ada Language Reference Manual section A.4.4) or unbounded strings (Ada Language Reference Manual section A.4.5).
If the strings of the input file are fixed in size you can use Ada.Text_IO.Text_Streams (Ada Language Reference Manual section A.12.2) to read each field of the record. If the strings can be different sizes then you will need to read and parse each field manually using Ada.Text_IO.
-- Read record data from a file
with Ada.Text_Io; use Ada.Text_IO;
with Ada.Strings.Unbounded; use Ada.Strings.Unbounded;
with Ada.Strings.Fixed; use Ada.Strings.Fixed;
procedure read_record is
type Prices is delta 0.01 digits 7 range 0.0..99999.99;
type Auto_Inventory is record
Price : Prices := 0.0;
Company : Unbounded_String := Null_Unbounded_String;
Model : Unbounded_String := Null_Unbounded_String;
end record;
package AI_IO is new Ada.Text_IO.Decimal_IO(Prices);
use AI_IO;
Inventory_Item : Auto_Inventory;
The_File : File_Type;
File_Name : String := "inventory.txt";
Inpt_Str : String(1..1024);
Length : Natural;
Start, Finis : Positive;
begin
Open(File => The_File,
Mode => In_File,
Name => File_Name);
Get(File => The_File,
Item => Inventory_Item.Price);
Get_Line(File => The_File,
Item => Inpt_Str,
Last => Length);
Close(The_File);
Start := Index_Non_Blank(Source => Inpt_Str(1..Length));
Finis := Start;
while Finis < Length and then Inpt_Str(Finis) /= ' ' loop
Finis := Finis + 1;
end loop;
Inventory_Item.Company := To_Unbounded_String(Inpt_Str(Start..Finis));
Start := Index_Non_Blank(Inpt_Str(Finis + 1..Length));
Inventory_Item.Model := To_Unbounded_String(Inpt_Str(Start..Length));
Put_Line("Price: " & Prices'Image(Inventory_Item.Price));
Put_Line("Company: " & To_String(Inventory_Item.Company));
Put_Line("Model: " & To_String(Inventory_Item.Model));
end read_record;
If you want to read a file containing many records you need to collect the information in some kind of container. The following example uses a Vector from the generic package Ada.Containers.Vectors.
-- Auto Inventory Package specification
with Ada.Text_IO; use Ada.Text_IO;
with Ada.Containers.Vectors;
with Ada.Strings.Unbounded; use Ada.Strings.Unbounded;
package Auto_Inventory is
type Prices is delta 0.01 digits 7 range 0.0..99999.99;
type Automobile is tagged private;
procedure Print(Item : Automobile);
function Set return Automobile;
function Get_Price(Item : Automobile) return Prices;
function Get_Company(Item : Automobile) return String;
function Get_Model(Item : Automobile) return String;
type Inventory is tagged private;
procedure Read(Item : out Inventory; File : File_Type) with
Pre => Mode(File) = In_File;
procedure Write(Item : in Inventory; File : File_type) with
Pre => Mode(File) = Out_File;
procedure Print(Item : Inventory);
private
type Automobile is tagged record
Price : Prices := 0.0;
Company : Unbounded_String := Null_Unbounded_String;
Model : Unbounded_String := Null_Unbounded_String;
end record;
package Auto_Vect is new
Ada.Containers.Vectors(Index_Type => Positive,
Element_Type => Automobile);
use Auto_Vect;
type Inventory is tagged record
List : Vector;
end record;
end Auto_Inventory;
The body for this package is:
with Ada.Strings.Fixed; use Ada.Strings.Fixed;
package body Auto_Inventory is
package Prices_IO is new Ada.Text_IO.Decimal_IO(Prices);
use Prices_IO;
-----------
-- Print --
-----------
procedure Print (Item : Automobile) is
use Prices_Io;
begin
Put_Line("Price : " & Prices'Image(Item.Price));
Put_Line("Company: " & To_string(Item.Company));
Put_Line("Model : " & To_String(Item.Model));
New_Line;
end Print;
---------
-- Set --
---------
function Set return Automobile is
Temp : Automobile;
Inpt_Str : String(1..1024);
Length : Natural;
begin
Put("Enter the automobile price: ");
Get(Item => Temp.Price);
Put("Enter the automobile company: ");
Get_Line(Item => Inpt_Str, Last => Length);
Temp.Company := To_Unbounded_String(Inpt_Str(1..Length));
Put("Enter the automobile model: ");
Get_Line(Item => Inpt_Str, Last => Length);
Temp.Model := To_Unbounded_String(Inpt_Str(1..Length));
return Temp;
end Set;
---------------
-- Get_Price --
---------------
function Get_Price (Item : Automobile) return Prices is
begin
return Item.Price;
end Get_Price;
-----------------
-- Get_Company --
-----------------
function Get_Company (Item : Automobile) return String is
begin
return To_String(Item.Company);
end Get_Company;
---------------
-- Get_Model --
---------------
function Get_Model (Item : Automobile) return String is
begin
return To_String(Item.Model);
end Get_Model;
----------
-- Read --
----------
procedure Read (Item : out Inventory;
File : File_Type) is
Temp : Inventory;
Auto : Automobile;
Inpt_Str : String(1..1024);
Length : Natural;
Start, Finis : Positive;
begin
while not End_Of_File(File) loop
Get(File => File, Item => Auto.Price);
Get_Line(File => File, Item => Inpt_str, Last => Length);
Start := Index_Non_Blank(Inpt_Str(1..Length));
Finis := Start;
while Finis < Length and then Inpt_Str(Finis) /= ' ' loop
Finis := Finis + 1;
end loop;
Auto.Company := To_Unbounded_String(Inpt_Str(Start..Finis - 1));
Start := Index_Non_Blank(Inpt_Str(Finis..Length));
Auto.Model := To_Unbounded_String(Inpt_Str(Start..Length));
Temp.List.Append(Auto);
end loop;
Item := Temp;
end Read;
-----------
-- Write --
-----------
procedure Write (Item : in Inventory;
File : File_type) is
begin
for Element of Item.List loop
Put(File => File, Item => Prices'Image(Element.Price) &
" " & To_String(Element.Company) & " " &
To_String(Element.Model));
New_Line;
end loop;
end Write;
-----------
-- Print --
-----------
procedure Print (Item : Inventory) is
begin
for Element of Item.List loop
Element.Print;
end loop;
end Print;
end Auto_Inventory;
An example of a main procedure to exercise this package:
------------------------------------------------------------------
-- Read a file of many records --
------------------------------------------------------------------
with Auto_Inventory; use Auto_Inventory;
with Ada.Text_IO; use Ada.Text_IO;
procedure read_file is
The_Inventory : Inventory;
The_File : File_Type;
File_Name : String := "inventory.txt";
begin
Open(File => The_File,
Mode => In_File,
Name => File_Name);
The_Inventory.Read(The_File);
Close(The_File);
The_Inventory.Print;
end read_file;
An example input file for this program is:
22134.09 Kia Bernice
12201.15 Nissan Versa
22349.99 Chevrolet Cruse
It is not clear for me what language you are using .However the concept is to deal with each line from the file alone then process it with a function that do the token-zing or splitting depending on the language you use and save each token in a variable depending on how the function you are using will save the tokens
for example:
In java there is a class
StringTokenizer(String str, String delim)
StringTokenizer st = new StringTokenizer("this is a test", "$a; ");
while (st.hasMoreTokens()) {
System.out.println(st.nextToken());
}
delim in your case is space so use the format
StringTokenizer st = new StringTokenizer("this is a test");
or
String line = reader.readLine();
String[] tokens = line.split("\\s");
note that you need to save the line you read in a string so you can use those functions in java then access each token from the array
String price = tokens[1] and so on
for other languages please find the following resources:
In c https://www.tutorialspoint.com/c_standard_library/c_function_strtok.htm
In pyhton https://www.tutorialspoint.com/python/string_split.htm

how to handle exception in if condition pl/sql

ineed your help when i wrote if else condition in plsql to check whether it is even or odd number using if conditopn . if i enter character then it will raise an error.how to handle it using exceptions to understand client. help me
declare
num number:=&number;
begin
if num=1 then
dbms_output.put_line('composite');
else if mod(num,2)=0 then
dbms_output.put_line('even');
else if mod(num,2)=1 then
dbms_output.put_line('odd');
else
dbms_output.put_line('enter proper data');
end if;
end if;
end if;
end;
/
Please see the following working example:
declare
-- user input is always a string so treat it as a string
v_input varchar2(32767) := '&number';
num number;
begin
-- convert input to a number. to_number() throws if input string can't be
-- converted to a number. see to_number() documentation for details.
-- the conversion has to happen inside begin-end block because the exception
-- handler of the block won't catch exceptions raised in declare block.
num:=to_number(v_input);
-- do your math
if num=1 then
dbms_output.put_line('composite');
elsif mod(num,2)=0 then
dbms_output.put_line('even');
elsif mod(num,2)=1 then
dbms_output.put_line('odd');
else
dbms_output.put_line('enter proper data');
end if;
exception
-- the exception raised by to_number()
when value_error then
dbms_output.put_line('not a valid number: ' || v_input);
-- all other exceptions are still raised to the caller.
end;
/
Example results:
SQL> #so
Enter value for number: 1
old 2: v_input varchar2(32767) := '&number';
new 2: v_input varchar2(32767) := '1';
composite
PL/SQL procedure successfully completed.
SQL> #so
Enter value for number: 2
old 2: v_input varchar2(32767) := '&number';
new 2: v_input varchar2(32767) := '2';
even
PL/SQL procedure successfully completed.
SQL> #so
Enter value for number: 3
old 2: v_input varchar2(32767) := '&number';
new 2: v_input varchar2(32767) := '3';
odd
PL/SQL procedure successfully completed.
SQL> #so
Enter value for number: foo
old 2: v_input varchar2(32767) := '&number';
new 2: v_input varchar2(32767) := 'foo';
not a valid number: foo
PL/SQL procedure successfully completed.
SQL>

Resources