Pascal Arrays Can't figure out how to print info from arrays - arrays

I'm trying to write a program tracks the products made by 7 machines in a factory, which all make the same 10 kinds of products.
The program asks the user to enter the machine id (A, B, C...G)
Then the product id (0, 1, 2...9)
and finally the program asks for a 0 or 1 to mark the product as either good quality or bad quality.
I put all of that information into three arrays. What I have to do next is the part I'm stuck with. I have to create two tables from all that information.
The first table is a table of good quality products. It has to be a two dimensional table with the MachineIDs running along the top and the ProductIDs running down the side.
There needs to be a running total for every single machine and every single product. So, in the cell [A0] needs to be a total of how many times machineA has made product0 and it was marked as GOOD. Then [A1] and so on all the way to [G9]
The second table is exactly the same only this time only the bad products are counted.
At the bottom there needs to be a total of ALL products made by each machine. Down the right hand side there has to be a total of ALL the individual products made be all machines.
Here's my code so far.
program Production;
var machine: array of char;
var product: array of integer;
var quality: array of integer;
var min, number, extra: integer;
var machineID: array[1..7] of char;
var productID: array[1..10] of char;
var x, y, count: integer;
begin
x:= 0;
y:= 0;
number := 0;
min := 5;
extra := 5;
SETLENGTH( machine, min );
SETLENGTH( product, min );
SETLENGTH( quality, min );
writeln('Input Machine ID ( A, B, C, D, E, F or G ) ');
readln(machine[number] );
while (machine[number] <> '*') do
begin
while ( ORD( machine[number] ) < 65 ) or ( ORD( machine[number] ) > 71 ) do
begin
writeln('Input Invalid. Please try again.');
readln(machine[number] );
end;
writeln('Input Product Number ( 0, 1, 2, 3, 4, 5, 6, 7, 8 or 9 ) ');
readln(product[number] );
while ( product[number] < 0 ) or ( product[number] > 9 ) do
begin
writeln('Input Invalid. Please try again.');
readln(product[number] );
end;
writeln('Quality Control Check. Input 0 for GOOD or 1 for BAD.');
readln(quality[number] );
while ( quality[number] <> 0 ) and ( quality[number] <> 1 ) do
begin
writeln('Input Invalid. Please try again.');
readln(quality[number] );
end;
number := number + 1;
writeln('Input Machine ID ( A, B, C, D, E, F or G ) ');
readln(machine[number] );
end;
for count := 0 to number - 1 do
begin
writeln('Machine ID = ',machine[count] );
writeln('Product ID = ',product[count] );
writeln('Quality = ',quality[count] );
end;
writeln('');
writeln('');
writeln('EXIT');
readln;
end.
Could anyone tell me even how I would go about it? I'm completely at a loss.

I would approach the problem a bit differently. After I've recorded all the input from the user for one piece of data (machine, product and quality), I'd store it in a table - or in this case in a three-dimensional array. If I recall correctly, you can create such thing as follows:
var myTable: array[1..7, 1..10, 1..2] of integer;
The dimensions of the table would come as follows:
1..7 would be the machine Ids
1..10 would be the product Ids
1..2 would be the good/bad.
Once I got all the input from the user, I would simply increment the appropriate field in the table. E.g. assume the user inputs are: { machine B, product 2, Good } then we would
myTable[1, 2, 0] := myTable[1, 2, 0] + 1;
Therefore when we would want to print out our table we could just:
for i := 0 to 7 do
begin
for j := 0 to 10 do
begin
write(myTable[i,j,0] + ' '); { i,j,0 for the good; i,j,1 for the bad values }
end;
writeln('');
end;

Related

Comparison of two arrays in Lazarus

