创建自定义对象数组时,输入参数错误不足

时间:2016-03-09 18:37:50

标签: matlab oop

问题

如果我使用值SIDE_NO = 1运行此代码,它可以正常工作,但是当我将其更改为任何其他值时,它会给我一个错误。发生了什么事?

错误

  

没有足够的输入参数。

     

CubePiece中的错误(第15行)              obj.posX = x;

     

主要错误(第21行)          cubePieces(x,y)= piece;

主要方法

SIDE_NO = 2;
PIECE_NO = 9;

cubeColors = zeros(SIDE_NO, PIECE_NO);

for s = 1: SIDE_NO
   cimg = Snapshot();
   simg = ScanFace(cimg);
   cubeColors(s,:) = GetColours(simg);
end

for x = 1: SIDE_NO
   for y = 1: PIECE_NO
      piece = CubePiece(x,y,cubeColors(x,y));
      cubePieces(x,y) = piece;
   end
end

CubePiece

classdef CubePiece

properties
    posX      % 1, 2, 3
    posY      % 1, 2, 3
    color     % red, blue, orange, yellow, white, green
    faceCode  % R,L,U,D,F,B
    faceNo    % 1, 2, 3, 4, 5, 6
end

methods
    function obj = CubePiece(x,y,col)
        obj.posX = x;
        obj.posY = y;
        obj.color = col;
        obj.faceNo = x;
        obj.faceCode = obj.getFaceCode(x);

    end

    function code = getFaceCode(~,num)
        if num == 1
            code = 'R';
        elseif num == 2
            code = 'L';
        elseif num == 3
            code = 'U';
        elseif num == 4
            code = 'D';
        elseif num == 5
            code = 'F';
        elseif num == 6
            code = 'B';
        end
    end

   end

end

2 个答案:

答案 0 :(得分:3)

问题

您发出的问题是,当您继续将数据附加到循环内的数组时(导致数组的大小增加)。 MATLAB需要尝试扩展数组以包含数据,同时确保数组保持矩形。

所以,让我们看看发生了什么。

当您使用x在1处循环并且每次将y递增1时。每次循环都会创建一个新的CubePiece实例。然后你隐式地cubePieces添加一个新列以存储它。

cubePieces(x,y) = piece;

我强调隐含,因为你不能pre-allocate cubePieces成为它的最终尺寸([SIDE_NO x PIECE_NO])。因此,如果我们在循环中粘贴以下语句,我们可以监视cubePieces的大小。

disp(size(cubePieces));

在保持y等于1的同时循环浏览x值时,您会看到以下条目。

1   1
1   2
1   3
...
1   9

正如您所看到的那样,在我们达到9(PIECE_NO)之前,列数会继续增加。

现在,当你到达x = 2时,MATLAB不能再添加一个条目(就像它一直在做的那样),而是必须添加整行因为矩阵/数组不能是不规则形状。因此,如果我们观察disp语句的输出,我们将再次获得。

2   9
2   9
...
2   9

如果您注意到,这次通过内循环不会改变大小。这是因为MATLAB填写了8" dummy"第一次通过循环时整行的值,因为它要求新行有9列,但它只知道第一行的值。

所以问题是,如果你没有设置它们,MATLAB如何填写整个值?答案是(对于对象) MATLAB为完全填充新行所需的每个元素调用默认的contstructor(没有输入参数调用的构造函数)。这是因为数组的所有成员必须是相同的类型(忽略heterogeneous objects)。

因此,当您第一次分配到cubePieces(2,1)时。 MATLAB 实际的作用是什么。

cubePieces(2,:) = [piece, CubePiece(), CubePiece(), CubePiece()...];

所以这些空洞的构造函数正在为您解决问题。在您的构造函数中,您不会检查以确保提供正确的输入,而是您只是开始尝试使用它们。这显然会导致输入参数不足的错误。

CubePiece() 
  

没有足够的输入参数。

     

CubePiece中的错误(第13行)

     

obj.posX = x;

     

13 obj.posX = x;

解决方案

你想要做的是优雅地处理contstructor没有输入参数的情况,并提供有效的"默认"宾语。

您可以通过返回而不指定任何属性(if nargin == 0; return; end)来执行此操作。或者,如果您需要各种属性的默认值,则可以在类定义的properties块中指定这些默认值。

classdef CubePice

    properties
        posX = 1
        posY = 1
        color = 'red';
        faceCode = 'R';
        faceNo = 1
    end

    methods
        function obj = CubePiece(x,y,col)
            if nargin == 0
                return;
            end

            obj.posX = x;  

            % Do other assignments
        end
    end
