Its because it converts it into C constructs. From: "Stefan Mueller" <[EMAIL PROTECTED]> To: "'NZ Borland Developers Group - Delphi List'" <[email protected]> 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 <[email protected]> <mailto:[EMAIL PROTECTED]> <mailto:[EMAIL PROTECTED]>
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: [EMAIL PROTECTED] [mailto:delphi- [EMAIL PROTECTED] 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: [EMAIL PROTECTED] [mailto:delphi- [EMAIL PROTECTED] 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: [EMAIL PROTECTED] [mailto:delphi- [EMAIL PROTECTED] 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: [EMAIL PROTECTED] [mailto:delphi- [EMAIL PROTECTED] 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 [email protected] 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 [email protected] 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 [email protected] 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 [EMAIL PROTECTED] or [EMAIL PROTECTED] ================================================== ==================== _______________________________________________ Delphi mailing list [email protected] http://ns3.123.co.nz/mailman/listinfo/delphi
