samer@3
|
1 % seq - Base class for sequences
|
samer@3
|
2 classdef seq
|
samer@36
|
3 % properties (Constant, GetAccess=public)
|
samer@36
|
4 % NIL = seq.nil
|
samer@36
|
5 % end
|
samer@30
|
6
|
samer@3
|
7 methods (Abstract)
|
samer@3
|
8 x=head(a) % seq(A) -> A.
|
samer@3
|
9 b=next(a) % seq(A) -> seq(A).
|
samer@3
|
10 s=elsize(a) % seq([E->T]) -> E:[[1,D]].
|
samer@3
|
11 s=tostring(a) % seq(A) -> string.
|
samer@3
|
12 end
|
samer@3
|
13
|
samer@3
|
14 methods (Sealed=true)
|
samer@3
|
15 function varargout=size(a,n)
|
samer@3
|
16 s=elsize(a);
|
samer@3
|
17 if nargin>1, varargout={s(n)};
|
samer@3
|
18 elseif nargout<=1, varargout={s};
|
samer@3
|
19 else
|
samer@3
|
20 varargout=[num2cell(s(1:nargout-1)),{prod(s(nargout:end))}];
|
samer@3
|
21 end
|
samer@3
|
22 if any(isnan(cell2mat(varargout)))
|
samer@3
|
23 error('seq:Element size indeterminate');
|
samer@3
|
24 end
|
samer@3
|
25 end
|
samer@3
|
26
|
samer@23
|
27 function l=length(a), l=max(size(a)); end
|
samer@3
|
28 function n=numel(a, varargin), n=prod(size(a)); end
|
samer@3
|
29 function n=end(a,k,m)
|
samer@3
|
30 if m==length(elsize(a)), n=size(a,k);
|
samer@3
|
31 else n=prod(size(a)); end
|
samer@3
|
32 end
|
samer@3
|
33
|
samer@37
|
34 function n=count(a),
|
samer@23
|
35 n=0; while ~isempty(a), n=n+1; a=next(a); end
|
samer@23
|
36 end
|
samer@23
|
37
|
samer@3
|
38 function display(o)
|
samer@3
|
39 display(sprintf(' %s :: seq([[%s]])\n', tostring(o), ...
|
samer@8
|
40 catsep(',',cellmap(@fmt_dim,num2cell(elsize(o))))));
|
samer@3
|
41 function s=fmt_dim(x), if isnan(x), s='_'; else s=num2str(x); end; end
|
samer@3
|
42 end
|
samer@3
|
43
|
samer@3
|
44 function y=subsref(a,S)
|
samer@3
|
45 switch S(1).type
|
samer@3
|
46 case '()', y=paren(a,S(1));
|
samer@3
|
47 case '.', fn=S(1).subs; y=map(@(z)getfield(z,fn),a);
|
samer@3
|
48 end
|
samer@3
|
49 if length(S)>1, y=subsref(y,S(2:end)); end
|
samer@3
|
50 end
|
samer@3
|
51 end
|
samer@3
|
52
|
samer@3
|
53 methods
|
samer@3
|
54 function [x,b]=decons(a)
|
samer@36
|
55 % decons - head and tail of sequence
|
samer@36
|
56 % decons :: seq(A) -> A, seq(A).
|
samer@3
|
57 x=head(a);
|
samer@3
|
58 b=next(a);
|
samer@3
|
59 end
|
samer@3
|
60
|
samer@3
|
61 function z=last(y)
|
samer@36
|
62 % last - get the last array in a sequence (there must be at least one element)
|
samer@36
|
63 % last :: seq(A) -> A.
|
samer@3
|
64 while ~isempty(y), x=y; y=next(x); end
|
samer@3
|
65 z=head(x);
|
samer@3
|
66 end
|
samer@3
|
67
|
samer@3
|
68 function f=headfn(o), f=@()head(o); end
|
samer@3
|
69
|
samer@3
|
70
|
samer@3
|
71 % ------------- WRAPPERS FOR OTHER SEQ CLASSES ---------------------------
|
samer@36
|
72 function y=take(n,x), y=seq.taker.make(n,x); end;
|
samer@23
|
73 function y=takewhile(f,x), y=seq.takewhile.make(f,x); end
|
samer@3
|
74
|
samer@23
|
75 function s=drop(n,s),
|
samer@36
|
76 % drop - Drop the first n alements of sequence
|
samer@36
|
77 % drop :: natural, seq(A) -> seq(A)
|
samer@23
|
78 for i=1:n
|
samer@23
|
79 if ~isempty(s), s=next(s); end;
|
samer@23
|
80 end
|
samer@23
|
81 end
|
samer@3
|
82
|
samer@3
|
83 function s=dropwhile(f,s)
|
samer@36
|
84 % dropwhile - Drop elements of sequence that satisfy condition
|
samer@36
|
85 % drop :: (A->bool), seq(A) -> seq(A)
|
samer@3
|
86 while ~isempty(s) && f(head(s)), s=next(s); end
|
samer@3
|
87 end
|
samer@3
|
88
|
samer@3
|
89 function [x,tail]=nth1(n,X)
|
samer@36
|
90 % nth1 - Get nth element of a sequence starting at 1
|
samer@36
|
91 % nth1 :: natural, seq(X) -> X, seq(X) ~'tail of sequence'.
|
samer@3
|
92 z=drop(n-1,X);
|
samer@3
|
93 x=head(z);
|
samer@3
|
94 if nargout>=2, tail=next(z); end
|
samer@3
|
95 end
|
samer@3
|
96
|
samer@36
|
97 function Y=once(X), Y=cons(head(X),[]); end
|
samer@3
|
98 % once - equivalent to take(1,...)
|
samer@36
|
99 % once :: seq(A) -> seq(A).
|
samer@36
|
100
|
samer@36
|
101 function x=map(fn,y)
|
samer@36
|
102 % map - seq where values is a stateless function of another sequence
|
samer@3
|
103 %
|
samer@36
|
104 % map :: (A->B), seq(A) -> seq(B).
|
samer@36
|
105 if isempty(y), x=nil; else x=seq.mapper(fn,y); end;
|
samer@36
|
106 end
|
samer@3
|
107
|
samer@36
|
108 function x=select(fn,y)
|
samer@36
|
109 % select - Use boolean function to keep only matching elements from sequence
|
samer@36
|
110 %
|
samer@36
|
111 % select :: (A->bool), seq(A) -> seq(A).
|
samer@36
|
112 x=seq.selector.make(fn,y);
|
samer@36
|
113 end
|
samer@36
|
114
|
samer@36
|
115 function y=cache(x), if isempty(x), y=nil; else y=seq.cacher(x); end; end
|
samer@36
|
116 function y=buffer(x,varargin), y=seq.bufferer.make(x,varargin{:}); end
|
samer@3
|
117
|
samer@13
|
118 function x=mapaccum(fn,s0,y)
|
samer@13
|
119 if iscell(y), x=cmapaccum(fn,s0,y);
|
samer@23
|
120 elseif isempty(y), x=nil;
|
samer@36
|
121 else x=seq.stmapper(fn,s0,y);
|
samer@23
|
122 end
|
samer@13
|
123 end
|
samer@13
|
124
|
samer@36
|
125 function x=scanl(fn,e,y,varargin)
|
samer@36
|
126 % scanl - scanl combinator for sequences
|
samer@36
|
127 %
|
samer@36
|
128 % This function applies an associative operator to a list of arguments,
|
samer@36
|
129 % starting from the left using the given starting element.
|
samer@36
|
130 %
|
samer@36
|
131 % scanl ::
|
samer@36
|
132 % (X,Y->X) ~'associative binary operator',
|
samer@36
|
133 % X ~'initial element',
|
samer@36
|
134 % seq(Y) ~'a sequence'
|
samer@36
|
135 % -> seq(X).
|
samer@13
|
136 if iscell(y), x=cscanl(fn,e,y,varargin{:});
|
samer@23
|
137 elseif isempty(y), x=nil;
|
samer@36
|
138 else x=seq.scanner(fn,e,y,varargin{:});
|
samer@23
|
139 end
|
samer@13
|
140 end
|
samer@3
|
141
|
samer@3
|
142 % concat - Concatenate sequences
|
samer@3
|
143 %
|
samer@23
|
144 % concat :: seq(seq(A)) -> seq(A).
|
samer@36
|
145 function y=concat(x), y=seq.concatenator.make(x); end
|
samer@3
|
146
|
samer@3
|
147 % append - Append one or more sequences
|
samer@3
|
148 %
|
samer@3
|
149 % append :: seq(A), seq(A), ... -> seq(A).
|
samer@23
|
150 function x=append(varargin), x=concat(cellseq(varargin)); end
|
samer@3
|
151
|
samer@23
|
152 function z=and(x,y),
|
samer@23
|
153 if isempty(x)
|
samer@23
|
154 if isempty(y), z=nil; else z=y; end
|
samer@23
|
155 else
|
samer@36
|
156 if isempty(y), z=x; else z=seq.concatenator(cons(x,cons(y,nil))); end
|
samer@23
|
157 end
|
samer@23
|
158 end
|
samer@3
|
159
|
samer@23
|
160 function y=cycle(x)
|
samer@36
|
161 % cycle - cycles through input sequence repeatedly
|
samer@36
|
162 %
|
samer@36
|
163 % cycle :: seq(A) -> seq(A).
|
samer@23
|
164 if isempty(x), error('seq: cannot cycle empty sequence'); end;
|
samer@36
|
165 y=seq.cycler(x);
|
samer@23
|
166 end
|
samer@3
|
167
|
samer@36
|
168 function y=bindcat(x,f)
|
samer@36
|
169 % bindcat - sort of monadic bind for sequences.
|
samer@36
|
170 %
|
samer@36
|
171 % bindcat ::
|
samer@36
|
172 % seq(A) ~ 'the first sequence',
|
samer@36
|
173 % (A->seq(A)) ~ 'function to return second sequence given last element of first'
|
samer@36
|
174 % -> seq(A) ~ 'resultant sequence'.
|
samer@36
|
175 %
|
samer@36
|
176 % The resulting sequence consists of the entire sequence represented by the
|
samer@36
|
177 % first parameter, followed by the sequence obtained by applying the second
|
samer@36
|
178 % parameter to the last element of the first sequence.
|
samer@36
|
179 %
|
samer@36
|
180 % Example:
|
samer@36
|
181 %
|
samer@36
|
182 % gather(2,bindcat(cellseq({1,2,3,4}),@(x)take(head(x),0)))
|
samer@36
|
183 %
|
samer@36
|
184 % ans = 1 2 3 4 0 0 0 0
|
samer@36
|
185 y=seq.binder(x,f);
|
samer@36
|
186 end
|
samer@3
|
187
|
samer@36
|
188 function y=subsample(n,x), if isempty(x), y=nil; else y=seq.subsampler(n,x); end; end
|
samer@3
|
189
|
samer@3
|
190 function x=zip(varargin)
|
samer@3
|
191 % zip - combine several sequences into one
|
samer@3
|
192 %
|
samer@3
|
193 % zip :: seq(A), seq(B), ... -> seq(cell {A,B,...}).
|
samer@36
|
194 x=seq.zipper(@tuple,varargin);
|
samer@3
|
195 function z=rtuple(varargin), z=varargin; end
|
samer@3
|
196 end
|
samer@3
|
197
|
samer@23
|
198 function x=zipwith(fn,varargin),
|
samer@36
|
199 % zipwith - apply function to several sequences
|
samer@36
|
200 %
|
samer@36
|
201 % zipwith ::
|
samer@36
|
202 % (A,B,...->X),
|
samer@36
|
203 % seq(A), seq(B), ...
|
samer@36
|
204 % -> seq(X).
|
samer@23
|
205 if any(cell2mat(cellmap(@isempty,varargin))), x=nil;
|
samer@36
|
206 else x=seq.zipper(fn,varargin);
|
samer@23
|
207 end
|
samer@23
|
208 end
|
samer@3
|
209
|
samer@13
|
210 function x=zipaccum(fn,s0,varargin),
|
samer@36
|
211 % zipaccum - apply stateful function to several sequences
|
samer@36
|
212 %
|
samer@36
|
213 % zipaccum ::
|
samer@36
|
214 % (A,B,...,S->X,S),
|
samer@36
|
215 % S,
|
samer@36
|
216 % seq(A), seq(B), ...
|
samer@36
|
217 % -> seq(X).
|
samer@13
|
218 if isseq(s0) && any(map(@iscell,varargin)) % catch dispatching errors
|
samer@13
|
219 z=czipcaccum(fn,s0,varargin{:});
|
samer@23
|
220 elseif any(cell2mat(cellmap(@isempty,varargin))), x=nil;
|
samer@36
|
221 else x=seq.stzipper(fn,s0,varargin);
|
samer@13
|
222 end
|
samer@13
|
223 end
|
samer@3
|
224
|
samer@3
|
225 function xx=unzip(y)
|
samer@3
|
226 % unzip - Separate sequence of tuples into several sequences
|
samer@3
|
227 %
|
samer@3
|
228 % unzip ::
|
samer@3
|
229 % seq({I:[D]->A(I)}).
|
samer@3
|
230 % -> {I:[D]->seq(A(I))}.
|
samer@3
|
231 %
|
samer@23
|
232 % Note: input MUST be a non-empty sequence of constant size cell arrays.
|
samer@3
|
233 % Output is a cell array of sequences of the same size and shape.
|
samer@3
|
234
|
samer@3
|
235 xx=cell(size(y));
|
samer@3
|
236 for i=1:numel(xx)
|
samer@3
|
237 xx{i}=map(@(a)a{i},y);
|
samer@3
|
238 end
|
samer@3
|
239 end
|
samer@3
|
240
|
samer@23
|
241 function y=merge(f,varargin),
|
samer@23
|
242 if all(cell2mat(cellmap(@isempty,varargin))), y=nil;
|
samer@36
|
243 else y=seq.merger(f,varargin); end
|
samer@23
|
244 end
|
samer@3
|
245
|
samer@3
|
246
|
samer@3
|
247 % ------------------ MAPPERS ------------------------------------
|
samer@3
|
248
|
samer@3
|
249 % BINOP - Binary operation
|
samer@3
|
250 %
|
samer@36
|
251 % binop :: seq(A), seq(B), (A,B->C), string -> seq(C).
|
samer@3
|
252 %
|
samer@3
|
253 % Three cases
|
samer@3
|
254 % A is seq, B is an array
|
samer@3
|
255 % A is array, B is seq
|
samer@3
|
256 function o=binop(A,B,fn,opstr)
|
samer@3
|
257 o=binfun(A,B,fn,@(a,b)sprintf('(%s%s%s)',a,b,opstr));
|
samer@3
|
258 end
|
samer@3
|
259
|
samer@3
|
260 function o=binfun(A,B,fn,fmtstr)
|
samer@3
|
261 if isseq(A),
|
samer@3
|
262 if isseq(B), o=zipwith(fn,A,B); %@(o)fmtstr(tostring(A),tostring(B)));
|
samer@3
|
263 else
|
samer@3
|
264 o=map(@(x)fn(x,B),A); %@(o)fmtstr('.',tostring(B)));
|
samer@3
|
265 end
|
samer@3
|
266 else
|
samer@3
|
267 o=map(@(y)fn(A,y),B); %,@(o)fmtstr(tostring(A),'.'));
|
samer@3
|
268 end
|
samer@3
|
269 end
|
samer@3
|
270
|
samer@3
|
271 % vecop - apply binary function to different sized array sequences
|
samer@3
|
272 %
|
samer@3
|
273 % vecop ::
|
samer@3
|
274 % ([[D]],[[D]]->[[D]]) ~'some function requiring equal size args',
|
samer@3
|
275 % seq [[DX]] ~'first arg of size DX',
|
samer@3
|
276 % seq [[DY]] ~'second arg of size DY'
|
samer@3
|
277 % -> seq [[DZ]] ~'result of size DZ' :- DZ=max(DX,DY).
|
samer@3
|
278 %
|
samer@3
|
279 % The input sequences must maintain the same size throughout.
|
samer@3
|
280 function Z=vecop(F,X,Y)
|
samer@3
|
281 DX=size(X); DY=size(Y);
|
samer@3
|
282 E=max(length(DX),length(DY));
|
samer@3
|
283 EDX=pad1s(E,DX);
|
samer@3
|
284 EDY=pad1s(E,DY);
|
samer@3
|
285 if all(EDX>=EDY)
|
samer@3
|
286 S=EDX./EDY;
|
samer@3
|
287 Z=binop(X,Y,@(x,y)F(x,repmat(y,S)),['<' tostring(F) '>']);
|
samer@3
|
288 elseif all(EDY>=EDX)
|
samer@3
|
289 S=EDY./EDX;
|
samer@3
|
290 Z=binop(X,Y,@(x,y)F(repmat(x,S),y),['<' tostring(F) '>']);
|
samer@3
|
291 else
|
samer@3
|
292 DZ=max(EDX,EDY);
|
samer@3
|
293 Z=binop(X,Y,@(x,y)F(repmat(x,DZ./EDX),repmat(y,DZ./EDY)),['<' tostring(F) '>']);
|
samer@3
|
294 end
|
samer@3
|
295 end
|
samer@3
|
296
|
samer@3
|
297
|
samer@3
|
298 function h=plot(A,varargin), h=plotseq(@(x)plot(x,varargin{:}),A); end
|
samer@3
|
299 function h=imagesc(A,varargin), h=plotseq(@(x)imagesc(x,varargin{:}),A); end
|
samer@3
|
300
|
samer@3
|
301 function o=isfinite(A), o=map(@isfinite, A); end
|
samer@3
|
302 function o=isinf(A), o=map(@isinf, A); end
|
samer@3
|
303 function o=isnan(A), o=map(@isnan, A); end
|
samer@3
|
304 function y=powspec(x), y=map(@powspec,x); end
|
samer@3
|
305 function y=magspec(x), y=map(@magspec,x); end
|
samer@3
|
306 function y=phasespec(x), y=map(@phasespec,x); end
|
samer@3
|
307 function o=uminus(A), o=map(@uminus, A); end
|
samer@3
|
308 function y=exp(x), y=map(@exp,x); end
|
samer@3
|
309 function y=cos(x), y=map(@cos,x); end
|
samer@3
|
310 function y=sin(x), y=map(@sin,x); end
|
samer@3
|
311 function y=abs(x), y=map(@abs,x); end
|
samer@3
|
312 function y=sqrt(x), y=map(@sqrt,x); end
|
samer@3
|
313 function y=tanh(x), y=map(@tanh,x); end
|
samer@3
|
314 function y=log(x), y=map(@log,x); end
|
samer@3
|
315 function y=log10(x), y=map(@log10,x); end
|
samer@3
|
316 function y=log2(x), y=map(@log2,x); end
|
samer@3
|
317 function o=ctranspse(A), o=map(@ctranspose,A); end
|
samer@3
|
318 function o=transpse(A), o=map(@transpose,A); end
|
samer@3
|
319 function y=fft(x), y=map(@fft,x); end
|
samer@3
|
320 function y=ifft(x), y=map(@ifft,x); end
|
samer@3
|
321
|
samer@3
|
322 function o=reshape(source,varargin)
|
samer@3
|
323 % reshape - Map reshape over elements of sequence
|
samer@3
|
324 %
|
samer@3
|
325 % reshape :: seq [Size->A], ... - > seq [Size1->A].
|
samer@3
|
326 % Works exactly like the usual reshape function but when applied
|
samer@3
|
327 % to a sequence object, returns a new sequence.
|
samer@3
|
328
|
samer@3
|
329 sz=tosize(varargin{:});
|
samer@3
|
330 o=map(@(x)reshape(x,varargin{:}),source);
|
samer@3
|
331
|
samer@3
|
332 function s=charfn(sz,o)
|
samer@3
|
333 s=sprintf('%s >> reshape[%s]',tostring(source(o)),tostring(sz));
|
samer@3
|
334 end
|
samer@3
|
335 end
|
samer@3
|
336
|
samer@3
|
337 function y=paren(a,S)
|
samer@36
|
338 % paren - Map application of subsref with parentheses to sequence
|
samer@36
|
339 %
|
samer@36
|
340 % paren :: seq(A), subs -> seq(B) :- (subsref :: A, subs -> B).
|
samer@3
|
341
|
samer@36
|
342 % NOTE TO SELF: it would be good to work out the size of the
|
samer@36
|
343 % array that will result when the function is evaluated, to
|
samer@36
|
344 % save map evaluating it once on construction.
|
samer@3
|
345 y=map(@(z)subsref(z,S),a); % 'charfn',@(o)charfn(tostring(S.subs{:}),o));
|
samer@3
|
346
|
samer@3
|
347 function s=charfn(argstr,o)
|
samer@3
|
348 s=sprintf('%s >> (%s)',tostring(source(o)),argstr);
|
samer@3
|
349 end
|
samer@3
|
350 end
|
samer@3
|
351
|
samer@3
|
352 function o=plus(A,B), o=binop(A,B,@plus,'+'); end
|
samer@3
|
353 function o=power(A,B), o=binop(A,B,@power,'.^'); end
|
samer@3
|
354 function o=eq(A,B), o=binop(A,B,@eq,'=='); end
|
samer@3
|
355 function o=ge(A,B), o=binop(A,B,@ge,'>='); end
|
samer@3
|
356 function o=gt(A,B), o=binop(A,B,@gt,'>'); end
|
samer@3
|
357 function o=ldivide(A,B), o=binop(A,B,@ldivide,'.\'); end
|
samer@3
|
358 function o=le(A,B), o=binop(A,B,@le,'<='); end
|
samer@3
|
359 function o=lt(A,B), o=binop(A,B,@lt,'<'); end
|
samer@3
|
360 function o=times(A,B), o=binop(A,B,@times,'.*'); end
|
samer@3
|
361 function o=minus(A,B), o=binop(A,B,@minus,'-'); end
|
samer@3
|
362 function o=mldivide(A,B),o=binop(A,B,@mldivide,'\'); end
|
samer@3
|
363 function o=mod(A,B), o=binop(A,B,@mod,'mod'); end
|
samer@3
|
364 function o=mrdivide(A,B),o=binop(A,B,@mrdivide,'/'); end
|
samer@3
|
365 function o=rdivide(A,B), o=binop(A,B,@rdivide,'./'); end
|
samer@3
|
366 function o=mtimes(A,B), o=binop(A,B,@mtimes,'*'); end
|
samer@3
|
367
|
samer@3
|
368
|
samer@3
|
369 % max - max mapped over sequence (ie NOT aggregate)
|
samer@3
|
370 function o=cat(dim,varargin)
|
samer@3
|
371 if length(varargin)==2
|
samer@3
|
372 o=binfun(varargin{1},varargin{2},@(a,b)cat(dim,a,b),@(a,b)sprintf('cat(%d,%s,%s)',dim,a,b));
|
samer@3
|
373 else
|
samer@3
|
374 o=zipwith(@catdim,varargin{:});
|
samer@3
|
375 end
|
samer@3
|
376 function x=catdim(varargin), x=cat(dim,varargin); end
|
samer@3
|
377 end
|
samer@3
|
378
|
samer@3
|
379 function o=vertcat(varargin)
|
samer@3
|
380 if length(varargin)==2
|
samer@3
|
381 o=binfun(varargin{1},varargin{2},@vertcat,@(a,b)sprintf('[%s;%s]',a,b));
|
samer@3
|
382 else
|
samer@3
|
383 o=zipwith(@vertcat,varargin{:});
|
samer@3
|
384 end
|
samer@3
|
385 end
|
samer@3
|
386
|
samer@3
|
387 function o=horzcat(varargin)
|
samer@3
|
388 if length(varargin)==2
|
samer@3
|
389 o=binfun(varargin{1},varargin{2},@horzcat,@(a,b)sprintf('[%s,%s]',a,b));
|
samer@3
|
390 else
|
samer@3
|
391 o=zipwith(@horzcat,varargin{:});
|
samer@3
|
392 end
|
samer@3
|
393 end
|
samer@3
|
394 end
|
samer@3
|
395 end
|