I couldn't find the error in my binary search function - binary-search

I don't know why this function I made in pascal only gives the index of the number I'm searching for (n) and doesn't give -1 when it doesnt find it..
(i checked theres no problem with other functions the only problem is that is doesnt print the message 'num is not here' when it doesnt exist) i would also appreciate it if someone points out where my code could have been more efficient.
`
Function binary_search(L : Array Of Integer; n : Integer) : Integer;
Var
i, p, middle, first, last : Integer;
Begin
first := 0;
binary_search := -1;
last := Sizeof(L) Div Sizeof(L[0]);
While (first <= last) Do
Begin
middle := (first + last) Div 2;
If (middle = n) Then
Begin
binary_search := middle;
break;
End;
If (middle < n) Then first := middle +1;
If (middle > n) Then last := middle -1;
End;
End;
Begin
Write('num of elements in array : ');
read(m);
fillup(arr, m);
For i :=0 To m-1 Do
Begin
permutarr[i] := arr[i];
End;
Write('the num youre looking for : ');
read(A);
sort(arr, 1, m);
If (binary_search(arr, A)= -1) Then Writeln('the number isnt here') //this doesnt work
Else
Begin
For i:=0 To m-1 Do
Begin
If (permutarr[i] = binary_search(arr, A)) Then
Begin
Writeln('index : ', i);
break;
End;
End;
End;
End.
`

I am not familiar with your dialect of Pascal (it's been 15 years since I did any serious programming), but I'm quite sure that the chief problem is that
Your code is not looking at the entries of the array L at all!
All the comparisons in the function binary_search only involve the indices, middle, first, last. The first thing I would try is to edit all the comparisons with middle to use L[middle] instead (on lines 11, 16 and 17). Then your code will, at least, actually be looking at the array :-)
Other things:
I'm not 100 per cent sure that your code handles arrays of all lengths correctly (can't test right now). It might happen that first and last never meet. I'm probably wrong about this, because then your code would get stuck in an endless loop.
When I was coding I took care never to assign a value to the function_name, here binary_search prematurely. In other words, I would not be surprised to learn that your function always returns $-1$, because I fully expect the execution of a function to end the instant anything is assigned to it, so here at the line binary_search:=-1;. As I said, my dialect was different, and the recollection is "dated" at best.
Anyway, the problem in bold fits your description of the problematic behavior. You can test my theory by giving A a value that exceeds the length of the array. Then it should be unable to find it with your current code.

Related

Adressing multiple, similar variables/objects in Delphi

I am writing a program that uses many shapes and I need to create a procedure to turn them all white. The shapes in question are named SectorBorder1 to SectorBorder20.
Is there a way to adress the shapes like this or similarly?
SectorBorder[X].brush.color := ClWhite;
Inc(X);
...where X is the number (obviously), instead of having to do:
SectorBorder1.brush.color := ClWhite;
SectorBorder2.brush.color := ClWhite;
...
SectorBorder20.brush.color := ClWhite;
So basically being able to differentiate names through a variable. This is the only way I could think of describing it. (Sorry, could someone also maybe include a better discription?) Any advice would be greatly apreciated.
Use an array
private
SectorBorders: array[1..20] of TShape;
procedure TMyForm.FormCreate(Sender: TObject):
begin
SectorBorders[1] := SectorBorder1;
..
SectorBorders[20] := SectorBorder20;
end;
procedure TMyForm.SetAllToWhite;
var
X: Integer;
begin
for X := Low(SectorBorders) to High(SectorBorders) do
SectorBorders[X].Brush.Color := clWhite;
end;
As an alternative to an array you can use a List. If you use the TList from System.Generics.Containers you can easily pass through all elements of the list.
You use it like this:
interface
uses System.Generics.Collections; // you need this to get the TList, you also need your other refereces of course
...
protected
pShapes: TList<TShape>;
procedure TMyForm.FormCreate(Sender: TObject):
var
nLoop: Integer;
begin
Self.pShapes:=TList<TShape>.Create;
nLoop:=0;
while(nLoop<Self.ComponentCount) do
begin
if(Self.Components[nLoop] is TShape) then
pList.Add(TShape(Self.Components[nLoop]));
Inc(nLoop);
end;
end;
procedure TMyForm.SetAllToWhite;
var
pShape: TShape;
begin
for pShape in Self.pShapes do
pShape.Brush.Color := clWhite;
end;
Choosing whether you want to use an array or a TList will be partially down to preference, but also down to what else you may want to do with the collection of TShapes and how they are managed within the object.
You can use the FindComponent method of the form.
shape:= FindComponent('SectorBorder' + IntToStr(i)) as TShape;
if shape <> nil then
pShape.Brush.Color := clWhite;
See http://docwiki.embarcadero.com/CodeExamples/Sydney/en/FindComponent_(Delphi)

