changeset 12:fbc0540a9208

Moved some high-order functions from funutils to arrutils, updated docs and cleaned up funutils.
author samer
date Mon, 14 Jan 2013 22:21:11 +0000
parents 8eec969fd4d3
children 03694e5c8365
files general/arrutils/foldcols.m general/arrutils/foldrcols.m general/arrutils/forels.m general/arrutils/mapcols.m general/arrutils/maprows.m general/arrutils/scancols.m general/arrutils/scanrcols.m general/arrutils/zipmap.m general/cellutils/@cell/foldr.m general/funutils/@function_handle/mtimes.m general/funutils/@function_handle/plus.m general/funutils/TODO general/funutils/apply.m general/funutils/bind.m general/funutils/bind1.m general/funutils/compose.m general/funutils/doer.m general/funutils/feval2cell.m general/funutils/flip.m general/funutils/foldcols.m general/funutils/foldrcols.m general/funutils/forels.m general/funutils/funseq.m general/funutils/mapcols.m general/funutils/maprows.m general/funutils/returns.m general/funutils/scancols.m general/funutils/scanner_add_plotter.m general/funutils/scanrcols.m general/funutils/tail.m general/funutils/tail_call.m general/funutils/tail_return.m general/funutils/zip.m general/funutils/zipmap.m
diffstat 34 files changed, 368 insertions(+), 334 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/general/arrutils/foldcols.m	Mon Jan 14 22:21:11 2013 +0000
@@ -0,0 +1,13 @@
+% foldcols - fold from left combinator for columns of an array
+%
+% foldcols :; 
+%    (A,[[N]]->A) ~'folding function',
+%    A            ~'initial value',
+%    [[N,L]]      ~'data to scan, sequence of length L'
+% -> A.
+
+function y=foldcols(f,y,X)
+	for i=1:size(X,2), y=f(y,X(:,i)); end
+end
+	
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/general/arrutils/foldrcols.m	Mon Jan 14 22:21:11 2013 +0000
@@ -0,0 +1,11 @@
+% foldcols :; 
+%    ([[M]], [[N]] -> [[M]])   ~'folding function',
+%    [[M]]                     ~'initial value',
+%    [[N,L]]                   ~'data to scan, sequence of length L'
+% -> [[M]].
+
+function y=foldrcols(f,y,X,varargin)
+	for i=size(X,2):-1:1, y=f(y,X(:,i)); end
+end
+	
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/general/arrutils/forels.m	Mon Jan 14 22:21:11 2013 +0000
@@ -0,0 +1,23 @@
+function forels(f,X,varargin)
+% forels - do an action for each element of an array in order
+%
+% forels :: 
+%    (A->action) ~'action to apply to each element', 
+%    [[Size]->A] ~'array of elements of type',
+%    options {
+%       pause :: bool/0
+%       drawnow :: bool/1
+%    }           ~'see ITERATE for more options'
+% -> action.
+%
+% Note, the array can be multidimensional - the standard order
+% cycles through the earlier indices before the later ones, eg
+% rows, then columns, then slices etc.
+
+	N=numel(X);
+	iterate(@g,1,varargin{:});
+
+	function i=g(i)
+		if i>N, i=[]; else f(X(i)); i=i+1; end
+	end
+end
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/general/arrutils/mapcols.m	Mon Jan 14 22:21:11 2013 +0000
@@ -0,0 +1,23 @@
+function Y=mapcols(f,X)
+% mapcols - Map a function of a vector over the columns of an array
+%
+% mapcols :: 
+%    ([[N]->A] -> [[M]->B]) ~'function maps a column of A to one of B',
+%    [[N,L]->A]
+% -> [[M,L]->B].
+
+n=size(X,2);
+if n==0, Y=[];
+else
+	y1=f(X(:,1)); 
+	if size(y1,2)>1
+		Y=repmat(y1(:),1,n);
+		for i=2:n
+			Y(:,i)=flatten(f(flatten(X(:,i))));
+		end
+	else
+		Y=repmat(y1,1,n);
+		for i=2:n, Y(:,i)=f(X(:,i)); end
+	end
+end
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/general/arrutils/maprows.m	Mon Jan 14 22:21:11 2013 +0000
@@ -0,0 +1,16 @@
+function Y=maprows(f,X)
+% maprows - Map a function of a vector over the rows of an array
+%
+% maprows :: 
+%    ([[1,N]->A] -> [[1,M]->B]) ~'function maps a row of A to a row of B',
+%    [[L,N]->A]
+% -> [[L,M]->B].
+
+n=size(X,1);
+if n==0, Y=[];
+else
+	for i=n:-1:1
+		Y(i,:)=f(X(i,:));
+	end
+end
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/general/arrutils/scancols.m	Mon Jan 14 22:21:11 2013 +0000
@@ -0,0 +1,12 @@
+% scancols :; 
+%    ([[M]], [[N]] -> [[M]])   ~'scannning function',
+%    [[M]]                     ~'initial value',
+%    [[N,L]]                   ~'data to scan, sequence of length L'
+% -> [[M,L]].
+
+function Y=scancols(f,y,X)
+	Y=zeros(size(y,1),size(X,2));
+	for i=1:size(X,2), y=f(y,X(:,i)); Y(:,i)=y; end
+end
+		
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/general/arrutils/scanrcols.m	Mon Jan 14 22:21:11 2013 +0000
@@ -0,0 +1,13 @@
+% scanrcols - scan array columns from the right
+%
+% scanrcols :; 
+%    ([[M]], [[N]] -> [[M]])   ~'scannning function',
+%    [[M]]                     ~'initial value',
+%    [[N,L]]                   ~'data to scan, sequence of length L'
+% -> [[M,L]].
+
+function Y=scanrcols(f,y,X,varargin)
+	Y=zeros(size(y,1),size(X,2));
+	for i=size(X,2):-1:1, y=f(y,X(:,i)); Y(:,i)=y; end
+		
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/general/arrutils/zipmap.m	Mon Jan 14 22:21:11 2013 +0000
@@ -0,0 +1,139 @@
+function varargout=zipmap(spec,f,varargin)
+% zipmap - Map and zip function over multidimensional arrays
+%
+% ZIPMAP allows a certain simple functions to be applied to multidimensional
+% array arguments, manipulating the dimensions to combine elementwise and
+% outer-product-like behaviour. (The name comes from functional programming
+% terminology: functions/operators like .* and + which apply elementwise to
+% vector arguments are said to zip their arguments, while outer products are
+% like mapping a function over a list, or a list of functions over another list.)
+%
+% Let f be a vectorised function which knows how to zip array arguments. Eg,
+% f(x,y)=x+y. If x and y are vectors, they must be the same size, and
+% the function is applied to corresponding pairs of elements, resulting in
+% a vector of the same size. With ZIPMAP, you can build functions, that, eg,
+% add EVERY pair of elements (not just corresponding pairs) resulting in a
+% 2D array: zipmap(0,@(x,y)x+y,[1:4]',[1:6]') will do this
+%
+% The domain of a multidimensional array can be described a list containing the
+% size of each dimension, as returned by the SIZE function (or SIZE1 which strips
+% trailing 1s). The arguments to f must have a certain domain structure:
+%
+% 	dom arg1 = [p1 zipdom]
+% 	dom arg2 = [p2 zipdom]
+%	         :
+%
+% where p1, p2, etc are the domains of the elementary function of which f
+% is a vectorisation, eg the elementary domain of .* can be scalars and
+% hence p1=p2=[], but the two elementary domains of CROSS are both [3], because it 
+% it operates on pairs of 3-D vectors. The zipdom part must be the same for
+% all arguments, otherwise, we cannot extract matching argument tuples.
+% If this is the case, the f must return a result:
+%
+% 	dom f(...) : [rdom zipdom]
+%
+% where rdom is the domain of the elementary function, eg [3] for the vector
+% valued CROSS product, but [] for the scalar valued DOT product. Note that
+% these domains can be vectors of any length from zero up, eg the determinant
+% of a square matrix has a domain of [n n].
+% 
+% ZIPDIM allows the domains of the arguments and result to be generalised as follows:
+%
+% 	dom arg1 : [p1 m1 zipdom]
+% 	dom arg2 : [p2 m2 zipdom]
+%	         :
+%
+%	dom zipdom(...) : [rdom m1 m2 ... zipdom ]
+%
+% All the mapped dimensions m1, m2 etc go into a sort of outer product where 
+% every combination of values is evaluated. In addition, the order of the
+% m1, m2 etc can be permuted in the result at essentially no extra computational cost.
+%
+% Usage: r=zipmap(spec,f,<varargs>)
+% 
+% spec defines how the domain of each of the arguments is to be interpreted, ie
+% how is is split into elementary, mappable, and zippable domains.
+%
+% If spec is a number z, then the last z dimensions of each argument are marked for
+% zipping, while the rest are for mapping. The elementary domains are assumed to
+% be scalar and hence p1, p2 etc =[]. So, zipmap(0,...) does the complete outer
+% product type of evaluation.
+%
+% If spec is structure, the field 'zipdims' determines how many dimensions are zipped.
+% The field 'pdims' is a the vector [length(p1) length(p2) ..], ie the dimensionality
+% of each elementary domain: 0 for scalars, 1 for vectors, 2 for matrices etc.
+% The optional field 'mapord' determines the reordering of the mapped domains, eg
+% if mapord=[2 1 3], then the result domain is [m2 m1 m3] instead of [m1 m2 m3].
+%
+% If spec is a cell array, it is interpreted as {zipdims pdims mapord}.
+%
+% Extra options
+%
+% If spec is a structure, it can contain further options: if spec.shift=1,
+% then the domains of all arguments are pre-shifted to remove singleton
+% dimensions before anything else is done. Amongst other effects, this means
+% that row vectors can be used in place of column vectors.
+
+	% unpack args from spec
+	if iscell(spec)
+		[zipdims,pdims,mapord]=getargs(spec,{0 [] []});
+	elseif isstruct(spec)
+		zipdims=getparam(spec,'zipdims',0);
+		pdims=getparam(spec,'pdims',[]);
+		mapord=getparam(spec,'mapord',[]);
+		if getparam(spec,'shift',0),
+			varargin=cellmap(@shiftdim,varargin);
+		end
+	elseif isscalar(spec)
+		zipdims=spec; pdims=[]; mapord=[];
+	end
+
+
+	argdoms=cellmap(@size1,varargin);
+	if isempty(pdims), pdims=zeros(size(varargin)); end
+	for i=1:length(varargin)
+		[pdoms{i},mapdoms{i},zipdoms{i}]=split(argdoms{i},pdims(i),zipdims);
+	end
+
+
+	% check all zipdoms match and use first
+	zipdom=zipdoms{1};
+	for i=2:length(zipdoms)
+		if ~all(zipdoms{i}==zipdom), error('Zip domains do not match'); end
+	end
+
+
+	if isempty(mapord), mapord=1:length(varargin); end
+	outerdom=cat(2,mapdoms{mapord});
+	outer1=cellmap(@to1s,mapdoms);
+	for i=1:length(varargin)
+		% need to reshape each arg and then repmat up to correct size
+		newsizes=outer1; newsizes{i}=mapdoms{i};
+		newsize=[pdoms{i} cat(2,newsizes{mapord}) zipdom];
+		varargin{i}=reshape(varargin{i},tosize([pdoms{i} cat(2,newsizes{mapord}) zipdom]));
+		newsizes=mapdoms; newsizes{i}=outer1{i};
+		newsize=[to1s(pdoms{i}) cat(2,newsizes{mapord}) to1s(zipdom)];
+		varargin{i}=repmat(varargin{i},tosize(newsize));
+	%	size(varargin{i})
+	end
+
+	[varargout{1:max(1,nargout)}]=feval(f,varargin{:});
+end
+
+function [a,b,c]=split(x,n,m)
+	a=x(1:n);
+	b=x(n+1:end-m);
+	c=x(end-m+1:end);
+end
+
+% getargs - get values of optional parameters with defaults
+%
+% getargs :: {I:[N]->(A(I) | nil)}, {I:[N]->A(I)} -> A(1), ..., A(N).
+function varargout=getargs(args,defs)
+	nout=max(length(args),length(defs));
+	varargout=defs;
+	for i=1:length(args)
+		if ~isempty(args{i}), varargout(i)=args(i); end
+	end
+end
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/general/cellutils/@cell/foldr.m	Mon Jan 14 22:21:11 2013 +0000
@@ -0,0 +1,13 @@
+function X=foldr(fn,e,args)
+% foldr - Fold combinator from functional programming
+%
+% This function applies an associative operator to a list of arguments,
+% starting from the right, eg
+% elements, eg foldr(@plus,0,{1,2,3,4}) = (1+(2+(3+(4+0)))).
+%
+% foldr :: 
+%    (Y,X->X)	~'associative binary operator',
+%    Y         ~'initial element',
+%    {[N]->Y} ~'list (cell array) of arguments (at least 2 elements)'
+% -> Y.
+X=e; for i=length(args):-1:1, X=fn(args{i},X); end
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/general/funutils/@function_handle/mtimes.m	Mon Jan 14 22:21:11 2013 +0000
@@ -0,0 +1,10 @@
+function h=compose(f,g)
+% compose - Constructs composition of two functions
+%
+% compose :: (B->C), (A{1:N}->B) -> (A{1:N}->C).
+%
+% returns the function h such that h(...) = f(g(...))
+
+	h=@fg;
+	function x=fg(varargin), x=f(g(varargin{:})); end
+end
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/general/funutils/@function_handle/plus.m	Mon Jan 14 22:21:11 2013 +0000
@@ -0,0 +1,6 @@
+function h=plus(f,g)
+% plus - Constructs parallel composition of two functions
+%
+% plus :: (A1->B1), (A2->B2) -> (A1,A2->B1,B2).
+	h=@(x,y)deal(f(x),g(y));
+end
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/general/funutils/TODO	Mon Jan 14 22:21:11 2013 +0000
@@ -0,0 +1,16 @@
+Think about algebra of function handles - funseq, scanner_add_plotter etc
+are special cases function combinators. What is the most general language
+for function algebra? Is it the arrow algebra restricted to functional arrows?
+
+
+Function combinators
+	composition :        f*g(x) = f(g(x))
+	parallel composition f+g(x,y)=f(x),g(y)
+
+Function types
+	state transformers: S->S
+	scanner:            Y,X->Y
+	folder:             X,S->S
+	accum:              Y,S->X,S
+
+Also what about actions? (Function with side-effects or dependence on global state)
--- a/general/funutils/apply.m	Mon Jan 14 15:52:31 2013 +0000
+++ b/general/funutils/apply.m	Mon Jan 14 22:21:11 2013 +0000
@@ -1,9 +1,9 @@
 function varargout=apply(fn,args)
 % apply - Apply function to list (cell array) of arguments
 %
