例如,在这个简单/愚蠢的例子中:
n = 3;
x = zeros(n, 1);
for ix=1:4
x(ix) = ix;
end
数组已预先分配,但在循环中动态调整大小。在这样的动态调整大小时,Matlab中是否存在会引发错误的设置?在这个例子中,我可以轻而易举地重写它:
n = 3;
x = zeros(n, 1);
for ix=1:4
if ix > n
error('Size:Dynamic', 'Dynamic resizing will occur.')
end
x(ix) = ix;
end
但我希望将此作为检查,以确保我已正确预先分配我的矩阵。
答案 0 :(得分:8)
您可以创建double
的子类,并使用subsasgn
方法限制分配:
classdef dbl < double
methods
function obj = dbl(d)
obj = obj@double(d);
end
function obj = subsasgn(obj,s,val)
if strcmp(s.type, '()')
mx = cellfun(@max, s.subs).*~strcmp(s.subs, ':');
sz = size(obj);
nx = numel(mx);
if nx < numel(sz)
sz = [sz(1:nx-1) prod(sz(nx:end))];
end
assert(all( mx <= sz), ...
'Index exceeds matrix dimensions.');
end
obj = subsasgn@double(obj, s, val);
end
end
end
所以现在当你预先分配使用dbl
>> z = dbl(zeros(3))
z =
dbl
double data:
0 0 0
0 0 0
0 0 0
Methods, Superclasses
double
的所有方法现在都由dbl
继承,您可以照常使用它,直到您为z
分配内容
>> z(1:2,2:3) = 6
z =
dbl
double data:
0 6 6
0 6 6
0 0 0
Methods, Superclasses
>> z(1:2,2:5) = 6
Error using dbl/subsasgn (line 9)
Index exceeds matrix dimensions.
我没有对它进行基准测试,但我预计这会对性能影响不大。
如果您希望值的显示看起来正常,您也可以重载display
方法:
function display(obj)
display(double(obj));
end
然后
>> z = dbl(zeros(3))
ans =
0 0 0
0 0 0
0 0 0
>> z(1:2,2:3) = 6
ans =
0 6 6
0 6 6
0 0 0
>> z(1:2,2:5) = 6
Error using dbl/subsasgn (line 9)
Index exceeds matrix dimensions.
>> class(z)
ans =
dbl
答案 1 :(得分:5)
我能想到的最简单,最直接和最健壮的方法就是在分配索引之前访问索引。不幸的是,你不能为基本类型重载subasgn(在任何情况下都是正确的头痛)。
for ix=1:4
x(ix); x(ix) = ix;
end
% Error: 'Attempted to access x(4); index out of bounds because numel(x)=3.'
或者,您可以尝试聪明并使用end
关键字执行某些操作...但无论您做什么,您最终都会得到某种无意义的错误消息(上面提供的很好) 。
for ix=1:4
x(ix*(ix<=end)) = ix;
end
% Error: 'Attempted to access x(0); index must be a positive integer or logical.'
或者你可以检查一个函数,它可以获得你的错误消息,但仍然非常冗长和模糊:
for ix=1:4
x(idxchk(ix,end)) = ix;
end
function idx = idxchk(idx,e)
assert(idx <= e, 'Size:Dynamic', 'Dynamic resizing will occur.')
end
答案 2 :(得分:4)
这不是一个完全有效的例子(见代码后的免责声明!)但它显示了一个想法......
您可以(至少在调试代码时)使用以下类代替零来分配原始变量。
随后使用原始分配大小范围之外的数据将导致“索引超出矩阵维度”。错误。
例如:
>> n = 3;
>> x = zeros_debug(n, 1)
x =
0
0
0
>> x(2) = 32
x =
0
32
0
>> x(5) = 3
Error using zeros_debug/subsasgn (line 42)
Index exceeds matrix dimensions.
>>
班级代码:
classdef zeros_debug < handle
properties (Hidden)
Data
end
methods
function obj = zeros_debug(M,N)
if nargin < 2
N = M;
end
obj.Data = zeros(M,N);
end
function sref = subsref(obj,s)
switch s(1).type
case '()'
if length(s)<2
% Note that obj.Data is passed to subsref
sref = builtin('subsref',obj.Data,s);
return
else
sref = builtin('subsref',obj,s);
end
otherwise,
error('zeros_debug:subsref',...
'Not a supported subscripted reference')
end
end
function obj = subsasgn(obj,s,val)
if isempty(s) && strcmp(class(val),'zeros_debug')
obj = zeros_debug(val.Data);
end
switch s(1).type
case '.'
obj = builtin('subsasgn',obj,s,val);
case '()'
if strcmp(class(val),'double')
switch length(s(1).subs{1})
case 1,
if s(1).subs{1} > length(obj.Data)
error('zeros_debug:subsasgn','Index exceeds matrix dimensions.');
end
case 2,
if s(1).subs{1} > size(obj.Data,1) || ...
s(1).subs{2} > size(obj.Data,2)
error('zeros_debug:subsasgn','Index exceeds matrix dimensions.');
end
end
snew = substruct('.','Data','()',s(1).subs(:));
obj = subsasgn(obj,snew,val);
end
otherwise,
error('zeros_debug:subsasgn',...
'Not a supported subscripted assignment')
end
end
function disp( obj )
disp(obj.Data);
end
end
end
会有相当大的性能影响(以及使用继承自句柄的类产生的问题),但它似乎是原始问题的一个有趣的解决方案。
答案 3 :(得分:2)
允许赋值给数组边界之外的索引并用零填充间隙确实是Matlab丑陋的部分之一。除了实现自己的存储类之外,我没有明确的检查以避免这种情况。我会坚持在你的循环中添加一个简单的assert(i <= n)
并忘记它。由于分配了一些超出限制的东西,我从未被难以发现的错误所困扰。
如果遗忘或预分配太小,在“理想”情况下,由于二次行为,您的代码变得非常慢,之后您会发现错误并修复它。但是现在,Matlab的JIT有时很聪明,不会导致任何减速(在某些情况下它可能会动态增长数组,比如python的列表),所以它甚至可能不再是一个问题。所以它实际上允许一些更粗略的编码...