Main Content

解决问题:元胞数组元素在使用前必须完全定义

问题

与动态定型语言 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) 元胞数组。repmatAssignmentError1 赋给 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 函数为同构元胞数组预分配内存,而无需专门为元胞数组元素赋值。一般情况下,同构元胞数组包含的元素都具有相同的类型,例如 doublestring。然而,在某些情况下,代码生成器可以将同一元胞数组在某些上下文中分类为同构元胞数组,而在其他上下文中将其分类为异构元胞数组。请参阅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。您必须确保为元胞数组中的所有元素赋值。如果访问未初始化的元胞数组元素,结果可能无法预测。

另请参阅

| |

相关主题