我有一个缓存评估功能。作为参数之一,它需要一个函数句柄。在某些情况下,函数句柄是不可访问的,我不太明白为什么。下面的例子显示了让我难过的原因:
>> 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
之前的@
...
答案 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
的情况下,确定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>
答案 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