Main Content

代码生成的元胞数组限制

当您在用于代码生成的 MATLAB® 代码中使用元胞数组时,必须遵守以下限制。

分配一致的元素类型

分配给元胞数组元素的值类型在所有执行路径中必须相同。以 inconsistentTypeError 函数为例。此函数的代码生成失败,因为 ca{2} 在执行路径中是 double,而在另一个执行路径中是 char

function out = inconsistentTypeError(n) %#codegen
ca = cell(1,3);
if n > 1
    ca{1} = 1;
    ca{2} = 2;
    ca{3} = 3;
else
    ca{1} = 10;
    ca{2} = 'a';
    ca{3} = 30;
end
out = ca{1};
end

可变大小元胞数组必须为同构元胞数组

当您为包含元胞数组的 MATLAB 函数生成代码时,代码生成器会将每个元胞数组分类为同构异构。通常,代码生成器将包含相同类型元素的元胞数组归类为同构,将包含不同类型元素的元胞数组归类为异构。要了解有关代码生成器如何处理同构和异构元胞数组的更多信息,请参阅Code Generation for Cell Arrays

您可以使用 coder.varsize 将同构元胞数组定义为可变大小,并为可变大小元胞数组设置上界。例如,以下代码将 caXcaY 从固定大小的 1x3 元胞数组更改为可变大小元胞数组,其中第二个维度的大小最大为 5 (1x:5)。

...
caX = {1 2 3};
caY = cell(1,3);
coder.varsize('caX',[1 5])
coder.varsize('caY',[1 5])
...

为所有元胞数组元素赋值

在使用元胞数组之前,必须在所有执行路径上对所有元胞数组元素赋值。当您使用 cell 函数创建可变大小元胞数组时,元胞数组元素的大小和类型在生成的代码中未定义。因此,在要进行代码生成的 MATLAB 代码中,必须确保将初始值赋给为使用 cell 函数创建的所有可变大小元胞数组的所有元素。以 incompleteAssignmentError 函数为例。此函数的代码生成失败,因为代码生成器检测到 ca{2} 函数返回的元素未在 n 小于 5 的执行路径中定义。

function out = incompleteAssignmentError(n) %#codegen
ca = cell(1,2);
if n < 5
    ca{1} = 0;    
else
    ca{2} = 3;
end
out = ca{2};
end

有时,即使您的 MATLAB 代码对元胞数组的所有元素进行了赋值,代码生成也会失败,因为代码生成器无法识别所有元胞数组元素都已赋值。通常,使用 cellArrayPatternExample 中所示的编码模式对使用 cell 创建的可变大小元胞数组的元素赋值。

function out = cellArrayPatternExample(n) %#codegen
ca = cell(1,n);   
for i = 1:n
    ca{i} = i;
end
out = ca{n};
end

有关可用于确保定义所有元胞数组元素的其他模式,请参阅解决问题:元胞数组元素在使用前必须完全定义

在与 cell 函数相同的执行路径中赋予初始值

使用 cell 函数创建元胞数组时,必须将为元胞数组元素赋予初始值的循环包含在与创建该元胞数组的 cell 函数相同的唯一执行路径中。例如,函数 cellLoopError 的代码生成会失败,因为将初始值赋给元胞数组 ca 的元素的 for 循环没有出现在创建该元胞数组的 cell 函数所在的唯一执行路径中。

function out = cellLoopError(n) %#codegen
if n < 5
    ca = cell(1,n);
else
    ca = cell(n,1);
end
for i = 1:n
    ca{i} = i;
end
out = ca;
end

要解决此问题,请在包含创建每个元胞数组的 cell 函数的代码块中将初始值赋给元胞数组元素。

function out = cellLoopExample(n) %#codegen
if n < 5
    ca = cell(1,n);
    for i = 1:n
        ca{i} = 0;
    end
else
    ca = cell(n,1);
    for i = 1:n
        ca{i} = 0;
    end
