是否可以更改保持工作空间的匿名功能?

时间:2017-02-24 15:00:27

标签: matlab anonymous-function

我希望能够以编程方式更改匿名函数,例如将所有加号更改为函数中的乘号。在许多情况下,这个例子可以按如下方式完成:

function f2 = changefunction(f1)
    fs = func2str(f1);
    fs(fs=='+') = '*';
    f2 = str2func(fs);
end

但请考虑示例

f = @(x) x+5;
a = 5;
g = @(x) x+a;

fg都是匿名函数,可以为插入的内容添加5;但f函数只能正确更改changefunction,而g将更改为任何输入错误的函数。

所以我的问题是是否可以从函数句柄中提取工作区并将其保留在创建的新函数句柄中?我需要以编程方式执行此操作,最好不使用内置函数functions

2 个答案:

答案 0 :(得分:5)

一个简单的实现是将str2func替换为eval,这样您就不会遇到str2func不允许访问本地变量的障碍。我们可以使用functions来获取输入函数句柄的工作空间信息。

例如:

a = 5;
f = @(x) x+a;
finfo = functions(f)

收率:

finfo = 

  struct with fields:

            function: '@(x)x+a'
                type: 'anonymous'
                file: 'X:\testcode-matlab\testcode.m'
           workspace: {[1×1 struct]}
    within_file_path: 'testcode'

其中workspace是包含结构(来自MathWorks ...)的单元格数组,其中包含函数句柄名称空间中的所有变量:

>> wspace = finfo.workspace{1}

wspace = 

  struct with fields:

    a: 5

使用此功能,天真的解决方案是遍历此工作空间中的变量,在changefunction的命名空间中分配它们,然后使用eval生成新的函数句柄。

例如:

function f2 = changefunction_new(f1)
    tmp = functions(f1);
    workspacevars = tmp.workspace{1};
    varnames = fieldnames(workspacevars);
    for ii = 1:length(varnames)
        evalstr = sprintf('%s = %d;', varnames{ii}, workspacevars.(varnames{ii}));
        eval(evalstr);
    end

    fs = func2str(f1);
    fs(fs=='+') = '*';
    f2 = eval(fs);
end

这里我假设变量是严格数字的。如果情况并非如此,您可以添加逻辑来检查要生成的数据类。

有了这个,我们有:

a = 5;
g = @(x) x+a;
test1 = changefunction(g);
test2 = changefunction_new(g);

>> g(1)

ans =

     6

>> test1(1)
Undefined function or variable 'a'.

Error in testcode>@(x)x*a

>> test2(1)

ans =

     5

所有这一切,最好的解决方案就是明确定义你的函数句柄。这可能很痛苦,但理解和调试要容易得多。

一些警告:

  • 因为eval任意执行传递给它的所有代码,所以它可以是非常危险的函数,必须谨慎使用。
  • functions的文档警告不要以编程方式使用它,因此在MATLAB版本更改时请注意检查行为:
  

使用functions功能仅用于查询和调试。

     

注意:不要以编程方式使用函数,因为它的行为可能会在后续的MATLAB®版本中发生变化。

答案 1 :(得分:2)

一种可行的方法是将函数句柄保存到.mat文件(使用-v7.3标志,以便创建一个易于修改的HDF5文件),修改{{1}在包含匿名函数的工作空间数据的文件中(使用MATLAB中内置的HDF5工具),然后从文件中再次加载匿名函数。

这是一个小功能,它完全正确(并且适用于相对简单的变量类型)

struct

您可以像以下一样使用它:

function result = modifyfunc(f, varname, value)
    % modifyfunc - Modify the workspace of an anonymous function
    %
    % INPUTS:
    %   f:          Function Handle, Anonymous function to modify
    %   varname:    String, Name of the variable to modify
    %   value:      Data to replace the specified variable

    % If the value is a struct, recursively modify the function handle
    if isstruct(value)
        fields = fieldnames(value);
        result = f;

        % Modify each field separately
        for k = 1:numel(fields)
            % Append the fieldname to the variable name and modify
            name = [varname, '.', fields{k}];
            result = modifyfunc(result, name, value.(fields{k}));
        end
        return;
    end

    % Write the anonymous function to an HDF5 file
    fname = tempname;
    save(fname, 'f', '-mat', '-v7.3');

    % Replace any "." in the variable name with "/" to construct the HDF5 path
    varname = strrep(varname, '.' , '/');

    % Now modify the data in the file
    h5write(fname, ['/#refs#/e/' varname], value);

    % Load the modified function handle from the file
    result = load(fname, '-mat');
    result = result.f;

    % Remove the temporary file
    delete(fname);
end

一些警告包括:

  • 替换数据必须与原始数据的大小相同
  • 这取决于.mat文件的格式,匿名函数完全没有文档,因此在将来的版本中可能会失败。
  • 这当前不适用于函数工作区中a = 1; b = struct('field', 2); f = @(x)disp(a + b.field + x); f(10) % 13 f2 = modifyfunc(f, 'a', 2); f2(10) % 14 f3 = modifyfunc(f2, 'b.field', 3); f3(10) % 15 b.field = 4; f4 = modifyfunc(f3, 'b', b); f4(10) % 16 数组。
  • 的变量