将匿名函数扩展为字符串

wjm*_*ann 2 string matlab scope anonymous-function

我有一组匿名函数,我想将它们转换为字符串.通常我会使用func2str,但问题是我希望变量和内部函数扩展到它们的"真实"值.我遇到的问题是MATLAB通过名称保留它们,但是识别这些值.例

classdef Bclass

    properties
        equation
    end

    function obj = Bclass(inEquation, inValue)
        obj.equation = @(t,y) inEquation(t,y) * inValue;
    end

    function out = getStr(obj)
        out = func2str(obj.equation); 
    end
end
Run Code Online (Sandbox Code Playgroud)

问题是,当我真的希望它输出类似的东西时,如果我们说过,那么func2str调用正在输出.@(t,y) inEquation(t,y) * inValue@(t,y) t*y * 5b = Bclass(@(t,y) t*y, 5)

有没有办法从MATLAB中检索这些变量值?

gno*_*ice 5

可以这样做,但如果你的问题变得比上面给出的例子复杂得多(即更复杂的匿名函数,多个嵌套级别等),它很快就会变得非常困难.您必须使用该functions函数来获取有关函数句柄的信息,并且其行为可能会在不同版本之间发生变化.此外,你就必须做相当多的字符串操作(使用类似功能regexp,regexprep,strsplit,和strrep,因为我做如下).

我试图在这里包括最常用的方法,允许以下可能性:

  • inEquation可以是非匿名函数句柄(即@times).
  • inEquation 可以简单地按原样传递而不实际调用.
  • inEquation 可以在匿名函数中多次调用.
  • 输入参数的名称inEquation可以与调用它的名称不同obj.equation.
  • obj.equation 可以包含索引操作.

首先,我们将初始化一些变量以模仿您的示例:

f1 = @(m, n) m*n;  % Note the different variable names, but it will still work
inEquation = f1;
inValue = 5;
f2 = @(t, y) inEquation(t, y)*inValue;  % Function constructed using workspace variables
Run Code Online (Sandbox Code Playgroud)

接下来,我们将获取以下功能信息f2:

s = functions(f2);
varNames = fieldnames(s.workspace{1});
varValues = struct2cell(s.workspace{1});
out = s.function;
Run Code Online (Sandbox Code Playgroud)

workspace字段保存被用来构建变量名称和值f2,和function字段是你通过调用得到字符串func2strf2.我们还需要计算一些东西,以便我们可以正确地解析左括号中的开括号和右括号f2:

openIndex = (out == '(');
closeIndex = (out == ')');
parenIndex = cumsum(openIndex-[false closeIndex(1:end-1)]).*(openIndex | closeIndex);
Run Code Online (Sandbox Code Playgroud)

现在,我们将遍历工作区变量,将它们的值转换为字符串(如果可能),并将它们替换为out:

for iVar = 1:numel(varNames)

  name = varNames{iVar};
  value = varValues{iVar};

  if isa(value, 'function_handle')  % Workspace variable is a function handle

    value = func2str(value);
    callIndex = strfind(out, [name, '('])+numel(name);
    fcnParts = regexp(value, '@\({1}([^\)])*\){1}(\S)*', 'once', 'tokens');

    if isempty(callIndex)  % Function handle is not invoked
      if isempty(fcnParts)  % Non-anonymous function handle (i.e. @times)
        value = ['@' value];
      end
      out = strrep(out, name, value);
    elseif isempty(fcnParts)  % Invoked function handle (i.e. @times)
      out = strrep(out, name, value);
    else  % Invoked anonymous function handle
      for iCall = callIndex
        args = out(iCall+(1:find(parenIndex(iCall+1:end) == parenIndex(iCall), 1)-1));
        value = regexprep(fcnParts{2}, ...
                          strcat('(?<!\w)', strsplit(fcnParts{1}, ','), '(?!\w)'), ...
                          strsplit(args, ','));
        out = strrep(out, [name, '(', args, ')'], value);
      end
    end

  elseif isnumeric(value) && isscalar(value)  % Workspace variable is a numeric scalar
    out = strrep(out, name, num2str(value));
  end

end
Run Code Online (Sandbox Code Playgroud)

我们得到了预期的结果out:

>> out

out =

@(t,y)t*y*5
Run Code Online (Sandbox Code Playgroud)

请注意,这也将按预期使用非匿名函数句柄:

>> f1 = @times;
>> inEquation = f1;
>> inValue = 5;
>> f2 = @(t, y) inEquation(t, y)*inValue;

% Repeat above processing...

>> out

out =

@(t,y)times(t,y)*5
Run Code Online (Sandbox Code Playgroud)

它还可以用于一些更复杂的功能:

>> postVolt = @(g, V) -.05*g*(V+80);
>> preIdx = 5;
>> postIdx = 1;
>> index = 6;
>> obj.values = {};
>> f2 = @(t) postVolt(obj.values{preIdx}(index), obj.values{preIdx}(obj.voltIdx{postIdx}));

% Repeat above processing...

>> out

out =

@(t)-.05*obj.values{5}(6)*(obj.values{5}(obj.voltIdx{1})+80)
Run Code Online (Sandbox Code Playgroud)