Reputation: 4856
Any variables and their values originally stored in a function handle when it was created are lost if you convert the function handle to a string and back again using the func2str and str2func functions.
Is there a way to overcome this ?
Like
y = 1;
fun = @(x) x + y
fun_str = func2str(fun);
clear y
fun = eval(fun_str);
result = fun(12); % This doesn't work
Concrete I have a measurement method creating some data which then gets fitted (with the help of fminsearch
) against some custom functions for an alignment procedure. I would now like to store all the data for future analysis.
Since Data normally survives longer than code, I would like to make it independent from any source file. That's why I also convert the function handle to string. In the string only matlab builtin
functions are allowed to be called.
Now what I can do of course is to also store y
in the example. But I was wondering if there is a more elegant solution to this where I would not need to do this.
Upvotes: 1
Views: 1185
Reputation: 8401
The problem stems from Matlab storing the value of y
within a workspace local to the anonymous function while keeping the symbol y
part of the function definition.
Then, as you indicated, the func2str
function converts the definition without replacing the symbol with its value.
Since your example indicates that you want to function handle to work even after clearing the variable y
(both value and symbol) from the current workspace, I'd suggest two paths forward.
The first is to create the function handle with str2func
using the value of y
instead of letting the anonymous function hold it:
fun = str2func(['@(x) x + ',num2str(y)]);
The other is to write a variant of func2str
that does the value replacement after converting the function to a string.
The functions
function can be used to acquire the anonymous function's workspace, and some regular expressions can be used to replace the symbols with literals.
Here is a minimal working example:
function str = func2strrep(fun)
% Grab workspace information
info = functions(fun);
workspace = info.workspace{1};
symbols = fieldnames(workspace);
% Initialize string
str = func2str(fun);
% Replace workspace variables with numeric literals
if not(isempty(symbols))
seps = '[\s\+\-\*\/\\\^\:\=\>\<]';
expr = @(s) ['(',seps,'{1})',s,'(',seps,'{1})'];
for k = 1:numel(symbols)
symbol = symbols{k};
value = num2str(workspace.(symbol));
% Check end of string
str = regexprep(str,['(',seps,'{1})',symbol,'$'],['$1',value]);
% Check bulk
stro = regexprep(str,expr(symbol),['$1',value,'$2']);
%
% Put in loop for repeated replacement since
% 'all' is not working as I thought it would.
while not(strcmp(stro,str))
str = stro;
stro = regexprep(str,expr(symbol),['$1',value,'$2']);
end
end
end
end
I'm not that talented with regular expressions, so the replacement method may need some tweaking (particularly since regexprep
didn't replace what I thought would be all matches for repeated symbols).
However, this first go produces
>> y = 1;
>> z = 2;
>> yy = 3;
>> f = @(x) x + y + y + z + yy;
>> func2strrep(f)
ans =
@(x)x+1+1+2+3
Both approaches rely on num2str
and, therefore, are only good for scalar values.
For vector values, a num2str
variant that produces a valid array literal in string form is required for the method to work.
The second approach also only works for simple variable storage as in the example. Nested handles and closures and the-like will require a more complicated algorithm for symbol-value determination and possibly replacement.
Upvotes: 1