magu_
magu_

Reputation: 4856

Matlab: Convert function handle to string preserve parameters

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

Answers (1)

TroyHaskin
TroyHaskin

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

Related Questions