end
for i = 1:n
    ca{i} = i;
end
out = ca{n};
end

要在对象属性或结构体字段中将初始值赋给可变大小的元胞数组,请使用临时变量

使用 cell 函数创建可变大小元胞数组时,必须先将初始值赋给元胞数组的所有元素,然后在对象属性或结构体字段中使用该数组。例如,函数 fieldAssignmentError 的代码生成会失败,因为它直接将初始值赋给结构体字段中的元胞数组元素。

function out = fieldAssignmentError(n) %#codegen
s.field = cell(1,n);
for i = 1:n
    s.field{i} = i+1;
end
out = s.field{n};
end
要避免此错误,请使用临时变量初始化元胞数组。然后,将初始化的元胞数组赋给结构体字段或对象属性。之后,您可以通过直接访问结构体字段或对象属性来修改元胞数组的元素。
function out = fieldAssignmentExample(n) %#codegen
ca = cell(1,n);
for i = 1:n
   ca{i} = 0;
end
s.field = ca;
for i = 1:n
   s.field{i} = i+1;
end
out = s.field{n};
end

要对异构数组进行索引,请使用常量索引或使用具有常量边界的 for 循环

当您为包含元胞数组的 MATLAB 函数生成代码时,代码生成器会将每个元胞数组分类为同构异构。通常,代码生成器将包含相同类型元素的元胞数组归类为同构,将包含不同类型元素的元胞数组归类为异构。要了解有关代码生成器如何处理同构和异构元胞数组的更多信息,请参阅Code Generation for Cell Arrays

您必须使用常量索引或使用具有常量边界的 for 循环对异构元胞数组进行索引。例如,函数 cellIndexingError 的代码生成会失败,因为索引不是常量。

function cellIndexingError(n) %#codegen
ca = {1 "a" 2 5:7 3 "c"};
disp(ca{n});
end

您可以通过使用具有常量边界的 for 循环对异构元胞数组进行索引,因为代码生成器会展开循环,这意味着它为每个循环迭代创建一个循环体的单独副本。

function cellIndexingExample(n) %#codegen
ca = {1 "a" 2 5:7 3 "c"};
for i = 1:6
    if i == n
        disp(ca{i});
    end
end
end

注意

如果 for 循环体很大或者有很多迭代,则展开可能增加编译时间和生成无效代码。

要使用 {end + 1} 增大元胞数组,请使用特定编码模式

代码生成通过在用于代码生成的 MATLAB 代码中使用 {end + 1} 来支持增大元胞数组。例如,您可以为函数 growCellArray 生成代码,该函数使用 {end + 1} 来增大数组 ca

function out = growCellArrayExample(n) %#codegen
ca = {1  2};
for i = 1:n
    ca{end+1} = 3+i;
end
out = ca{n};
end

