3

I guess my understanding of the way of writing code is still very limited. I try to modify the solution from SEARCH GENERIC LISTS, but I can not change the code in a way that he is accepting arbitrary key words as a search parameter

unit Unit_TsearchableTList;

interface

uses Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants,
  System.Classes, Vcl.Graphics,
  Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls, Vcl.Buttons,
  contnrs, Generics.Collections;

type
  TSearchableObjectList<T: class> = class(TObjectList<T>)

  public type
    TPredicate = reference to function(aItem: T; asearchValue: String): boolean;
  public
    function search(aFound: TPredicate<T>; asearchValue: String): T;
  end;

implementation

function TSearchableObjectList<T>.search(aFound: TPredicate<T>;
  asearchValue: String): T;
var
  item: T;
begin
  for item in Self do
    * * * * * * * * COMPILE ERROR IS HERE * * * * * * * * * * * * * * * *
      * ! ! ! ! ! !
  if aFound(item, asearchValue) then
    Exit(item);
  Result := nil;
end;

end.

Usage example:

type
  TReplaceElementNames = class
    FindName: String;
    ReplaceName: String;
    ReplacementCondition: TReplacementCondition; // not relevant code
  end;

var
  LookUpList: TList<TReplaceElementNames>;
  search    : TReplaceElementNames;

begin
  LookUpList := TSearchableObjectList<TReplaceElementNames>.Create;

  search := LookUpList.search(
    function(aItem: TReplaceElementNames; searchname: String): boolean
    begin
      Result := aItem.FindName = searchname;
    end);
Community
  • 1
  • 1
Franz
  • 1,883
  • 26
  • 47
  • 1
    :the nice thing about the anonymous methods is that they have access to all the scope sorrounding it. Because that, you don´t need to pass the name you are looking for as a parameter. it can be a variable declared before the call of the method Search or a constant value or whatever you want. In the example I gave the search was for a constant ('WantedName'), but you can change that for another constant or a variable name. It will work fine! – AlexSC Jul 19 '13 at 12:13
  • 1
    I question the fact that you have imported vast numbers of units that are not needed. Remove everything that is not used. The unholy mixing of `contnrs` and `Generics.Collections` is especially disturbing. – David Heffernan Jul 19 '13 at 12:32

2 Answers2

2

The type that you defined in your code is TPredicate. But you then went on to use TPredicate<T> which is a type defined in SysUtils. Simply replace

TPredicate<T>

with

TPredicate

in your code, and it will compile.


Having said that, it would be simpler if you used the code in the answer you accepted. There's no need for a two parameter predicate since variable capture is used to provide the search string.