I have some illegal expressions in my code but I don't know how to fix it

This is my code, I have illegal expressions at line 39/45, 40/50, 51/45, 52/36 and fatal: Unexpected end of file at line 64 It would be very helpful if some you will teach me a few things about pascal.
Program PatientRecords;
{Aurthor: Vincent Lopez,
February 16,2015,
Program will read names, age and treatment cost for at least ten patients}
Var {declaration of variables}
Name: array [1..11] of string;
Age: array [1..11] of integer;
T_Cost: array [1..11] of real;
G_Cost: array [1..11] of real;
P_Cost: array [1..11] of real;
Count: integer;
Begin {The program starts here}
{Initialization of variables}
For count := 1 to 10 DO
WriteLn('Welcome to Ministry of Health Cost of Health Care');
WriteLn('Enter the name of the patient');
Readln(name[count]);
Writeln('Enter the age of the patient');
Readln(Age[count]);
Writeln('Enter the treatment cost for patient');
Readln(T_cost[count]);
IF Age>= 65 THEN
Begin
G_cost[count]=T_cost[count]*(100/80);
P_cost[count]=T_cost[count]-G_cost[count];
Writeln('Government will pay = $',G_cost[count]);
Writeln('The patient will pay = $',P_cost[count]);
end
ELSE
Begin
G_cost[count]=T_cost[count]*(100/50);
P_cost=T_cost-G_cost[count];
Writeln('Government will pay = $',G_cost[count]);
Writeln('The patient will pay = $',P_cost[count]);
Readln;
Writeln('Press enter to continue');
Readln;
END; {The program ends here}
One obvious error is the statement
IF Age>= 65 THEN
Age is an array of integers, so this presumably should be
IF Age[count] >= 65 THEN
Another obvious error is that the last line should be 'END.' with a full stop. The 'end;' which you have at the moment is aligned to the 'begin' block following the 'else' branch of the 'if age[count]' statement.
I suggest that first you learn how to run the program for one set of data, then expand it. You will discover that the 'writeln/readln' pair are in the wrong place.

UTL_FILE.WRITE_ERROR when calling utl_file.put in a loop

