2017-02-24 49 views
3

我希望能够做的是以编程方式更改匿名函数,例如通过将函数中的所有加号改为乘号。这个例子可以在许多情况下,这是可以做到如下:是否可以更改保留工作区的匿名函数?

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

回答

5

一个幼稚的做法是eval这样你就不会运行到str2func的障碍不允许访问本地变量来代替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®版本中可能会改变。

+2

对于温和的附加安全边界,您可以将结构字段另存为一个mat文件(https://www.mathworks.com/help/matlab/matlab_env/save-load- and-delete-workspace-variables.html#bvdx_92-1)并重新加载。它仍然很脏,但仍然使用“功能”。 –

2

一种可能的方式来做到这一点是将功能句柄保存到.mat文件(使用-v7.3标志,以便它创建了一个容易修改的HDF5文件),修改struct包含工作空间数据的文件内对于匿名函数(使用内置于MATLAB中的HDF5工具),然后再次从文件加载匿名函数。

这里是一个小功能,这正是这么做的(和它的工作原理相对简单的变量类型)

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 

而且你可以用它喜欢:

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 

一些注意事项包括:

  • 替换数据必须与原始数据大小相同
  • 这依赖于.mat文件的格式,对于匿名函数完全没有记录,所以它可能会在将来的版本中失败。
  • 目前,该功能工作区中的变量不适用于cell数组。
相关问题