-% Not typable.
-%
-% if [y1,y2,...]=fn(x1,x2,...), then
-% [y1,y2,...]=apply(fn,{x1,x2,...})
+% apply :: 
+%    (A:typelist(N)->B:typelist(M)) ~'function with N inputs and M outputs',
+%    cell { A:typelist(N) }         ~'cell array with N elements of types AX'
+% -> B{1}, ..., B{M}                ~'M return values of types B{1} to B{M}'.
 
-[varargout{1:max(1,nargout)}] = feval(fn,args{:});
+[varargout{1:max(1,nargout)}] = fn(args{:});
--- a/general/funutils/bind.m	Mon Jan 14 15:52:31 2013 +0000
+++ b/general/funutils/bind.m	Mon Jan 14 22:21:11 2013 +0000
@@ -1,6 +1,10 @@
-function cfn=bind(fn,varargin)
 % bind - Create a partially applied function
 % 
+% bind :: 
+%    func(A{1}, ..., A{N}->B{1:L})   ~'func from N inputs to L outputs',
+%    A{1}, ..., A{M}                 ~'M<=N input arguments of the correct types'
+% -> func(A{M+1}, ..., A{N}->B{1:L}) ~'func from remaining arguments to returns'.
+%
 % BIND(fn,a1,a2,...)
 %    If fn is an ordinary function, the parameters a1, a2 etc
 %    are bound as the first few arguments
