Ada "Subtype mark required" - visibility

I was given a project at uni where I need to write an ADA package of a graph. The points of the graph are stored in an array, the edges are stored in a matrix.(In the matrix if there is an edge between two points the number at that index is the weight/length of the edge)
The two procedures needed are NewPoint and NewEdge.
The three functions needed are IsItaPoint, IsitAnEdge, and print (the matrix and array).
I started learning ADA a week ago and I'm sure this is a 2 minute code for some of you.
I wrote this:
Graph.ads
package Graph is
function IsItAPoint (G: Graph;I: Integer) return Boolean;
function IsItAnEdge (G: Graph;I: Integer; J: Integer ) return Boolean;
procedure NewEdge (G: Graph;I: Integer; J: Integer; S: Integer);
procedure NewPoint(G: Graph;I: Integer);
type PointArray is array(Integer range <>) Of Integer;
type EdgeMatrix is array(Integer range <>,
Integer range <>) of INTEGER;
PointCount: Integer:=0;
end Graph;
Graph.adb
package body Graph is
procedure NewPoint(G: Graph;I: Integer) is
begin
G.PointCount:=G.PointCount+1;
G.PointArray(G.PointCount):=I;
end;
procedure NewEdge(G: Graph;I: Integer; J: Integer; S: Integer) is
begin
G.EdgeMatrix(I,J):=S;
end;
function IsItAPoint (G: Graph;I: Integer) return Boolean is
begin
for J in 1..100 loop
if (G.PointArray(J)=I) then return True; end if;
end loop;
return False;
end;
function IsItAnEdge (G: Graph;I: Integer; J: Integer ) return Boolean is
begin
return (G.EdgeMatrix(I,J)=Null);
end;
end Graph;
I get "Graf is not visable" and "subtype mark required in this context" error in the adb file.
Can you help me fix this whole thing?

OK, looking at the code so far, I think you may be mistaking a Package as a replacement for a C++ Class, whereas it's really more like a C++ Namespace.
C++ didn't have namespaces when I first used it but they are such a good organising principle that it tacked them on later. In contrast, packages were an original part of Ada.
Now a C++ Class (or Struct, or Union) would map onto an Ada Record. If it stands alone it can be a simple record, but if it's intended to be inheritable, it'll be a Tagged Record. Tagged Record, allowing inheritance, wasn't part of Ada-83, it was added 20 years ago in Ada-95.
And typical practice would be to wrap the Record and all its externally visible operations in a Package.
So I think you're looking for something like:
package Graph_Pkg is
type Graph is tagged private; -- hide everything about the actual record!
function IsItAPoint (G: Graph;I: Integer) return Boolean;
function IsItAnEdge (G: Graph;I: Integer; J: Integer ) return Boolean;
procedure NewEdge (G: in out Graph;I: Integer; J: Integer; S: Integer);
procedure NewPoint(G: in out Graph;I: Integer);
-- PointCount: Integer:=0; -- moved to package body
function PointCount return Integer;
private
-- Everything below here is hidden from package users
type PointArray is array(Integer range <>) Of Integer;
type EdgeMatrix is array(Integer range <>,
Integer range <>) of INTEGER;
type Graph is tagged record
-- here the member variables are declared
Points : PointArray;
end record;
end Graph_Pkg;
Now all the implementation details belong in the body.
package body Graph_Pkg is
-- The equivalent of C++ "static members" can be declared here
PointCount: Integer:=0;
-- and add the subprogram implementations here
end Graph_Pkg;
If you want the number of points in a graph to be variable after the graph has been constructed, I can see problems ahead in the implementation. You might want to look at Ada-2005 Container classes for that.

Related