I have a problem with Pascal, especially Lazarus.
First of all, I created two random arrays of integer:
procedure TForm1.b_arraycreate1Click(Sender: TObject);
begin
randomize;
for i := 1 to 5 do
arr1[i] := random(10);
end;
And
procedure TForm1.b_arraycreate2Click(Sender: TObject);
begin
randomize;
for j := 1 to 5 do
arr2[j] := random(10);
end;
I know, I could put it in one procedure as well but doesn't matter now.
I want to compare these two. I wrote the following code:
procedure TForm1.b_comparisonClick(Sender: TObject);
var v:boolean;
begin
for i := 1 to 5 do begin
for j := 1 to 5 do begin
if arr1[i] = arr2[j]
then
begin
v:=true;
end
else
begin
v:=false;
end;
end;
end;
if v = true
then
begin
ShowMessage('Yes, there is a similarity! You can find number ' +IntToStr(arr1[i])+ ' in array 1, position ' +IntToStr(i)+ ' and also in array 2, position ' +IntToStr(j)+ '.');
end
else
begin
ShowMessage('No similarities... Generate new ones!');
end
end;
In my own words: I want to push a button and then there should be a message window with the information if there is one number (for example 7) which exists in array 1 and array 2. If yes, it should also write the position (index) of this number.
Unfortunately, this program doesn't work and I don't know why. It always shows "No similarities" (and don't worry about the creation of the arrays. I also have a label where I can test the content of the arrays every time).
Is there a (silly) mistake in my code here?
As explained already by MartynA in his comment, your algorithm is wrong. Your words are:
if there is one number which exists in array 1 and array 2
To see if it is so, you must scan all array1 and, for each number, see if it exists somewhere in array2.
So yes, you need two cycles, one nested in the other. As soon as you find a correspondence, you must stop. Or, if you want more results (find multiple duplicates), show a message instead of stopping - and go ahead. Third possibility (more complicated): when found, store the couple of indexes (without overwrite old results...) and go ahead. I will only show the first option:
procedure TForm1.b_comparisonClick(Sender: TObject);
var
i,j: integer;
v: boolean;
begin
v := false;
for i := 1 to 5 do begin
for j := 1 to 5 do begin
if arr1[i] = arr2[j] then begin
v := true;
break
end
end // inner, j
end; // outer, i
if v = true
then ShowMessage(.....)
else ShowMessage('No similarities...');
end; // proc comparison
I tried to respect your code a bit, there are a few possible "shortcuts"; for example, if v is a boolean variable, it is better to write if v then instead of if v=true then, and some others, like
v := arr1[i]=arr[j];
...or... the outer loop does not need begin+end.
******* BEWARE (see comment below about break)
To stop/exit from two nested cycle is not so simple... perhaps a goto... the code above works, but the break does little work.
******* second update, as described by comment below. IT DOES NOT WORK, because if the break does not exit BOTH loops, the outer index gets modified. The correct cycle using TWO breaks is as follows:
for i := 1 to 5 do begin
for j := 1 to 5 do begin
if arr1[i] = arr2[j] then begin
v := true;
break
end
end; // inner, j
if v then break
end; // outer, i
Sorry for the mistakes... :-)
I would prefer a GOTO to exit both loops: it is faster, single instruction, and more clear ("goto found" instead of a generic break). But GOTOs are not very popular... so I've been afraid!

Initialization of field in array of records (Pascal) [duplicate]

