samer@4
|
1 classdef func
|
samer@4
|
2 properties (GetAccess=private, SetAccess=private)
|
samer@4
|
3 fn
|
samer@4
|
4 args
|
samer@4
|
5 slots
|
samer@4
|
6 end
|
samer@4
|
7 methods
|
samer@4
|
8 % A func is a partially applied function: Any given function can
|
samer@4
|
9 % have some arguments bound to some given parameters, producing a
|
samer@4
|
10 % new function which needs only the remaining unbound parameters to
|
samer@4
|
11 % be supplied. In addition, the order in which parameters supplied
|
samer@4
|
12 % in a subsequent invocation are bound to unbound arguments can be
|
samer@4
|
13 % controlled using a slot permutation mechanism. Subscripting operation
|
samer@4
|
14 % () is overloaded so that function application works in the normal
|
samer@4
|
15 % way except when there are no arguments, in which case you must use FEVAL.
|
samer@4
|
16 %
|
samer@4
|
17 % For example, if linspace is a function such that
|
samer@4
|
18 %
|
samer@4
|
19 % linspace(a,b,n)
|
samer@4
|
20 %
|
samer@4
|
21 % returns an array of n values linearly spaced between a and b,
|
samer@4
|
22 % then after
|
samer@4
|
23 %
|
samer@4
|
24 % fromzero=func(@linspace,0)
|
samer@4
|
25 %
|
samer@4
|
26 % fromzero(b,n) returns n linearly spaced values from 0 to b.
|
samer@4
|
27 % The arguments can be permuted, eg,
|
samer@4
|
28 %
|
samer@4
|
29 % perm(func(@linspace),[3 1 2])
|
samer@4
|
30 %
|
samer@4
|
31 % returns a function that takes the arguments in the order (n,a,b)
|
samer@4
|
32 % instead of (a,b,n). Permutation and binding can be combined, eg
|
samer@4
|
33 %
|
samer@4
|
34 % countdown=bind(perm(func(@linspace),[3 2 1]),10)
|
samer@4
|
35 % countdown(5,50)
|
samer@4
|
36 % ans=
|
samer@4
|
37 % 50 45 40 35 30 25 20 15 10 5
|
samer@4
|
38 %
|
samer@4
|
39 % See Also:
|
samer@4
|
40 % PERM, BIND, CURRY, FEVAL, BINDAT
|
samer@4
|
41 %
|
samer@4
|
42 % Internal structure:
|
samer@4
|
43 % cfn.fn : an ordinary matlab function handle
|
samer@4
|
44 % cfn.args : a cell array with bound arguments filled in in the order
|
samer@4
|
45 % that the ordinary function fn expects them. There may be
|
samer@4
|
46 % empty slots if the arguments have been permuted (see CFPERM)
|
samer@4
|
47 % cfn.slots : List of empty slots in the order that they are intended to be
|
samer@4
|
48 % bound when this curried function is evaluated or has more
|
samer@4
|
49 % parameters bound. Eg, the first parameter will be bound to
|
samer@4
|
50 % to the slots(1)'th argument of the eventual call to cfn.fn
|
samer@4
|
51
|
samer@4
|
52 % default function is null function
|
samer@4
|
53 function c=func(fn,varargin)
|
samer@4
|
54
|
samer@4
|
55 if nargin<1, fn=@nop; end
|
samer@4
|
56 if isa(fn,'func'), c=fn;
|
samer@4
|
57 elseif isa(fn,'struct'),
|
samer@4
|
58 c.fn=fn.fn;
|
samer@4
|
59 c.args=fn.args;
|
samer@4
|
60 c.slots=fn.slots;
|
samer@4
|
61 else
|
samer@4
|
62 c.fn=fn;
|
samer@4
|
63 c.args=varargin;
|
samer@4
|
64 c.slots=[];
|
samer@4
|
65 end
|
samer@4
|
66 end
|
samer@4
|
67
|
samer@4
|
68 % bind - Bind arguments to a func to create partially applied function
|
samer@4
|
69 %
|
samer@4
|
70 % bind(fn,a1,a2,..)
|
samer@4
|
71 % fn is curried function returned by FUNC, BIND or PERM,
|
samer@4
|
72 % the given parameters are bound to the free slots of the function
|
samer@4
|
73 % and a new curried function is returned.
|
samer@4
|
74
|
samer@4
|
75 % Matlab 'feature': if ANY of the paremeters
|
samer@4
|
76 % to bind are function objects, then this function gets called, even
|
samer@4
|
77 % if the first parameter is just an ordinary function pointer.
|
samer@4
|
78 function c=bind(c,varargin)
|
samer@4
|
79 if isa(c,'function_handle'), c=func(c); end
|
samer@4
|
80
|
samer@4
|
81 % first, fill available slots in arg list using supplied args
|
samer@4
|
82 m=min(length(c.slots),length(varargin));
|
samer@4
|
83 c.args(c.slots(1:m))=varargin(1:m);
|
samer@4
|
84 c.slots=c.slots(m+1:end); % remove filled slots
|
samer@4
|
85 c.args=horzcat(c.args,varargin(m+1:end)); % append any left over args
|
samer@4
|
86 end
|
samer@4
|
87
|
samer@4
|
88 % BINDAT: Bind arguments at specified positions to create partially applied
|
samer@4
|
89 % function
|
samer@4
|
90 %
|
samer@4
|
91 % BIND(cfn,pos,a1,a2,..)
|
samer@4
|
92 % If cfn is an ordinary function, the parameters a1, a2 etc
|
samer@4
|
93 % are bound at the positions specified in pos(1),pos(2), etc.
|
samer@4
|
94 % Otherwise, the same as BIND.
|
samer@4
|
95 %
|
samer@4
|
96 % See BIND
|
samer@4
|
97
|
samer@4
|
98 % need to make sure pos is a complete permutation before
|
samer@4
|
99 % calling perm
|
samer@4
|
100 function c=bindat(c,pos,varargin)
|
samer@4
|
101 pos=[pos setdiff(1:max(pos),pos)];
|
samer@4
|
102 c=bind(perm(c,pos),varargin{:});
|
samer@4
|
103 end
|
samer@4
|
104
|
samer@4
|
105 % DISPLAY - Display FUNC object as string
|
samer@4
|
106 function display(c), disp([tostring(c),sprintf(' :: func(%d->%d)',nargin(c),nargout(c))]); end
|
samer@4
|
107
|
samer@4
|
108 % FEVAL: Evaluate partially applied function
|
samer@4
|
109 %
|
samer@4
|
110 % FEVAL(cfn,a1,a2,...)
|
samer@4
|
111 % The given parameters a1, a2, etc. are bound to the arguments of cfn,
|
samer@4
|
112 % and eventually to the arguments of a matlab function, possibly
|
samer@4
|
113 % permuted according to cfn.slots (see CFPERM)
|
samer@4
|
114 function varargout=feval(c,varargin)
|
samer@4
|
115 % first, fill available slots in arg list using supplied args
|
samer@4
|
116 if ~isa(c,'func'),
|
samer@4
|
117 [varargout{1:max(1,nargout)}] = builtin('feval',c,varargin{:});
|
samer@4
|
118 return;
|
samer@4
|
119 % error('This shouldn''t be happening!');
|
samer@4
|
120 end
|
samer@4
|
121 m=min(length(c.slots),length(varargin));
|
samer@4
|
122 args=c.args;
|
samer@4
|
123 args(c.slots(1:m))=varargin(1:m);
|
samer@4
|
124
|
samer@4
|
125 % append any left over args
|
samer@4
|
126 if nargout==0, c.fn(args{:},varargin{m+1:end});
|
samer@4
|
127 else [varargout{1:max(1,nargout)}] = c.fn(args{:},varargin{m+1:end});
|
samer@4
|
128 end
|
samer@4
|
129 end
|
samer@4
|
130
|
samer@4
|
131
|
samer@4
|
132 % GETARG - Extract a bound argument from a FUNC object
|
samer@4
|
133 % getarg :: (func (D1,...,Dn)->R, k:natural) -> Dk
|
samer@4
|
134 function a=getarg(c,n), a=c.args{n}; end
|
samer@4
|
135
|
samer@4
|
136 % GETFN - Get ordinary function handle from FUNC object
|
samer@4
|
137 function fn=getfn(c), fn=c.fn; end
|
samer@4
|
138
|
samer@4
|
139 % PERM: permute arguments to a curried function
|
samer@4
|
140 % PERM(cfn,perm)
|
samer@4
|
141 % cfn can be an ordinary function handle or a curried function as returned
|
samer@4
|
142 % by BIND or PERM.
|
samer@4
|
143 %
|
samer@4
|
144 % The 1st argument of PERM(F,P) will bind to the P(1)'th argument of F.
|
samer@4
|
145 % Sequential permutations compose properly.
|
samer@4
|
146 % P must be a complete permutation: if length(P)=L, then P must
|
samer@4
|
147 % contain each of the intergers 1 to P exactly once.
|
samer@4
|
148 % The binding order of arguments after the Lth is not affected.
|
samer@4
|
149 function c=perm(c,slots)
|
samer@4
|
150 m=length(c.slots);
|
samer@4
|
151 n=length(slots);
|
samer@4
|
152 l=length(c.args);
|
samer@4
|
153 if m<n, c.slots=[c.slots l+1:l+n-m];
|
samer@4
|
154 elseif m>n, slots=[slots n+1:m]; end
|
samer@4
|
155 c.slots=c.slots(slots);
|
samer@4
|
156 end
|
samer@4
|
157
|
samer@4
|
158 function varargout=subsref(c,S)
|
samer@4
|
159 % SUBSREF - Subscript referencing for FUNC objects
|
samer@4
|
160 %
|
samer@4
|
161 % f(a,b,c)=feval(f,a,b,c) - parenthesis subsref invokes the function
|
samer@4
|
162 % f{a,b,c}=bind(f,a,b,c) - braces subsref binds more arguments
|
samer@4
|
163
|
samer@4
|
164
|
samer@4
|
165 s=S(1);
|
samer@4
|
166 switch s.type
|
samer@4
|
167 case '()'
|
samer@4
|
168 % first, fill available slots in arg list using supplied args
|
samer@4
|
169 m=min(length(c.slots),length(s.subs));
|
samer@4
|
170 args=c.args;
|
samer@4
|
171 args(c.slots(1:m))=s.subs(1:m);
|
samer@4
|
172 [varargout{1:nargout}] = c.fn(args{:},s.subs{m+1:end});
|
samer@4
|
173 case '{}'
|
samer@4
|
174 varargout{1}=bind(c,s.subs{:});
|
samer@4
|
175 if length(S)>1,
|
samer@4
|
176 [varargout{1:nargout}] = subsref(varargout{1},S(2:end));
|
samer@4
|
177 end
|
samer@4
|
178 end
|
samer@4
|
179 end
|
samer@4
|
180
|
samer@4
|
181 function str=tostring(c)
|
samer@4
|
182 % TOSTRING - Implementation of string conversion for FUNC objects
|
samer@4
|
183 %
|
samer@4
|
184 % tostring :: func -> string
|
samer@4
|
185
|
samer@4
|
186 str=[];
|
samer@4
|
187 slots=c.slots; m=length(slots);
|
samer@4
|
188 args=c.args; l=length(args);
|
samer@4
|
189 n=max(slots);
|
samer@4
|
190 if l<n, args=[args cell(1,n-l)]; l=n; end;
|
samer@4
|
191 for i=1:m
|
samer@4
|
192 args{slots(i)}=['#' num2str(i)];
|
samer@4
|
193 end
|
samer@4
|
194
|
samer@4
|
195 for i=1:l
|
samer@4
|
196 str=[str tostring(args{i}) ','];
|
samer@4
|
197 end
|
samer@4
|
198 if n>0, str=[str ' ']; end
|
samer@4
|
199 if isa(c.fn,'function_handle') fstr=['@' func2str(c.fn)];
|
samer@4
|
200 elseif isa(c.fn,'inline') fstr=['\' char(c.fn) '\'];
|
samer@4
|
201 end
|
samer@4
|
202 str=[ fstr '(' str '...)'];
|
samer@4
|
203 end
|
samer@4
|
204 function n=nargout(f), n=nargout(f.fn); end
|
samer@4
|
205 function n=nargin(f), n=nargin(f.fn)-length(f.args); end
|
samer@4
|
206 end
|
samer@4
|
207 end
|
samer@4
|
208
|