Code covered by the BSD License  

Highlights from
func2func.m v1.0 (Jul 2009)

func2func.m v1.0 (Jul 2009)

by

Carlos Adrian Vargas Aguilera

 

Updates anonymous function workspace. Workaround of STR2FUNC(FUNC2STR(f)) problem.

func2func(F,varargin)
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

Contact us