Function to return a specific thing from a procedure that are in the same package [closed]

Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 2 years ago.
Improve this question
I have a procedure that calculates the minimum and the maximum number of 2 numbers and I need to write a function that will return the minimum number from the procedure and another function that will return the maximum number from the same procedure. The procedure and the functions are in the same package. I need to call the function, and the function will have to find the requested value inside the procedure. So far, I created the package and the package body, wrote the procedure but I really don't know how to write the functions in order to retrieve the respective values from the procedure. Can anyone help me with this?
create or replace package min_max is
p_min integer;
p_max integer;
function f_min(n1 in integer, n2 in integer) return integer;
function f_max(n1 in integer,n2 in integer) return integer;
end;
create or replace package body min_max is
procedure do_all...]
I don't know how to get the parameters from the functions to use them in the procedure,
should I just put the functions signature before defining the procedure?
I am thinking to have the procedure modify the p_min and p_max variables and have the functions just return the modified variables, can I do this?
Sure; though it isn't clear when the procedure should be called. Assuming you need it to be called from outside the package and the functions just return the current state, you would need the specification to include the procedure too:
create or replace package min_max is
p_min integer;
p_max integer;
procedure do_all;
function f_min(n1 in integer, n2 in integer) return integer;
function f_max(n1 in integer,n2 in integer) return integer;
end;
/
Then have the procedure set the values (here using random numbers, just as a demo); and the functions just return them:
create or replace package body min_max is
procedure do_all is
begin
p_min := dbms_random.value(1, 100);
p_max := dbms_random.value(500, 1000);
end do_all;
function f_min(n1 in integer, n2 in integer) return integer is
begin
-- do something with n1/n2?
return p_min;
end f_min;
function f_max(n1 in integer, n2 in integer) return integer is
begin
-- do something with n1/n2?
return p_max;
end f_max;
end min_max;
/
Then call the procedure to set the values:
begin
min_max.do_all;
end;
/
And call the functions, together or separately:
select min_max.f_min(0, 0), min_max.f_max(0, 0) from dual;
MIN_MAX.F_MIN(0,0) MIN_MAX.F_MAX(0,0)
------------------ ------------------
59 987
db<>fiddle
If you need the values from a PL/SQL context, not from a SQL context (i.e. from a query), then as you've declared the variables in the package specification you can refer to them directly, e.g.:
begin
dbms_output.put_line(min_max.p_min);
end;
/
How can I pass the parameters from the functions to the procedure, in order to get the minimum and the maximum values from the functions parameter?
Your procedure needs arguments, and you just pass the values through. If that is all it's used for then the procedure can be private.
create or replace package min_max is
p_min integer;
p_max integer;
function f_min(n1 in integer, n2 in integer) return integer;
function f_max(n1 in integer,n2 in integer) return integer;
end;
/
create or replace package body min_max is
procedure do_all (n1 in integer, n2 in integer) is
begin
p_min := least(n1, n2);
p_max := greatest(n1, n2);
end do_all;
function f_min(n1 in integer, n2 in integer) return integer is
begin
do_all(n1, n2);
return p_min;
end f_min;
function f_max(n1 in integer, n2 in integer) return integer is
begin
do_all(n1, n2);
return p_max;
end f_max;
end min_max;
/
Then:
select min_max.f_min(70, 900), min_max.f_max(70, 900) from dual;
MIN_MAX.F_MIN(70,900) MIN_MAX.F_MAX(70,900)
--------------------- ---------------------
70 900
select min_max.f_min(30, 1), min_max.f_max(30, 1) from dual;
MIN_MAX.F_MIN(30,1) MIN_MAX.F_MAX(30,1)
------------------- -------------------
1 30
Your variables can also be private (with either version).
db<>fiddle, as above but with variables in the body not the specification so they're private.
But if that is what you're doing then you don't need package variables at all, your procedure could use OUT variables:
create or replace package min_max is
function f_min(n1 in integer, n2 in integer) return integer;
function f_max(n1 in integer,n2 in integer) return integer;
end;
/
create or replace package body min_max is
procedure do_all (n1 in integer, n2 in integer, p_min out integer, p_max out integer) is
begin
if n2 >= n1 then
p_min := n1;
p_max := n2;
else
p_max := n1;
p_min := n2;
end if;
p_min := least(n1, n2);
p_max := greatest(n1, n2);
end do_all;
function f_min(n1 in integer, n2 in integer) return integer is
l_min integer;
l_max integer;
begin
do_all(n1, n2, l_min, l_max);
return l_min;
end f_min;
function f_max(n1 in integer, n2 in integer) return integer is
l_min integer;
l_max integer;
begin
do_all(n1, n2, l_min, l_max);
return l_max;
end f_max;
end min_max;
/
db<>fiddle
But then, you don't need this package at all; you can just use the built-in functions directly:
select least(70, 900), greatest(70, 900) from dual;
LEAST(70,900) GREATEST(70,900)
------------- ----------------
70 900
select least(30, 1), greatest(30, 1) from dual;
LEAST(30,1) GREATEST(30,1)
----------- --------------
1 30
but it's an exercise, so...

