Below is to find same customer and if he is in list, the number add one. If he is not in the list, just add him in the list.
I use Search function to do this, but failed and generated incorrect records. It can not find the customer or the right number of customers. But if I use For..loop to iterate the list, it does well and can find the customer and add new customer in that for..loop search procedure. (I did not paste for ..loop search procedrue here).
Another problem is that there is no difference between setting list.sorted true and false. It seems Search function is not correct. This search function is from an example of delphi textbook.
The below is with Delphi 7.
Thank you.
Procedure Form1.create;
begin
list:=Tstringlist.create;
list.sorted:=true; // Search function will generate exactly Same and Incorrect
//records no matter list.sorted is set true or false.
//list.duplicates:=dupignore;
end;
Procedure addcustomer;
var
customer: string;
begin
//... Here is the code part that P1 is created as regular expression (omitted)
while p1.MatchAgain do begin //p1 is regular expression
customer:=p1.MatchedExpression; // try to match customer name.
if (search(customer)=false) then begin //this line output wrong number
// if (forloopsearch(customer)=false) then begin // this forloopsearch is ok
list.Add(customer+'=1');
end;
allcustomer:=allcustomer+1; // global allcustomer is integer to count all customers
end;
Function Tform1.search(customer: string): boolean;
var
fre:string;
num:integer;
L:integer;
R:integer;
M: Integer;
CompareResult: Integer;
found: boolean;
begin
result:=false;
found:=false;
L := 0;
R := List.Count - 1;
while (L <= R) and ( not found ) do
begin
M := (L + R) div 2;
CompareResult := Comparetext(list.Names[m]), customer);
if (compareresult=0) then
begin
fre:=list.ValueFromIndex [m];
num:=strtoint(fre);
num:=num+1;
list.ValueFromIndex[m]:=inttostr(num);
Found := True;
Result := true;
exit;
end
else if compareresult > 0 then
r := m - 1
else
l := m + 1;
end;
end;
Edit:
Thank you all.
In order to clarify the problem, I copy the test code from my computer here and I am sorry for my redundancy.
Below is the name file that contains 3 persons' names (totally, 45 johns, 45 maries, and 45 erics).
mary, mary, mary, john, john, john, eric, eric, eric,
mary, mary, mary, john, john, john, eric, eric, eric,
mary, mary, mary, john, john, john, eric, eric, eric,
mary, mary, mary, john, john, john, eric, eric, eric,
mary, mary, mary, john, john, john, eric, eric, eric,
mary, mary, mary, john, john, john, eric, eric, eric,
mary, mary, mary, john, john, john, eric, eric, eric,
mary, mary, mary, john, john, john, eric, eric, eric,
mary, mary, mary, john, john, john, eric, eric, eric,
mary, mary, mary, john, john, john, eric, eric, eric,
mary, mary, mary, john, john, john, eric, eric, eric,
mary, mary, mary, john, john, john, eric, eric, eric,
mary, mary, mary, john, john, john, eric, eric, eric,
mary, mary, mary, john, john, john, eric, eric, eric,
mary, mary, mary, john, john, john, eric, eric, eric,
The forloopsearch will generate the following. Below is a copy result that reads key and value and add into a listview (using add method of listview. items). This result is correct and is what I want.
mary 45
john 45
eric 45
Below is forloopsearch.
Function Tform1.forloopsearch(customer: string): boolean;
var
fre:string;
i: integer;
aname:string;
num:integer;
begin
result:=false;
for i:=0 to list.count-1 do begin
aname:=list.names[i];
if aname=customer then
begin
fre:=list.ValueFromIndex [i];
num:=strtoint(fre);
num:=num+1;
list.ValueFromIndex[i]:=inttostr(num);
Result := true;
End;
end;
end;
The forloopsearch function output above is correct and it modifies values of keys in iteration.
The Search function will output the following. It is not correct and is not what I want. It also modifies values of keys in loop, but output is obviously wrong.
In both cases, list.sorted is set true when form is created as shown above. They both use same procedure to iterate the list and copy key and value in order into a listview (by using add method). Below is Search function result.
mary 3
john 2
john 1
eric 1
eric 4
eric 40
mary 3
john 1
john 40
john 1
mary 39开发者_StackOverflow中文版
How to solve it. Thank you again.
(The question initially was a failing binary search on a StringList
, and the reason turned out to be that the list was not sorted while the search was executed. See comments on the question.)
What I would do would be to keep the stringlist sorted at all times, and instead of a 'Name=Value' pair to keep the frequency of occurrence, use the
Objects
property for that. This would help avoid the Integer<->String conversion the code originally had and can be achieved with sth like: list.Objects[i] := Pointer(f + 1);
Use the stringlist's Find
method to find out if an entry already existed or not. Find
executes a binary search itself, you don't need 3rd party code to do that. If the item exists already, just increment the frequency. If the item does not exist, the Index
parameter that is set by the function is the place where the item should be inserted (see the documentation link above). Call the protected method InsertItem
to insert the new item. The reason for the protected call is to avoid the find method run again needlessly (see TStringList.AddObject
function in 'classes.pas'). This would be achieved with sth. like:
type
TSL = class(TStringList);
var
i: Integer;
s: string;
begin
s := 'Joe';
if list.Find(s, i) then
list.Objects[i] := Pointer(Integer(list.Objects[i]) + 1)
else
TSL(list).InsertItem(i, s, Pointer(1));
end;
But you really need to profile and test alternatives, otherwise it is all based on guesswork.
edit: I would like to note that if there's no adding/deleting items outside of this procedure, the stringlist's sorted property could be kept un-set, and then the protected hack would be unnecessary as one could use the InsertObject
method then.
Perhaps this does what you need
var
sl1, sl2: TStringList;
sPrev: string;
I: Integer;
begin
sl1 := TStringList.Create;
sl2 := TStringList.Create;
try
sl1.CommaText := 'mary, mary, eric, eric, john, mary, john, john, eric, eric';
sl1.Sorted := True;
sPrev := '';
for I := 0 to sl1.Count - 1 do
begin
if sl1[I] <> sPrev then
sl2.Add(sl1[I]+'=1')
else
sl2.ValueFromIndex[sl2.Count-1] := IntToStr(StrToInt(sl2.ValueFromIndex[sl2.Count-1])+1);
sPrev := sl1[I];
end;
for I := 0 to sl2.Count - 1 do
ListView1.AddItem(sl2[I], nil);
finally
sl1.Free;
sl2.Free;
end;
end;
精彩评论