I have below code in my PL/SQL procedure, which I called in API_XXX.put(it calls utl_file.put) in a while loop. And the l_xmldoc is CLOB from a function of getReportXML, which returns the xml clob.
the code I write to write xml into a file is like:
l_offset := 1;
WHILE (l_offset <= l_length)
LOOP
l_char := dbms_lob.substr(l_xmldoc,1,l_offset);
IF (l_char = to_char(10)) ---I also tried if (l_char=chr(10)) but it did not work
THEN
API_XXXX.new_line(API_XXX.output, 1);
ELSE
API_XXXX.put(fnd_API_XXX.output, l_char);
END IF;
l_offset := l_offset + 1;
END LOOP;
Please note that the API_XXX is the existing package which I am not able to modify, and this api calls fflush in the end of put.
API_XXX.put's part is like below("WHICH" is the first param):
elsif WHICH = API_XXX.OUTPUT then
temp_file := OUT_FNAME;
utl_file.put(F_OUT, BUFF);
utl_file.fflush(F_OUT);
API_XXX.new_line is like(LINES is the number of lines to write):
elsif WHICH = API_XXX.OUTPUT then
temp_file := OUT_FNAME;
utl_file.new_line(F_OUT, LINES);
utl_file.fflush(F_OUT);
I notice a that the put/new_line procedure in my customer's side will sometimes raise UTL_FILE.WRITE_ERROR for unknown reason(maybe due to the l_length is too large(up to 167465)) in the while loop from my customer.
I read Oracle PL/SQL UTL_FILE.PUT buffering
. And I found that this is the same cause, my l_xmldoc is really large and when I loop it, I found that it is without a new line terminator so the buffer is up to 32767 even though I fflush every time.
So, how should I convert the l_xmldoc into a varchar with new line terminator.
PS: I confirmed that my customer is using Oralce 11g
Post the Oracle Version you are using! Or we can just guess around...
Your fflush will not work as you expect - From the documentation:
FFLUSH physically writes pending data to the file identified by the file handle. Normally, data being written to a file is buffered. The FFLUSH procedure forces the buffered data to be written to the file. The data must be terminated with a newline character.
tbone is abolutely right the line TO_CHAR(10) is wrong! Just try SELECT TO_CHAR(10) FROM DUAL; you will get 10 which you then compare to a single character. A single character will never be '10' since 10 has two characters!
Your problem is most likely a buffer-overflow with too large XML-Files, but keep in mind, also other problems on the target system can lead to write_errors, which should be handled.
Solutions
Quick&Dirty: Since you don't seem to care about performance anyways you can just close the file every X byte and reopen it with A for append. So just add to the loop:
IF MOD( l_offset, 32000 ) = 0
THEN
UTL_FILE.FCLOSE( f_out );
UTL_FILE.FOPEN( out_fpath, out_fname, f_out, 'a', 32767 );
END IF;
Use the right tool for the right job: UTL_FILE is not suited for handling complex data. The only usecase for UTL_FILE are small newline-separated lines of text. For everything else you should write RAW bytes! (Which will also allow you porper control over ENCODING, which is currently just mini-vanilly-lucky-guess)
Write a Java-Stored-Procedure with NIO-Filechannels - fast, safe, nice... But be careful, your program might run 10 times as fast!
Just a guess, but instead of "to_char(10)" you might try chr(10) to determine/write a newline. Not sure if this will solve your problem, but sometimes very long lines (without newlines) can cause issues.
For example:
declare
l_clob clob;
l_char char;
begin
l_clob := 'Line 1' || chr(10) || 'Line 2' || chr(10);
for i in 1 .. DBMS_LOB.GETLENGTH(l_clob)
loop
l_char := dbms_lob.substr(l_clob, 1, i);
if (l_char = chr(10)) then
--if (l_char = to_char(10)) then
dbms_output.put_line('Found a newline at position ' || i);
end if;
end loop;
end;
Notice the difference between chr(10) and to_char(10). Easy enough to test if this solves your problem anyway.

SQL table change sequence

I have a table displayed in DBGrid the sort order is based on a Sequence field and I want to be able to move an item up or down one place at a time. I have researched here and cannot find anything exactly like I need.
The problem comes when I disable the Sort order to make a change, the list reverts to the order in which the data was originally entered, so I have lost the item next in line.
This is what I have...
Folder Sequence
----------------
Buttons 1
Thread 2 << Current Row
Cotton 3
Rags 4
On clicking the "MoveDown" button I want...
Folder Sequence
----------------
Buttons 1
Cotton 2
Thread 3 << Current Row
Rags 4
But - when I remove the Sort order on Sequence I get the order I entered the items...
Folder Sequence
----------------
Buttons 1
Cotton 2
Rags 4
Thread 3 << Current Row
So far my attempts are proving pretty cumbersome and involve loading the Rows into a listbox, shuffling them and then writing them back to the Table. Gotta be a better way, but it is beyond my current grasp of SQL.
Can someone please point me in the direction to go.
I don't want to trouble anyone too much if it is a difficult thing to do in SQL, as I can always stay with the listbox approach. If it is relatively simple to an SQL-expert, then I would love to see the SQL text.
Thanks
My solution is based on the TDataSet being sorted by the Sequence field:
MyDataSet.Sort := 'Sequence';
And then swapping the Sequence field between the current row and the Next (down) / Prior (up) records e.g.:
type
TDBMoveRecord = (dbMoveUp, dbMoveDown);
function MoveRecordUpDown(DataSet: TDataSet; const OrderField: string;
const MoveKind: TDBMoveRecord): Boolean;
var
I, J: Integer;
BmStr: TBookmarkStr;
begin
Result := False;
with DataSet do
try
DisableControls;
J := -1;
I := FieldByName(OrderField).AsInteger;
BmStr := DataSet.Bookmark;
try
case MoveKind of
dbMoveUp: Prior;
dbMoveDown: Next;
end;
if ((MoveKind = dbMoveUp) and BOF) or ((MoveKind = dbMoveDown) and EOF) then
begin
Beep;
SysUtils.Abort;
end
else
begin
J := DataSet.FieldByName(OrderField).AsInteger;
Edit;
FieldByName(OrderField).AsInteger := I;
Post;
end;
finally
Bookmark := BmStr;
if (J <> -1) then
begin
Edit;
FieldByName(OrderField).AsInteger := J;
Post;
Result := True;
end;
end;
finally
EnableControls;
end;
end;
Usage:
MoveRecordUpDown(MyDataSet, 'Sequence', dbMoveDown);
// or
MoveRecordUpDown(MyDataSet, 'Sequence', dbMoveUp);
If i understand right, you want to find "next sequence item"?
May be you may do something like "get first min value, grater then X"?
In this way, you can pass prev. Row sequence value.

