matlab中匿名函数的传递与保存

matlab中匿名函数的传递与保存,matlab,anonymous-function,Matlab,Anonymous Function,我想要一个函数(例如,fit函数)返回一个匿名函数(通常存储在struct中),以便以后保存和使用。但是,传递@func倾向于传递函数指针,而不是函数本身。内联函数是实现这一点的唯一方法吗?我希望避免内联,因为它非常慢 如果这个问题不清楚,下面是一个有问题的代码示例:我在一些路径中编写了一个testFunc.m文件 %testFunc.m function myfunc = testFunc() myfunc = @(x) x.^2; end 然后,我将

我想要一个函数(例如,fit函数)返回一个匿名函数(通常存储在
struct
中),以便以后保存和使用。但是,传递
@func
倾向于传递函数指针,而不是函数本身。
内联
函数是实现这一点的唯一方法吗?我希望避免内联,因为它非常慢

如果这个问题不清楚,下面是一个有问题的代码示例:我在一些
路径中编写了一个
testFunc.m
文件

    %testFunc.m
    function myfunc = testFunc()
        myfunc = @(x) x.^2;
    end
然后,我将函数存储在
结构中。(我知道这真的应该是一个对象!)

如果随后移动
myfile.mat
testFunc.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(mystruct.func)

    ans = 

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

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

这是不可能的,因为它违反了。假设您以这种方式定义匿名函数:

   function f = LocalFunc()
       y = 3;
       f = @(x)(x+y);
   end
除了保存工作区信息,还有人怎么知道
y
3

编辑(1) 有人可能会说,您可以捕获变量的
值。但事实并非总是如此。想象一下下面的情况-您有一个handle类,它有一个向输入添加某些属性的方法

classdef Foo  < handle    
    properties
        DX;
    end

    methods
        function y = AddDX(this,x)
            y = x+ this.DX;
        end        
    end
end
现在创建一个新的Foo(),并将其传递给
TestConcept
func:

 handleClass = Foo();
 fOut = TestConcept(handleClass);
 handleClass.DX = 3;
 fOut(4)
结果是
7

然后更改句柄,然后再次调用它:

 handleClass.DX = 100;
 fOut(4)
结果是
104
<代码>handleClass
值无法保存,因为它不是值类型,而是句柄

正如您所见,您不能总是捕获值,有时您有一个引用。

简单的情况 如果您想要匿名的函数仅限于根据其输入参数定义(如
内联
函数),并且您可以承诺在路径上保留一个函数,那么您可以创建“净化”匿名函数

function out = sanitized_anon_fcn(str)
out = eval(str);
end
所以,在你的代码中,如果你想做一个匿名函数,就这样做

%testFunc2.m
function myfunc = testFunc2()
    myfunc = sanitized_anon_fcn('@(x) x.^2');
end
只要已清除的\u anon\u fcn.m保留在您的路径上,您就可以删除testFunc2,并且保存的函数将继续工作。保存或加载时无需特殊处理
Sanitized_anon_fcn
基本上与
inline
类似,但生成的函数与匿名函数一样快(因为它们是匿名函数)。在我的电脑上,R2011b的速度差大约是10倍

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

警告:这是一个有点病态的黑客行为,我不赞成在生产代码中使用它。但作为语言工作原理的一个例子,我忍不住要发布它

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

然后把它装回去。您仍然会收到相同的警告,但现在该警告仅适用于顶级匿名函数工作区内捕获的函数句柄。如果您的函数实际上没有引用它们(它也不应该引用),您可以忽略该警告,它就会工作

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!
现在,所有加载的匿名函数都将声明来自这个新文件,但这并不重要,因为匿名函数使用的只是工作区的快照状态,而不是关闭变量。如果工作区中存在计算实际使用的匿名函数句柄,您将得到一个适当的错误,称为“Undefined function handle”


这是一个黑客攻击,但也许你可以把它扩展到一些相当健壮的东西。

我以前也遇到过这个问题
str2func
几乎与句柄操作符(
@
)相同——它不创建文件关联,也不保留工作空间变量。就你而言:

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

尝试用MathWorks将此行为作为bug归档,因为它实际上破坏了匿名函数的非常有用的应用程序。如果我们有足够多的人抱怨,他们可能会解决它。

进一步扩展@ArthurWard的建议:

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

myStruct.myFunc = str2func(func2str( myFunc ));

save('myStruct.mat',myStruct);
func2str()将函数转换为文字字符串,str2func()将其转换为句柄,matlab将其视为无依赖项

您可以通过将操作命名为

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

任何能够在func2str()之后存活的函数都应该工作。

不完全正确:匿名函数不是闭包;它们捕获匿名函数构造时变量中的值。它只是命名为嵌套函数,实际上是闭包,并捕获封闭工作区中变量的活动引用。@AndrewJanke,你当然是对的。但是
y
可能是一个句柄,您无法捕获它的值。@AndrewJanke,请查看更新的答案。我写了一个较长的解释。我想听听你的意见。非常感谢。啊对。更糟糕的是-
y
可能是一个句柄,但是输出arg
f
肯定是一个句柄。这是有道理的。但这并不意味着你永远不能保存一个匿名函数句柄及其工作空间——如果它们发生了,你就不能
myfunc = str2func('@(x) x.^2');
myStruct.myFunc = str2func(func2str( myFunc ));

save('myStruct.mat',myStruct);
export_function = @(h) str2func(func2str( h ));