Someone mind explaining what a Functor is to me. And when and where I would want to use one?
Someone mind explaining what a Functor is to me. And when and where I would want to use one?
My DGDev forum pascal syntax highlight settings:
<br />[background=#FFFFFF][comment=#8080FF][normal=#000080]
<br />[number=#C00000][reserved=#000000][string=#00C000]
I searched the net and found:
So effectivly its an Object that takes parameters.....A functor is a function that can be manipulated as an object.
In Java, functors are typically implemented as interfaces defined by a single, generic member function, and that is the approach taken here.
Functors support and encourage a number of powerful programming techniques including:
programming in a functional style
higher order functions
internal iterators
reuse and specialization through composition rather than inheritance and overloading
generic "callback" or "extension point" APIs
generic "filters" or predicate APIs
many "behavioral" design patterns, such as Visitor, Strategy, Chain of Responsibility, etc.
I suppose in Delphi this would be supported by
Code:Function MyFunctor(Parm1 : Type1) : TMyFunctorObject; Begin MyObj := TMyFunctorObject.Create; Result := MyObj; End; .... With MyFunctor(Var1) do Begin DoStuff; End;
William Cairns
My Games: http://www.cairnsgames.co.za (Currently very inactive)
MyOnline Games: http://TheGameDeveloper.co.za (Currently very inactive)
I believe that a functor is a concept to create a class that can behave primarily as a "function" - usually, in C++, that would be overloading the () operator to provide function-like semantics for a class (possibly as a base class). C++ does feel a little dirty in this respect, though, because it doesn't provide proper interfaces.
As far as I can tell (and I may be wrong here since I've not really bothered looking into functors yet), functors are best used to pass in as parameters to functions, to provide different behaviours. Here's a Delphi example, although I may have missed the point (not sure):
Notice how the behaviour of the function "DoStuff" depends entirely on what function is passed in - you're changing its behaviour in any way you see fit . Also, the functors are derived from TInterfacedObject so they'll be squished automatically when they go out of scope.Code:unit Unit1; interface uses Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls, ExtCtrls; type IBinaryFunc = interface ['{11231180-585E-11D7-B787-00D0B7BBC908}'] function DoIt(X,Y: Integer): Integer; end; TBinaryAdd = class(TInterfacedObject, IBinaryFunc) public function DoIt(X, Y: Integer): Integer; end; TBinarySub = class(TInterfacedObject, IBinaryFunc) public function DoIt(X, Y: Integer): Integer; end; TBinaryMultiply = class(TInterfacedObject, IBinaryFunc) public function DoIt(X, Y: Integer): Integer; end; TBinaryDivide = class(TInterfacedObject, IBinaryFunc) public function DoIt(X, Y: Integer): Integer; end; TForm1 = class(TForm) Button1: TButton; rgpWhatever: TRadioGroup; procedure Button1Click(Sender: TObject); private { Private declarations } public { Public declarations } end; var Form1: TForm1; implementation {$R *.DFM} function TBinaryAdd.DoIt(X, Y: Integer): Integer; begin Result := X + Y; end; function TBinarySub.DoIt(X, Y: Integer): Integer; begin Result := X - Y; end; function TBinaryMultiply.DoIt(X, Y: Integer): Integer; begin Result := X * Y; end; function TBinaryDivide.DoIt(X, Y: Integer): Integer; begin Result := X div Y; end; function DoStuff(Num1, Num2: Integer; Operation: IBinaryFunc): Integer; begin Result := Operation.DoIt(Num1, Num2); end; procedure TForm1.Button1Click(Sender: TObject); begin case rgpWhatever.ItemIndex of -1: ; 0: ShowMessage(IntToStr(DoStuff(3, 6, TBinaryAdd.Create))); 1: ShowMessage(IntToStr(DoStuff(13, 4, TBinarySub.Create))); 2: ShowMessage(IntToStr(DoStuff(3, 5, TBinaryMultiply.Create))); 3: ShowMessage(IntToStr(DoStuff(12, 4, TBinaryDivide.Create))); end; end; end.
The advantage of using classes as "functions" is that they can retain state if required, which gives them more capabilities than just function pointers.
You could probably take this further - one rocking feature of Delphi is class references ("class of") - for example, TClass. There should be a way to use them (passing in the type of the functor instead of using a case statement on it), though it escapes me at the moment.
Class references are lovely. :cyclops:
[P.S. BlueCat, the pascal forum tag seems to screw up with the above code - it seems to be the interface guid line that's the cause]
"All paid jobs absorb and degrade the mind."
<br />-- Aristotle
Originally Posted by AlimonsterYou need the costructor to be virtual, so when you use a class reference it will resolve to the correct constructor.Code:type TBinaryFunc = interface(TInterfacedObject, IBinaryFunc) Constructor create; virtual; function DoIt(X,Y: Integer): Integer; virtual; abstract; end; TBinaryFuncClass = class of TBinaryFunc; .... function DoStuff(Num1, Num2: Integer; OperationClass: IBinaryFuncClass): Integer; begin Result := IBinaryFunc(OperationClass.Create).DoIt(Num1, Num2); end; procedure TForm1.Button1Click(Sender: TObject); begin case rgpWhatever.ItemIndex of -1: ; 0: ShowMessage(IntToStr(DoStuff(3, 6, TBinaryAdd))); 1: ShowMessage(IntToStr(DoStuff(13, 4, TBinarySub))); 2: ShowMessage(IntToStr(DoStuff(3, 5, TBinaryMultiply))); 3: ShowMessage(IntToStr(DoStuff(12, 4, TBinaryDivide))); end; end;
You can get away with "IBinaryFunc(OperationClass.Create).DoIt" because of 2 reasons:
1) if an exception occurs, the constructor fails and the detsructor is called. Thus the Constructor always returns a valid value, or it raises an exception.
2) IBinaryFunc(OperationClass.Create) makes sure the returned class is treaded as an interface. This will cause the referance count to equal one while DoIt is executing, but as soon as the statement finishes, the reference count is decreased, and the interface freed. You might be able to remove the cast, but I havent compiled that to check to see if that would work.
Also you can have virtual class functions too.
Then something like "OperationClass.Doit" is valid and will call the correct function.Code:type TBinaryFunc = interface(TInterfacedObject, IBinaryFunc) Constructor create; virtual; class function DoIt(X,Y: Integer): Integer; virtual; abstract; end;
This is useful in a system when you have a list of class types which you create a new instance of each time you use the class type, but need extra information from each class type but dont want to have to create a new class to contain this information (which is static).
Tobject.ClassName is an example of this.
Bookmarks