[DUG] Discussion & Donation

Rohit Gupta rohit at cfl.co.nz
Wed May 17 09:37:55 NZST 2006


Its because it converts it into C constructs.

From:	"Stefan Mueller" <muellers at orcl-toolbox.com>
To:	"'NZ Borland Developers Group - Delphi List'" <delphi at ns3.123.co.nz>
Subject:	RE: [DUG] Discussion & Donation
Date sent:	Tue, 16 May 2006 14:14:35 +1200
Organization:	ORCL Toolbox Ltd.
Send reply to:	NZ Borland Developers Group - Delphi List <delphi at ns3.123.co.nz>
	<mailto:delphi-request at ns3.123.co.nz?subject=unsubscribe>
	<mailto:delphi-request at ns3.123.co.nz?subject=subscribe>


Nhmm .. surprising. The "for"loop should evaluate lower/upper bounds only 
once .. while the "while"statement always has to evaluate the expression for 
each iteration. This doesn´t make sense ;-/

Anyway .. a bit busy with other things rather than exploring the "why". A 
russian guy cracked my licenseprotection (not that hard) last weekend and 
is offering the crack to my customers on my own support forums ( 
http://www.orcl-toolbox.com/forum_new2/forum_posts.asp?TID=341&PN=1 
). Still trying to get on the good site with him(talking on email) but patience is 
starting to run out.

Kind Regards,
Stefan Mueller 




From: delphi-bounces at ns3.123.co.nz [mailto:delphi-
bounces at ns3.123.co.nz] On Behalf Of Todd Martin
Sent: Tuesday, 16 May 2006 1:42 p.m.
To: NZ Borland Developers Group - Delphi List
Subject: Re: [DUG] Discussion & Donation


Kylie 

Ire-ran your timed loopwith aminor change to IndexOfTodd(). I removed 
the CurrentName variable.



function IndexOf_todd(AName : String; AStringArray : array of string) : 
Integer;
var
 NameIndex, FoundIndex : Integer;
// CurrentName : String;
begin
 NameIndex := 0;
 FoundIndex := 0;
 while (NameIndex <length(AStringArray)) and (FoundIndex = 0) do
 begin
// CurrentName := AStringArray[NameIndex];
// if SameText(AName,CurrentName) then
 if SameText(AName,AStringArray[NameIndex]) then
 begin
 FoundIndex := NameIndex;
 end;



 inc(NameIndex);
 end;



 Result := FoundIndex;
end;


Now compare the results



Button 1

Test Todd 1101ms

Test Stef 1142ms

Test StringList 921ms



Button 2

Test Todd 2553ms

Test Stef 2534ms

Test StringList 230ms



So Stefan's break statement is slower in the first case and adds a negligible 
time saving in the second case. Personally I hate break and continue 
statements, so I never use them.

The string list still looks impressive though.



But look what happens when the list sizeis increasedfrom 200 to 2000 
items in button 2 click.



--using TStringList



Test Todd 23023ms

Test Stef 23554ms

Test StringList 361ms



--using THashedStringList



Test Todd 22703ms

Test Stef 22632ms

Test StringList 110ms



----- Original Message ----- 

From: Kyley Harris 

To: NZ Borland Developers Group - Delphi List 

Sent: Monday, May 15, 2006 2:44 PM

Subject: RE: [DUG] Discussion & Donation



Hmm your comment that doing it that way intrigued me enough to test it. 
Seeing as I like to make things quicker. You were right in certain cases. 

Given the following code.

TEST 1. High iterations on a small set 5 elements

Todds loop took 150ms, 
Stefans loop took 100ms
StringList took 108ms

But given test 2 high itereations on a larger list. 200 elements Which is more 
likely when dealing with messaging

Todds took 4125 ms
Stefans took 2641ms
Stringlist took 297ms

So I think I will stick with sorted stringlists. Because then you can also attach 
a data object to it for retrieving the value, rather than relying on the index 
position being the value, which I find to be a very bad idea

unit Unit1;

interface

uses
 Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, 
Forms,
 Dialogs, StdCtrls;

type
 TForm1 = class(TForm)
 Button1: TButton;
 Button2: TButton;
 procedure Button1Click(Sender: TObject);
 procedure Button2Click(Sender: TObject);
 private
 { Private declarations }
 public
 { Public declarations }
 end;

var
 Form1: TForm1;

implementation

uses
 uHssStopWatch;

{$R *.dfm}