This question already has an answer here:
Lazarus readln doesn't read the variable
(1 answer)
Closed 4 years ago.
I have a small code where I want to initialize an array of records by fields and then just output this records on screen.
Data types:
type
grade = 1..5;
Person = record
Name: string[16];
isMale: boolean;
grades: array [1..6] of grade;
end;
var
Table: array [1..10] of Person;
R: Person;
N,J,I: Integer;
Part of code with initialization and output:
readln(n);
if N>10 then N:=10; if N<1 then N:=1;
for I:=1 to N do begin
R:=Table[I];
//write('Gender?'); readln(j); R.isMale:=j>=0; <= This works just fine
write('Name? '); readln(R.Name);
write('Gender? '); readln(j); R.isMale:=j>=0;
write('Grades? '); for j:=1 to 6 do read(R.grades[J]); writeln;
end;
for I:=1 to N do begin
R:=Table[I];
write(I,' ', R.Name,' ',R.isMale);
end;
When I enter info about first person it works fine, but then every other person's name input is skipped (output is "Name? Gender? ). If I switch entering boolean and string, code works correct, but that's not a logic order.
Why is this happening?
At the end of the loop, you should assign the record to the array. Note that, unlike with classes, assigning a record copies the data in the record, it does not reference the record. So instead of what you have, rather do:
for I := 1 to N do
begin
//write('Gender?'); readln(j); R.isMale:=j>=0; <= This works just fine
write('Name? ');
readln(R.Name);
write('Gender? ');
readln(j);
R.isMale := j >= 0;
write('Grades? ');
for j := 1 to 5 do
read(R.grades[J]);
readln(R.grades[6]); // readln reads the end-of-line too.
writeln;
Table[I] := R; // copy the data from R into the table
end;
That way, the data from the record R is copied into the table. There is no need to copy R from the table at the beginning of the loop, as the table is empty anyway.
Unlike with classes, with records like this, you could do the following too:
write('Name? ');
readln(Table[I].Name);
write('Gender? ');
readln(j);
Table[I].isMale := j >= 0;
// etc...
And in the final loop:
Writeln(I, ' ', Table[I].Name, ' ', Table[I].IsMale);
without using R at all.

Populate a database gives strange results

I have a database and I need to populate it's first 2 columns on every row. The first column is the date and the second column is an id.
My code is as follows:
.......
febr29:array[1..12] of byte = (31,29,31,30,31,30,31,31,30,31,30,31);
.......
procedure TForm.populate_database;
var
i,j,m,n: Integer;
begin
for i := 1 to 12 do
for j := 1 to febr29[i] do
for m := 1 to 9 do
for n := 1 to 15 do begin
database.tbl1.Append;
database.tbl1['date']:= inttostr(j)+'.'+inttostr(i)+'.2016';
database.tbl1['id']:='a'+inttostr(m)+inttostr(n);
database.tbl1.Post;
end;
end;
So basically I need to have all the ids on all the days of the year. But I have a problem with the code above: it gives me some strange output in the database, as in the following picture:
What am I doing wrong?
If your ID field is supposed to identify the data row, it would be better to declare it in the database as an integer column, not a character/string one.
It would also be better & less error prone not to try and calculate it from your loop variables, but use a running counter instead
procedure TForm.populate_database;
var
i,j,m,n: Integer;
ID : Integer;
begin
ID := 0;
for i := 1 to 12 do
for j := 1 to febr29[i] do
for m := 1 to 9 do
for n := 1 to 15 do begin
Inc(ID);
database.tbl1.Append;
database.tbl1['date']:= inttostr(j)+'.'+inttostr(i)+'.2016';
database.tbl1['id'].AsInteger :=ID;
database.tbl1.Post;
Of course, if you must have the 'a' prefix and a character coumn type for some reason, you could do
database.tbl1['id'].AsString :='a' + IntToStr(ID);
but even that may give you results you aren't expecting, unless to pad the result of IntToStr(ID) to a fixed length with leading zeroes.

PASCAL Can I take array elements and assign them to integer variables?

