function F = func2func(F,varargin)
%FUNC2FUNC Updates anonymous function workspace.
%
% SYNTAX:
% F = func2func(F);
% F = func2func(F,'base');
% F = func2func(F,...,'VarName',VarValue);
% F = func2func(F,...,VarStruct);
%
% INPUT:
% F - Function handle or name or anonymous definition, as a
% single input or in a cell or structure array.
% 'base' - Updates function workspace with variables from 'base'
% workspace. Use 'caller' to use the workspace from within
% the function that calls this one. See EVALIN for details.
% DEFAULT: 'caller' (Not used by default if 'VN',VV is used)
% 'VN',VV - Pair inputs specifying the variable name to be updated with
% its corresponding value. Or it may be a variable structure,
% with variable names as fields: VarStruct.VN = VV.
% DEFAULT: (not used by default)
%
% OUTPUT:
% F - Updated function handle.
%
% DESCRIPTION:
% Anonymous functions is a great tool created since MATLAB v7. As you
% know, you can create an anonymous function with/without arguments
% but also with variables on your current workspace. For example,
% >> A = 10;
% >> F = @(x) A*x;
% gives
% >> F(5)
% ans =
% 50
% But, if you change the A value, it is not updated in the function
% definition.
% >> A = 20;
% >> F(5)
% ans =
% 50
% So, A is still 10 in F workspace. This is sometimes undesirable, and
% one should update once again F to get the new A.
% >> F = @(x) A*x;
% >> F(5)
% ans =
% 100
% But i) what happens if I only have the function handle? That is, I
% cannot updated like in the last step. One can think in use the
% FUNC2STR and STR2FUNC utilities as in:
% >> G = str2func(func2str(F));
% but, besides it returns a warning, the workspace of F is not
% transfered to G. So, one is forced to use the EVAL way:
% >> A = 30;
% >> F = eval(func2str(F));
% >> F(5)
% ans =
% 150
% and the problem is solved. But, an error occurs if F is not an
% anonymous function. This programs solves this problems and offers new
% options like updating only specific variables, or using variables from
% an specific workspace ('caller' o 'base'), F may be a cell of function
% handles, or an structure, or a function name string or anonymous
% definition.
%
% Besides, you can even define variables existing only on the function
% workspace like in:
% >> clear A
% >> F = func2func(@(x)A*x,'A',40);
% >> F(5)
% ans =
% 200
% Enjoy it!
%
% NOTE:
% * Optional inputs use its DEFAULT value when not given or [].
% * When given specific variables definition (either by pair inputs or
% an structure) the workspace is not used by default. To force the
% use of any of 'base' or 'caller' workspace give it in the second
% input.
% * When any variable in the anonymous function remains undefined, a
% warning is returned. To eliminate this warning use
% >> warning('off','CVARGAS:func2func:undefinedVariableOrFunction')
% before the use of this function.
%
% EXAMPLE:
% A = 5;
% F = @(x)A*x;
% F(1) % RESULT: 5
% A = 8;
% F(1) % RESULT: 5
% F = func2func(F);
% F(1) % RESULT: 8, with updated A value.
%
% SEE ALSO:
% FUNCTION_HANDLE, FUNCTIONS, FUNC2STR and STR2FUNC.
%
%
%
% ---
% MFILE: func2func.m
% VERSION: 1.0 (Jul 29, 2009) (<a href="matlab:web('http://www.mathworks.com/matlabcentral/fileexchange/authors/11258')">download</a>)
% MATLAB: 7.7.0.471 (R2008b)
% AUTHOR: Carlos Adrian Vargas Aguilera (MEXICO)
% CONTACT: nubeobscura@hotmail.com
% REVISIONS:
% 1.0 Released. (Jul 29, 2009)
% DISCLAIMER:
% func2func.m is provided "as is" without warranty of any kind, under the
% revised BSD license.
% Copyright (c) 2009 Carlos Adrian Vargas Aguilera
% INPUTS CHECK-IN
% -------------------------------------------------------------------------
% Default.
ws = 'caller'; % Default workspace. One of 'caller' or 'base'.
forceWs = false; % Forces to use the WS when variable name/value given.
% Checks number of inputs and outputs.
if nargin<1
error('CVARGAS:func2func:notEnoughInputs',...
'At least 1 input is required.')
elseif nargout>1
error('CVARGAS:func2func:tooManyOutputs',...
'At most 1 output is allowed.')
end
% Checks F input and forces it to be a cell array.
if isempty(F), return, end
[F,type,fieldNames] = checkF(F);
% Parse varargin.
[ws,forceWs,uWs] = parseVarargin(ws,forceWs,varargin{:});
clear varargin
% -------------------------------------------------------------------------
% MAIN
% -------------------------------------------------------------------------
% Gets number of functions.
N = numel(F);
% Main loop.
for n = 1:N
% Gets function properties.
fWs = functions(F{n});
% Checks if anonymous. If not, there is nothing to update.
if ~strcmp(fWs.type,'anonymous'), continue, end
% Gets function defnition (string).
if length(fWs.workspace)==1
fDef = fWs.function;
else
fDef = func2str(fWs.workspace{2}.F);
end
% Gets function workspace.
fWs = fWs.workspace{1};
% Gets function workspace variable names.
var = fieldnames(fWs);
Nvar = length(var);
% Looks for hidden variables.
if strcmp(fDef(2),'(')
ind = regexp(fDef,'[)]','once');
inp = symvar(fDef(1:ind));
war = symvar(fDef(ind+1:end)); war = {war{:}};
war(cellfun(@(x)any(strcmp(x,inp)),war)) = [];
war(cellfun(@(x)any(strcmp(x,var)),war)) = [];
var = {var{:},war{:}};
else
war = {};
end
% If no variables, there is nothing to update.
if isempty(var), continue, end
% a) Evaluates function F variables on THIS function workspace.
for k = 1:Nvar
eval([var{k} ' = fWs.(var{k});'])
end
% b) Evaluates workspace WS variables on THIS function workspace.
if forceWs
for k = 1:length(var)
try
eval([var{k} ' = evalin(ws,var{k});'])
catch
% continue
end
end
end
% c) Evaluates user specified variables on THIS function workspace.
uar = fieldnames(uWs);
for k = 1:numel(uar)
eval([uar{k} ' = uWs.(uar{k});'])
end
% finally, updates function.
F{n} = eval(fDef);
% Checks if all variables were updated.
if ~isempty(war)
war(cellfun(@(x)exist(x,'var')==1,war)) = [];
if ~isempty(war)
Nw = length(war);
if Nw==1, temp = []; else temp = Nw-1; end
list = war;
list(2,1:Nw-2) = {''', '''};
list(2,temp) = {''' and '''};
list(2,Nw) = {'''.'};
list = {'''' list{:}};
list = [list{:}];
warning('CVARGAS:func2func:undefinedVariableOrFunction',...
['The following variable(s) or function(s) on "' fDef ...
'" remained undefined: ' list])
end
end
% Deletes generated workspace.
clear(var{:},uar{:})
end
% OUTPUTS CHECK-OUT
% -------------------------------------------------------------------------
switch type
case 'function_handle'
F = F{1};
case 'cell'
%continue'
case 'structure'
F = cell2struct(F,fieldNames,1);
end
% =========================================================================
% SUBFUNCTIONS
% -------------------------------------------------------------------------
function [F,type,fieldNames] = checkF(F)
% Checks F input and forces it to be a cell array.
fieldNames = [];
if isa(F,'function_handle')
type = 'function_handle';
F = {F};
elseif iscell(F)
type = 'cell';
% Check functions.
ind = ~cellfun(@(x)isa(x,'function_handle'),F);
if any(ind)
if ~all(cellfun(@ischar,F(ind)))
error('CVARGAS:func2func:invalidCellFunctionChar',...
['Elements of cell function must be either function handles, name ' ...
'or anonymous definition string.'])
end
F(ind) = cellfun(@str2func2,F(ind),'UniformOutput',false);
end
elseif isstruct(F)
type = 'structure';
fieldNames = fieldnames(F);
F = struct2cell(F);
% Check functions.
ind = ~cellfun(@(x)isa(x,'function_handle'),F);
if any(ind)
if ~all(cellfun(@ischar,F(ind)))
error('CVARGAS:func2func:invalidStructureFunctionChar',...
['Elements of structure function must be either function handles, ' ...
'name or anonymous definition string.'])
end
F(ind) = cellfun(@str2func2,F(ind),'UniformOutput',false);
end
elseif ischar(F)
type = 'function_handle';
F = {str2func2(F)};
else
error('CVARGAS:func2func:invalidFunctionType',...
['Function input must be either a function handle, name or an ' ...
'anonymous function definition as a single input or in a cell or ' ...
'structure of functions.'])
end
function F = str2func2(F)
% My STR2FUNC that works with anonymous functions.
if strcmp(F(1),'@')
try
F = eval(F);
catch
error('CVARGAS:func2func:invalidCharFunctionAnonymous',...
['Incorrect anonymous function string definition "' F '".'])
end
else
try
F = str2func(F);
catch
error('CVARGAS:func2func:invalidCharFunctionName',...
['Unknown function name "' F '".'])
end
end
function [ws,forceWs,uWs] = parseVarargin(ws,forceWs,varargin)
% Parse varargin.
% Checks WS input.
if ~isempty(varargin) && ischar(varargin{1}) && ...
(strcmpi(varargin{1},'base') || strcmpi(varargin{1},'caller'))
ws = lower(varargin{1});
forceWs = true;
varargin(1) = [];
end
% Forces to use the workspace?
if isempty(varargin)
forceWs = true;
end
% Checks variable inputs.
if ~isempty(varargin) && isstruct(varargin{1})
% Structure input.
uWs = varargin{1};
if length(varargin)>1
warning('CVARGAS:func2func:ignoredInputs',...
'Ignored extra inputs after variable structure.')
end
else
% Paired inputs.
try
uWs = cell2struct(varargin(2:2:end),varargin(1:2:end),2);
catch
error('CVARGAS:func2func:incorrectPairedInputs',...
'Extra inputs must be paired variable name (string) and value.')
end
end
% [EOF] func2func.m