end

更好的解决方案(预分配)

更好的解决方案是实际预先分配您要分配的变量,以便MATLAB不会不断改变它的大小。这有一些性能优势。有几种方法可以做到这一点

<强> repmat

如果您确实需要CubePiece个对象的2D数组,您实际上可以预先分配CubePiece个对象的2D数组,使其大小合适。您可以使用repmat应用于您的班级的单个实例来执行此操作。

template = CubePiece(1,1,1);
cubePieces = repmat(template, [SIDE_NO, PIECE_NO]);

现在您将拥有一个2D对象数组(技术上是相同对象的所有句柄),MATLAB不必对此数组进行任何动态扩展,并尝试猜测默认值你想要使用的价值。

&#34;展开一次&#34;方法

如果您不介意MATLAB调用默认构造函数(并且您已经设置了类构造函数来处理此问题),那么可以通过简单地分配值来来初始化此数组。首先是最大的行和列,它会立即将数组扩展到最大大小。

cubePieces(SIDE_NO, PIECE_NO) = CubePiece(1,1,1);

size(cubePieces)

    2   9

单元格数组

您还可以在创建过程中将对象存储在单元格数组中。

cubePieces = cell(SIDE_NO, PIECE_NO);
for x = 1:size(cubePieces, 1)
    for y = 1:size(cubePieces, 2)
        cubePieces{x,y} = CubePiece(x, y, cubeColors(x,y))
    end
end

然后如果需要,可以使用cell2mat将单元格转换为2D数组。

使用内置数据类型的示例

现在这篇文章一直在谈论自定义MATLAB类;但是,这也适用于所有 MATLAB内置类。

double s。

为例的数字数组
x = 1:5

   1     2     3     4     5

现在让我们在x(2,1)添加一个元素。

x(2,1) = 6

   1     2     3     4     5
   6     0     0     0     0

显然,double的默认值为0,因为MATLAB用0填充所有未指定的值。

我们可以对单元格数组执行相同操作,其中默认值为空数组([])。

y = num2cell(1:5)

  [1]    [2]    [3]    [4]    [5]

y{2,1} = 6;

  [1]    [2]    [3]    [4]    [5]
  [6]     []     []     []     []

有关此行为的更多信息,请访问this page

摘要

当您动态扩展二维对象数组(无论它们是内置数据类型还是自定义类)时,MATLAB必须填充缺失值以保持矩形形状。 &#34;默认&#34;用于填充未指定值的值取决于数据类型。对于自定义类,此&#34;默认&#34;通过不带参数调用构造函数来创建数据类型。通过显式预分配多维数组,可以避免此默认行为。

答案 1 :(得分:0)

与Suever的答案没有达到同样精彩的细节水平,我只想为问题本身添加一些实用的解决方案。

正如Suever所说,问题是由于您正在添加Matlab尝试使用该类型的“默认”值填充的整行对象。这个难题有四种替代解决方案,但前两种已在另一个答案中提出:

  • 通过提供默认构造函数,让您的班级优雅地处理案例(Suever的解决方案#1)。但是,这可能不是一个好主意,因为它可能会破坏您的类不变量。
  • 确保使用您选择的默认对象预先分配数组(在另一个答案中,这是repmat的答案)。
  • 使用可以无限增长的一维数组,然后reshape将其设置为正确的形状。但是,对于大型数组和非句柄类型,这可能会很慢,特别是由于不断重新分配。

    for x = 1: SIDE_NO
       for y = 1: PIECE_NO
          i = sub2ind([SIDE_NO, PIECE_NO], x, y);
          piece = CubePiece(x,y,cubeColors(x,y));
          cubePieces(i) = piece; % Grow one by one
       end
    end
    cubePieces = reshape(cubePieces, [SIDE_NO, PIECE_NO]);
    
  • 使用单元格数组而不是类矩阵。您甚至可以将其预分配到正确的大小,因为默认值是空矩阵。完成后,您可以按原样使用它,或者使用cell2mat将其转换为正确的类型数组。

    cubePieces = cell(SIDE_NO, PIECE_NO);
    for x = 1: SIDE_NO
       for y = 1: PIECE_NO
          piece = CubePiece(x,y,cubeColors(x,y));
          cubePieces{x,y} = piece;
       end
    end
    cubePieces = cell2mat(cubePieces); % Or use as-is