Variable structure for database results

A lot of times when we query the database, we just need one column with varchar.
So I've made a nice function for querying the database and putting the results in a stringlist:
function Getdatatostringlist(sqlcomponent, sqlquery: string): TStringlist;
What I'm looking for now is basically the same function but for results with multiple columns where you don't know in advance what type the data is, be it varchar, int, datetime.
What kind of datastructure would be good to use here.
The reason I want this is that I try not to work on open datasets. I like much more to fetch all results into a temporary structure, close the dataset and work on the results.
After Kobiks reply about using in Memory datasets I came up with the following, it's fast put together to test the concept:
procedure TForm1.Button2Click(Sender: TObject);
var
MyDataSet : TAdoDataSet;
begin
MyDataSet := GetDataToDataSet('SELECT naam FROM user WHERE userid = 1', ADOConnection1);
try
Form1.Caption := MyDataSet.FieldByName('naam').AsString;
finally
MyDataSet.free;
end;
end;
function TForm1.GetDataToDataSet(sSql: string; AdoConnection: TADOConnection): TAdoDataSet;
begin
Result := TAdoDataSet.Create(nil);
Result.LockType := ltBatchOptimistic;
Result.Connection := AdoConnection;
Result.CommandText := sSql;
Result.Open;
Result.Connection := nil;
end;
I think this is something to build on.
You should use any disconnected in-memory TDataSet descendant, such as TClientDataSet.
Do not attempt to re-invent the wheel by storing a record-set in some new "Variant" structure. A TClientDataSet already contains all features you need to manipulate a "temporary" data structure.
Here is how you create a TClientDataSet structure:
cds.FieldDefs.Add('id', ftInteger);
cds.FieldDefs.Add('name', ftString, 100);
// ...
// create it
cds.CreateDataSet;
// add some data records
cds.AppendRecord([1, 'Foo']);
cds.AppendRecord([2, 'Bar']);
Many TDataSets has an ability to be used as an in-memory (client) datasets depending on the provider and LockType, for example a TADODataSet with LockType=ltBatchOptimistic could fetch results-set from the server, and then remain disconnected.
For exchanging Data with Excel this structure is usefull, might be useful for other purposes.
Function GetDatasetasDynArray(Ads: TDataset; WithHeader: Boolean = true): Variant;
// 20130118 by Thomas Wassermann
var
i, x, y: Integer;
Fields: Array of Integer;
begin
x := 0;
y := Ads.RecordCount;
if WithHeader then
inc(y);
SetLength(Fields, Ads.FieldCount);
for i := 0 to Ads.FieldCount - 1 do
if Ads.Fields[i].Visible then
begin
Fields[x] := i;
inc(x);
end;
SetLength(Fields, x);
Result := VarArrayCreate([0, y - 1 , 0, length(Fields) - 1], VarVariant);
y := 0;
if WithHeader then
begin
for i := Low(Fields) to High(Fields) do
begin
Result[y, i] := Ads.Fields[Fields[i]].DisplayLabel;
end;
inc(y);
end;
try
Ads.DisableControls;
Ads.First;
while not Ads.EOF do
begin
for i := Low(Fields) to High(Fields) do
begin
Result[y, i] := Ads.Fields[Fields[i]].Value;
end;
Ads.Next;
inc(y);
end;
finally
Ads.EnableControls;
end;
end;
procedure TForm1.Button1Click(Sender: TObject);
var
DynArray:Variant;
begin
DynArray := GetDatasetasDynArray(Adodataset1,true);
//DynArray[0,x] Header or First row
//DynArray[1,x] First row or SecondRow
Excel.Range.Value := DynArray;
end;
Why don't you like working with open datasets? They usually do not block the server. Copying the data from the dataset to whatever you want is extra overhead which is most likely not necessary.
A dataset provides exactly the functionality you want: A matrix with variable columns and rows.
EDIT: However, if you have iterate through the dataset often, you should consider creating a class holding the relevant information and then copy the data into a generic list, dictionary, tree or whatever you need as fast lookup structure.
Of course you could think of building something smart which can be as flexible as a dataset but: The more general things get, the poorer the performance (usually).