function IndexOf_stef(AName : String; AStringArray : array of string) : 
Integer;
var
 i : Integer;
begin



 result := -1;



 for i := 0 to high(AStringArray) do
 begin

 if SameText(AName, AStringArray[i]) then
 begin

result := i;

break;

 end;

 end;

end;


function IndexOf_todd(AName : String; AStringArray : array of string) : 
Integer;
var
 NameIndex, FoundIndex : Integer;
 CurrentName : String;
begin
 NameIndex := 0;
 FoundIndex := 0;
 while (NameIndex <length(AStringArray)) and (FoundIndex = 0) do
 begin
 CurrentName := AStringArray[NameIndex];
 if SameText(AName,CurrentName) then
 begin
 FoundIndex := NameIndex;
 end;



 inc(NameIndex);
 end;



 Result := FoundIndex;
end;






procedure TForm1.Button1Click(Sender: TObject);
var
 s:TStringList;
 data:TStringList;
 a1:array of string;
 i:integer;
begin
 initialize(a1);
 s := TStringList.Create;
 setlength(a1,5);
 a1[0] := 'test';
 a1[1] := 'testa';
 a1[2] := 'testb';
 a1[3] := 'testc';
 a1[4] := 'testd';

 s.CaseSensitive := false;
 s.Sorted := true;

 s.add(a1[0]);
 s.add(a1[1]);
 s.add(a1[2]);
 s.add(a1[3]);
 s.add(a1[4]);

 with THssStopWatch.Start('Test Todd') do
 try
 for i := 0 to 1000000 do
 case i mod 5 of
0: Assert(SameText('test',a1[ IndexOf_todd('TEST',a1)]));
1: Assert(SameText('testa',a1[IndexOf_todd('TESTa',a1)]));
2: Assert(SameText('testb',a1[IndexOf_todd('TESTb',a1)]));
3: Assert(SameText('testc',a1[IndexOf_todd('TESTc',a1)]));
4: Assert(SameText('testd',a1[IndexOf_todd('TESTd',a1)]));
 end;
 finally
 Free;
 end;

 with THssStopWatch.Start('Test Stef') do
 try
 for i := 0 to 1000000 do
 case i mod 5 of
0: Assert(SameText('test',a1[ IndexOf_stef('TEST',a1)]));
1: Assert(SameText('testa',a1[IndexOf_stef('TESTa',a1)]));
2: Assert(SameText('testb',a1[IndexOf_stef('TESTb',a1)]));
3: Assert(SameText('testc',a1[IndexOf_stef('TESTc',a1)]));
4: Assert(SameText('testd',a1[IndexOf_stef('TESTd',a1)]));
 end;
 finally
 Free;
 end;


 with THssStopWatch.Start('Test StringList') do
 try
 for i := 0 to 1000000 do
 case i mod 5 of
0: Assert(SameText('test',s[s.IndexOf('test')]));
1: Assert(SameText('testa',s[s.IndexOf('testa')]));
2: Assert(SameText('testb',s[s.IndexOf('testb')]));
3: Assert(SameText('testc',s[s.IndexOf('testc')]));
4: Assert(SameText('testd',s[s.IndexOf('testd')]));
 end;
 finally
 Free;
 end;


end;

procedure TForm1.Button2Click(Sender: TObject);
var
 s:TStringList;
 data:TStringList;
 a1:array of string;
 i,x:integer;
begin
 initialize(a1);
 s := TStringList.Create;
 setlength(a1,200);
 s.CaseSensitive := false;
 s.Sorted := true;
 for i := 0 to 199 do
 begin
 a1[i] := 'test'+inttostr(i);
 s.add(a1[i]);
 end;


 with THssStopWatch.Start('Test Todd') do
 try
 for i := 0 to 100000 do
 x := IndexOf_Todd(a1[i mod 200],a1);
 finally
 Free;
 end;

 with THssStopWatch.Start('Test Stef') do
 try
 for i := 0 to 100000 do
 x := IndexOf_Stef(a1[i mod 200],a1);
 finally
 Free;
 end;


 with THssStopWatch.Start('Test StringList') do
 try
 for i := 0 to 100000 do
 x := s.IndexOf(a1[i mod 200]);
 finally
 Free;
 end;


end;

end..





From: delphi-bounces at ns3.123.co.nz [mailto:delphi-
bounces at ns3.123.co.nz] On Behalf Of Stefan Mueller
Sent: Monday, 15 May 2006 1:36 p.m.
To: 'NZ Borland Developers Group - Delphi List'
Subject: RE: [DUG] Discussion & Donation


