[DUG] Discussion & Donation

Kyley Harris kyley at harrissoftware.com
Mon May 15 14:44:03 NZST 2006


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 breaking existing code.

 

4) Have you considered RegisterIntegerConsts() 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 about streaming numbers to XML

     

Todd.

 

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

	From: Kyley Harris <mailto:kyley at harrissoftware.com>  

	To: NZ Borland Developers Group - Delphi List
<mailto:delphi at ns3.123.co.nz>  

	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 to complication elsewhere

	 

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

	 

	the second condition is automatically satisfied 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 time what "messages"
are being broadcast/handled. This problem can of 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 the use of THashedstringlist could mitigate this further.

	 

	Todd.

	 

	 

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

		From: Kyley Harris <mailto:kyley at harrissoftware.com>  

		To: NZ Borland Developers Group - Delphi List
<mailto:delphi at ns3.123.co.nz>  

		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

-------------- next part --------------
An HTML attachment was scrubbed...
URL: http://ns3.123.co.nz/pipermail/delphi/attachments/20060515/93b09f23/attachment-0001.html


More information about the Delphi mailing list