I am currently trying to write a program which provides a quarterly overview of the amount of money that a user defined number of salesmen has made for a whole year.
Here is the code I have at the moment:
program Verkopers;
var month, day, sellernr:array[0..99] of integer;
sale:array[0..99] of real;
count, num: integer;
begin
num := 0;
writeln('Enter seller number. To stop enter "0"');
readln(sellernr[num] );
while (sellernr[num] <> 0) do
begin
writeln('Enter date in DD MM format');
readln(day[num] , month[num] );
writeln('Enter sale amount');
readln(sale[num] );
num := num + 1;
writeln('');
writeln('Enter seller number. To stop enter "0"');
readln(sellernr[num] );
end;
writeln('Seller Nr.':10, 'Date':14, 'Amount':16);
for count := 0 to num-1 do
begin
writeln(sellernr[count], day[count]:20,'/',month[count], sale[count]:14:2);
end;
writeln('');
writeln('ENTER to stop');
readln();
end.
So as you see, the program asks for the seller number, then date in DD MM format and then the amount of the sale.
It the prints out to the screen.
What I have to do next is provide the per quarter overview. I have to take each seller number which the user has defined in the array sellernr and add up the total sales per quarter.
That's where I have a problem. How do I take the values that are stored in the array and recognise when they are equal (ie when sellernr[x] and sellernr[y] are the same)
Say the seller numbers the user inputs are 10, 50, 100. I must then take only the array elements which correspond to, first, 10 and print out, for example:
Seller Quarter1 Quarter2 Quarter3 Quarter4
10 $360.32 $567.21 $988.27 $1023.66
How can I take sellernr[x] which correspond to a particular user-defined value?
You can do something like this:
program Verkopers;
var
SalesByQuarters:array[1..4]of real;//here you will store the sums
ReporSeller:integer; //this will be the nr. of the seller for report
begin
...//append your code with this
writeln('Write seller number for report: ');
readln(ReporSeller);
for count:=1 to 4 do
SalesByQuarters[count]:=0;//initialize the output variables for each quarter
for count:= Low(sellernr) to High(sellernr) do
begin
if sellernr[count]=ReporSeller then //count will store the proper index
SalesByQuarters[((month[count]-1) div 3) +1]:=
SalesByQuarters[((month[count]-1) div 3) +1]+sale[count];
end; //((month[count]-1) div 3) +1 gives us the number of quarter.
// You can get it in a different way (if/case/etc)
write('Report for seller ');
writeln(ReporSeller);
for count:=1 to 4 do
begin
write('Quarter ');
writeln(count);
writeln(SalesByQuarters[count]:14:2); //output the stored sums
end;
readln;
end.
You can also beautify the output by something like this:
writeln('Seller Quarter1 Quarter2 Quarter3 Quarter4');
writeln(ReporSeller:6,
' $', SalesByQuarters[1]:7:2,
' $', SalesByQuarters[2]:7:2,
' $', SalesByQuarters[3]:7:2,
' $', SalesByQuarters[4]:7:2);

What data structure to use in order to sort this data in PL/SQL?