@@ -8,8 +12,8 @@
 %    If fn is a function object as returned by FUNC, BIND, or PERM,
 %    the given parameters are bound to the free slots of the function
 %    and a new partially applied function is returned.
-%
 
+function cfn=bind(fn,varargin)
 if ischar(fn), fn=str2func(fn); end
 if ~isa(fn,'func'), fn=func(fn); end
 cfn=bind(fn,varargin{:});
--- a/general/funutils/bind1.m	Mon Jan 14 15:52:31 2013 +0000
+++ b/general/funutils/bind1.m	Mon Jan 14 22:21:11 2013 +0000
@@ -1,6 +1,9 @@
 % bind1 - Bind arguments to a function using Matlab closure
 %
-% bind1 :: (A1, A2, ... -> B1, B2 ..), A1 -> (A2, ...) -> B1, B2 ...
+% bind1 :: 
+%    func(A{1}, ..., A{N}->B{1:L})   ~'func from N inputs to L outputs',
+%    A{1}, ..., A{M}                 ~'M<=N input arguments of the correct types'
+% -> func(A{M+1}, ..., A{N}->B{1:L}) ~'func from remaining arguments to returns'.
 
 function g=bind1(f,varargin)
 	args=varargin;
--- a/general/funutils/compose.m	Mon Jan 14 15:52:31 2013 +0000
+++ b/general/funutils/compose.m	Mon Jan 14 22:21:11 2013 +0000
@@ -1,7 +1,7 @@
 function h=compose(f,g)
 % compose - Constructs composition of two functions
 %
