解决问题:元胞数组元素在使用前必须完全定义
问题
与动态定型语言 MATLAB® 不同,C 和 C++ 是静态定型的。这意味着代码生成器必须能够确定 MATLAB 代码中所有变量的类型,以便在生成的代码中正确定义变量。当您使用 cell
函数创建元胞数组时,元胞数组元素的大小和类型在生成的代码中是未定义的。因此,在要进行代码生成的 MATLAB 代码中,必须确保将初始值赋给使用 cell
函数创建的所有元胞数组的所有元素。
如果代码生成器无法确定 MATLAB 代码中所有元胞数组的所有元素的类型,您将看到包含以下句子之一的错误消息:
要进行代码生成,所有变量在使用前都必须完全定义。
使用后无法初始化元胞数组元素。
对于代码生成,请在使用之前定义元胞数组元素。
即使您的 MATLAB 代码对元胞数组的所有元素进行了赋值,对于某些编码模式,代码生成仍可能会失败,因为代码生成器无法识别所有元胞数组元素都已赋值。这些编码模式包括:
在不同循环中为元胞数组元素赋予了初始值。以函数
cellArrayAssignmentError1
为例。MATLAB 为n
的所有值正确填充了元胞数组ca
。但cellArrayAssignmentError1
的代码生成失败了,因为代码生成器无法确定是否已针对n
的所有值定义了所有元胞数组元素。function out = cellArrayAssignmentError1(n) %#codegen ca = cell(1,n); for i = 1:5 ca{i} = 5; end for i = 6:n ca{i} = i; end out = ca{n}; end
使用
cell
构造元胞数组所用的变量不同于用于控制for
循环的变量,在该循环中,您为元胞数组元素赋予了初始值。以函数cellArrayAssignmentError2
为例。MATLAB 为n
的所有值正确填充了元胞数组ca
。但cellArrayAssignmentError2
的代码生成失败了,因为代码生成器无法确定是否已针对n
的所有值定义了所有元胞数组元素。function out = cellArrayAssignmentError2(n) %#codegen ca = cell(1,n); counter = n; for i = 1:counter ca{i} = 2*i; end out = ca{n}; end
在用于为元胞数组元素赋予初始值的
for
循环中,循环计数器递增或递减的数值不是1
。以函数cellArrayAssignmentError3
为例。MATLAB 为n
的所有值正确填充了元胞数组ca
。但cellArrayAssignmentError3
的代码生成失败了,因为代码生成器无法确定是否已针对n
的所有值定义了ca
的所有元素。function out = cellArrayAssignmentError3(n) %#codegen ca = cell(1,n*2); for i = 1:2:n*2-1 ca{i} = 1; ca{i+1} = 2; end out = ca{n}; end
使用下标或点索引为元胞数组元素赋予初始值。以函数
cellArrayAssignmentError4
为例。MATLAB 正确填充了元胞数组ca
。但cellArrayAssignmentError4
的代码生成失败了,因为在定义ca
的所有元素之前,代码生成器无法对ca
进行下标赋值。function out = cellArrayAssignmentError4 %#codegen ca = cell(1,3); for j = 1:3 ca{j}(1) = 10; ca{j}(2) = 20; ca{j}(3) = 30; end out = ca{2}; end
可能的解决方案
要解决此问题,请尝试以下解决办法之一。
使用一种可识别的编码模式对元胞数组元素进行赋值
使用代码生成器可以识别的编码模式为元胞数组的元素赋值。如果使用 for
循环将初始值赋给元胞数组元素,并且元胞数组大小可变,则请确保:
为同一循环中的所有元素赋予初始值。
对循环计数器使用与在
cell
函数中使用的相同变量来创建元胞数组。循环计数器的递增或递减量为
1
。
要为 1×n
元胞数组的每个元素赋予标量值,请遵循以下模式。
function out = codingPatternExample1(n) %#codegen ca = cell(1,n); for i = 1:n ca{i} = sqrt(i); end out = ca{n}; end
要将由值组成的数组分配给 1×n
元胞数组的每个元素,请在一个语句中将由值组成的数组赋给每个元素。不要使用下标赋值或点赋值。
function out = codingPatternExample2(n) %#codegen ca = cell(1,n); for i = 1:n ca{i} = [sqrt(i) i i^2]; end out = ca{n}; end
要为一个多维元胞数组的每个元素赋值,请使用嵌套循环。例如,将以下编码模式用于一个三维 (m
×n
×p
) 数组。
function out = codingPatternExample3(m,n,p) %#codegen ca = cell(m,n,p); for i = 1:m for j =1:n for k = 1:p ca{i,j,k} = i+j+k; end end end out = ca{m,n,p}; end
使用函数 repmat
repmat
函数接受一个输入数组,并在行维度和列维度中将其复制指定的次数以构造一个输出数组。在某些情况下,如果您的元胞数组包含重复元素,则可以使用 repmat
重写代码来定义元胞数组的元素。以函数 repmatAssignmentError
为例,该函数使用 cell
函数创建一个 1×(n*2)
元胞数组。repmatAssignmentError
将 1
赋给 ca
的奇数元素,将 0
赋给偶数元素。此函数的代码生成失败了,因为初始值赋给了不同的 for
循环中的元胞数组元素,并且循环计数器的增量值不是 1。
function out = repmatAssignmentError(n) %#codegen ca = cell(1,n*2); for i = 1:2:n*2 ca{i} = 1; end for i = 2:2:n*2 ca{i} = 0; end out = ca{n}; end
要解决此问题,请首先使用 cell
函数创建一个临时的 1×2 元胞数组,其第一个元素为 1,第二个元素为 0。然后,使用 repmat
函数创建一个 1×(n*2)
元胞数组,其元素值在 1 和 2 之间交替。
function out = repmatAssignmentExample(n) %#codegen tmp = cell(1,2); tmp{1} = 1; tmp{2} = 0; ca = repmat(tmp,1,n); out = ca{n}; end
使用 coder.nullcopy
您可以使用 coder.nullcopy
函数为同构元胞数组预分配内存,而无需专门为元胞数组元素赋值。一般情况下,同构元胞数组包含的元素都具有相同的类型,例如 double
或 string
。然而,在某些情况下,代码生成器可以将同一元胞数组在某些上下文中分类为同构元胞数组,而在其他上下文中将其分类为异构元胞数组。请参阅Code Generation for Cell Arrays。
如果您使用 coder.nullcopy
来定义同构元胞数组,您可以生成代码而不必遵循上述可识别的编码模式。以 nullcopyExample
函数为例。由于此函数使用 coder.nullcopy
为同构元胞数组 ca
预分配内存,您可以在两个单独的循环中为元胞数组赋值。
function out = nullcopyExample(n) %#codegen tmp = cell(1,n); ca = coder.nullcopy(tmp); for i = 1:4 ca{i} = 0; end for i = 5:n ca{i} = i; end out = ca{n}; end
注意
请慎用 coder.nullcopy
。您必须确保为元胞数组中的所有元素赋值。如果访问未初始化的元胞数组元素,结果可能无法预测。
另请参阅
cell
| repmat
| coder.nullcopy