【问题标题】:Expanding anonymous function into a string将匿名函数扩展为字符串
【发布时间】:2018-07-10 21:08:06
【问题描述】:

我有一组匿名函数,我想将它们转换成字符串。通常我只会使用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

问题是func2str 调用正在输出@(t,y) inEquation(t,y) * inValue,而我实际上希望它输出类似@(t,y) t*y * 5 的东西,如果我们说的是b = Bclass(@(t,y) t*y, 5)

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

【问题讨论】:

    标签: string matlab scope anonymous-function


    【解决方案1】:

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

    我已尝试在此处包含我能做到的最通用的方法,允许以下可能性:

    • 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 字段是您通过在f2 上调用func2str 获得的字符串。我们还需要计算一些东西,以便正确解析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)
    

    【讨论】:

    • 新版好看!然而,这个版本似乎不适用于我的特定示例:@(t)postVolt(obj.values{preIdx}(index),obj.values{preIdx}(obj.voltIdx{postIdx})),其中postVolt = @(g, V) -.05*g*(V+80);preIdxpostIdx 都是数字。此代码输出 '@(t)-.05*obj.values{5}(6*(obj.values{5}(6+80),obj.values{5}(obj.voltIdx{1}))' 而不是 '@(t)-.05*obj.values{5}(6)*(obj.values{5}(obj.voltIdx{1})+80)。你当然已经提供了足够多的帮助,我只是想让你知道,以防你想让它没有错误
    • @wjmccann:这是我所指的那些“更复杂”的问题之一。 ;) 问题是匿名函数中的额外索引。嵌套括号使使用regexp 在函数调用中搜索参数列表变得复杂。需要另一种方法。以后我可能会再试一次。
    • @wjmccann:新版本!这个现在可以处理您更复杂的情况了。
    猜你喜欢
    • 2016-03-09
    • 1970-01-01
    • 2015-05-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多