How can I print an associative array as a matrix - oracle

I'm creating 2 associative arrays in which I put random values from 0 to 30, and after that I want to print then as a matrix. Is there a way I can do that?
Here is my code:
set serveroutput on
DECLARE
TYPE MyTab IS TABLE OF NUMBER INDEX BY VARCHAR2(10);
mat1 MyTab;
mat2 MyTab;
v_n NUMBER(2);
v_m NUMBER(2);
v_nr NUMBER(3);
v_dim NUMBER(3);
BEGIN
v_n := round(dbms_random.value(2,5));
v_m := round(dbms_random.value(2,5));
v_nr := 1;
v_dim := v_n*v_m;
DBMS_OUTPUT.PUT_LINE(v_n||' '||v_m);
FOR i in 1 ..v_dim LOOP
mat1(v_nr) := round(dbms_random.value(0,30));
v_nr := v_nr+1;
END LOOP;
v_nr := 1;
FOR i in 1 ..v_dim LOOP
mat2(v_nr) := round(dbms_random.value(0,30));
v_nr := v_nr+1;
END LOOP;
FOR i in 1 ..v_dim LOOP
DBMS_OUTPUT.PUT_LINE(mat1(i));
END LOOP;
DBMS_OUTPUT.PUT_LINE(chr(10));
FOR i in 1 ..v_dim LOOP
DBMS_OUTPUT.PUT_LINE(mat2(i));
END LOOP;
END;
/

