Recent

Author Topic: Associating a shortstring with a unique procedure  (Read 2546 times)

Curt Carpenter

  • Sr. Member
  • ****
  • Posts: 396
Associating a shortstring with a unique procedure
« on: June 06, 2020, 06:23:04 am »
I have a set of about 256 strings, each of from 1 to 5 characters in length.  Each string is associated with a unique procedure.  When I isolate one of those strings, surrounded by whitespace, in a "source text," I want to execute the procedure associated with that string.

My general question, for those with more experience in FPC and Lazarus than myself, is how I should best approach this problem. A large "case" statement would do the job of associating a string to its unique procedure, but I suspect that that isn't a very efficient approach. 

Are there any classes in the FPC libraries that would be better suited to solving this problem?  Something like proven code for doing an efficient tree search on a collection of strings?

(Hope my problem description is clear enough.  I've been using case statements to do this, but it seems too spaghetti-like, and I feel like there ought to be a better way.)

Suggestions/pointers appreciated.


TRon

  • Hero Member
  • *****
  • Posts: 2435
Re: Associating a shortstring with a unique procedure
« Reply #1 on: June 06, 2020, 06:52:29 am »
Hi Curt,

Why not keep it simple and use a (sorted) TStringList ?

julkas

  • Guest
Re: Associating a shortstring with a unique procedure
« Reply #2 on: June 06, 2020, 07:02:30 am »
« Last Edit: June 06, 2020, 07:48:18 am by julkas »

Thaddy

  • Hero Member
  • *****
  • Posts: 14213
  • Probably until I exterminate Putin.
Re: Associating a shortstring with a unique procedure
« Reply #3 on: June 06, 2020, 10:06:28 am »
Code: Pascal  [Select][+][-]
  1. TDictionary<Shortstring, TProcedure>
Specialize a type, not a var.

howardpc

  • Hero Member
  • *****
  • Posts: 4144
Re: Associating a shortstring with a unique procedure
« Reply #4 on: June 06, 2020, 10:21:30 am »
Several good approaches already suggested. Here's another:
Code: Pascal  [Select][+][-]
  1. program ExecuteProcedureFromString;
  2.  
  3. {$mode objfpc}{$H+}
  4.  
  5. uses
  6.   SysUtils;
  7.  
  8. const
  9.   sourceText = '  three    one   two ';
  10.  
  11. type
  12.  
  13.   TStrProc = procedure(const aStr: String);
  14.  
  15.   TStrProcArray = array of TStrProc;
  16.  
  17.   procedure One(const aString: String);
  18.   begin
  19.     WriteLn('#1 This is procedure ',aString);
  20.   end;
  21.  
  22.   procedure Two(const aString: String);
  23.   begin
  24.     WriteLn('#2 This is procedure ',aString);
  25.   end;
  26.  
  27.   procedure Three(const aString: String);
  28.   begin
  29.     WriteLn('#3 This is procedure ',aString);
  30.   end;
  31.  
  32.   function ParseSource(aSource: String): TStringArray;
  33.   var
  34.     s: String;
  35.   begin
  36.     SetLength(Result{%H-}, 0);
  37.     for s in Trim(aSource).Split([' ']) do
  38.       begin
  39.         if s <> '' then
  40.           Insert(s, Result, Length(Result));
  41.       end;
  42.   end;
  43.  
  44.  
  45. var
  46.   pta: TStrProcArray;
  47.   s, lc: String;
  48. begin
  49.   pta := [@One, @Two, @Three];
  50.   for s in ParseSource(sourceText) do
  51.     begin
  52.       lc := LowerCase(s);
  53.       case lc of
  54.         'one':   pta[0](lc);
  55.         'two':   pta[1](lc);
  56.         'three': pta[2](lc);
  57.         else
  58.           WriteLn('unknown procedure');
  59.       end;
  60.     end;
  61.   ReadLn;
  62. end.

PascalDragon

  • Hero Member
  • *****
  • Posts: 5446
  • Compiler Developer
Re: Associating a shortstring with a unique procedure
« Reply #5 on: June 06, 2020, 10:45:24 am »
Based on what howardpc had suggested, but using a constant function pointer table instead of a case-statement (Note: due to the dynamic array constant and the use of Insert on the result of ParseSource at least FPC 3.2 is required, but that can easily be adjusted to work with 3.0.4 as well):

Code: Pascal  [Select][+][-]
  1. program ExecuteProcedureFromString;
  2.  
  3. {$mode objfpc}{$H+}
  4.  
  5. uses
  6.   SysUtils;
  7.  
  8. const
  9.   sourceText = '  three    one   two ';
  10.  
  11. type
  12.  
  13.   TStrProc = procedure(const aStr: String);
  14.  
  15.   TStrProcEntry = record
  16.     Identifier: String;
  17.     Proc: TStrProc;
  18.   end;
  19.  
  20.   procedure One(const aString: String);
  21.   begin
  22.     WriteLn('#1 This is procedure ',aString);
  23.   end;
  24.  
  25.   procedure Two(const aString: String);
  26.   begin
  27.     WriteLn('#2 This is procedure ',aString);
  28.   end;
  29.  
  30.   procedure Three(const aString: String);
  31.   begin
  32.     WriteLn('#3 This is procedure ',aString);
  33.   end;
  34.  
  35.   function ParseSource(aSource: String): TStringArray;
  36.   var
  37.     s: String;
  38.   begin
  39.     SetLength(Result{%H-}, 0);
  40.     for s in Trim(aSource).Split([' ']) do
  41.       begin
  42.         if s <> '' then
  43.           Insert(s, Result, Length(Result));
  44.       end;
  45.   end;
  46.  
  47. const
  48.   Procs: array of TStrProcEntry = (
  49.     (Identifier: 'one'; Proc: @One),
  50.     (Identifier: 'two'; Proc: @Two),
  51.     (Identifier: 'three'; Proc: @Three)
  52.   );
  53.  
  54. var
  55.   found: Boolean;
  56.   s, lc: String;
  57.   entry: TStrProcEntry;
  58. begin
  59.   for s in ParseSource(sourceText) do
  60.     begin
  61.       lc := LowerCase(s);
  62.       found := False;
  63.       for entry in Procs do
  64.         if entry.Identifier = lc then begin
  65.           entry.Proc(lc);
  66.           found := True;
  67.         end;
  68.       if not found then
  69.         WriteLn('unknown procedure');
  70.     end;
  71.   ReadLn;
  72. end.

Curt Carpenter

  • Sr. Member
  • ****
  • Posts: 396
Re: Associating a shortstring with a unique procedure
« Reply #6 on: June 06, 2020, 06:05:24 pm »
Thank you all for the inputs.  I'll explore each of them today.

Curt Carpenter

  • Sr. Member
  • ****
  • Posts: 396
Re: Associating a shortstring with a unique procedure
« Reply #7 on: June 07, 2020, 12:48:36 am »
I am intrigued by TDictionary, but haven't been able to find out how to use it with my current Lazarus (2.0.8) and FPC (3.0.4).  By following a few threads here though, I think TFPGMap in unit a unit named "fgl" sounds like it might work for me instead, but I haven't been able to find the source for the unit.  I've looked in fpcsrc, but no fgl unit in there.  Any clues about where it can be found would be much appreciated.

My string-to-procedure code will be used in an interpreter, so it needs to be as fast as I can make it, and that weighs against going with a linear search -- although if push comes to shove, I will follow the code ideas you've provided here.

Meanwhile though -- I'd really like to learn more about TFPGMap -- or TDictionary, if there's a version that I can use with my Lazarus configuration.

Thanks again for the inputs.

jamie

  • Hero Member
  • *****
  • Posts: 6091
Re: Associating a shortstring with a unique procedure
« Reply #8 on: June 07, 2020, 01:01:22 am »
there is a quick way to execute that code instead of simply always scanning for the Text..

 when you initially run the script you should rebuild a run P-code instead.. Meaning you first scan all the procedures and recreate a script with generates an easy to resolve indexing value that can be used within a indexing table of procedures... etc..

 to make things simpler for parameter passings an array of variants can be used where as you define those after you parse the procedure...  Then call the procedure. The procedures will query these variants for their values..

 This basically works like a stack does. in the Old days it was Postscript, RPN etc..you first pushed the parameters onto a stack then called the methods..

 Using this approach you don't need to have your native methods to accept parameters... they will just read them from the stack..
The only true wisdom is knowing you know nothing

Curt Carpenter

  • Sr. Member
  • ****
  • Posts: 396
Re: Associating a shortstring with a unique procedure
« Reply #9 on: June 07, 2020, 01:31:40 am »
Found the source for fgl:  it's in fpcsrc/rtl/objpas.  Who knew? ::)

I appreciate the idea of doing an intermediate preprocessing step between reading a script file and executing it in an interpretive sort of way -- and I will definitely give that more thought.  It might be just the sort of architecture I need. 

Meanwhile, I've become obsessed with trying to learn something about TDictionary and/or TFPGMap just because it sounds like something it would be good to know.  That's the joy of programming for fun -- you can give in to any distractions that come along... 

julkas

  • Guest
Re: Associating a shortstring with a unique procedure
« Reply #10 on: June 07, 2020, 08:33:02 am »
My string-to-procedure code will be used in an interpreter, so it needs to be as fast as I can make it
If you are writing interpreter, try implement your own light fast dictionary, hash table.

MaxCuriosus

  • Full Member
  • ***
  • Posts: 136
Re: Associating a shortstring with a unique procedure
« Reply #11 on: June 20, 2020, 09:25:39 pm »
Curt Carpenter,
I would be interested in knowing what you have come up with. Would you mind to share?

jamie

  • Hero Member
  • *****
  • Posts: 6091
Re: Associating a shortstring with a unique procedure
« Reply #12 on: June 20, 2020, 11:49:58 pm »
My string-to-procedure code will be used in an interpreter, so it needs to be as fast as I can make it
If you are writing interpreter, try implement your own light fast dictionary, hash table.

The requirement was 1..5 characters. That can be stored using a Uint64 where you can have up to 8 chars and thus the search pattern would be very fast..

 So instead of doing String searches you do a binary search which of course test the whole value one a single shot..

 You can even compress that and get 9 characters in there if you don't go over value 127 for each

 and if you don't care about the lower values you can even get more....

also while I am at it...

As for a parser, you can also have a secondary list that gets built on the first pass from the master list.. this way if you have a repeating script operating it could then first scan the secondary list and If not found then scan the master and if found add it to the secondary list.

 At least this way if you have a something that is only using a few out of a large list it won't need to search so fast  in loops. It can even put it at the top of the list..

« Last Edit: June 20, 2020, 11:54:55 pm by jamie »
The only true wisdom is knowing you know nothing

Curt Carpenter

  • Sr. Member
  • ****
  • Posts: 396
Re: Associating a shortstring with a unique procedure
« Reply #13 on: June 21, 2020, 02:07:32 am »
I've ended up using TFPHashList from the "contnrs" unit of the fcl, and rethinking my entire approach to move away from objects all together.  Each element of the hash list pairs an alphanumeric key with a record, where each record contains information that is used to verify that the key is well-formed, and a pointer to a procedure that translates the key into a command that is sent by WiFi to some special hardware.

The end objective is to provide an easy-to-use scripting interface for the hardware, which provides 32 digital inputs, 32 digital outputs and 8 SPI channels.

I'm still not satisfied with the "aesthetics" of my solution though so the project is still a work in progress.  Its been a good learning experience anyway! 

jamie

  • Hero Member
  • *****
  • Posts: 6091
Re: Associating a shortstring with a unique procedure
« Reply #14 on: June 21, 2020, 02:18:26 am »
Code: Pascal  [Select][+][-]
  1. Function CompressTag64(AString:String):Uint64;
  2.  var
  3.    I:Integer;
  4. Begin
  5.    AString := Upcase(Astring);
  6.    Result := 0; I:= 1;
  7.    While (I <= Length(AString))and(AString[I] in ['A'..'Z']) Do
  8.       Result := (Result  Shl 5)+ (Ord(AString[I])-Ord('@'));
  9. end;                                  
  10.  

Ok, if you ever decide to think some more
the above example will convert a A..Z and maybe a few special chars into a Uint64 up to 12 digits

 So basically at the start of the of program you build a table from the ASCII data strings you have to create a index table of these values.

 So after that you now have the table which never needs to be recreated again and as you scan your text during runtime, you use the same function above on the fly to generate a number and thus scan your table for the index etc..

 I you keep it down to fewer chars  you can use a 32 bit type on output..
 
 The function uses 5 bits for each character . you can get up to 32 characters so that means you can also include some special characters too.

  I've used code like this to compile a Ladder logic script from the tools on the laptop into a indexable runtime in the controller using this approach .
 
  its the easier way to do it since the firmware can change within the controller.

The only true wisdom is knowing you know nothing

 

TinyPortal © 2005-2018