Functions and procedures that call other functions and procedures

I need to:
Write a function called ReadCar(): Car; that reads from the terminal values for each of the fields in a Car record and returns the completed record.
Write a procedure called WriteCar(c: Car); that takes a car record and writes each of the fields to the terminal with a description for the field as well as the field value.
Write a function called ReadAllCars(count: Integer): Cars; that calls your ReadCar() function count times and stores each car in Cars.
Write a procedure called WriteAllCars(carArray: Cars); that calls your WriteCar() procedure for each car in carArray.
So far I believe I have done steps 1 and 2 correctly but I am not sure how to do steps 3 and 4. How should I begin those steps? By the end of this program I am supposed to be able to enter data for 3 cars and it print the data properly.
program carDetails;
uses TerminalUserInput;
type Cars = Array of Car;
Car = record
ID : integer;
Manufacturer : string;
Model : string;
Registration : integer;
end;
function ReadCar(): Car;
begin
WriteLn(promt);
ReadCar.ID := readInteger('Please enter the Car ID ');
ReadCar.Manufacturer := readString('Please enter the manufacturer of car '+ ReadCar.ID);
ReadCar.Model := readString('Please enter the model of car '+ ReadCar.ID);
ReadCar.Registration := readInteger('Please enter the registration number for car '+ ReadCar.ID);
end;
procedure WriteCar(c: Car);
begin
WriteLn('ID - ', c.ID);
WriteLn('Manufacturer - ', c.Manufacturer);
WriteLn('Model - ', c.Model);
WriteLn('Registration - ', c.Registration);
end;
function ReadAllCars(count: integer): Cars;
begin
end;
procedure WriteAllCars(carArray: Cars);
begin
end;
procedure Main();
var cars: Array of Car;
index: Integer;
begin
cars := ReadAllCars(3);
WriteAllCars(cars);
end;
begin
Main();
end.
I'm not going to do your coursework (from Swinburne Uni?) for you, but here are a few points.
You need to declare your Car record before your Cars array.
type //Cars = Array of Car;
Car = record
ID : integer;
Manufacturer : string;
Model : string;
Registration : integer;
end;
Cars = Array of Car;
In ReadCar, your Prompt variable is undeclared (and mispelt). It should be
function ReadCar(const Prompt : String): Car;
begin
// WriteLn(promt);
WriteLn(Prompt);
Also in ReadCar, you need to convert Car.ID to a string before you can use it in your calls to readString, like so:
ReadCar.Manufacturer := readString('Please enter the manufacturer of car ' + IntToStr(ReadCar.ID));
ReadCar.Model := readString('Please enter the model of car ' + IntToStr(ReadCar.ID));
ReadCar.Registration := readInteger('Please enter the registration number for car ' + IntToStr(ReadCar.ID));
In ReadCars, you need to set the length of the array returned by it:
function ReadAllCars(count: integer): Cars;
begin
SetLength(Result, Count);
end;
Having done all that, writeCars is actually very simple. All you need is
procedure WriteAllCars(carArray: Cars);
var
i : Integer;
begin
for i:= Low(carArray) to High(carArray) do
WriteCar(carArray[i]);
end;
Using the Low() and High() functions sidesteps the issue of having to know the lower and upper bounds of an array declared the way your Cars one is (i.e. an array of record). Actually they are zero-based, not 1-based.
For some reason, SO's code-formatter doesn't do its normal stuff with the code in this answer, I'll try and tidy it up later.