David Heffernan
  • 601,492
  • 42
  • 1,072
  • 1,490
  • I guess the matter here is something else. Our friend is trying to add a second parameter to the TPredicate type, but it´s not necessary, since the value to be searched can be inserted directly inside the anonymous method body. No need for the second parameter. – AlexSC Jul 19 '13 at 12:20
  • @AlexSC Well, I just naively answered the question in front of me. And my answer is accurate, for sure. The confusion is possibly caused by you introducing `TPredicate` in your original answer, and then never even using that type. I've just fixed that in your original question. – David Heffernan Jul 19 '13 at 12:23
  • the type is used alright. The nested type declaration preceeds the one existing in other unit. Besides, TPredicate is different than TPredicate and that´s the type used in the Search method. This discussion is about some other question I answered and the code posted is a bit wrongfully copied, since the nested TPredicate type should be TPredicate. I noticed that you removed the TPredicate declaration from my answer but by doing that, you ruined the solution. – AlexSC Jul 19 '13 at 12:55
  • @AlexSC No, the code in your answer at the other question was incorrect. You defined `TPredicate` but then used `TPredicate`. These are different types. The nested type `TPredicate` that you defined was in fact never used. That's why I removed it in my edit to your answer there. Have a close read of my answer here. It applied to your original code. Note that your original code contained a number of errors that have now been fixed. – David Heffernan Jul 19 '13 at 13:00
  • alright, I missed the in the TPredicate declaration, but simply removing the declaration instead adding the three missing characters didn´t make the whole thing more correct. I just edited the code to reinsert the TPredicate declaration paying more attention. I believe the matter in question was how to create a generic search method that could be used to search by any criterium. This is what that solution does. – AlexSC Jul 19 '13 at 13:10
  • @AlexSC At the very least you could spell the type correctly. And yes, removing the declaration does make it better. You have just introduced a type which already exists! It's declared in just the same way in `SysUtils`. So, I regard your edit as making the answer worse. I'm disappointed. Are you going to re-define `Integer` next? – David Heffernan Jul 19 '13 at 13:15
  • I looked for the TPredicate type in SysUtils as you suggested. It doesn´t exist as TPredicate, but there is a TPredicate. So, you are right, I just declared another type to replace an already existing type. I agree, it´s not a good thing and it can be removed. However, you made a similar mistake, since in you answer you say to use TPredicate, not TPredicate, so you failed in spelling the type corrently just like I did! Anyway, I´m not interested in discussions that does not help the asker, so I will re-edit the answer and explain that the TPredicate is declared in SysUtils. – AlexSC Jul 19 '13 at 13:25
  • @AlexSC - no! that is not an error! That is an intended diference! David declared type `TSearchableObjectList.TPredicate` which is a different type than `TSearchableObjectList.TPredicate` that you declared! David's type does inherit and use `TSearchableObjectList.T` and can be used as local predicate, while your type does not. – Arioch 'The Jul 19 '13 at 13:30
  • @AlexSC Not really. I answered this question. Using `TPredicate` would not help here since in this question the function has two parameters. I have explained why the code in this Q would not compile, and what needs to be done to make it compile. I also suggested using the original code from your answer, which hopefully now uses `TPredicate` from `SysUtils`. What I meant by spelling is that you wrote `TPrecicate`. I also am not terribly interested in this discussion since it has made a complete mess of this answer. I would be happy if we could delete all comments from here. – David Heffernan Jul 19 '13 at 13:31
  • very hard to follow your expertise comments; I want to change 'WantedName' from first solutiion to a value I can pass as a paremeter. In a later step I also want to pass a TReplaceCondition for checking and looking up the correct class I#, searching in my list – Franz Jul 19 '13 at 13:33
  • @Franz - hard because they went offtopic. They are discussing http://stackoverflow.com/a/17732154 and frankly they'd better do it there than here. However here David told you to replace wrong type to a correct one - just do it :-) – Arioch 'The Jul 19 '13 at 13:35
  • In fact there's nothing to discuss. AlexSC got a bit confused, and posted some erroneous comments here. @Franz you have the answer to the question that you asked in the answer. You can safely ignore the comments. – David Heffernan Jul 19 '13 at 13:37
  • but Alex got my point here :-) "I guess the matter here is something else. Our friend is trying to add a second parameter to the TPredicate type, but it´s not necessary, since the value to be searched can be inserted directly inside the anonymous method body. No need for the second parameter" – Franz Jul 19 '13 at 13:41
  • @Franz in the second example you try to use `Tlist.search` methos that does not exist: http://docwiki.embarcadero.com/Libraries/XE4/en/System.Generics.Collections.TList_Methods Perhaps you can try Spring4D or Delphi-coll or some other 3rd-party collections library, more advanced than stock one – Arioch 'The Jul 19 '13 at 13:42
  • @Franz The answer to the question you asked is found in the first part of my answer. The real solution to your problem is found in the second part of my answer. – David Heffernan Jul 19 '13 at 13:45
1

By definition, predicate is somethign, that only takes one parameter like http://docwiki.embarcadero.com/Libraries/XE2/en/System.SysUtils.TPredicate

So, how can we pas more parameters to it, like what to compare against ? We do it by capturing the ocntext and generating new temporary function with that context fixed.

Modifying your example that would be (i try to be verbose, so you can track the steps):

Type CanCompareWithString<T> = function (const Value:T; const Target: string): boolean;
// overcoming Delphi limitation

Function CreatePredicateFunction<T>(Const Target: String; Const Matcher: CanCompareWithString<T>): TPredicate<T>;
begin
   Result := function // creating new function (more correct: new closure)
                (Arg1: T): Boolean // see top link to Delphi docs
                begin
                  Result := 
                     Matcher // capturing the passed function
                     ( Arg1, 
                       Target ) // capturing the target value
                end;    // finished creating closure
    end;


  TSearchableObjectList<T: class> = class(TObjectList<T>)
  public
    function search(aFound: TPredicate<T>): T; overload;
    function search( asearchValue: String; Matcher: CanCompareWithString<T>): T; overload;
  end;

function TSearchableObjectList<T>.search(aFound: TPredicate<T>): T;
var
  item: T;
begin
  for item in Self do
  if aFound(item) then
    Exit(item);
  Result := Default(T);
end;

