将匿名函数扩展为字符串

时间:2018-07-10 21:08:06

标签: string matlab scope anonymous-function

我有一组匿名函数,我想将它们转换为字符串。通常,我只使用func2str,但是问题是我希望将变量和内部函数扩展为它们的“ true”值。我遇到的问题是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

问题在于,如果我们实际上说过func2str,那么在我实际上希望它输出诸如@(t,y) inEquation(t,y) * inValue之类的内容时,@(t,y) t*y * 5调用正在输出b = Bclass(@(t,y) t*y, 5)

是否可以从MATLAB中检索这些变量值?

1 个答案:

答案 0 :(得分:5)

可以执行此操作,但是如果您的问题比上面给出的示例更加复杂(即,更复杂的匿名函数,多个嵌套级别等),它可能会很快变得非常困难。您必须使用functions函数来获取有关函数句柄的信息,并且其行为可能在发行版之间发生变化。此外,您还必须进行大量的字符串操作(如下面所述,使用诸如regexpregexprepstrsplitstrrep之类的函数)。 / p>

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

  • 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

接下来,我们将获得f2的功能信息:

s = functions(f2);
varNames = fieldnames(s.workspace{1});
varValues = struct2cell(s.workspace{1});
out = s.function;

workspace字段保存用于构造f2的变量名称和值,而function字段是您通过在以下位置调用func2str获得的字符串f2。我们还需要计算一些内容,以便我们可以正确解析f2中的左括号和右括号:

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

现在,我们将遍历工作空间变量,将它们的值转换为字符串(如果可能),然后将其替换为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

我们得到out的理想结果:

>> out

out =

@(t,y)t*y*5

请注意,这也将与非匿名函数句柄一起正常工作:

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

% Repeat above processing...

>> out

out =

@(t,y)times(t,y)*5

它还将在一些更复杂的功能上起作用:

>> 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)