samer@4: classdef func samer@4: properties (GetAccess=private, SetAccess=private) samer@4: fn samer@4: args samer@4: slots samer@4: end samer@4: methods samer@4: % A func is a partially applied function: Any given function can samer@4: % have some arguments bound to some given parameters, producing a samer@4: % new function which needs only the remaining unbound parameters to samer@4: % be supplied. In addition, the order in which parameters supplied samer@4: % in a subsequent invocation are bound to unbound arguments can be samer@4: % controlled using a slot permutation mechanism. Subscripting operation samer@4: % () is overloaded so that function application works in the normal samer@4: % way except when there are no arguments, in which case you must use FEVAL. samer@4: % samer@4: % For example, if linspace is a function such that samer@4: % samer@4: % linspace(a,b,n) samer@4: % samer@4: % returns an array of n values linearly spaced between a and b, samer@4: % then after samer@4: % samer@4: % fromzero=func(@linspace,0) samer@4: % samer@4: % fromzero(b,n) returns n linearly spaced values from 0 to b. samer@4: % The arguments can be permuted, eg, samer@4: % samer@4: % perm(func(@linspace),[3 1 2]) samer@4: % samer@4: % returns a function that takes the arguments in the order (n,a,b) samer@4: % instead of (a,b,n). Permutation and binding can be combined, eg samer@4: % samer@4: % countdown=bind(perm(func(@linspace),[3 2 1]),10) samer@4: % countdown(5,50) samer@4: % ans= samer@4: % 50 45 40 35 30 25 20 15 10 5 samer@4: % samer@4: % See Also: samer@4: % PERM, BIND, CURRY, FEVAL, BINDAT samer@4: % samer@4: % Internal structure: samer@4: % cfn.fn : an ordinary matlab function handle samer@4: % cfn.args : a cell array with bound arguments filled in in the order samer@4: % that the ordinary function fn expects them. There may be samer@4: % empty slots if the arguments have been permuted (see CFPERM) samer@4: % cfn.slots : List of empty slots in the order that they are intended to be samer@4: % bound when this curried function is evaluated or has more samer@4: % parameters bound. Eg, the first parameter will be bound to samer@4: % to the slots(1)'th argument of the eventual call to cfn.fn samer@4: samer@4: % default function is null function samer@4: function c=func(fn,varargin) samer@4: samer@4: if nargin<1, fn=@nop; end samer@4: if isa(fn,'func'), c=fn; samer@4: elseif isa(fn,'struct'), samer@4: c.fn=fn.fn; samer@4: c.args=fn.args; samer@4: c.slots=fn.slots; samer@4: else samer@4: c.fn=fn; samer@4: c.args=varargin; samer@4: c.slots=[]; samer@4: end samer@4: end samer@4: samer@4: % bind - Bind arguments to a func to create partially applied function samer@4: % samer@4: % bind(fn,a1,a2,..) samer@4: % fn is curried function returned by FUNC, BIND or PERM, samer@4: % the given parameters are bound to the free slots of the function samer@4: % and a new curried function is returned. samer@4: samer@4: % Matlab 'feature': if ANY of the paremeters samer@4: % to bind are function objects, then this function gets called, even samer@4: % if the first parameter is just an ordinary function pointer. samer@4: function c=bind(c,varargin) samer@4: if isa(c,'function_handle'), c=func(c); end samer@4: samer@4: % first, fill available slots in arg list using supplied args samer@4: m=min(length(c.slots),length(varargin)); samer@4: c.args(c.slots(1:m))=varargin(1:m); samer@4: c.slots=c.slots(m+1:end); % remove filled slots samer@4: c.args=horzcat(c.args,varargin(m+1:end)); % append any left over args samer@4: end samer@4: samer@4: % BINDAT: Bind arguments at specified positions to create partially applied samer@4: % function samer@4: % samer@4: % BIND(cfn,pos,a1,a2,..) samer@4: % If cfn is an ordinary function, the parameters a1, a2 etc samer@4: % are bound at the positions specified in pos(1),pos(2), etc. samer@4: % Otherwise, the same as BIND. samer@4: % samer@4: % See BIND samer@4: samer@4: % need to make sure pos is a complete permutation before samer@4: % calling perm samer@4: function c=bindat(c,pos,varargin) samer@4: pos=[pos setdiff(1:max(pos),pos)]; samer@4: c=bind(perm(c,pos),varargin{:}); samer@4: end samer@4: samer@4: % DISPLAY - Display FUNC object as string samer@4: function display(c), disp([tostring(c),sprintf(' :: func(%d->%d)',nargin(c),nargout(c))]); end samer@4: samer@4: % FEVAL: Evaluate partially applied function samer@4: % samer@4: % FEVAL(cfn,a1,a2,...) samer@4: % The given parameters a1, a2, etc. are bound to the arguments of cfn, samer@4: % and eventually to the arguments of a matlab function, possibly samer@4: % permuted according to cfn.slots (see CFPERM) samer@4: function varargout=feval(c,varargin) samer@4: % first, fill available slots in arg list using supplied args samer@4: if ~isa(c,'func'), samer@4: [varargout{1:max(1,nargout)}] = builtin('feval',c,varargin{:}); samer@4: return; samer@4: % error('This shouldn''t be happening!'); samer@4: end samer@4: m=min(length(c.slots),length(varargin)); samer@4: args=c.args; samer@4: args(c.slots(1:m))=varargin(1:m); samer@4: samer@4: % append any left over args samer@4: if nargout==0, c.fn(args{:},varargin{m+1:end}); samer@4: else [varargout{1:max(1,nargout)}] = c.fn(args{:},varargin{m+1:end}); samer@4: end samer@4: end samer@4: samer@4: samer@4: % GETARG - Extract a bound argument from a FUNC object samer@4: % getarg :: (func (D1,...,Dn)->R, k:natural) -> Dk samer@4: function a=getarg(c,n), a=c.args{n}; end samer@4: samer@4: % GETFN - Get ordinary function handle from FUNC object samer@4: function fn=getfn(c), fn=c.fn; end samer@4: samer@4: % PERM: permute arguments to a curried function samer@4: % PERM(cfn,perm) samer@4: % cfn can be an ordinary function handle or a curried function as returned samer@4: % by BIND or PERM. samer@4: % samer@4: % The 1st argument of PERM(F,P) will bind to the P(1)'th argument of F. samer@4: % Sequential permutations compose properly. samer@4: % P must be a complete permutation: if length(P)=L, then P must samer@4: % contain each of the intergers 1 to P exactly once. samer@4: % The binding order of arguments after the Lth is not affected. samer@4: function c=perm(c,slots) samer@4: m=length(c.slots); samer@4: n=length(slots); samer@4: l=length(c.args); samer@4: if mn, slots=[slots n+1:m]; end samer@4: c.slots=c.slots(slots); samer@4: end samer@4: samer@4: function varargout=subsref(c,S) samer@4: % SUBSREF - Subscript referencing for FUNC objects samer@4: % samer@4: % f(a,b,c)=feval(f,a,b,c) - parenthesis subsref invokes the function samer@4: % f{a,b,c}=bind(f,a,b,c) - braces subsref binds more arguments samer@4: samer@4: samer@4: s=S(1); samer@4: switch s.type samer@4: case '()' samer@4: % first, fill available slots in arg list using supplied args samer@4: m=min(length(c.slots),length(s.subs)); samer@4: args=c.args; samer@4: args(c.slots(1:m))=s.subs(1:m); samer@4: [varargout{1:nargout}] = c.fn(args{:},s.subs{m+1:end}); samer@4: case '{}' samer@4: varargout{1}=bind(c,s.subs{:}); samer@4: if length(S)>1, samer@4: [varargout{1:nargout}] = subsref(varargout{1},S(2:end)); samer@4: end samer@4: end samer@4: end samer@4: samer@4: function str=tostring(c) samer@4: % TOSTRING - Implementation of string conversion for FUNC objects samer@4: % samer@4: % tostring :: func -> string samer@4: samer@4: str=[]; samer@4: slots=c.slots; m=length(slots); samer@4: args=c.args; l=length(args); samer@4: n=max(slots); samer@4: if l0, str=[str ' ']; end samer@4: if isa(c.fn,'function_handle') fstr=['@' func2str(c.fn)]; samer@4: elseif isa(c.fn,'inline') fstr=['\' char(c.fn) '\']; samer@4: end samer@4: str=[ fstr '(' str '...)']; samer@4: end samer@4: function n=nargout(f), n=nargout(f.fn); end samer@4: function n=nargin(f), n=nargin(f.fn)-length(f.args); end samer@4: end samer@4: end samer@4: