【问题标题】:Passing and saving anonymous function in matlab在matlab中传递和保存匿名函数
【发布时间】:2012-03-06 08:13:25
【问题描述】:

我想要一个函数(例如 fit 函数)返回一个匿名函数(通常存储在 struct 中),我可以保存并在以后使用。但是,传递@func 往往会传递函数指针而不是函数本身。 inline 函数是唯一的方法吗?我想避免inline,因为它非常慢。

如果这个问题不清楚,这里有一个有问题的代码示例:我在一些PATH 中写了一个testFunc.m 文件

    %testFunc.m
    function myfunc = testFunc()
        myfunc = @(x) x.^2;
    end

然后我将函数存储在struct 中。 (我知道这真的应该是一个对象!)

    >> mystruct = struct;
    >> mystruct.func = testFunc()
    >> mstruct.x = [1 2 3];
    >> save('myfile.mat','mystruct')
    >> mystruct.func(mystruct.x)

    ans = 

         1     4     9

如果我随后移动myfile.mattestFunc.m 并加载myfile.mat,我将无法加载旧结构。相反,我得到了错误:

    >> cd 'otherdir'
    >> load('../myfile.mat')

    Warning: Could not find appropriate function on path
    loading function handle PATH/testFunc.m>@(x)x.^2 

我知道有问题,因为如果我检查functions

    >> functions(mystruct.func)

    ans = 

         function: '@(x)x.^2'
             type: 'anonymous'
             file: 'PATH/testFunc.m'
        workspace: {2x1 cell}

有什么方法可以去除文件工作区信息吗? inline 函数是唯一的解决方案吗?

【问题讨论】:

  • 我想我更明白这一点——感谢大家的讨论!目前,我正在使用内联函数,即使它们很慢: %testFunc.m function myfunc = testFunc() myfunc = inline('x.^2');结束
  • 啊 - 如果您只需要可以表示为内联的函数,我认为您可以使用来自公共、干净工作区的“已清理”匿名函数作为 inline() 的直接替代品并避免放缓;查看我的更新答案。
  • 另外,如果您还没有,您可以使用 addpath() 来管理您的路径。这使您可以将源代码保留在 Matlab 路径中,而不管您当前的目录是什么。

标签: matlab anonymous-function


【解决方案1】:

简单案例

如果您希望匿名的函数仅限于根据其输入参数进行定义(例如 inline 函数),并且您可以承诺在您的路径上保留一个函数,那么您可以将“sanitized " 匿名函数。

function out = sanitized_anon_fcn(str)
out = eval(str);
end

所以,在你的代码中,如果你想创建一个匿名函数,就这样做吧。

%testFunc2.m
function myfunc = testFunc2()
    myfunc = sanitized_anon_fcn('@(x) x.^2');
end

只要 sanitized_anon_fcn.m 保留在您的路径上,您就可以删除 testFunc2,保存的函数将继续工作。保存或加载时无需特殊处理。 Sanitized_anon_fcn 基本上像 inline 一样工作,但生成的函数与匿名函数一样快(因为它们是匿名函数)。在我的电脑上,R2011b 的速度差异大约是 10 倍。

一般情况

在一般情况下,函数可能实际上使用其工作区中的变量,事情变得更加棘手。

警告:这有点病态,我不赞成在生产代码中使用它。但作为该语言如何工作的示例,我忍不住发布它。

我认为你已经达到了 90%。但是您需要保留工作区信息而不是将其剥离,因为它可能有助于函数的运行。不要保存匿名函数句柄,而是获取您正在进行的functions() 调用的输出并保存那个

fcn = testFunc();
fcn_info = functions(fcn);
save willbreak.mat fcn
save blah.mat fcn_info

然后将其加载回来。您仍然会收到相同的警告,但现在该警告仅适用于在顶级匿名函数的工作空间内捕获的函数句柄。如果您的函数实际上没有引用它们(而且它不应该),您可以忽略警告并且它会起作用。

s0 = load('willbreak.mat')  % will warn and return unusable function
warning off MATLAB:dispatcher:UnresolvedFunctionHandle
s = load('blah.mat')  % will warn, but the first-level function will be usable
warning on MATLAB:dispatcher:UnresolvedFunctionHandle

然后将它传递给类似这个函数的函数,它将使你的匿名函数在一个新的工作空间中恢复,或多或少具有相同的工作空间值。