why call by name doesn't give error as divide by zero in following code?

begin
integer n;
procedure p(k: integer);
begin
print(n);
end;
n := 5;
p(n/0);
end;
Output:
call by value: divide by zero error
call by name: 5
Parameter of procedure P is integer, but when you call it with n/0, n/0 type is real, that isn't compatible.
And in Pascal, your can not create new procedure between begin end.
(Your code looks like mixed of C and Pascal)
Sorry about my English :(

How to reference an external function from inside a unit

I have two files. The first, a.pas, contains:
uses b;
function f(x: integer): integer;
begin
f := x+1;
end;
begin
writeln(g(10));
end.
The file b.pas contains:
unit b;
interface
function g(x: integer): integer;
implementation
function g(x: integer): integer;
begin
g := f(x)*2;
end;
end.
Is it possible to somehow reference the function f defined in a.pas? I thought about using the forward keyword like this: (b.pas)
unit b;
interface
function g(x: integer): integer;
implementation
function f(x: integer): integer; forward;
function g(x: integer): integer;
begin
g := f(x)*2;
end;
end.
But it doesn't work. It gives a "Forward declaration not solved" error.
I also thought about using the external keyword but, in order to use the external f function, b.pas requires a.pas to be already compiled (but a.pas require b.pas as well).
The only way to do this seems to be moving f (interface and implementation) to a new helper.pas file, and modifying b.pas like this:
unit b;
interface
function g(x: integer): integer;
implementation
uses helper;
function g(x: integer): integer;
begin
g := f(x)*2;
end;
end.
I would prefer not having a helper.pas file. But maybe it's just impossible?
With the current source code layout it is not possible. If you do not want to make a.pas a unit (why?), you can rewrite function g to have a function parameter:
{$ifdef FPC}
{$mode delphi}
{$endif}
type
tintfunc = function(x: integer): integer;
function g(x: integer; f: tintfunc): integer;
begin
g := 2*f(x);
end;
function f(x: integer): integer;
begin
f := x+1;
end;
begin
writeln(g(3,f));
end.
There are other methods (including global function pointers), but function parameters are IMO the most clean/safe way to implement it.

FreePascal adding elements on binaryTree

So im trying to add all 'spent' data, belonging to specific client number, on a binary tree.
Type
pointer_tree = ^bTree;
bTree = record
nclient: integer;
spent: integer;
big, small: pointer_tree
end;
{adder is 0}
function add (pT: pointer_tree; client_number: integer; adder: integer): integer;
begin
if pT <> nil then begin
if pT^.nclient = client_number then
adder:= pT^.spent + adder
add(pT^.big,client_number,adder);
add(pT^.small,client_number,adder);
end;
add:= adder;
end;
Function add will not return the added elements and will return a random number instead. Also is there a better way to add them all up?
I don't like the way that you're using the variable 'adder' as both a parameter to the function and also as a temporary store for the calculated value. I think it would be better if the function were written as follows, without the 'adder' variable:
function add (pT: pointer_tree; client_number: integer): integer;
begin
result:= 0;
if pT <> nil then
begin
if pT^.nclient = client_number then result:= pT^.spent;
inc (result, add (pT^.big, client_number));
inc (result, add (pT^.small, client_number));
end;
end;
Incidentally, your code is missing a semicolon after the 'adder:= pT^.spent + adder' line.

Resources