-% compose :: (B->C), (A->B) -> (A->C).
+% compose :: (B->C), (A{1:N}->B) -> (A{1:N}->C).
 %
 % returns the function h such that h(...) = f(g(...))
 
--- a/general/funutils/doer.m	Mon Jan 14 15:52:31 2013 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,22 +0,0 @@
-function h=funseq(varargin)
-% funseq - Constructs sequential application of several functions
-%
-% h=funseq(f,...,g)
-%
-% returns the function h such that h(...) = g(...) but
-% all functions are evaluated in order.
-
-	fns=varargin;
-	h=@seq;
-
-	function varargout=seq(varargin)
-		for i=1:length(fns)-1
-			feval(fns{i},varargin{:});
-		end
-		if nargout==0,
-			feval(fns{end},varargin{:});
-		else
-			[varargout{1:nargout}]=feval(fns{end},varargin{:});
-		end
-	end
-end
--- a/general/funutils/feval2cell.m	Mon Jan 14 15:52:31 2013 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,12 +0,0 @@
-function Y=returns(I,F,varargin)
-% returns - Evaluate function and return multiple values in cell array
-%
-% returns ::
-%    I:[[M]->[N]]                       ~'M numbers between 1 and N',
-%    (A(1),...,A(L) -> B(1), ..., B(N)) ~'function with L in and N outputs',
-%    A(1), ..., A(L)                    ~'arguments for function'
-% -> cell { B(I(1)), ..., B(I(M)) }     ~'selected returns in a cell array'.
-
-Z=cell(max(I),1);
-[Z{:}]=F(varargin{:});
-Y=Z(I);
--- a/general/funutils/flip.m	Mon Jan 14 15:52:31 2013 +0000
+++ b/general/funutils/flip.m	Mon Jan 14 22:21:11 2013 +0000
@@ -2,6 +2,7 @@
 % flip - Flip order of first two arguments to a function
 %
 % flip :: (A,B->C) -> (B,A->C).
