作为玩具示例,我有一个类,它只是将一个向量或矩阵包装在一个对象中,并包含一个创建时间的时间戳。我正在尝试重载subsref
以便
()
引用的工作方式与标准向量和矩阵类型完全相同{}
引用在完全中的工作方式与()
引用相同(换句话说与细胞无关).
引用允许我访问对象的私有属性以及非技术属性的其他字段。 代码:
classdef TimeStampValue
properties (Access = private)
time;
values;
end
methods
%% Constructor
function x = TimeStampValue(values)
x.time = now();
x.values = values;
end
%% Subscripted reference
function x = subsref(B, S)
switch S.type
case '()'
v = builtin('subsref', B.values, S);
x = TimeStampValue(v);
case '{}'
S.type = '()';
v = builtin('subsref', B.values, S);
x = TimeStampValue(v);
case '.'
switch S.subs
case 'time'
x = B.time;
case 'values'
x = B.values;
case 'datestr'
x = datestr(B.time);
end
end
end
function disp(x)
fprintf('\t%d\n', x.time)
disp(x.values)
end
end
end
然而,大括号{}
引用不起作用。我运行此代码
clear all
x = TimeStampValue(magic(3));
x{1:2}
我收到此错误:
Error using TimeStampValue/subsref
Too many output arguments.
Error in main (line 3)
x{1:2}
MException.last
告诉我这个信息:
identifier: 'MATLAB:maxlhs'
message: 'Too many output arguments.'
cause: {0x1 cell}
stack: [1x1 struct]
这没有帮助,因为异常堆栈中唯一的东西是包含我在上面运行的三行代码的文件。
我在subsref
的switch语句的第一行放置了一个断点,但MATLAB从未到达它。
这笔交易是什么? ()
和.
引用工作正如您所期望的那样,为什么{}
没有引用工作?
答案 0 :(得分:9)
当重叠大括号{}
以返回比平常更多数量的输出参数时,还需要重载numel
以返回预期的数字(在本例中为1)。 更新:从 R2015b 开始,新功能 numArgumentsFromSubscript
已创建为重载而非numel
。问题仍然存在,但是这个函数应该重载而不是numel
,正如我在下面的原始答案中描述的那样。另请参见页面"Modify nargout and nargin for Indexing Methods"。摘录:
当一个类重载
numArgumentsFromSubscript
时,MATLAB调用此方法而不是numel
来计算subsref
nargout
和subsasgn
{{的预期参数数量1}}。如果类没有重载
nargin
,MATLAB会调用numArgumentsFromSubscript
来计算numel
或nargout
的值。
对基础问题的更多解释(需要指定输出参数的数量)如下。
原始回答(使用nargin
代替numArgumentsFromSubscript
进行R2015b +)
为了在使用花括号进行索引时处理逗号分隔的输出参数列表的可能性,MATLAB调用numel
以根据输入索引的大小确定输出参数的数量(根据this MathWorks answer )。如果重载numel
定义中的输出参数数与subsref
提供的数字不一致(即小于),则会出现“输出参数太多”错误。如MathWorks所述:
因此,为了允许大括号索引到您的对象,同时返回大量具有输入大小的参数INCONSISTENT,您将需要重载类目录中的NUMEL函数。
由于numel
通常提供两个输出(x{1:2}
),因此定义X{1},X{2}
与此输入不兼容。解决方案是在类中包含一个简单的function x = subsref(B, S)
方法来重载内置函数,如下所示:
numel
现在,function n = numel(varargin)
n = 1;
end
索引按预期工作,模仿{}
:
()
但是,以这种方式重载花括号是apparently“我们[MathWorks]不希望客户写的特定类型的代码”。 MathWorks建议:
如果您设计的类只输出一个参数,则不建议您使用大括号索引来要求您重载NUMEL。相反,建议您使用smooth brace()索引。
更新:有趣的是,R2015b release notes state:
在MATLAB发布R2015b之前,对于返回或分配给逗号分隔列表的某些索引表达式,MATLAB错误地计算了
>> clear all % needed to reset the class definition >> x = TimeStampValue(magic(3)); >> x(1:2) ans = 7.355996e+05 8 3 >> x{1:2} ans = 7.355996e+05 8 3
输出和subsref
输出的预期参数数。在版本R2015b中,MATLAB根据索引表达式所需的参数数量正确计算
subsasgn
和nargout
的值。
所以也许现在已经修好了?
想到的另一种解决方案是将nargin
更改为function x = subsref(B, S)
并添加function varargout = subsref(B, S)
。正如Amro在评论中指出的那样,预先分配单元格是必要的,以避免关于未分配参数的错误。
答案 1 :(得分:1)
我遇到了同样的问题。更糟糕的是,输出参数的数量被强制等于numel()
不仅返回花括号{}
,还返回点.
操作。
这意味着如果重写numel()
以返回通常的prod(size(obj))
,则无法访问基础对象的任何属性(例如上例中的x.time
),如然后,subsref()
将返回多个输出。
但如果numel()
只返回1,则它与prod(size(obj))
不匹配,这是大多数代码使用数值或基于reshape()
所期望的。事实上,MATLAB编辑器的气球帮助立即表明'NUMEL(x)通常比PROD(SIZE(x))更快,这表明它们是等价的,但显然不是。
一种可能的解决方案是让numel()
返回prod(size(obj))
并为所有这些属性编写显式的getter / setter函数,例如,
x.get_time()
在上面的例子中。这似乎有效,因为在subsref()
被调用之前,方法调用显然得到了解决。但是,如果其中一个属性是矩阵,则不能再直接索引,因为Matlab不理解链式索引,即代替编写
x.matrix(1,:)
一个人必须写
m = x.get_matrix();
m(1,:)
至少可以说是丑陋的。
这开始有点令人沮丧。我仍然希望我只是忽略了一些显而易见的事情,我无法相信它应该如何发挥作用。
答案 2 :(得分:0)
此解决方案似乎在2014b中有效(但不完全确定原因)
classdef TestClass < handle
methods
function n = numel(~,varargin)
n = 1;
end
function varargout = subsref(input,S)
varargout = builtin('subsref',input,S);
end
function out = twoOutputs(~)
out = {}; out{1} = 2; out{2} = 3;
end
end
end
然后通过命令窗口
>> testClass = TestClass();
>> [a,b] = testClass.twoOutouts()
a =
2
b =
3
答案 3 :(得分:0)
我正在研究一个处理多项式和多项式矩阵的类。我有同样的困难,因为在标量多项式和多项式矩阵的情况下,我想要'.'
索引的不同行为。
在我的情况下,如果P.coef
是标量多项式,我希望P
返回系数向量。如果P
是多项式矩阵,P.coef
必须返回大小为P
的单元格数组,其中单元格{i,j}
包含多项式P(i,j)
的系数向量1}}。
P.coef
与矩阵一起使用时出现问题。我想要的行为只返回一个对象作为答案,但Matlab期望该函数返回numel(P)
个对象。
我找到了一个非常简单的解决方案。声明subsref
时,我使用了一个强制性输出和一个varargout
:
function [R,varargout] = subsref(P,S)
根据我的设计,函数的主体根据需要定义R
。在函数的最后,我添加了:
varargout(1:nargout-1) = cell(1,nargout-1);
只返回空矩阵作为Matlab想要的额外输出。
如果始终使用单个输出参数调用函数,则不会产生任何问题,例如,如R = P.coef
中所示。如果在没有分配的情况下调用该函数,则用户将看到numel(P)-1
空矩阵,这实际上并不是什么大问题。无论如何,在功能帮助中会向用户发出警告。