I just understood there are 2 matrixes, mat1 and mat2, which have various size (but both have same dimensions).
Here is how to display them:
set serveroutput on
DECLARE
TYPE MyTab IS TABLE OF NUMBER INDEX BY pls_integer;
mat1 MyTab;
mat2 MyTab;
v_n pls_integer;
v_m pls_integer;
v_nr pls_integer;
v_dim pls_integer;
BEGIN
v_n := round(dbms_random.value(2,5));
v_m := round(dbms_random.value(2,5));
if (v_n > v_m) then
-- switch values for V_m to be the biggest dim
v_nr:=v_n;
v_n:=v_m;
v_m:=v_nr;
end if;
v_nr := 1;
v_dim := v_n*v_m;
DBMS_OUTPUT.PUT_LINE(v_n||' '||v_m);
FOR i in 1 ..v_dim LOOP
mat1(v_nr) := round(dbms_random.value(0,30));
v_nr := v_nr+1;
END LOOP;
v_nr := 1;
FOR i in 1 ..v_dim LOOP
mat2(v_nr) := round(dbms_random.value(0,30));
v_nr := v_nr+1;
END LOOP;
DBMS_OUTPUT.PUT_LINE('MATRIX1');
FOR i in 1 ..v_n LOOP
FOR j in 1 ..v_m LOOP
DBMS_OUTPUT.PUT(' - '|| rpad(mat1((j-1)*v_n + i), 4));
END LOOP;
DBMS_OUTPUT.PUT_LINE('');
END LOOP;
DBMS_OUTPUT.PUT_LINE('MATRIX2');
FOR i in 1 ..v_n LOOP
FOR j in 1 ..v_m LOOP
DBMS_OUTPUT.PUT(' - '|| rpad(mat2((j-1)*v_n + i), 4));
END LOOP;
DBMS_OUTPUT.PUT_LINE('');
END LOOP;
END;
/
I changed the types to pls_integer which looks more simple. Then keep in mind that I put the matrices in the good form where they can be multiplied (swith v_nand v_m in the loops, and added formatting to understand what happens.
DECLARE
TYPE MyTab IS TABLE OF NUMBER INDEX BY pls_integer;
mat1 MyTab;
mat2 MyTab;
v_n pls_integer;
v_m pls_integer;
v_nr pls_integer;
v_dim pls_integer;
idx pls_integer;
idx1 pls_integer;
idx2 pls_integer;
v_p number;
BEGIN
v_n := round(dbms_random.value(2,5));
v_m := round(dbms_random.value(2,5));
-- v_n := 2; -- formating works better with 2 and 3
-- v_m := 3;
v_nr := 1;
v_dim := v_n*v_m;
DBMS_OUTPUT.PUT_LINE(v_n||' '||v_m);
FOR i in 1 ..v_dim LOOP
mat1(v_nr) := round(dbms_random.value(0,30));
v_nr := v_nr+1;
END LOOP;
v_nr := 1;
FOR i in 1 ..v_dim LOOP
mat2(v_nr) := round(dbms_random.value(0,30));
v_nr := v_nr+1;
END LOOP;
DBMS_OUTPUT.PUT_LINE('MATRIX1:a');
FOR i in 1 ..v_n LOOP
DBMS_OUTPUT.PUT(' .................. ');
FOR j in 1 ..v_m LOOP
idx:=(j-1)*v_n + i;
DBMS_OUTPUT.PUT(' |'||j||','||i||'a['||idx||']'|| rpad(mat1(idx), 4));
END LOOP;
DBMS_OUTPUT.PUT_LINE('');
END LOOP;
DBMS_OUTPUT.PUT_LINE('MATRIX2:b');
FOR i in 1 ..v_m LOOP
FOR j in 1 ..v_n LOOP
idx:=(j-1)*v_m + i;
DBMS_OUTPUT.PUT(' |'||j||','||i||'b['||idx||']'|| rpad(mat2(idx), 4));
END LOOP;
DBMS_OUTPUT.PUT_LINE('');
END LOOP;
DBMS_OUTPUT.PUT_LINE('product: a x b');
FOR L in 1 ..v_m LOOP
DBMS_OUTPUT.PUT(' ---------------------------');
FOR K in 1 ..v_m LOOP
v_p:=0;
DBMS_OUTPUT.PUT(' | ');
FOR j in 1 ..v_n LOOP
idx1 := j + (K-1)*v_n;
idx2 := (j-1)*v_m + L;
v_p := v_p + mat1(idx1) * mat2(idx2) ;
DBMS_OUTPUT.PUT('a['||idx1||']b['||idx2||']+');
END LOOP;
DBMS_OUTPUT.PUT('->'|| rpad(v_p, 4));
END LOOP;
DBMS_OUTPUT.PUT_LINE('');
END LOOP;
END;
/

Do you want to use Matrix for calculation or logging?
It is quite difficult to logging with Matrix. I don't think it is a good way. If you insist, here's the code.
FOR i in 1 ..v_n LOOP
FOR j in 1 ..v_m LOOP
DBMS_OUTPUT.PUT( mat.at<double>(i,j));

Related

Error: BREAK not allowed in for looping pascal

I try to create a for looping for my Bubble Sort Algorithm and use break as a condition if there is no more number to be sorted anymore. But then the compiler said that BREAK not allowed. Here is my code
Procedure Sort(var data : arr; j : integer);
var
temp: integer;
begin
temp := data[j];
data[j] := data[j + 1];
data[j + 1] := temp;
end;
Procedure sortDescending(var data : arr; n : integer);
var
i, j : integer;
marker : boolean;
begin
for i := 1 to n do
marker := false;
begin
for j := 1 to n do
begin
if(data[j] < data[j + 1]) then
begin
Sort(data, j);
marker := true;
end;
end;
if(marker = false) then
begin
break;
end;
end;
end;
Here's your code.
for i := 1 to n do
marker := false;
begin
for j := 1 to n do
begin
if(data[j] < data[j + 1]) then
begin
Sort(data, j);
marker := true;
end;
end;
if(marker = false) then
begin
break;
end;
end;
Let's add some whitespace after loops and indentation to make the issue clearer.
for i := 1 to n do
marker := false;
begin
for j := 1 to n do
begin
if (data[j] < data[j + 1]) then
begin
Sort(data, j);
marker := true;
end;
end;
if (marker = false) then
begin
break;
end;
end;
Which is equivalent to:
for i := 1 to n do
marker := false;
for j := 1 to n do
begin
if (data[j] < data[j + 1]) then
begin
Sort(data, j);
marker := true;
end;
end;
if (marker = false) then
begin
break;
end;
The break is not within a loop.
You likely meant to put begin before marker := false;.
for i := 1 to n do
begin
marker := false;
for j := 1 to n do
begin
if (data[j] < data[j + 1]) then
begin
Sort(data, j);
marker := true;
end;
end;
if (marker = false) then
begin
break;
end;
end;

How can I show the sorting progress step by step on output screen?

I don't know how to make sorting process visible on output.. (like you can see the step by step of each sorting phase on output).
Below is example of a similar program.
And here's my current progress:
program insertsort;
const
max = 100;
type
arr = array [1..max] of integer;
var
data : arr;
n, i, j : integer;
procedure InsertionSort(size : integer);
var
i, j, index : integer;
begin
for i := 2 to size do
begin
index := data[i];
j := i;
while ((j > 1) and (data[j-1] < index)) do
begin
data[j] := data[j-1];
j := j - 1;
end;
data[j] := index;
end;
end;
begin
write('Input Data : ');
readln(n);
for i := 1 to n do
begin
write ('Data-',i,' = '); readln(data[i]);
end;
writeln;
write ('Unsorted : ');
for i := 1 to n do
write(data[i],' ');
InsertionSort(max);
writeln;
writeln;
writeln;
write('Sorted : ');
for i := 1 to n do
write(data[i],' ');
readln;
end.

how to populate an Associative Array index by VARCHAR2

I have 2 associative arrays:
v1 index by binary integer
v2 index by varchar2
type r1 is record
( c1 number
, c2 varchar2(64));
type t1 is table of r1 index by binary_integer;
v1 t1;
type t2 is table of varchar2(64) index by binary_integer;
v2 t2;
counter number := 0;
type r3 is record
( no_visits number);
type t3 is table of r3 index by varchar2(64);
v3 t3;
I want to have a list (probably another associative array - v3 with the cities from v1 which are defined in v2 and how many times I have visited them (the visits are defined in v1).
I was thinking that v3 to be index by VARCHAR2 (the index to be the name of the cities) and contains only one value, the number of visits.
Is it possible to have implement something like:
begin
v1(1).c1 := 1990;
v1(1).c2 := 'PARIS';
V1(2).c1 := 2000;
V1(2).c2 := 'PARIS';
v1(3).c1 := 2001;
v1(3).c2 := 'PARIS';
v1(4).c1 := 1992;
v1(4).c2 := 'MADRID';
v1(5).c1 := 1994;
v1(5).c2 := 'LONDON';
v1(6).c1 := 1998;
v1(6).c2 := 'PRAGUE';
v2(1) := 'PARIS';
v2(2) := 'LONDON';
v2(3) := 'MADRID';
for i in 1 .. v1.count loop
for j in 1 .. v2.count loop
if v1(i).c2 = v2(j)
then
v3(v2(j).c2) := counter + 1;
end if;
end loop;
end loop;
end;
Initialize v3 with values from v2, modify counting loop slightly and show results in final loop:
declare
type r1 is record ( c1 number, c2 varchar2(64));
type t1 is table of r1 index by binary_integer;
v1 t1;
type t2 is table of varchar2(64) index by binary_integer;
v2 t2;
type r3 is record( no_visits number);
type t3 is table of r3 index by varchar2(64);
v3 t3;
begin
v1(1).c1 := 1990;
v1(1).c2 := 'PARIS';
V1(2).c1 := 2000;
V1(2).c2 := 'PARIS';
v1(3).c1 := 2001;
v1(3).c2 := 'PARIS';
v1(4).c1 := 1992;
v1(4).c2 := 'MADRID';
v1(5).c1 := 1994;
v1(5).c2 := 'LONDON';
v1(6).c1 := 1998;
v1(6).c2 := 'PRAGUE';
v2(1) := 'PARIS';
v2(2) := 'LONDON';
v2(3) := 'MADRID';
for i in 1..v2.count loop
v3(v2(i)).no_visits := 0;
end loop;
for i in 1 .. v1.count loop
for j in 1 .. v2.count loop
if v1(i).c2 = v2(j) then
v3(v2(j)).no_visits := v3(v2(j)).no_visits + 1;
end if;
end loop;
end loop;
for i in 1..v2.count loop
dbms_output.put_line('City: '||v2(i));
dbms_output.put_line('Visited: '||v3(v2(i)).no_visits);
end loop;
end;
Output:
City: PARIS
Visited: 3
City: LONDON
Visited: 1
City: MADRID
Visited: 1
In general, yes, you can do that. In this case there are some problems - for example, in the line v3(v2(j).c2) := counter + 1; there's no c2 element in v2 so you'll get a compilation error there, and in that same line the v3 reference should be followed by .no_visits, but generally speaking what you're trying to do here is certainly allowed by the language.

Reverse the given number program in PL/SQL

I was trying to execute this program but it only outputs the ~ symbol.
This is the code:
DECLARE
N_NUM NUMBER := 234;
N_REM NUMBER;
N_REV NUMBER := 0;
BEGIN
WHILE N_NUM != 0
LOOP
N_REM := MOD (N_NUM, 10);
N_NUM := (N_NUM / 10);
N_REV := N_REV * 10 + N_REM;
END LOOP;
DBMS_OUTPUT.PUT_LINE (N_REV);
END;
Before dividing by 10 you have to subtract the remainder. The following code will work.
DECLARE
N_NUM NUMBER := 234;
N_REM NUMBER;
N_REV NUMBER := 0;
BEGIN
WHILE N_NUM <> 0
LOOP
N_REM := MOD (N_NUM, 10);
N_NUM := ( (N_NUM - N_REM) / 10);
N_REV := N_REV * 10 + N_REM;
END LOOP;
DBMS_OUTPUT.PUT_LINE (N_REV);
END;
Use Floor function as shown below:
N_NUM := FLOOR(N_NUM/10)
As this floor function returns only 23 instead of 23.4
A second option is using the REVERSE() function.
However, that function would like to input a CHAR, not a NUMBER.
So you will have to convert that first.
For example:
DECLARE
N_Num NUMBER := 234;
N_Rev NUMBER := 0;
BEGIN
SELECT REVERSE(TO_CHAR(N_Num)) INTO N_Rev FROM Dual;
DBMS_OUTPUT.PUT_LINE (N_Rev);
END;
/
create or replace function ReverseNo(no1 in number)
return number
Is
n number:=no1;
p number;
c number:=0;
begin
while ( n>0)
LOOP
p:=mod(n,10);
c:=c*10+p;
n:=(n-p)/10;
END LOOP;
DBMS_OUTPUT.PUT_LINE('NO IS ' || c);
return c;
end;

GET_TIME in Oracle

I have a code that is supposed to give me the compilation time for the procedure, but it only gets me 0 secs! Is the calculation wrong? Is there any other way?
create or replace PROCEDURE proc_time AS
v NUMBER := 1;
x NUMBER := 0;
counter number := 0;
summ NUMBER;
ex_start NUMBER;
ex_end NUMBER;
ex_time NUMBER;
BEGIN
ex_start := dbms_utility.get_time;
while counter <= 19
loop
counter := counter + 1;
summ := x+v;
x := v;
V := summ;
dbms_output.put_line('Fibonacci nr'||counter||': '||summ);
END loop;
ex_end := DBMS_UTILITY.GET_TIME;
ex_time := (ex_end-ex_start)/100;
DBMS_OUTPUT.PUT_LINE( 'Exekveringstid: ' || ex_time || ' sekunder.' );
END;
/
As explained in the above answer by Lalit. Your snippet runs too fast
to calculate the time difference. Try to increase the while loop with
some greater number and here you go !!!
CREATE OR REPLACE PROCEDURE proc_time
AS
v NUMBER := 1;
x NUMBER := 0;
counter NUMBER := 0;
summ NUMBER;
ex_start NUMBER;
ex_end NUMBER;
ex_time NUMBER;
BEGIN
ex_start := dbms_utility.get_time;
dbms_output.put_line('start time ==>'||NVL(ex_start,0));
WHILE counter <= 190000
LOOP
counter := counter + 1;
summ := x +v;
x := v;
V := summ;
-- dbms_output.put_line('Fibonacci nr'||counter||': '||summ);
END LOOP;
ex_end := DBMS_UTILITY.GET_TIME;
dbms_output.put_line('End time ==>'||ex_end);
ex_time := (ex_end-ex_start);
DBMS_OUTPUT.PUT_LINE( 'Exekveringstid: ' || ex_time || ' sekunder.' );
END;
set serveroutput on;
exec proc_time;
-------------------------------OUTPUT----------------------------------------
start time ==>1761607275
End time ==>1761607281
Exekveringstid: 6 sekunder.
As an alternative use the TIMESTAMP data type, this supports up to nanoseconds (provided your server supports it as well)
DECLARE
ex_start TIMESTAMP(9);
Duration INTERVAL DAY TO SECOND;
v NUMBER := 1;
x NUMBER := 0;
counter NUMBER := 0;
summ NUMBER;
BEGIN
ex_start := LOCALTIMESTAMP;
WHILE counter <= 10000 LOOP
counter := counter + 1;
summ := x +v;
x := v;
V := summ;
END LOOP;
Duration := LOCALTIMESTAMP - ex_start;
DBMS_OUTPUT.PUT_LINE ( EXTRACT(SECOND FROM Duration) /1000||' msec.');
END;
Perhaps, it is executing too quickly. You can try to increase the number of loops instead of 19 to something larger.
Also, it would give you the execution time, not the compilation time. The procedure is already compiled and stored in database. The time in the output is at run time, i.e. when you execute the procedure.
For example,
SQL> CREATE OR REPLACE PROCEDURE test_time
2 AS
3 l_start NUMBER;
4 l_loops NUMBER := 10000000;
5 l_number NUMBER := 0;
6 BEGIN
7 l_start := DBMS_UTILITY.get_time;
8 FOR i IN 1 .. l_loops
9 LOOP
10 l_number := l_number + i;
11 END LOOP;
12 DBMS_OUTPUT.put_line('time taken: ' || (DBMS_UTILITY.get_time - l_start) || ' hsecs');
13 END test_time;
14 /
Procedure created.
SQL> set serveroutput on
SQL> BEGIN
2 test_time;
3 END;
4 /
time taken: 101 hsecs
PL/SQL procedure successfully completed.
SQL>

Resources