+% flip :: (A,B,D{1:N}->C) -> (B,A,D{1:N}->C).
 
 if isa(f,'func'), 
 	g=perm(f,[2,1]);
--- a/general/funutils/foldcols.m	Mon Jan 14 15:52:31 2013 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,24 +0,0 @@
-% foldcols - fold from left combinator for columns of an array
-%
-% foldcols :; 
-%    (A,[[N]]->A) ~'folding function',
-%    A            ~'initial value',
-%    [[N,L]]      ~'data to scan, sequence of length L'
-% -> A.
-
-function y=foldcols(f,y,X,varargin)
-	if nargin>3
-		opts=prefs('draw',0,varargin{:});
-		for i=1:size(X,2)
-			y1=f(y,X(:,i));
-			if opts.draw
-				opts.plotfn(i,y,X(:,i),y1); 
-			end
-			optpause(opts);
-			y=y1;
-		end
-	else % streamlined version
-		for i=1:size(X,2), y=f(y,X(:,i)); end
-	end
-	
-
--- a/general/funutils/foldrcols.m	Mon Jan 14 15:52:31 2013 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,22 +0,0 @@
-% foldcols :; 
-%    ([[M]], [[N]] -> [[M]])   ~'folding function',
-%    [[M]]                     ~'initial value',
-%    [[N,L]]                   ~'data to scan, sequence of length L'
-% -> [[M]].
-
-function y=foldrcols(f,y,X,varargin)
-	if nargin>3
-		opts=prefs('draw',0,varargin{:});
-		for i=size(X,2):-1:1
-			y1=f(y,X(:,i));
-			if opts.draw
-				opts.plotfn(i,y,X(:,i),y1); 
-			end
-			optpause(opts);
-			y=y1;
-		end
-	else % streamlined version
-		for i=size(X,2):-1:1, y=f(y,X(:,i)); end
-	end
-	
-
--- a/general/funutils/forels.m	Mon Jan 14 15:52:31 2013 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,23 +0,0 @@
-function forels(f,X,varargin)
-% forels - do an action for each element of an array in order
-%
-% forels :: 
-%    (A->action) ~'action to apply to each element', 
-%    [[Size]->A] ~'array of elements of type',
-%    options {
-%       pause :: bool/0
-%       drawnow :: bool/1
-%    }           ~'see ITERATE for more options'
-% -> action.
-%
-% Note, the array can be multidimensional - the standard order
-% cycles through the earlier indices before the later ones, eg
-% rows, then columns, then slices etc.
-
-	N=numel(X);
-	iterate(@g,1,varargin{:});
-
-	function i=g(i)
-		if i>N, i=[]; else f(X(i)); i=i+1; end
-	end
-end
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/general/funutils/funseq.m	Mon Jan 14 22:21:11 2013 +0000
@@ -0,0 +1,22 @@
+function h=funseq(varargin)
+% funseq - Constructs sequential application of several functions
+%
+% h=funseq(f,...,g)
+%
+% returns the function h such that h(...) = g(...) but
+% all functions are evaluated in order.
+
+	fns=varargin;
+	h=@seq;
+
+	function varargout=seq(varargin)
+		for i=1:length(fns)-1
+			feval(fns{i},varargin{:});
+		end
+		if nargout==0,
+			feval(fns{end},varargin{:});
+		else
+			[varargout{1:nargout}]=feval(fns{end},varargin{:});
+		end
+	end
+end
--- a/general/funutils/mapcols.m	Mon Jan 14 15:52:31 2013 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,16 +0,0 @@
-function Y=mapcols(f,X)
-% mapcols - Map a function of a vector over the columns of an array
-%
-% mapcols :: 
-%    ([[N]->A] -> [[M]->B]) ~'function maps a column of A to one of B',
-%    [[N,L]->A]
-% -> [[M,L]->B].
-
-n=size(X,2);
-if n==0, Y=[];
-else
-	for i=n:-1:1
-		Y(:,i)=flatten(feval(f,flatten(X(:,i))));
-	end
-end
-
--- a/general/funutils/maprows.m	Mon Jan 14 15:52:31 2013 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,16 +0,0 @@
-function Y=maprows(f,X)
-% maprows - Map a function of a vector over the rows of an array
-%
-% maprows :: 
-%    ([[1,N]->A] -> [[1,M]->B]) ~'function maps a row of A to a row of B',
-%    [[L,N]->A]
-% -> [[L,M]->B].
-
-n=size(X,1);
-if n==0, Y=[];
-else
-	for i=n:-1:1
-		Y(i,:)=f(X(i,:));
-	end
-end
-
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/general/funutils/returns.m	Mon Jan 14 22:21:11 2013 +0000
@@ -0,0 +1,12 @@
+function Y=returns(I,F,varargin)
+% returns - Evaluate function and return multiple values in cell array
+%
+% returns ::
+%    I:[[M]->[N]]                       ~'M numbers between 1 and N',
+%    (A(1),...,A(L) -> B(1), ..., B(N)) ~'function with L in and N outputs',
+%    A(1), ..., A(L)                    ~'arguments for function'
+% -> cell { B(I(1)), ..., B(I(M)) }     ~'selected returns in a cell array'.
+
+Z=cell(max(I),1);
+[Z{:}]=F(varargin{:});
+Y=Z(I);
--- a/general/funutils/scancols.m	Mon Jan 14 15:52:31 2013 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,23 +0,0 @@
-% scancols :; 
-%    ([[M]], [[N]] -> [[M]])   ~'scannning function',
-%    [[M]]                     ~'initial value',
-%    [[N,L]]                   ~'data to scan, sequence of length L'
-% -> [[M,L]].
-
-function Y=scancols(f,y,X,varargin)
-	Y=zeros(size(y,1),size(X,2));
-
-	if nargin>3
-		opts=prefs('draw',0,varargin{:});
-		for i=1:size(X,2)
-			y1=f(y,X(:,i));
-			Y(:,i)=y1;
-			if opts.draw, opts.plotfn(i,y,X(:,i),y1);  end
-			optpause(opts);
-			y=y1;
-		end
-	else
-		for i=1:size(X,2), y=f(y,X(:,i)); Y(:,i)=y; end
-	end
-		
-
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/general/funutils/scanner_add_plotter.m	Mon Jan 14 22:21:11 2013 +0000
@@ -0,0 +1,16 @@
+% scanner_add_plotter - Add plotting capabilities to scan function
+%
+% scanner_add_plotter ::
+%    (S,X->S) ~'scanning function',
+%    options {
+%       draw    :: bool/false ~'enable drawing if true';
+%       pause   :: natural/0 ~'pause duration in ms';
+%       drawnow :: bool/true ~'whether or not to call drawnow afterwards'
+%    }
+% -> (S,X->X) ~'new scanning function'.
+
+function f1=scanner_add_plotter(f,opts)
+	opts=prefs('draw',0,varargin{:});
+	if ~opts.draw, f1=f; else ps=pauser(opts); f1=@g; end
+	function y1=g(y,x), y1={f(y,x),i}; opts.plotfn(x,y,y1); ps(); end
+end
--- a/general/funutils/scanrcols.m	Mon Jan 14 15:52:31 2013 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,25 +0,0 @@
-% scanrcols - scan array columns from the right
-%
-% scanrcols :; 
-%    ([[M]], [[N]] -> [[M]])   ~'scannning function',
-%    [[M]]                     ~'initial value',
-%    [[N,L]]                   ~'data to scan, sequence of length L'
-% -> [[M,L]].
-
-function Y=scanrcols(f,y,X,varargin)
-	Y=zeros(size(y,1),size(X,2));
-
-	if nargin>3
-		opts=prefs('draw',0,varargin{:});
-		for i=size(X,2):-1:1
-			y1=f(y,X(:,i));
-			Y(:,i)=y1;
-			if opts.draw, opts.plotfn(i,y,X(:,i),y1);  end
-			optpause(opts);
-			y=y1;
-		end
-	else
-		for i=size(X,2):-1:1, y=f(y,X(:,i)); Y(:,i)=y; end
-	end
-		
-
--- a/general/funutils/tail.m	Mon Jan 14 15:52:31 2013 +0000
+++ b/general/funutils/tail.m	Mon Jan 14 22:21:11 2013 +0000
@@ -5,7 +5,7 @@
 % thunk(B{1:M}) ::= 
 %    exists C{1:L} . cell { C{1:L}->B{1:M}, cell { C{1:L} } }
 %  | cell { {0}, cell { B{1:L} } }