This is Oracle 11.2g. In a PL/SQL function, I've got a loop whereby each iteration, I create a string and an integer associated with that string. The function returns the final concatenation of all the generated strings, sorted (depending on a function input parameter), either alphabetically or by the value of the integer. To give an idea, I'm generating something like this:
Iteration String Integer
1 Oslo 40
2 Berlin 74
3 Rome 25
4 Paris 10
If the input parameter says to sort alphabetically, the function output should look like this :
Berlin, Oslo, Paris, Rome
Otherwise, we return the concatenated strings sorted by the value of the associated integer:
Paris, Rome, Oslo, Berlin
What is the most appropriate data structure to achieve this sort? I've looked at collections, associative arrays and even varrays. I've been kind of shocked how difficult this seems to be to achieve in Oracle. I saw this question but it doesn't work in my case, as I need to be able to sort by both index and value: How to sort an associative array in PL/SQL? Is there a more appropriate data structure for this scenario, and how would you sort it?
Thanks!
It is very easy if you use PL/SQL as SQL and not like other languages. It is quite specific and sometimes is very nice exactly because of that.
Sometimes I really hate PL/SQL, but this case is absolutely about love.
See how easy it is:
create type it as object (
iter number,
stringval varchar2(100),
intval integer
);
create type t_it as table of it;
declare
t t_it := new t_it();
tmp1 varchar2(32767);
tmp2 varchar2(32767);
begin
t.extend(4);
t(1) := new it(1,'Oslo',40);
t(2) := new it(2,'Berlin',74);
t(3) := new it(3,'Rome',25);
t(4) := new it(4,'Paris',10);
select listagg(stringval,', ') within group (order by stringval),
listagg(stringval,', ') within group (order by intval)
into tmp1, tmp2
from table(t);
dbms_output.put_line(tmp1);
dbms_output.put_line(tmp2);
end;
/
drop type t_it;
drop type it;
Here you can see the problem that you must create global types, and this is what I hate it for. But they say in Oracle 12 it can be done with locally defined types so I am waiting for it :)
The output is:
Berlin, Oslo, Paris, Rome
Paris, Rome, Oslo, Berlin
EDIT
As far as you do not know the amount of iterations from the beginning the only way is to do extend on each iteration (this is only example of extending):
declare
iterator pls_integer := 1;
begin
/* some type of loop*/ loop
t.extend();
-- one way to assign
t(t.last) := new it(1,'Oslo',40);
-- another way is to use some integer iterator
t(iterator) := new it(1,'Oslo',40);
iterator := iterator + 1;
end loop;
end;
I prefer the second way because it is faster (does not calculate .last on each iteration).
This is an example of pure PL/SQL implementation that is based on the idea associative array (aka map or dictionary in other domains) is an ordered collection that is sorted by a key. That is a powerful feature that I have used multiple times. For input data structure in this example I decided to use a nested table of records (aka a list of records).
In this particular case however I'd probably go for similar implementation than in simon's answer.
create or replace package so36 is
-- input data structures
type rec_t is record (
iter number,
str varchar2(20),
int number
);
type rec_list_t is table of rec_t;
function to_str(p_list in rec_list_t, p_sort in varchar2 default 'S')
return varchar2;
end;
/
show errors
create or replace package body so36 is
function to_str(p_list in rec_list_t, p_sort in varchar2 default 'S')
return varchar2 is
v_sep constant varchar2(2) := ', ';
v_ret varchar2(32767);
begin
if p_sort = 'S' then
-- create associative array (map) v_map where key is rec_t.str
-- this means the records are sorted by rec_t.str
declare
type map_t is table of rec_t index by varchar2(20);
v_map map_t;
v_key varchar2(20);
begin
-- populate the map
for i in p_list.first .. p_list.last loop
v_map(p_list(i).str) := p_list(i);
end loop;
v_key := v_map.first;
-- generate output string
while v_key is not null loop
v_ret := v_ret || v_map(v_key).str || v_sep;
v_key := v_map.next(v_key);
end loop;
end;
elsif p_sort = 'I' then
-- this branch is identical except the associative array's key is
-- rec_t.int and thus the records are sorted by rec_t.int
declare
type map_t is table of rec_t index by pls_integer;
v_map map_t;
v_key pls_integer;
begin
for i in p_list.first .. p_list.last loop
v_map(p_list(i).int) := p_list(i);
end loop;
v_key := v_map.first;
while v_key is not null loop
v_ret := v_ret || v_map(v_key).str || v_sep;
v_key := v_map.next(v_key);
end loop;
end;
end if;
return rtrim(v_ret, v_sep);
end;
end;
/
show errors
declare
v_list so36.rec_list_t := so36.rec_list_t();
v_item so36.rec_t;
begin
v_item.iter := 1;
v_item.str := 'Oslo';
v_item.int := 40;
v_list.extend(1);
v_list(v_list.last) := v_item;
v_item.iter := 2;
v_item.str := 'Berlin';
v_item.int := 74;
v_list.extend(1);
v_list(v_list.last) := v_item;
v_item.iter := 3;
v_item.str := 'Rome';
v_item.int := 25;
v_list.extend(1);
v_list(v_list.last) := v_item;
v_item.iter := 4;
v_item.str := 'Paris';
v_item.int := 10;
v_list.extend(1);
v_list(v_list.last) := v_item;
dbms_output.put_line(so36.to_str(v_list));
dbms_output.put_line(so36.to_str(v_list, 'I'));
end;
/
show errors

Resources