本页对应的英文页面已更新,但尚未翻译。 若要查看最新内容,请点击此处访问英文页面。

代码生成的元胞数组限制

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

元胞数组元素赋值

在使用某元胞数组元素之前,必须在所有执行路径上对该元素赋值。例如:

function z = foo(n)
%#codegen
c = cell(1,3);
if n < 1
    c{2} = 1;
    
else
    c{2} = n;
end
z = c{2};
end

将元胞数组传递给函数或从函数返回元胞数组时,代码生成器会将其视为使用元胞数组的所有元素。因此,在将元胞数组传递给函数或从函数返回该元胞数组之前,必须为其所有元素赋值。例如,不允许使用以下代码,因为它没有为 c{2} 赋值,而 c 是函数输出。

function c = foo()
%#codegen
c = cell(1,3);
c{1} = 1;
c{3} = 3;
end

对元素的赋值必须在所有执行路径上保持一致。不允许使用以下代码,因为 y{2} 在一条执行路径上为 double 类型,而在另一条执行路径上为 char 类型。

function y = foo(n)
y = cell(1,3)
if n > 1;
    y{1} = 1
    y{2} = 2;
    y{3} = 3;
else
    y{1} = 10;
    y{2} = 'a';
    y{3} = 30;
end

可变大小元胞数组

  • 异构元胞数组不支持 coder.varsize

  • 如果使用 cell 函数定义固定大小元胞数组,则无法使用 coder.varsize 指定元胞数组具有可变大小。例如,以下代码会导致代码生成错误,因为 x = cell(1,3) 使 x 成为固定大小的 1×3 元胞数组。

    ...
    x = cell(1,3);           
    coder.varsize('x',[1 5])
    ...

    您可以将 coder.varsize 与使用花括号定义的元胞数组结合使用。例如:

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

  • 要使用 cell 函数创建可变大小的元胞数组,请使用以下代码模式:

    function mycell(n)
    %#codegen
    x = cell(1,n);   
    for i = 1:n
        x{i} = i;
    end
    end

    请参阅使用 cell 定义可变大小元胞数组

    要指定元胞数组的上界,请使用 coder.varsize

    function mycell(n)
    %#codegen
    x = cell(1,n);   
    for i = 1:n
        x{i} = i;
    coder.varsize('x',[1,20]);
    end
    end

使用 cell 定义可变大小元胞数组

对于代码生成,在使用某元胞数组元素之前,必须为其赋值。当您使用 cell 创建可变大小元胞数组(例如 cell(1,n))时,MATLAB 会用一个空矩阵对每个元素赋值。然而,代码生成并不会对元素进行赋值。对于代码生成,在您使用 cell 创建可变大小元胞数组后,必须对该元胞数组的所有元素赋值,然后才能使用该元胞数组。例如:

function z = mycell(n, j)
%#codegen
x = cell(1,n);   
for i = 1:n
    x{i} = i;
end
z = x{j};
end

在首次使用元胞数组之前,代码生成器会分析您的代码,以确定是否所有元素均已赋值。如果代码生成器检测到某些元素未赋值,代码生成将失败,并显示错误消息。例如,将 for 循环的上界修改为 j

function z = mycell(n, j)
%#codegen
x = cell(1,n);   
for i = 1:j %<- Modified here
    x{i} = i;
end
z = x{j};
end

如果进行了此修改且输入 j 小于 n,则该函数不会为所有元胞数组元素赋值。代码生成会产生错误:

Unable to determine that every element of 'x{:}' is assigned
before this line.

有时,尽管您的代码对元胞数组的所有元素进行了赋值,但代码生成器仍报告此消息,这是因为分析未检测到所有元素均已赋值。请参阅Unable to Determine That Every Element of Cell Array Is Assigned