function out = reconstruct_anon_fcn(s)

for iWks = 1:numel(s.workspace)
    wkspace = s.workspace{iWks};
    varnames = fieldnames(wkspace);
    for i = 1:numel(varnames)
        tmp = wkspace.(varnames{i});
        eval([varnames{i} ' = tmp;']);
    end
end

fcn_str = s.function;
fcn = eval(fcn_str);
out = fcn;
end

在我们的例子中:

fcn = reconstruct_anon_fcn(s.fcn_info)
fcn(2)   % and it works!

现在,所有加载的匿名函数都将声称来自这个新文件,但这无关紧要,因为匿名函数使用的只是工作空间的快照状态,而不是封闭变量。如果工作空间中有匿名函数句柄被计算实际使用,您将收到相应的错误消息“未定义函数句柄”。

这是一个 hack,但也许你可以接受它并将其扩展为相当强大的东西。

【讨论】:

  • 抱歉昨天没能和你聊天。我有点困 :) 无论如何,只要您了解限制,您的 hack 看起来还不错。
【解决方案2】:

这是不可能的,因为它违反了closure 的概念。想象一下,您以这种方式定义匿名函数:

   function f = LocalFunc()
       y = 3;
       f = @(x)(x+y);
   end

除了保存工作区信息之外,别人怎么知道y3

编辑(1) 有人可能会说您可以捕获变量的value。但这并不总是正确的。想象一下下面的情况 - 你有一个句柄类,它有一个向输入添加一些属性的方法。

classdef Foo  < handle    
    properties
        DX;
    end

    methods
        function y = AddDX(this,x)
            y = x+ this.DX;
        end        
    end
end

现在您创建一个函数,该函数创建调用该方法的匿名函数:

function fOut = TestConcept(handleClass)
    handleClass.DX = 3;    
    fOut = @(x)(handleClass.AddDX(x));
end

现在您创建一个新的 Foo() ,并将其传递给 TestConcept func:

 handleClass = Foo();
 fOut = TestConcept(handleClass);
 handleClass.DX = 3;
 fOut(4)

结果是7

然后改句柄,再次调用:

 handleClass.DX = 100;
 fOut(4)

结果是104handleClass 值无法保存,因为它不是值类型,而是句柄。

如您所见,您不能总是捕获值,有时您有参考。

【讨论】:

  • 不太正确:匿名函数不是闭包;它们在匿名函数构造时捕获变量中的。它只是命名的嵌套函数,它们实际上是闭包并捕获对封闭工作区中变量的实时引用。
  • @AndrewJanke,你当然是对的。但是y 可能是一个句柄,你无法捕捉到它的价值。
  • @AndrewJanke,请查看更新后的答案。我写了一个更长的解释。我想听听你的意见。谢谢!
  • 对了。更糟糕的是 - y 可能是句柄,但输出 arg f 肯定是句柄。
  • 好吧。这就说得通了。但这并不意味着您可以永远将匿名函数句柄与其工作区一起保存 - 如果它们碰巧包含句柄,您就不能。相同的论点适用于单元格:通常,单元格数组可以包含句柄,但只要我们保存的特定单元格值没有,我们仍然可以保存单元格。我认为这同样适用于具有捕获工作空间的匿名函数句柄;不幸的是,命名输出参数意味着工作区始终包含匿名函数的句柄。
【解决方案3】:

我以前也遇到过这个问题。 str2func 几乎与句柄运算符 (@) 相同——它不创建文件关联,也不保留工作区变量。在你的情况下:

myfunc = str2func('@(x) x.^2');

尝试将此行为作为一个错误提交给 MathWorks,因为它确实破坏了匿名函数的非常有用的应用程序。如果我们有足够多的人抱怨,他们可能会解决它。

【讨论】:

    【解决方案4】:

    扩展@ArthurWard 的建议:

    要从您的代码环境中保存简单的内联函数,请尝试

    myStruct.myFunc = str2func(func2str( myFunc ));
    
    save('myStruct.mat',myStruct);
    

    func2str() 把你的函数变成一个文字字符串,str2func() 把它变成一个句柄,matlab 认为它没有依赖关系。

    你可以通过命名操作来整理它

    export_function = @(h) str2func(func2str( h ));
    

    任何可以在 func2str() 中存活的函数都应该可以工作。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2015-11-21
      • 2016-05-08
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多