2017-02-24 75 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數組。
相關問題