我何时可以传递函数句柄?

时间:2013-10-16 14:46:54

标签: matlab namespaces stack function-handle matlab-class

我有一个缓存评估功能。作为参数之一,它需要一个函数句柄。在某些情况下,函数句柄是不可访问的,我不太明白为什么。下面的例子显示了让我难过的原因:

>> A.a = @plus; feval(@A.a, 1, 1)

ans =

     2

>> clear A
>> A.a.a = @plus; feval(@A.a.a, 1, 1)
Error using feval
Undefined function 'A.a.a' for input arguments of type 'double'.

所以,如果我将一个函数句柄存储为一个结构成员,如果它是一个深度,我可以将它传递给它,但如果它是两个级别,则不能。在我的实际用例中,我有一个结构D,它包含许多(117)个不同类的实例,所以我实际上有stct.obj.meth,其中stct是一个结构,{{1} }是一个类实例/对象,obj是一个方法。传递meth失败,但如果我分配@stct.obj.meth,则传递A = stct.obj会成功。

在什么条件下我可以将函数句柄作为参数传递,以便它仍然可以在堆栈中访问?


修改:虽然在上面的用例中,我只需删除@A.meth,因为@已经是一个函数句柄。但是,请考虑以下情况:

@plus

在这种情况下,我需要<{em> >> type cltest.m classdef cltest < handle methods function C = mymeth(self, a, b) C = a + b; end end end >> A.a = cltest(); >> feval(@A.a.mymeth, 1, 1) Error using feval Undefined function 'A.a.mymeth' for input arguments of type 'double'. >> b = A.a; >> feval(@b.mymeth, 1, 1) ans = 2 之前的@ ...

3 个答案:

答案 0 :(得分:5)

介绍类对MATLAB来说是一个大问题。事实上,这么大,他们今天仍然无法正常工作。您的示例显示结构访问和类方法访问冲突,因为它们必须重载点'。'的含义。并没有让它无缝地工作。当您在MATLAB控制台上通过名称显式调用类方法时,或多或少都可以正常工作,例如:在您的示例&gt;&gt; A.a.mymeth(1,1)。但是当你有任何类型的间接时,它很快就会破裂。

你试过通过>> @A.a.mymeth来获取函数句柄,这是MATLAB无法理解的,可能是因为它混淆了混合结构/类的东西。尝试使用str2func进行解决方案也无效。它再次仅用于显式名称访问,如here所示。它打破你的例子,例如>> str2func('b.mymeth')。它甚至不在课堂上工作。尝试其他方向并观察它们失败。

另外,MATLAB不喜欢给你一个类方法的句柄。它没有任何功能。无法一次性获取所有函数句柄,甚至无法通过名称字符串动态获取所有函数句柄。

我在这里看到三个选项。首先,如果可能,尝试更改您的程序。这些功能是否需要放在classdef中?

其次,请遵循您的或nispio的解决方法。它们都创建一个临时变量来保存对类实例的引用,以便创建对其成员方法的非混合访问。问题是,它们都需要明确命名函数。您必须为所涉及的每个函数明确地放置此代码。没办法把它抽象出来。

第三,通过从内部发出你的类'方法句柄来作弊。你可以把它们放在一个结构中。

classdef cltest < handle
    methods
        function C = mymeth(self, a, b)
            C = a + b;
        end
        function hs = funhandles(self)
            hs = struct('mymeth', @self.mymeth, ...
                        'mymeth2', @self.mymeth2);
        end
    end
end

然后,您可以按名称访问句柄,甚至可以动态访问。

>> A.a = cltest;
>> feval(A.a.funhandles.mymeth, 1, 1);
>> feval(A.a.funhandles.('mymeth'), 1, 1)

ans =

     2

但要小心,通过使用它,您可以从外部访问Access = private方法。

答案 1 :(得分:1)

试试这个:

feval(@(varargin)A.a.mymeth(varargin{:}),1,1);

这有点笨拙,但应该有用。

编辑:

它的工作方式是创建一个带有可变数量参数的Anonymous Function,并将这些参数转储到方法A.a.mymeth()中。所以你实际上并没有传递指向函数A.a.mymeth的指针,而是传递一个指向调用 A.a.mymeth的函数的指针。

在不使用varargin的情况下实现相同目标的另一种方法是:

feval(@(x,y)A.a.mymeth(x,y),1,1);

这会创建一个接受两个参数的匿名函数,并将它们传递给A.a.mymeth

<speculation>我认为它必须是一元函数处理运算符@的工作方式所固有的。 Matlab解析器可能会查看@token并确定token是否是有效函数。在a.mymeth的情况下,确定mymetha的成员足够聪明,然后返回适当的句柄。但是,当它看到A.a.mymeth时,它可能会发现A不是一个类,A也没有一个名为a.mymeth的成员,因此找不到有效的函数。这似乎得到以下事实的支持:

A.a.a = @plus; feval(A.a.a,1,1)

而这不是:

A.a.a = @plus; feval(@A.a.a,1,1)

</speculation>

答案 2 :(得分:0)

你可以通过引入一个单独的函数来解决它,它可以纠正@运算符没有做的事情:

function h=g(f)
x = functions(f);
if ~strcmp(x.type, 'anonymous')
    h = evalin('caller', ['@(varargin)' x.function '(varargin{:})']);
else
    h = f;
end
end

现在举个例子:

>> feval(g(@A.a.mymeth), 1, 1)
ans =
     2
>> feval(g(@b.mymeth), 1, 1)
ans =
     2

我认为这对您的代码影响最小。您可以使它更优雅,但不那么健壮和/或可读。 uplus类未定义function_handle方法,因此您可以使用此内容在路径中的某个位置的文件夹uplus.m中创建@function_handle

function h=uplus(f)
x = functions(f);
if ~strcmp(x.type, 'anonymous')
    h = evalin('caller', ['@(varargin)' x.function '(varargin{:})']);
else
    h = f;
end
end

现在您只需使用+@代替@。对于您的示例:

>> feval(+@A.a.mymeth, 1, 1)
ans =
     2
>> feval(+@b.mymeth, 1, 1)
ans =
     2