function TSearchableObjectList<T>.search( asearchValue: String;  Matcher: CanCompareWithString<T>): T;
begin
  Result := 
    Search(
       CreatePredicateFunction(
           Matcher, aSearchValue)
    ); 
end;

How can you use it ? Hopefully something like that:

function StringMatcher(const Value:string; const Target: string): boolean;
begin
   Result := Value = Target;
end;

var L_S : TSearchableObjectList<String>; S: String;

S := L_S.Search('abcde', StringMAtcher);

Now you may ask "Why ? Why that StringMatcher at all?"

And the problem is that in Delphi you cannot add partial constraints on operators with types (what in Scala is called exisential types AFAIR). You just cannot compile

function Equal<T>(const value: String): boolean;
begin
  Result := T = Value;
end;

function Sum<U>(const value1, value2: U): U;
begin
  Result := value1 + value2;
end;

Delphi does not now what that type T would be and cannot ensure whether it can or cannot be compared to string, thus it would NOT be able to compile that "T = Value".

Delphi does not now what that type U would be and cannot ensure whether it can or cannot be added, thus it would NOT be able to compile that "value1 + value2".

It is a big limitation - but that is how it is.

Look at the implementations of the following to types:

And you would see which kind of boilerplate is required to do a somewhat similar task (but easier one! there the task is just to compare T to T, not T to String).

Arioch 'The
  • 15,799
  • 35
  • 62
  • at this line compiler error happened >>> Function CreatePredicateFunction(Const Target: String; Const Matcher: CanCompareWithString): TPredicate; ->>>> Unknown type T Error – Franz Jul 19 '13 at 14:25
  • @Franz Could you perhaps clarify what your question really is. It's not at all clear to me why discussions are continuing. Which makes me think that I do not understand what you are asking. – David Heffernan Jul 19 '13 at 14:33
  • I copied the code from *Arioch'the* the my unit and received the above mentioned error while starting to compile my unit – Franz Jul 19 '13 at 14:37
  • 1
    @Franz fixed (hopefully). Yes, one has to have a new "generator" function for each new type T... "Boooring" (c) The Bard's Tale. – Arioch 'The Jul 19 '13 at 14:38
  • @Franz that is not what David asks for. What he asks is your GOAL in the large scale: http://catb.org/esr/faqs/smart-questions.html#goal – Arioch 'The Jul 19 '13 at 14:38
  • And fixes are %s, %s and %s. – Arioch 'The Jul 19 '13 at 14:40
  • GOAL in the large scale : the real problem I#m working on a large and complex Lists of Lists .... instead of endless loop I try to figure out a new more effective way of coding this search in Lists – Franz Jul 19 '13 at 14:43
  • PS: Franz, if you cannot deal with [E2050](http://docwiki.embarcadero.com/RADStudio/XE4/en/E2050_Statements_not_allowed_in_interface_part_(Delphi)) and [E2003](http://docwiki.embarcadero.com/RADStudio/XE4/en/E2003_Undeclared_identifier_'%25s'_(Delphi)) yourself - then you understand nothing about programming Pascal and nothing about searchign in text files for a given text. Then stretching the boundaries of Delphi Compiler and its Generics implementation is a bit too early for you. However if you can deal with those but just do not want to... then it is even worse. – Arioch 'The Jul 19 '13 at 14:44
  • @Franz: I like what you are trying to do, however I gave you an answer in the original question that was based in anonymous methods. That line of work dismiss the need to pass a second parameter because an anonymous method can access the extern variables directly. For instance, in the example I gave, you could have a variable named searchKey assigned with any value and then, in the body of the anonymous method compare aItem.FirstName to it. That´s why a second parameter to TPredicate is not needed. I will edit the answer in the original question to show this, ok? – AlexSC Jul 19 '13 at 14:55
  • 1
    My difficulty is that I think I answered the question that you asked. But apparently not. In which case I don't know what your question is asking. Are you interested in the compiler error in the question or not? And if not, why is that the main focus of your question. It's not fair to expect us to stay with your problem until we solve all subsequent issues. I'm somewhat frustrated. – David Heffernan Jul 19 '13 at 14:57
  • Pls. do it, I hope this also helps others to use modern feature of delphi more efficiently – Franz Jul 19 '13 at 14:57
  • @David : can understand you last post; all the things you and AlexSc and Arioch did are clearly above my current knowledge of programming. I really like the cool things you write in your answers but I#m in deep trouble to understand and try to catch up – Franz Jul 19 '13 at 15:00