Hi I'm learning pascal and testing some functions
I made a recursive insert procedure like this.
if node exist compare two keys, and if not, make a room for new node.
procedure INSERT (KEY : integer; var NODE : NODEPTR);
begin
if NODE = Nil then
begin
New (NODE);
NODE^.KEY := KEY;
NODE^.LEFT := Nil;
NODE^.RIGHT := Nil
end
else
if KEY < NODE^.KEY then
INSERT (KEY, NODE^.LEFT)
else
INSERT (KEY, NODE^.RIGHT)
end;
and What I'm trying to do is changing recursive function to while-loop.
so I made procedure like this
if node exist do while loop until node is empty.
and after while loop is over, make a new node
procedure INSERT (KEY : integer; var NODE : NODEPTR);
begin
while NODE <> nil do
begin
if KEY < NODE^.KEY then
NODE:=NODE^.LEFT
else
NODE:=NODE^.RIGHT
end;
New (NODE);
NODE^.KEY := KEY;
NODE^.LEFT := Nil;
NODE^.RIGHT := Nil
end;
when first node is root, while loop is true and execute this code
but after this, while loop changes to false and make a new node.
if KEY < NODE^.KEY then
NODE:=NODE^.LEFT
else
NODE:=NODE^.RIGHT
Eventually there is no node connection, and this program just keep making new node.
Is there anything that i missed? or any improvising about the second code?
thanks in advance :)
The thing you missed is that you never link up the nodes (and in the current setup, you can't actually linkup the nodes). You have fields on your record for the left and right node of a node, but you don't assign them. So you end up just creating nodes without every linking them up; you have a linked list with the links missing.
At one point, you'll need something along the lines of NODE^.RIGHT := NEWNODE (or NODE^.LEFT := NEWNODE, of course).
You'll need something along the ways of (my Pascal is a bit rusty, so beware of syntax errors ;) ):
procedure INSERT(key: Integer; var NODE: NODEPTR)
begin
NODEPTR root := NODE;
if (key < root^.KEY) then begin
while (root^.LEFT <> nil) do begin
root := root^.LEFT;
end;
new(NODE);
NODE^.KEY := key;
root^.LEFT := NODE;
node^.RIGHT := root;
end else begin
while (root^.Right <> nil) do begin
root^ := root^.RIGHT;
end;
new(NODE);
NODE^.KEY := key;
root^.RIGHT := NODE;
NODE^.LEFT := root;
end;
end;
There's lots of improvements and beautifications to be made in the example above, but I think it shows the general idea.
Related
I'm working on porting a set of paradox tables to SQLite. In order to do so, I created a test application that simulates (somewhat) the current usage scenario: multiple users accessing the same DB file and performing simultaneous read and writes.
The application is very simple: it will start several threads that each create a connection, opens a table and will randomly read, update or insert inside the table.
Almost immediately, The application encounters a "database table locked" error. I have tried several things to attempt to work around it but nothing seems to work. What am I doing wrong ?
Here is the code internal to the threads:
procedure testDB(TargetFolder: string);
var
Conn: TFDConnection;
Table: TFDTable;
i: Integer;
begin
randomize;
Conn := TFDConnection.Create(nil);
try
Conn.DriverName := 'SQLite';
Conn.LoginPrompt := false;
Conn.Params.clear;
Conn.Params.Database := TPath.Combine(TargetFolder, 'testDB.sdb');
Conn.Params.Add('DriverID=SQLite');
// all this is the result of several attemp to fix the table locking error. none worked
Conn.Params.Add('LockingMode=Normal');
Conn.Params.Add('Synchronous=Normal');
Conn.UpdateOptions.UpdateMode := TUpdateMode.upWhereAll;
Conn.UpdateOptions.LockWait := True;
Conn.UpdateOptions.LockMode := TFDLockMode.lmPessimistic;
Conn.UpdateOptions.LockPoint := TFDLockPoint.lpImmediate;
Conn.UpdateOptions.AssignedValues := [uvLockMode,uvLockPoint,uvLockWait];
Conn.Open();
Conn.ExecSQL('CREATE TABLE IF NOT EXISTS ''test'' (''ID'' INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT UNIQUE,''data1'' TEXT NOT NULL,''data2'' INTEGER NOT NULL)');
Table := TFDTable.Create(nil);
try
table.Connection := Conn;
while True do
begin
case Trunc(Random(10)) of
0..3:
begin
table.Open('test');
try
if table.Locate('data1', 'name'+intToStr(Trunc(Random(10))),[TLocateOption.loCaseInsensitive]) then
begin
table.Edit;
table.FieldByName('data2').AsInteger := table.FieldByName('data2').AsInteger + 1;
table.Post;
end;
finally
table.close;
end;
end;
4..8:
begin
table.Open('test');
try
i := Trunc(Random(10));
if not table.Locate('data1', 'name'+ i.ToString,[TLocateOption.loCaseInsensitive]) then
begin
table.AppendRecord([null, 'name'+ i.ToString, 0]);
end;
finally
table.close;
end;
end
else
break;
end;
end;
finally
FreeAndNil(Table);
end;
finally
FreeAndNil(Conn);
end;
end;
Thanks to Victoria, I managed to find the right parameters.
Conn := TFDConnection.Create(nil);
try
Conn.DriverName := 'SQLite';
Conn.LoginPrompt := false;
Conn.Params.clear;
Conn.Params.Database := TPath.Combine(TargetFolder, 'testDB.sdb');
Conn.Params.Add('DriverID=SQLite');
Conn.Params.Add('SharedCache=False');
Conn.Params.Add('LockingMode=Normal');
Conn.Params.Add('Synchronous=Normal');
Conn.UpdateOptions.LockWait := True;
Conn.Open();
Thanks again
I have a index by table of records. Can I use member of function to check if a particular records exist in by PLSQL table or not.
DECLARE
TYPE emp_name_rec is RECORD(
firstname varchar2(10),
lastname varchar2(10),
hiredate varchar2(10));
TYPE staff IS TABLE OF emp_name_rec;
members staff := staff();
rec emp_name_rec;
rec1 emp_name_rec;
BEGIN
rec.firstname := 'peter';
rec.lastname := 'dunn';
rec.hiredate := 'x';
rec1.firstname := 'mary';
rec1.lastname := 'dunn';
rec1.hiredate := 'y';
members.extend;
members(members.last) := rec;
members.extend;
members(members.last) := rec1;
if rec member of members then
dbms_output.put_line('Yes its there');
else
dbms_output.put_line('no its not');
end if;
END;
This doesn't work for me, and I get no error, except I get disconnected... It looks like it compiles but it crashes my session.
I get disconnected when I try your code in Oracle 11g. => It doesn't work! as is
From http://docs.oracle.com/cd/B28359_01/appdev.111/b28370/collections.htm ,
You can check whether a collection is null. Comparisons such as greater than, less than, and so on are not allowed. This restriction also applies to implicit comparisons. [...]
Solution is: write your code:
[..] you must define your own notion of what it means for collections to be greater than, less than, and so on, and write one or more functions to examine the collections and their elements and return a true or false value.
Thus you can't use MEMBER OF directly, but define your own cmp_rec function (which is quite obvious there, but not for Oracle it seems):
DECLARE
TYPE emp_name_rec is RECORD(
firstname varchar2(10),
lastname varchar2(10),
hiredate varchar2(10));
TYPE staff IS TABLE OF emp_name_rec;
members staff := staff();
rec emp_name_rec;
rec1 emp_name_rec;
rec_is_found boolean :=false;
function cmp_rec ( a emp_name_rec, b emp_name_rec)
return boolean is
begin
return a.firstname=b.firstname and a.firstname=b.firstname and a.hiredate=b.hiredate;
end cmp_rec;
BEGIN
rec.firstname := 'peter';
rec.lastname := 'dunn';
rec.hiredate := 'x';
rec1.firstname := 'mary';
rec1.lastname := 'dunn';
rec1.hiredate := 'y';
members.extend;
members(members.last) := rec;
members.extend;
members(members.last) := rec1;
-- we must loop over the collection ourselves
-- - I choosed to look it all, but we could break the loop when found
for i in 1..members.count loop
dbms_output.put_line(members(i).firstname);
if cmp_rec(members(i), rec) then
rec_is_found:=true;
end if;
end loop;
-- if rec member of members then
-- becomes
if rec_is_found then
dbms_output.put_line('Yes its there');
else
dbms_output.put_line('no its not');
end if;
END;
I am trying to get 2 variables out of a cursor without using a loop.
CREATE OR REPLACE PROCEDURE NAK.SET_ORDERS(P_ORDER_ID NAK.ORDER_ID%TYPE)
CURSOR C_GET_ORDER_NO IS
SELECT O.ORDER_ID, O.ORDER_MAL FROM NAK.ORDERS O WHERE O.ORDER_ID = P_ORDER_ID;
BEGIN
V_ORDER_SEQ := NULL;
V_ORDER_MAL := NULL;
OPEN C_GET_ORDER_NO;
FETCH C_GET_ORDER_NO VALUES(O.ORDER_ID, O.ORDER_MAL)
INTO (V_ORDER_ID, V_ORDER_MAL);
CLOSE C_GET_ORDER_NO;
END;
Do you really need an explicit cursor? You can simply do this:
CREATE OR REPLACE PROCEDURE NAK.SET_ORDERS(P_ORDER_ID IN NAK.ORDER_ID%TYPE)
V_ORDER_SEQ := NULL;
V_ORDER_MAL := NULL;
BEGIN
SELECT O.ORDER_ID,
O.ORDER_MAL
INTO V_ORDER_SEQ,
V_ORDER_MAL
FROM NAK.ORDERS O
WHERE O.ORDER_ID = P_ORDER_ID;
EXCEPTION
WHEN NO_DATA_FOUND THEN
dbms_output.put_line("No record found");
WHEN TOO_MANY_ROWS THEN
dbms_output.put_line("More than one record found");
WHEN OTHER THEN
dbms_output.put_line("Other problem happend");
END;
Important: this procedure will return a exception if the query doesn't return exactly one record. (ORA-01403: no data found or ORA-00913: too many values)
Alternatively you should be able to make something like:
CREATE OR REPLACE PROCEDURE NAK.SET_ORDERS(P_ORDER_ID NAK.ORDER_ID%TYPE)
CURSOR C_GET_ORDER_NO IS
SELECT O.ORDER_ID,
O.ORDER_MAL
FROM NAK.ORDERS O
WHERE O.ORDER_ID = P_ORDER_ID;
BEGIN
V_ORDER_SEQ := NULL;
V_ORDER_MAL := NULL;
OPEN C_GET_ORDER_NO;
FETCH C_GET_ORDER_NO INTO V_ORDER_ID, V_ORDER_MAL;
CLOSE C_GET_ORDER_NO;
END;
I've been doing my university project and I've stumbled upon problem I cannot solve.
After declaring variable (a pointer to TButton) I cannot use it. I'll copy a part of my code to help show the problem.
So before the implementation I have
private
prevButton : ^TButton;
After that I use OnClick procedure with my buttons
procedure TForm2.MissingButtonClick(Sender : TObject);
var
b : TButton;
begin
b := Sender as TButton;
prevButton := #b;
showmessage(prevButton^.Caption);
end;
And caption is shown no problem. But then when I use my OnClick procedure and try to change labels caption I get access violation.
procedure TForm2.LabelClick(Sender : TObject);
var
l : TLabel;
begin
l := Sender as TLabel;
if prevButton = nil then
showmessage('nil');
if prevButton <> nil then begin
showmessage(prevButton^.Caption);
l.Caption := (prevButton^.Caption);
prevButton^.OnClick := #AlreadyClicked;
prevButton^.Free;
prevButton^ := nil;
prevButton := nil;
refreshLabels(words);
end;
end;
So here is the question, why can't I use my variable in this procedure if I could use it without problem second earlier in other procedure.
Cheers.
Your code is severely flawed in several places.
First, the initial declaration is incorrect. A variable of type TButton is already a pointer, so you don't have to dereference it:
private
prevButton: TButton;
You also don't have to dereference it when assigning to it or using it:
procedure TForm2.MissingButtonClick(Sender : TObject);
begin
prevButton := TButton(Sender);
showmessage(prevButton.Caption);
end;
I'm not sure what your LabelClick event is trying to accomplish. If you free prevButton, you also invalidate the original button to which you pointed it. In other words, if you assigned prevButton := Button1;, and then prevButton.Free;, you've also freed Button1, and I don't think that's your intent. You can safely assign nil to prevButton, but don't free it also:
prevButton := nil; // No longer points to existing object instance
If you want to change the OnClick of an existing button, don't jump through all of those hoops:
// Assign this to the button's OnClick in the Object Inspector
procedure TForm1.Button1FirstClick(Sender: TObject);
begin
ShowMessage('First time I was clicked');
Button1.OnClick := #Button1AfterClicked;
end;
// Create these two in the code editor, and declare it in the
// form declaration, but don't assign it to an event of the button
// in the Object Inspector
procedure TForm1.Button1AfterClick(Sender: TObject);
begin
ShowMessage('Second time clicked');
Button1.OnClick := #Button1MoreClicks;
end;
procedure TForm1.Button1MoreClicks(Sender: TObject);
begin
ShowMessage('Third and later clicks');
end;
I'm trying to bring in a string and assign it , character by character, to a linked list, declared like so:
type lstring is private;
type lstring_node;
type lstring_access is access lstring_node;
type lstring_node is
record
Char : character;
Position : integer;
Next : lstring_access;
end record;
private
type lstring is
record
First : lstring_access := null;
end record;
The function to assign it goes like so:
function toLstring (Input : string) return lstring is
LStr : lstring;
Current : lstring_access;
begin
-- Set the first item of LStr as Current.
-- LStr will from now on inherit the items of Current.
LStr.First := new lstring_node;
Current := LStr.First;
-- Iterate through the string and add it to the lstring.
for I in 1..Input'Length loop
if I /= 1 then
Current := new lstring_node;
end if;
Current.Char := Input(I);
Ada.Text_IO.Put(Current.Char);
Current.Position := I;
Current := Current.Next;
end loop;
-- Return the new lstring.
return LStr;
end toLstring;
I know through debugging that the for loop is working just fine, and the elements are being assigned to Current just fine. But for some reason the items aren't being added to LStr. Do I need to declare something after the for loop to finish it up? I'm under the impression that because Current is assigned to LStr.First, LStr would inherit the rest of the appended list. Am I wrong there?
Thanks
At the end of the loop, you assign Current.Next (which is null at this point) to Current. This is a value copy. Changing the value of Current in the next iteration will not change the value of Next in the previous node. (Mind that Current.Char and Current.Next are implicit dereferences that will actually do Current.all.Char / Next, but Current := new lstring_node is not a dereference, because it changes the value of the reference.)
Instead, you should assign new lstring_node to Next and then advance your Current reference:
for I in Input'Range loop
Current.Char := Input(I);
Ada.Text_IO.Put(Current.Char);
Current.Position := I - Input'First + 1;
if I /= Input'Last then
Current.Next := new lstring_node;
Current := Current.Next;
end if;
end loop;
Note that I changed the loop range (Strings are not required to start at 1) and tweaked the position calculation so you'll end up with a 1-based position index in your list.