我可以防止Matlab动态调整预分配的数组吗?

时间:2022-02-25 06:47:29

For example, in this simple/stupid example:

例如,在这个简单/愚蠢的例子中:

n = 3;
x = zeros(n, 1);
for ix=1:4
    x(ix) = ix;
end

the array is pre-allocated, but dynamically resized in the loop. Is there a setting in Matlab that will throw an error when dynamic resizing like this occurs? In this example I could trivially rewrite it:

数组是预先分配的,但在循环中动态调整大小。在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

but I'm hoping to use this as a check to make sure I've pre-allocated my matrices properly.

但我希望用它作为检查确保我已经正确地分配了我的矩阵。

4 个解决方案

#1


8  

You can create a subclass of double and restrict the assignment in subsasgn method:

您可以创建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

So now when you are preallocating use dbl

所以现在当你在预分配的时候使用dbl。

>> z = dbl(zeros(3))
z = 
  dbl

  double data:
     0     0     0
     0     0     0
     0     0     0
  Methods, Superclasses

All methods for double are now inherited by dbl and you can use it as usual until you assign something to z

现在,所有的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.

I haven't benchmarked it but I expect this to have insignificant performance impact.

我没有对它进行基准测试,但是我希望它对性能的影响是微不足道的。

If you want the display of the values look normal you can overload the display method as well:

如果你想要显示的值看起来很正常,你也可以重载显示方法:

function display(obj)
    display(double(obj));
end

Then

然后

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

#2


5  

The simplest, most straightforward and robust way I can think of to do this is just by accessing the index before assigning to it. Unfortunately, you cannot overload subsasgn for fundamental types (and it'd be a major headache to do correctly in any case).

我能想到的最简单、最直接、最健壮的方法就是在分配索引之前访问索引。不幸的是,对于基本类型,您不能重载subsasgn(在任何情况下,这都是一个非常头痛的问题)。

for ix=1:4
    x(ix); x(ix) = ix;
end
% Error: 'Attempted to access x(4); index out of bounds because numel(x)=3.'

Alternatively, you could try to be clever and do something with the end keyword... but no matter what you do you'll end up with some sort of nonsensical error message (which the above nicely provides).

或者,你也可以试着聪明点,用最后的关键词做一些事情……但是,无论你做什么,最终都会得到某种无意义的错误消息(上面很好地提供了)。

for ix=1:4
    x(ix*(ix<=end)) = ix;
end
% Error: 'Attempted to access x(0); index must be a positive integer or logical.'

Or you could do that check in a function, which gains you your nice error message but is still terribly verbose and obfuscated:

或者你可以在一个函数中做这个检查,它会给你一个漂亮的错误信息但是仍然非常啰嗦和模糊:

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

#3


4  

This is not a fully worked example (see disclaimer after the code!) but it shows one idea...

这并不是一个完全有效的示例(在代码之后,请参见免责声明!),但是它显示了一个想法……

You could (at least while debugging your code), use the following class in place of zeros to allocate your original variable.

您可以(至少在调试代码时)使用下面的类来代替0来分配原始变量。

Subsequent use of the data outside of the bounds of the originally allocated size would result in an 'Index exceeds matrix dimensions.' error.

在初始分配的大小范围之外的数据的后续使用将导致“索引超过矩阵维度”。的错误。

For example:

例如:

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

>> 

The class code:

类代码:

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

There would be considerable performance implications (and problems stemming from using a class inheriting from handle) but it seemed like an interesting solution to the original problem.

将会有相当多的性能影响(以及使用从句柄继承的类所产生的问题),但这似乎是对原始问题的一个有趣的解决方案。

#4


2  

Allowing assignment to indices outside of an array's bounds and filling the gaps with zeros is indeed one of Matlab's ugly parts. I am not aware of any simple tricks without an explicit check to avoid that, other than implementing your own storage class. I would stick to adding a simple assert(i <= n) to your loop and forget about it. I have never been bitten by hard-to-find bugs due to assigning something out of bounds.

允许赋值到数组的边界之外,用零来填充空格,这确实是Matlab中最丑陋的部分之一。除了实现您自己的存储类之外,我不知道有什么简单的技巧,没有明确的检查来避免这一点。我将坚持在您的循环中添加一个简单的断言(I <= n),然后忘记它。我从来没有被难以找到的bug咬过,因为我要给它分配一些超出范围的东西。

In case of a forgotten or too small preallocation, in the 'ideal' case your code gets really slow due to quadratic behavior, after which you find the bug and fix it. But these days, Matlab's JIT is sometimes smart enough to not cause any slowdowns (maybe it dynamically grows arrays in some cases, like python's list), so it might not even be an issue anymore. So it actually allows for some sloppier coding ...

