annotate sequences/@seq/seq.m @ 61:eff6bddf82e3 tip

Finally implemented perceptual brightness thing.
author samer
date Sun, 11 Oct 2015 10:20:42 +0100
parents beb8a3f4a345
children
rev   line source
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