samer@3: % seq - Base class for sequences samer@3: classdef seq samer@36: % properties (Constant, GetAccess=public) samer@36: % NIL = seq.nil samer@36: % end samer@30: samer@3: methods (Abstract) samer@3: x=head(a) % seq(A) -> A. samer@3: b=next(a) % seq(A) -> seq(A). samer@3: s=elsize(a) % seq([E->T]) -> E:[[1,D]]. samer@3: s=tostring(a) % seq(A) -> string. samer@3: end samer@3: samer@3: methods (Sealed=true) samer@3: function varargout=size(a,n) samer@3: s=elsize(a); samer@3: if nargin>1, varargout={s(n)}; samer@3: elseif nargout<=1, varargout={s}; samer@3: else samer@3: varargout=[num2cell(s(1:nargout-1)),{prod(s(nargout:end))}]; samer@3: end samer@3: if any(isnan(cell2mat(varargout))) samer@3: error('seq:Element size indeterminate'); samer@3: end samer@3: end samer@3: samer@23: function l=length(a), l=max(size(a)); end samer@3: function n=numel(a, varargin), n=prod(size(a)); end samer@3: function n=end(a,k,m) samer@3: if m==length(elsize(a)), n=size(a,k); samer@3: else n=prod(size(a)); end samer@3: end samer@3: samer@37: function n=count(a), samer@23: n=0; while ~isempty(a), n=n+1; a=next(a); end samer@23: end samer@23: samer@3: function display(o) samer@3: display(sprintf(' %s :: seq([[%s]])\n', tostring(o), ... samer@8: catsep(',',cellmap(@fmt_dim,num2cell(elsize(o)))))); samer@3: function s=fmt_dim(x), if isnan(x), s='_'; else s=num2str(x); end; end samer@3: end samer@3: samer@3: function y=subsref(a,S) samer@3: switch S(1).type samer@3: case '()', y=paren(a,S(1)); samer@3: case '.', fn=S(1).subs; y=map(@(z)getfield(z,fn),a); samer@3: end samer@3: if length(S)>1, y=subsref(y,S(2:end)); end samer@3: end samer@3: end samer@3: samer@3: methods samer@3: function [x,b]=decons(a) samer@36: % decons - head and tail of sequence samer@36: % decons :: seq(A) -> A, seq(A). samer@3: x=head(a); samer@3: b=next(a); samer@3: end samer@3: samer@3: function z=last(y) samer@36: % last - get the last array in a sequence (there must be at least one element) samer@36: % last :: seq(A) -> A. samer@3: while ~isempty(y), x=y; y=next(x); end samer@3: z=head(x); samer@3: end samer@3: samer@3: function f=headfn(o), f=@()head(o); end samer@3: samer@3: samer@3: % ------------- WRAPPERS FOR OTHER SEQ CLASSES --------------------------- samer@36: function y=take(n,x), y=seq.taker.make(n,x); end; samer@23: function y=takewhile(f,x), y=seq.takewhile.make(f,x); end samer@3: samer@23: function s=drop(n,s), samer@36: % drop - Drop the first n alements of sequence samer@36: % drop :: natural, seq(A) -> seq(A) samer@23: for i=1:n samer@23: if ~isempty(s), s=next(s); end; samer@23: end samer@23: end samer@3: samer@3: function s=dropwhile(f,s) samer@36: % dropwhile - Drop elements of sequence that satisfy condition samer@36: % drop :: (A->bool), seq(A) -> seq(A) samer@3: while ~isempty(s) && f(head(s)), s=next(s); end samer@3: end samer@3: samer@3: function [x,tail]=nth1(n,X) samer@36: % nth1 - Get nth element of a sequence starting at 1 samer@36: % nth1 :: natural, seq(X) -> X, seq(X) ~'tail of sequence'. samer@3: z=drop(n-1,X); samer@3: x=head(z); samer@3: if nargout>=2, tail=next(z); end samer@3: end samer@3: samer@36: function Y=once(X), Y=cons(head(X),[]); end samer@3: % once - equivalent to take(1,...) samer@36: % once :: seq(A) -> seq(A). samer@36: samer@36: function x=map(fn,y) samer@36: % map - seq where values is a stateless function of another sequence samer@3: % samer@36: % map :: (A->B), seq(A) -> seq(B). samer@36: if isempty(y), x=nil; else x=seq.mapper(fn,y); end; samer@36: end samer@3: samer@36: function x=select(fn,y) samer@36: % select - Use boolean function to keep only matching elements from sequence samer@36: % samer@36: % select :: (A->bool), seq(A) -> seq(A). samer@36: x=seq.selector.make(fn,y); samer@36: end samer@36: samer@36: function y=cache(x), if isempty(x), y=nil; else y=seq.cacher(x); end; end samer@36: function y=buffer(x,varargin), y=seq.bufferer.make(x,varargin{:}); end samer@3: samer@13: function x=mapaccum(fn,s0,y) samer@13: if iscell(y), x=cmapaccum(fn,s0,y); samer@23: elseif isempty(y), x=nil; samer@36: else x=seq.stmapper(fn,s0,y); samer@23: end samer@13: end samer@13: samer@36: function x=scanl(fn,e,y,varargin) samer@36: % scanl - scanl combinator for sequences samer@36: % samer@36: % This function applies an associative operator to a list of arguments, samer@36: % starting from the left using the given starting element. samer@36: % samer@36: % scanl :: samer@36: % (X,Y->X) ~'associative binary operator', samer@36: % X ~'initial element', samer@36: % seq(Y) ~'a sequence' samer@36: % -> seq(X). samer@13: if iscell(y), x=cscanl(fn,e,y,varargin{:}); samer@23: elseif isempty(y), x=nil; samer@36: else x=seq.scanner(fn,e,y,varargin{:}); samer@23: end samer@13: end samer@3: samer@3: % concat - Concatenate sequences samer@3: % samer@23: % concat :: seq(seq(A)) -> seq(A). samer@36: function y=concat(x), y=seq.concatenator.make(x); end samer@3: samer@3: % append - Append one or more sequences samer@3: % samer@3: % append :: seq(A), seq(A), ... -> seq(A). samer@23: function x=append(varargin), x=concat(cellseq(varargin)); end samer@3: samer@23: function z=and(x,y), samer@23: if isempty(x) samer@23: if isempty(y), z=nil; else z=y; end samer@23: else samer@36: if isempty(y), z=x; else z=seq.concatenator(cons(x,cons(y,nil))); end samer@23: end samer@23: end samer@3: samer@23: function y=cycle(x) samer@36: % cycle - cycles through input sequence repeatedly samer@36: % samer@36: % cycle :: seq(A) -> seq(A). samer@23: if isempty(x), error('seq: cannot cycle empty sequence'); end; samer@36: y=seq.cycler(x); samer@23: end samer@3: samer@36: function y=bindcat(x,f) samer@36: % bindcat - sort of monadic bind for sequences. samer@36: % samer@36: % bindcat :: samer@36: % seq(A) ~ 'the first sequence', samer@36: % (A->seq(A)) ~ 'function to return second sequence given last element of first' samer@36: % -> seq(A) ~ 'resultant sequence'. samer@36: % samer@36: % The resulting sequence consists of the entire sequence represented by the samer@36: % first parameter, followed by the sequence obtained by applying the second samer@36: % parameter to the last element of the first sequence. samer@36: % samer@36: % Example: samer@36: % samer@36: % gather(2,bindcat(cellseq({1,2,3,4}),@(x)take(head(x),0))) samer@36: % samer@36: % ans = 1 2 3 4 0 0 0 0 samer@36: y=seq.binder(x,f); samer@36: end samer@3: samer@36: function y=subsample(n,x), if isempty(x), y=nil; else y=seq.subsampler(n,x); end; end samer@3: samer@3: function x=zip(varargin) samer@3: % zip - combine several sequences into one samer@3: % samer@3: % zip :: seq(A), seq(B), ... -> seq(cell {A,B,...}). samer@36: x=seq.zipper(@tuple,varargin); samer@3: function z=rtuple(varargin), z=varargin; end samer@3: end samer@3: samer@23: function x=zipwith(fn,varargin), samer@36: % zipwith - apply function to several sequences samer@36: % samer@36: % zipwith :: samer@36: % (A,B,...->X), samer@36: % seq(A), seq(B), ... samer@36: % -> seq(X). samer@23: if any(cell2mat(cellmap(@isempty,varargin))), x=nil; samer@36: else x=seq.zipper(fn,varargin); samer@23: end samer@23: end samer@3: samer@13: function x=zipaccum(fn,s0,varargin), samer@36: % zipaccum - apply stateful function to several sequences samer@36: % samer@36: % zipaccum :: samer@36: % (A,B,...,S->X,S), samer@36: % S, samer@36: % seq(A), seq(B), ... samer@36: % -> seq(X). samer@13: if isseq(s0) && any(map(@iscell,varargin)) % catch dispatching errors samer@13: z=czipcaccum(fn,s0,varargin{:}); samer@23: elseif any(cell2mat(cellmap(@isempty,varargin))), x=nil; samer@36: else x=seq.stzipper(fn,s0,varargin); samer@13: end samer@13: end samer@3: samer@3: function xx=unzip(y) samer@3: % unzip - Separate sequence of tuples into several sequences samer@3: % samer@3: % unzip :: samer@3: % seq({I:[D]->A(I)}). samer@3: % -> {I:[D]->seq(A(I))}. samer@3: % samer@23: % Note: input MUST be a non-empty sequence of constant size cell arrays. samer@3: % Output is a cell array of sequences of the same size and shape. samer@3: samer@3: xx=cell(size(y)); samer@3: for i=1:numel(xx) samer@3: xx{i}=map(@(a)a{i},y); samer@3: end samer@3: end samer@3: samer@23: function y=merge(f,varargin), samer@23: if all(cell2mat(cellmap(@isempty,varargin))), y=nil; samer@36: else y=seq.merger(f,varargin); end samer@23: end samer@3: samer@3: samer@3: % ------------------ MAPPERS ------------------------------------ samer@3: samer@3: % BINOP - Binary operation samer@3: % samer@36: % binop :: seq(A), seq(B), (A,B->C), string -> seq(C). samer@3: % samer@3: % Three cases samer@3: % A is seq, B is an array samer@3: % A is array, B is seq samer@3: function o=binop(A,B,fn,opstr) samer@3: o=binfun(A,B,fn,@(a,b)sprintf('(%s%s%s)',a,b,opstr)); samer@3: end samer@3: samer@3: function o=binfun(A,B,fn,fmtstr) samer@3: if isseq(A), samer@3: if isseq(B), o=zipwith(fn,A,B); %@(o)fmtstr(tostring(A),tostring(B))); samer@3: else samer@3: o=map(@(x)fn(x,B),A); %@(o)fmtstr('.',tostring(B))); samer@3: end samer@3: else samer@3: o=map(@(y)fn(A,y),B); %,@(o)fmtstr(tostring(A),'.')); samer@3: end samer@3: end samer@3: samer@3: % vecop - apply binary function to different sized array sequences samer@3: % samer@3: % vecop :: samer@3: % ([[D]],[[D]]->[[D]]) ~'some function requiring equal size args', samer@3: % seq [[DX]] ~'first arg of size DX', samer@3: % seq [[DY]] ~'second arg of size DY' samer@3: % -> seq [[DZ]] ~'result of size DZ' :- DZ=max(DX,DY). samer@3: % samer@3: % The input sequences must maintain the same size throughout. samer@3: function Z=vecop(F,X,Y) samer@3: DX=size(X); DY=size(Y); samer@3: E=max(length(DX),length(DY)); samer@3: EDX=pad1s(E,DX); samer@3: EDY=pad1s(E,DY); samer@3: if all(EDX>=EDY) samer@3: S=EDX./EDY; samer@3: Z=binop(X,Y,@(x,y)F(x,repmat(y,S)),['<' tostring(F) '>']); samer@3: elseif all(EDY>=EDX) samer@3: S=EDY./EDX; samer@3: Z=binop(X,Y,@(x,y)F(repmat(x,S),y),['<' tostring(F) '>']); samer@3: else samer@3: DZ=max(EDX,EDY); samer@3: Z=binop(X,Y,@(x,y)F(repmat(x,DZ./EDX),repmat(y,DZ./EDY)),['<' tostring(F) '>']); samer@3: end samer@3: end samer@3: samer@3: samer@3: function h=plot(A,varargin), h=plotseq(@(x)plot(x,varargin{:}),A); end samer@3: function h=imagesc(A,varargin), h=plotseq(@(x)imagesc(x,varargin{:}),A); end samer@3: samer@3: function o=isfinite(A), o=map(@isfinite, A); end samer@3: function o=isinf(A), o=map(@isinf, A); end samer@3: function o=isnan(A), o=map(@isnan, A); end samer@3: function y=powspec(x), y=map(@powspec,x); end samer@3: function y=magspec(x), y=map(@magspec,x); end samer@3: function y=phasespec(x), y=map(@phasespec,x); end samer@3: function o=uminus(A), o=map(@uminus, A); end samer@3: function y=exp(x), y=map(@exp,x); end samer@3: function y=cos(x), y=map(@cos,x); end samer@3: function y=sin(x), y=map(@sin,x); end samer@3: function y=abs(x), y=map(@abs,x); end samer@3: function y=sqrt(x), y=map(@sqrt,x); end samer@3: function y=tanh(x), y=map(@tanh,x); end samer@3: function y=log(x), y=map(@log,x); end samer@3: function y=log10(x), y=map(@log10,x); end samer@3: function y=log2(x), y=map(@log2,x); end samer@3: function o=ctranspse(A), o=map(@ctranspose,A); end samer@3: function o=transpse(A), o=map(@transpose,A); end samer@3: function y=fft(x), y=map(@fft,x); end samer@3: function y=ifft(x), y=map(@ifft,x); end samer@3: samer@3: function o=reshape(source,varargin) samer@3: % reshape - Map reshape over elements of sequence samer@3: % samer@3: % reshape :: seq [Size->A], ... - > seq [Size1->A]. samer@3: % Works exactly like the usual reshape function but when applied samer@3: % to a sequence object, returns a new sequence. samer@3: samer@3: sz=tosize(varargin{:}); samer@3: o=map(@(x)reshape(x,varargin{:}),source); samer@3: samer@3: function s=charfn(sz,o) samer@3: s=sprintf('%s >> reshape[%s]',tostring(source(o)),tostring(sz)); samer@3: end samer@3: end samer@3: samer@3: function y=paren(a,S) samer@36: % paren - Map application of subsref with parentheses to sequence samer@36: % samer@36: % paren :: seq(A), subs -> seq(B) :- (subsref :: A, subs -> B). samer@3: samer@36: % NOTE TO SELF: it would be good to work out the size of the samer@36: % array that will result when the function is evaluated, to samer@36: % save map evaluating it once on construction. samer@3: y=map(@(z)subsref(z,S),a); % 'charfn',@(o)charfn(tostring(S.subs{:}),o)); samer@3: samer@3: function s=charfn(argstr,o) samer@3: s=sprintf('%s >> (%s)',tostring(source(o)),argstr); samer@3: end samer@3: end samer@3: samer@3: function o=plus(A,B), o=binop(A,B,@plus,'+'); end samer@3: function o=power(A,B), o=binop(A,B,@power,'.^'); end samer@3: function o=eq(A,B), o=binop(A,B,@eq,'=='); end samer@3: function o=ge(A,B), o=binop(A,B,@ge,'>='); end samer@3: function o=gt(A,B), o=binop(A,B,@gt,'>'); end samer@3: function o=ldivide(A,B), o=binop(A,B,@ldivide,'.\'); end samer@3: function o=le(A,B), o=binop(A,B,@le,'<='); end samer@3: function o=lt(A,B), o=binop(A,B,@lt,'<'); end samer@3: function o=times(A,B), o=binop(A,B,@times,'.*'); end samer@3: function o=minus(A,B), o=binop(A,B,@minus,'-'); end samer@3: function o=mldivide(A,B),o=binop(A,B,@mldivide,'\'); end samer@3: function o=mod(A,B), o=binop(A,B,@mod,'mod'); end samer@3: function o=mrdivide(A,B),o=binop(A,B,@mrdivide,'/'); end samer@3: function o=rdivide(A,B), o=binop(A,B,@rdivide,'./'); end samer@3: function o=mtimes(A,B), o=binop(A,B,@mtimes,'*'); end samer@3: samer@3: samer@3: % max - max mapped over sequence (ie NOT aggregate) samer@3: function o=cat(dim,varargin) samer@3: if length(varargin)==2 samer@3: o=binfun(varargin{1},varargin{2},@(a,b)cat(dim,a,b),@(a,b)sprintf('cat(%d,%s,%s)',dim,a,b)); samer@3: else samer@3: o=zipwith(@catdim,varargin{:}); samer@3: end samer@3: function x=catdim(varargin), x=cat(dim,varargin); end samer@3: end samer@3: samer@3: function o=vertcat(varargin) samer@3: if length(varargin)==2 samer@3: o=binfun(varargin{1},varargin{2},@vertcat,@(a,b)sprintf('[%s;%s]',a,b)); samer@3: else samer@3: o=zipwith(@vertcat,varargin{:}); samer@3: end samer@3: end samer@3: samer@3: function o=horzcat(varargin) samer@3: if length(varargin)==2 samer@3: o=binfun(varargin{1},varargin{2},@horzcat,@(a,b)sprintf('[%s,%s]',a,b)); samer@3: else samer@3: o=zipwith(@horzcat,varargin{:}); samer@3: end samer@3: end samer@3: end samer@3: end