当您使用 {end + 1} 在 MATLAB 代码中增大元胞数组以生成代码时,您必须遵守以下限制:

  • 您只能通过为 {end + 1} 元素赋值来增大元胞数组。不支持为后续元素赋值,如 {end + 2}

  • 您只能使用 {end + 1} 来增大元胞数组向量。例如,函数 growMatrixError 的代码生成失败,因为 ca 是矩阵,而不是向量。

    function ca = growMatrixError %#codegen
    ca = {1 2; 3 4};
    ca{1,end+1} = 5;
    end
  • {end + 1} 必须紧跟在变量后。例如,growArraySubscriptError 的代码生成会失败,因为 {end + 1} 出现在 ca{2} 旁边,而不是 ca 旁边。

    function out = growArraySubscriptError(x) %#codegen
    ca = {'a' { 1 2 3 }};
    ca{2}{end+1} = x;
    out = ca{2}{4};
    end
    要解决此问题,请将要修改的元胞数组的元素设置为临时变量。然后,使用 {end + 1} 增大临时变量。使用 coder.varsize 以允许修改后的元胞数组元素的大小发生变化。
    function out = growArrayExampleA(x) %#codegen
    ca = {'a' {1 2 3}};
    coder.varsize('ca{2}')
    temp = ca{2};
    temp{end+1} = x;
    ca{2} = temp;
    out = ca{2}{4};
    end

    或者,通过使用数组串联而不是使用 {end + 1} 来添加新元素。

    function out = growArrayExampleB(x) %#codegen
    ca = {'a' {1 2 3}};
    coder.varsize('ca{2}')
    ca{2} = [ca{2} {x}];
    out = ca{2}{4};
    end

  • 如果使用 {end + 1} 在循环中增大元胞数组,则元胞数组必须是同构的,这意味着所有元胞数组元素必须为同一类型。例如,函数 growHeterogeneousError 的代码生成会失败,因为元胞数组 ca 包含类型为 string 和类型为 double 的元素。

    function out = growHeterogeneousError(n) %#codegen
    ca = {1  2 "a"};
    for i = 1:n
        ca{end+1} = 3+i;
    end
    out = ca;
    end
    要了解有关代码生成器如何处理同构和异构元胞数组的更多信息,请参阅Code Generation for Cell Arrays

要直接对元胞数组进行迭代,元胞数组的第一个维度必须为 1

在 MATLAB 中,您可以直接对任意维数的元胞数组进行迭代。例如,假设有函数 directIterationError,该函数对一个 3×4 元胞数组进行迭代并显示每列的内容。此函数的代码生成会失败,因为数组 ca 的第一个维度不是 1

function directIterationError
ca = {1 2 3 4; "five" "six" "seven" "eight"; 9 10 11 12};
for i = ca
    disp(i);
end
end

要在 MATLAB 代码中迭代第一个维度不是 1 的元胞数组以生成代码,请遍历每个元胞数组维度的大小,而不是元胞数组本身。

function iterationExample %#codegen
ca = {1 2 3 4; "five" "six" "seven" "eight"; 9 10 11 12};
for i = 1:size(ca,1)
    for j = 1:size(ca,2)
        disp(ca{i,j});
    end
end
end

不要使用圆括号 () 对元胞数组进行索引

在用于代码生成的 MATLAB 代码中,不能使用圆括号 () 对元胞数组进行索引。使用花括号 {} 访问元胞内容。

不要在元胞数组构造函数 {} 中调用 cell 函数

代码生成不支持在元胞数组构造函数 {} 中使用 cell 函数。因此,函数 cellCellError 的代码生成失败。

function out = cellCellError(n) %#codegen
ca = {cell(1,n) cell(1,n)};
for i = 1:n
    ca{1}{i} = i+1;
    ca{2}{i} = i+2;
end
out = ca;
end

要创建包含其他元胞数组的元胞数组,请在创建外部数组之前初始化每个内部元胞数组的所有元素。然后,您可以直接访问内部数组的元素。

function out = cellCellExample(n) %#codegen
x = cell(1,n);
y = cell(1,n);
for i = 1:n
    x{i} = 0;
    y{i} = 0;
end
ca = {x y};
for i = 1:n
    ca{1}{i} = i+1;
    ca{2}{i} = i+2;
end
out = ca{1}{n}+ca{2}{n};
end

不要在元胞数组中存储 mxArray 数据

在用于代码生成的 MATLAB 代码中,不能在元胞数组中存储 MATLAB 数组(亦称 mxArray)。当您使用 coder.extrinsic 调用外部函数时,被调函数的运行时输出为 mxArray。因此,如果不先将外部函数的输出转换为已知类型,就无法将其存储在元胞数组中。请参阅使用 mxArray

不要使用 coder.ceval 将元胞数组传递给外部 C/C++ 函数

代码生成不支持使用 coder.ceval 将元胞数组传递给外部 C/C++ 函数。如果元胞数组是 coder.ceval 的输入参量,请将元胞数组重新定义为普通数组或结构体。

相关主题