-%  | unit -> B{1:M}.
+%  | void -> B{1:M}.
 
 function varargout=feval_tail1(fn,varargin)
 	ret=fn(varargin{:});
--- a/general/funutils/tail_call.m	Mon Jan 14 15:52:31 2013 +0000
+++ b/general/funutils/tail_call.m	Mon Jan 14 22:21:11 2013 +0000
@@ -5,5 +5,5 @@
 % thunk(B{1:M}) ::= 
 %    exists C{1:L} . cell { C{1:L}->B{1:M}, cell { C{1:L} } }
 %  | cell { {0}, cell { B{1:L} } }
-%  | unit -> B{1:M}.
+%  | void -> B{1:M}.
 function ret=tail_call(fn,varargin), ret={fn,varargin}; end
--- a/general/funutils/tail_return.m	Mon Jan 14 15:52:31 2013 +0000
+++ b/general/funutils/tail_return.m	Mon Jan 14 22:21:11 2013 +0000
@@ -5,5 +5,5 @@
 % thunk(B{1:M}) ::= 
 %    exists C{1:L} . cell { C{1:L}->B{1:M}, cell { C{1:L} } }
 %  | cell { {0}, cell { B{1:L} } }
-%  | unit -> B{1:M}.
+%  | void -> B{1:M}.
 function ret=tail_return(varargin), ret={0,varargin}; end