在“理想”的情况下,如果忘记或过小的预分配,您的代码会因为二次行为而变得非常慢,之后您会发现bug并修复它。但这些天,Matlab的JIT有时足够聪明,不会导致任何的减速(也许它会在某些情况下动态地增加数组,比如python的列表),所以它可能不再是一个问题了。所以它实际上允许一些倾斜编码…

#1


8  

You can create a subclass of double and restrict the assignment in subsasgn method:

您可以创建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

So now when you are preallocating use dbl

所以现在当你在预分配的时候使用dbl。

>> z = dbl(zeros(3))
z = 
  dbl

  double data:
     0     0     0
     0     0     0
     0     0     0
  Methods, Superclasses

All methods for double are now inherited by dbl and you can use it as usual until you assign something to z

现在,所有的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.

I haven't benchmarked it but I expect this to have insignificant performance impact.

我没有对它进行基准测试,但是我希望它对性能的影响是微不足道的。

If you want the display of the values look normal you can overload the display method as well:

如果你想要显示的值看起来很正常,你也可以重载显示方法:

function display(obj)
    display(double(obj));
end

Then

然后

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

#2


5  

The simplest, most straightforward and robust way I can think of to do this is just by accessing the index before assigning to it. Unfortunately, you cannot overload subsasgn for fundamental types (and it'd be a major headache to do correctly in any case).

我能想到的最简单、最直接、最健壮的方法就是在分配索引之前访问索引。不幸的是,对于基本类型,您不能重载subsasgn(在任何情况下,这都是一个非常头痛的问题)。

for ix=1:4
    x(ix); x(ix) = ix;
end
% Error: 'Attempted to access x(4); index out of bounds because numel(x)=3.'

Alternatively, you could try to be clever and do something with the end keyword... but no matter what you do you'll end up with some sort of nonsensical error message (which the above nicely provides).

或者,你也可以试着聪明点,用最后的关键词做一些事情……但是,无论你做什么,最终都会得到某种无意义的错误消息(上面很好地提供了)。

for ix=1:4
    x(ix*(ix<=end)) = ix;
end
% Error: 'Attempted to access x(0); index must be a positive integer or logical.'

Or you could do that check in a function, which gains you your nice error message but is still terribly verbose and obfuscated:

或者你可以在一个函数中做这个检查,它会给你一个漂亮的错误信息但是仍然非常啰嗦和模糊:

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

#3


4  

This is not a fully worked example (see disclaimer after the code!) but it shows one idea...

这并不是一个完全有效的示例(在代码之后,请参见免责声明!),但是它显示了一个想法……

You could (at least while debugging your code), use the following class in place of zeros to allocate your original variable.

您可以(至少在调试代码时)使用下面的类来代替0来分配原始变量。

Subsequent use of the data outside of the bounds of the originally allocated size would result in an 'Index exceeds matrix dimensions.' error.

在初始分配的大小范围之外的数据的后续使用将导致“索引超过矩阵维度”。的错误。

For example:

例如:

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

>> 

The class code:

类代码:

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

There would be considerable performance implications (and problems stemming from using a class inheriting from handle) but it seemed like an interesting solution to the original problem.

将会有相当多的性能影响(以及使用从句柄继承的类所产生的问题),但这似乎是对原始问题的一个有趣的解决方案。

#4


2  

Allowing assignment to indices outside of an array's bounds and filling the gaps with zeros is indeed one of Matlab's ugly parts. I am not aware of any simple tricks without an explicit check to avoid that, other than implementing your own storage class. I would stick to adding a simple assert(i <= n) to your loop and forget about it. I have never been bitten by hard-to-find bugs due to assigning something out of bounds.

允许赋值到数组的边界之外,用零来填充空格,这确实是Matlab中最丑陋的部分之一。除了实现您自己的存储类之外,我不知道有什么简单的技巧,没有明确的检查来避免这一点。我将坚持在您的循环中添加一个简单的断言(I <= n),然后忘记它。我从来没有被难以找到的bug咬过,因为我要给它分配一些超出范围的东西。

In case of a forgotten or too small preallocation, in the 'ideal' case your code gets really slow due to quadratic behavior, after which you find the bug and fix it. But these days, Matlab's JIT is sometimes smart enough to not cause any slowdowns (maybe it dynamically grows arrays in some cases, like python's list), so it might not even be an issue anymore. So it actually allows for some sloppier coding ...

在“理想”的情况下,如果忘记或过小的预分配,您的代码会因为二次行为而变得非常慢,之后您会发现bug并修复它。但这些天,Matlab的JIT有时足够聪明,不会导致任何的减速(也许它会在某些情况下动态地增加数组,比如python的列表),所以它可能不再是一个问题了。所以它实际上允许一些倾斜编码…