Hi Todd,

Here is how I usually write such scanning loops. Should be a bit faster than 
yours and less confusing (less juggling around with variables) ... and less 
error prone (raises exceptions if AName can´t be found):

function IndexOf(AName : String; AStringArray : array of string) : Integer;
var
 i : Integer;
begin

 result := -1;

 for i := 0 to high(AStringArray) do
 begin
if SameText(AName, AStringArray[i]) then
begin
 result := i;
 break;
 end;
 end;

 if result := -1 then 
 raise exception.create(`String ´+ AName +´ not found in array!´);
end;


Regards,
Stefan



From: delphi-bounces at ns3.123.co.nz [mailto:delphi-
bounces at ns3.123.co.nz] On Behalf Of Todd Martin
Sent: Monday, 15 May 2006 12:55 p.m.
To: NZ Borland Developers Group - Delphi List
Subject: Re: [DUG] Discussion & Donation


3) Okay. Good point. I didn't pick up on this, as you hadn't marked the 
procedures as virtual, but I guess that can always be added later without 
breakingexisting code.



4) Have you consideredRegisterIntegerConsts() for converting message 
integers to text?

Or failing that I often use something like this



type

TMessageType = (mtUnknown,mtChange,mtSave,mtLoad)



const

 MessageTypeToText : array[TMessageType ] of string = 
('Unknown','Change','Save','Load'); 



That way, whenever I add an enumeration, I have to also add an equivalent 
string description.

To convert the text back to an enumerated type, I just use a standard 
procedure to find the index of the text in the array. By making the first 
enumeration unknown/default I always get a meaningful result when casting 
the integer back to a type.



Type := TMessageType(IndexOf(MessageText,MessageTypeToText);



function IndexOf(AName : String; AStringArray : array of string) : Integer;
var
 NameIndex, FoundIndex : Integer;
 CurrentName : String;
begin
 NameIndex := 0;
 FoundIndex := 0;
 while (NameIndex < length(AStringArray)) and (FoundIndex = 0) do
 begin
 CurrentName := AStringArray[NameIndex];
 if SameText(AName,CurrentName) then
 begin
 FoundIndex := NameIndex;
 end;



 inc(NameIndex);
 end;



 Result := FoundIndex;
end;





However, I see your point aboutstreamingnumbers toXML

  

Todd.



----- Original Message ----- 

From: Kyley Harris 

To: NZ Borland Developers Group - Delphi List 

Sent: Monday, May 15, 2006 11:05 AM

Subject: RE: [DUG] Discussion & Donation



Thank.
Point 1/ Fair enough
Point 2/ correct . I had written that bit early on before I realized I could break 
it down correctly with TMethdo.code and TMethod.Data and I haven´t fully 
refactored it yet. I´ve removed it now.
Point 3/ Because you cant do anything with a bunch of procedures. Ie it is 
not portable. Class functions can be overridden. You don´t need to use 
TNotifier.AddListener,
You could have for example add a meta class and a classvariable to the 
global, Var NotifierClass:TNotiferClass = TNotifer. I then use this for 
accessing the functions and down the track in some cases or applications 
you may wish to modify how it works, without effecting All the other apps by 
simply changing the class pointer. static procedures do not lend themselves 
to upgrading software without breaking old software and this is one of the 
most powerful uses of static classes as libraries that many people in Delphi 
don´t take advantage of. 
Point 4. I use constants, so I don´t have typos in message names (except 
for demos). And I use SameText, so they are not case-sensitve. I do this 
purely because I prefer debugging meaningful strings rather than integers. It 
also makes It easier when dealing with storage of types etc in human 
readable XML. So that´s why I do it. Many books use integers. I just don´t 
like it. Also. A lot of my infrastructor permits streaming of the entire 
message, and the data object over TCP as part of the notification. I prefer 
unknown apps receiving this to get a meaningful 
`CLIENT_OBJECT_UPDATED´ rather than 3 ;)

The StringList is indexed, and its very fast, I´ve tested with thousands of 
binds with no real time effect. The work done by listeners is the time issue. 
Not the lookup.


I used to have an observer and observed class system too. And it was really 
annoying when you had to make changes. I even wrote a plugin for the IDE 
to convert an object into a listener etc by inserting all the code for me. So 
that took the hassle out. But I prefer the decoupled method. 