--- a/general/funutils/zip.m	Mon Jan 14 15:52:31 2013 +0000
+++ b/general/funutils/zip.m	Mon Jan 14 22:21:11 2013 +0000
@@ -1,4 +1,7 @@
 % zip - Polymorphic zip using zipwith(@tuple,...)
+%
+% zip :: seq(A1), seq(A2), ... -> seq(cell {A1,A2,..}).
+% zip :: cells(A1), cells(A2), ... -> cells(cell {A1,A2,..}).
 function y=zip(varargin)
 	y=zipwith(@tuple,varargin{:});
 end
--- a/general/funutils/zipmap.m	Mon Jan 14 15:52:31 2013 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,139 +0,0 @@
-function varargout=zipmap(spec,f,varargin)
-% zipmap - Map and zip function over multidimensional arrays
-%
-% ZIPMAP allows a certain simple functions to be applied to multidimensional
-% array arguments, manipulating the dimensions to combine elementwise and
-% outer-product-like behaviour. (The name comes from functional programming
-% terminology: functions/operators like .* and + which apply elementwise to
-% vector arguments are said to zip their arguments, while outer products are
-% like mapping a function over a list, or a list of functions over another list.)
-%
-% Let f be a vectorised function which knows how to zip array arguments. Eg,
-% f(x,y)=x+y. If x and y are vectors, they must be the same size, and
-% the function is applied to corresponding pairs of elements, resulting in
-% a vector of the same size. With ZIPMAP, you can build functions, that, eg,
-% add EVERY pair of elements (not just corresponding pairs) resulting in a
-% 2D array: zipmap(0,inline('x+y','x','y'),[1:4]',[1:6]') will do this
-%
-% The domain of a multidimensional array can be described a list containing the
-% size of each dimension, as returned by the SIZE function (or SIZE1 which strips
-% trailing 1s). The arguments to f must have a certain domain structure:
-%
-% 	dom arg1 = [p1 zipdom]
-% 	dom arg2 = [p2 zipdom]
-%	         :
-%
-% where p1, p2, etc are the domains of the elementary function of which f
-% is a vectorisation, eg the elementary domain of .* can be scalars and
-% hence p1=p2=[], but the two elementary domains of CROSS are both [3], because it 
-% it operates on pairs of 3-D vectors. The zipdom part must be the same for
-% all arguments, otherwise, we cannot extract matching argument tuples.
-% If this is the case, the f must return a result:
-%
-% 	dom f(...) : [rdom zipdom]
-%
-% where rdom is the domain of the elementary function, eg [3] for the vector
-% valued CROSS product, but [] for the scalar valued DOT product. Note that
-% these domains can be vectors of any length from zero up, eg the determinant
-% of a square matrix has a domain of [n n].
-% 
-% ZIPDIM allows the domains of the arguments and result to be generalised as follows:
-%
-% 	dom arg1 : [p1 m1 zipdom]
-% 	dom arg2 : [p2 m2 zipdom]
-%	         :
-%
-%	dom zipdom(...) : [rdom m1 m2 ... zipdom ]
-%
-% All the mapped dimensions m1, m2 etc go into a sort of outer product where 
-% every combination of values is evaluated. In addition, the order of the
-% m1, m2 etc can be permuted in the result at essentially no extra computational cost.
-%
-% Usage: r=zipmap(spec,f,<varargs>)
-% 
-% spec defines how the domain of each of the arguments is to be interpreted, ie
-% how is is split into elementary, mappable, and zippable domains.
-%
-% If spec is a number z, then the last z dimensions of each argument are marked for
-% zipping, while the rest are for mapping. The elementary domains are assumed to
-% be scalar and hence p1, p2 etc =[]. So, zipmap(0,...) does the complete outer
-% product type of evaluation.
-%
-% If spec is structure, the field 'zipdims' determines how many dimensions are zipped.
-% The field 'pdims' is a the vector [length(p1) length(p2) ..], ie the dimensionality
-% of each elementary domain: 0 for scalars, 1 for vectors, 2 for matrices etc.
-% The optional field 'mapord' determines the reordering of the mapped domains, eg
-% if mapord=[2 1 3], then the result domain is [m2 m1 m3] instead of [m1 m2 m3].
-%
-% If spec is a cell array, it is interpreted as {zipdims pdims mapord}.
-%
-% Extra options
-%
-% If spec is a structure, it can contain further options: if spec.shift=1,
-% then the domains of all arguments are pre-shifted to remove singleton
-% dimensions before anything else is done. Amongst other effects, this means
-% that row vectors can be used in place of column vectors.
-
-
-% unpack args from spec
-if iscell(spec)
-	[zipdims,pdims,mapord]=getargs(spec,{0 [] []});
-elseif isstruct(spec)
-	zipdims=getparam(spec,'zipdims',0);
-	pdims=getparam(spec,'pdims',[]);
-	mapord=getparam(spec,'mapord',[]);
-	if getparam(spec,'shift',0),
-		varargin=cellmap(@shiftdim,varargin);
-	end
-elseif isscalar(spec)
-	zipdims=spec; pdims=[]; mapord=[];
-end
-
-
-argdoms=cellmap(@size1,varargin);
-if isempty(pdims), pdims=zeros(size(varargin)); end
-for i=1:length(varargin)
-	[pdoms{i},mapdoms{i},zipdoms{i}]=split(argdoms{i},pdims(i),zipdims);
-end
-
-
-% check all zipdoms match and use first
-zipdom=zipdoms{1};
-for i=2:length(zipdoms)
-	if ~all(zipdoms{i}==zipdom), error('Zip domains do not match'); end
-end
-
-
-if isempty(mapord), mapord=1:length(varargin); end
-outerdom=cat(2,mapdoms{mapord});
-outer1=cellmap(@to1s,mapdoms);
-for i=1:length(varargin)
-	% need to reshape each arg and then repmat up to correct size
-	newsizes=outer1; newsizes{i}=mapdoms{i};
-	newsize=[pdoms{i} cat(2,newsizes{mapord}) zipdom];
-	varargin{i}=reshape(varargin{i},tosize([pdoms{i} cat(2,newsizes{mapord}) zipdom]));
-	newsizes=mapdoms; newsizes{i}=outer1{i};
-	newsize=[to1s(pdoms{i}) cat(2,newsizes{mapord}) to1s(zipdom)];
-	varargin{i}=repmat(varargin{i},tosize(newsize));
-%	size(varargin{i})
-end
-
-[varargout{1:max(1,nargout)}]=feval(f,varargin{:});
-
-function [a,b,c]=split(x,n,m)
-	a=x(1:n);
-	b=x(n+1:end-m);
-	c=x(end-m+1:end);
-
-% getargs - get values of optional parameters with defaults
-%
-% getargs :: {I:[N]->(A(I) | nil)}, {I:[N]->A(I)} -> A(1), ..., A(N).
-
-function varargout=getargs(args,defs)
-
-nout=max(length(args),length(defs));
-varargout=defs;
-for i=1:length(args)
-	if ~isempty(args{i}), varargout(i)=args(i); end
-end
-