7

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

>> 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, wherestct是一个结构,obj是一个类实例/对象,meth是一个方法。传递@stct.obj.meth失败,但如果我分配A = stct.obj,则传递@A.meth成功。

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


编辑:虽然在上面的用例中,我可以简单地删除@因为@plus已经是一个函数句柄。但是,请考虑这里的情况:

>> 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

在这种情况下,我需要之前...@A.a.mymeth

4

3 回答 3

5

对 MATLAB 来说,引入类是一件大事。实际上,它们太大了,以至于它们今天仍然无法正常工作。您的示例显示结构访问和类方法访问冲突,因为它们必须重载点 '.' 的含义。并没有让它无缝工作。当您在 MATLAB 控制台上通过名称显式调用类方法时,它或多或少都可以正常工作,例如在您的示例中 >> Aamymeth(1,1)。但是当你有任何类型的间接时,它很快就会中断。

您尝试通过 获取函数句柄>> @A.a.mymeth,MATLAB 无法理解,可能是因为它被混合结构/类的东西弄糊涂了。尝试解决使用str2func也不起作用。同样,它仅适用于显式名称访问,如此处所示。它打破了你的例子,例如>> 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 方法。

于 2013-10-17T11:30:45.213 回答
1

试试这个:

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

这有点笨拙,但它应该可以工作。

编辑:

它的工作方式是创建一个匿名函数,该函数接受可变数量的参数,并将这些参数转储到方法A.a.mymeth()中。因此,您实际上并没有将指针传递给函数 Aamymeth,而是将指针传递给调用 A.a.mymeth.

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

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

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

<speculation>我认为它必须是一元函数句柄运算符的@工作方式所固有的。Matlab 解析器可能会查看@token并确定是否token是有效函数。如果a.mymeth它足够聪明地决定它mymeth是 的成员a,然后返回适当的句柄。但是,当它看到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>

于 2013-10-16T19:54:45.410 回答
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
于 2013-10-18T13:21:49.657 回答