Thanks for the feedback. 



From: delphi-bounces at ns3.123.co.nz [mailto:delphi-
bounces at ns3.123.co.nz] On Behalf Of Todd Martin
Sent: Monday, 15 May 2006 10:38 a.m.
To: NZ Borland Developers Group - Delphi List
Subject: Re: [DUG] Discussion & Donation


Well you said you wanted feedback, so I hope this isn't too picky.



1) I find the inconsistent use of variable prefixes a bit annoying. I tend to 
stick with

F- class field

A - method parameter

P - pointer

and no prefixes for class properties or local variables.



2)TListenerItem.FListener is redundant, since the information it contains is 
already held in TListenerItem.FBindMethod

It could have been implemented as a function, but I don't see why it is 
needed.

As usual, redundancy leads tocomplication elsewhere



eg. if (MethodEquals(TMethod(Items[i].FBindMethod) ,TMethod(ABinder))) 
and (Items[i].FListener = AListener) then



the second condition is automaticallysatisfied by the first condition.



3) Why define a TNotifier class when all its methods are static? They could 
be defined as simple procedures/functions in the unit.



4) I'm not keen on the idea of passing a string parameter to identify the 
message type, for the simple reason that spelling mistakes can arise and 
you're never really sure at compile timewhat "messages" are being 
broadcast/handled. This problem canof course be ameliorated through the 
use of constants, but I prefer the use of an enumerated type.



Having said all that. I like the loose coupling of your solution. I have 
implemented the observer pattern previously with the lists maintained 
internally by the objects involved, so removing the need to inherit from a 
particular class is a great improvement. I hadn't considered that option 
before. Of course you do pay a miniscule performance penalty in having to 
find the correct list for the object. Perhaps theuse of THashedstringlist 
could mitigate this further.



Todd.





----- Original Message ----- 

From: Kyley Harris 

To: NZ Borland Developers Group - Delphi List 

Sent: Wednesday, May 10, 2006 3:36 PM

Subject: [DUG] Discussion & Donation



In an attempt to create some more interesting discussion, other than talking 
about the crud IDE issues, I am donating some useful code for discussion, 
rather than for help. Feel free to keep/use the code if you want to. Be mean 
if you feel the need (but if your personal then watch out ;) Hopefully this may 
introduce a discussion where people learn something, or are able to 
contribute some interesting ideas/information about the topic (which is 
object notification and observation).

In the attached zip is a sample application showing a basic, but horrible use 
of the class in uNotifier.pas. TNotifer is a class I wrote a long while back 
which loosely follows an observer pattern for allowing one-to-many 
observations between an object, and a bunch of interested object. 

The main difference this class has between many observer patterns is that 
you do not have to modify existing classes, or sub-class anything, or 
instantiate anything in your existing objects. The TNotifier handles all the 
bindings, you just need to implement one listening event on each listening 
object.

The sample app is a corny app that makes child forms (button click) update 
a memo based on changes to the memo on the main form. God help me if I 
ever did anything as potentially unthread safe as that in a real app.

Fun fun fun.... (I hope the app compiles. You never know with these tricky 
little sample apps)


_______________________________________________
Delphi mailing list
Delphi at ns3.123.co.nz
http://ns3.123.co.nz/mailman/listinfo/delphi


Internal Virus Database is out-of-date.
Checked by AVG Free Edition.
Version: 7.1.385 / Virus Database: 268.5.1/327 - Release Date: 
28/04/2006


_______________________________________________
Delphi mailing list
Delphi at ns3.123.co.nz
http://ns3.123.co.nz/mailman/listinfo/delphi


Internal Virus Database is out-of-date.
Checked by AVG Free Edition.
Version: 7.1.385 / Virus Database: 268.5.1/327 - Release Date: 
28/04/2006


_______________________________________________
Delphi mailing list
Delphi at ns3.123.co.nz
http://ns3.123.co.nz/mailman/listinfo/delphi


Internal Virus Database is out-of-date.
Checked by AVG Free Edition.
Version: 7.1.385 / Virus Database: 268.5.1/327 - Release Date: 
28/04/2006
Regards

Rohit

==================================================
====================
CFL - Computer Fanatics Ltd.  21 Barry's Point Road, AKL, New 
Zealand
PH    (649) 489-2280 
FX    (649) 489-2290
email rohit at cfl.co.nz  or  r.gupta at xtra.co.nz
==================================================
====================




More information about the Delphi mailing list