为避免此错误,请遵循以下准则:

  • 当您使用 cell 定义可变大小元胞数组时,请按照以下模式编写代码:

    function z = mycell(n, j)
    %#codegen
    x = cell(1,n);   
    for i = 1:n
        x{i} = i;
    end
    z = x{j};
    end
    

    以下是针对多维元胞数组的模式:

    function z = mycell(m,n,p)
    %#codegen
    x = cell(m,n,p);
    for i = 1:m
        for j =1:n
            for k = 1:p
                x{i,j,k} = i+j+k;
            end
        end
    end
    z = x{m,n,p};
    end

  • 循环计数器的递增或递减量为 1

  • 在一个循环或一组嵌套循环内定义元胞数组。例如,不允许使用以下代码:

    function z = mycell(n, j)
    x = cell(1,n);
    for i = 1:5
        x{i} = 5;
    end
    for i = 6:n
        x{i} = 5;
    end
    z = x{j};
    end            

  • 对元胞维度、循环初始值和结束值使用相同的变量。例如,以下代码的代码生成就会失败,因为元胞创建使用的是 n,而循环结束值使用的是 m

    function z = mycell(n, j)
    x = cell(1,n);
    m = n;
    for i = 1:m
        x{i} = 2;
    end
    z = x{j};
    end               

    重写代码以对元胞创建和循环结束值使用 n

    function z = mycell(n, j)
    x = cell(1,n);
    for i = 1:n
        x{i} = 2;
    end
    z = x{j};
    end

  • 按照以下模式创建元胞数组:

    x = cell(1,n)

    不要将元胞数组赋给结构体的字段或对象的属性。例如,不允许使用以下代码:

    myobj.prop = cell(1,n)
    for i = 1:n
    ...
    end
    

    不要在元胞数组构造函数 {} 内使用 cell 函数。例如,不允许使用以下代码:

    x = {cell(1,n)};

  • 为元胞数组元素赋值的元胞数组创建语句和循环赋值语句必须位于同一个唯一执行路径中。例如,不允许使用以下代码。

    function z = mycell(n)
    if n > 3
        c = cell(1,n);
    else
        c = cell(n,1);
    end
    for i = 1:n
        c{i} = i;
    end
    z = c{n};
    end

    要修复此代码,请将赋值循环移入创建元胞数组的代码块内。

    function z = cellerr(n)
    if n > 3
        c = cell( 1,n);
        for i = 1:n
            c{i} = i;
        end
    else
        c = cell(n,1);
        for i = 1:n
            c{i} = i;
        end
    end
    z = c{n};
    end

元胞数组索引

  • 您不能使用圆括号 () 对元胞数组进行索引。请考虑使用花括号 {} 对元胞数组进行索引来访问元胞的内容。

  • 您必须使用常量索引或使用具有常量边界的 for 循环对异构元胞数组进行索引。

    例如,不允许使用以下代码。

    x = {1, 'mytext'};
    disp(x{randi});

    您可以在具有常量边界的 for 循环中对异构元胞数组进行索引,因为代码生成器会展开循环。展开会为每次循环迭代创建循环体的一个单独副本,这使得每次循环迭代中的索引不变。然而,如果 for 循环体很大或者有很多迭代,则展开可能增加编译时间和生成无效代码。

    如果 AB 是常数,则下列代码展示了如何在具有常量边界的 for 循环中对异构元胞数组进行索引。

    x = {1, 'mytext'};
    for i = A:B
    	 disp(x{i});
    end

使用 {end + 1} 增大元胞数组

要增大元胞数组 X,可以使用 X{end + 1}。例如:

...
X = {1 2};
X{end + 1} = 'a';
...

当您使用 {end + 1} 增大元胞数组时,请遵循以下限制:

  • 仅使用 {end + 1}。不要使用 {end + 2}{end + 3} 等。

  • 仅对向量使用 {end + 1}。例如,不允许使用以下代码,因为 X 是矩阵,而不是向量:

    ...
    X = {1 2; 3 4};
    X{end + 1} = 5;
    
    ...

  • 仅对变量使用 {end + 1}。在以下代码中,{end + 1} 不会使 {1 2 3} 增大。在本例中,代码生成器将 {end + 1} 视为超出 X{2} 范围的索引。

    ...
    X = {'a' { 1 2 3 }};
    X{2}{end + 1} = 4;
    ...

  • {end + 1} 在循环中增大元胞数组时,该元胞数组必须是可变大小的。因此,元胞数组必须是同构元胞数组

    允许使用以下代码,因为 X 是同构元胞数组。

    ...
    X = {1  2};
    for i=1:n
        X{end + 1} = 3;
    end
    ...

    不允许使用以下代码,因为 X 是异构元胞数组。

    ...
    X = {1 'a' 2 'b'};
    for i=1:n
        X{end + 1} = 3;
    end
    ...

元胞数组内容

元胞数组不能包含 mxarrays。在元胞数组中,不能存储外部函数返回的值。

将元胞数组传递给外部 C/C++ 函数

您不能将元胞数组传递给 coder.ceval。如果某变量是 coder.ceval 的输入参数,则应将该变量定义为数组或结构体,而不能是元胞数组。

相关主题