メインコンテンツ

問題の解決: cell 配列の要素は使用前に完全に定義する必要がある

問題

動的に型指定される言語である MATLAB® とは異なり、C と C++ は静的に型指定されます。つまり、コード ジェネレーターは、生成されたコード内の変数を定義できるように、MATLAB コード内のすべての変数の型を判断できる必要があります。関数 cell を使用して cell 配列を作成した場合、生成されたコードでは cell 配列の要素のサイズと型が未定義になります。そのため、コード生成用の MATLAB コードでは、関数 cell を使用して作成したすべての cell 配列の全要素に必ず初期値を代入しなければなりません。

コード ジェネレーターが MATLAB コード内のすべての cell 配列の全要素の型を判別できない場合は、次の文を含むエラー メッセージが表示されます。

コード生成では、すべての cell 配列要素を使用前に完全に定義する必要があります。

MATLAB コードで cell 配列のすべての要素に値が代入されていても、特定のコーディング パターンでは、コード ジェネレーターが cell 配列のすべての要素に代入されていることを認識できないためにコード生成が失敗することがあります。該当するコーディング パターンには次のようなものがあります。

  • 異なるループで cell 配列要素に初期値を代入する。たとえば、関数 cellArrayAssignmentError1 について考えます。MATLAB は、n のすべての値について cell 配列 ca に値を代入します。ただし、すべての cell 配列要素が n のすべての値に対して定義されていることをコード ジェネレーターが判断できないため、cellArrayAssignmentError1 のコード生成は失敗します。

    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 を使用して cell 配列を構築するために使用する変数が、cell 配列要素に初期値を代入する for ループを制御するために使用する変数と異なっている。たとえば、関数 cellArrayAssignmentError2 について考えます。MATLAB は、n のすべての値について cell 配列 ca に値を代入します。ただし、すべての cell 配列要素が n のすべての値に対して定義されていることをコード ジェネレーターが判断できないため、cellArrayAssignmentError2 のコード生成は失敗します。

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

  • cell 配列要素に初期値を代入するために使用する for ループで、ループ カウンターを 1 以外の数値ずつインクリメントまたはデクリメントしている。たとえば、関数 cellArrayAssignmentError3 について考えます。MATLAB は、n のすべての値について cell 配列 ca に値を代入します。ただし、ca のすべての要素が n のすべての値に対して定義されていることをコード ジェネレーターが判断できないため、cellArrayAssignmentError3 のコード生成は失敗します。

    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

  • 添字またはドット インデックス付けを使用して、cell 配列要素に初期値を代入する。たとえば、関数 cellArrayAssignmentError4 について考えます。MATLAB は、cell 配列 ca に値を代入します。ただし、ca のすべての要素が定義される前に、コード ジェネレーターが ca に添字を付けることができないため、cellArrayAssignmentError4 のコード生成は失敗します。

    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

考えられる解決策

この問題を解決するには、次のいずれかの解決策を試してください。

認識されるコーディング パターンを使用して cell 配列要素に代入する

コード ジェネレーターが認識できるコーディング パターンを使用して、cell 配列の要素に値を代入します。for ループを使用して cell 配列の要素に初期値を代入する場合に、cell 配列が可変サイズのときは、必ず次のようにしてください。

  • 同じループ内ですべての要素に初期値を代入します。

  • cell 配列を作成するために関数 cell で使用した変数と同じ変数をループ カウンターに使用します。

  • ループ カウンターを 1 ずつインクリメントまたはデクリメントします。

1 行 n 列の cell 配列の各要素にスカラー値を代入するには、次のパターンに従います。

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

1 行 n 列の cell 配列の各要素に値の配列を代入するには、1 つのステートメントで各要素に値の配列を代入します。添字やドットによる代入は使用しないでください。

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

多次元 cell 配列の各要素に値を代入するには、入れ子にしたループを使用します。たとえば、3 次元 (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 は入力配列を受け取り、それを行次元と列次元で指定された回数複製して出力配列を作成します。cell 配列に繰り返し要素が含まれている場合は、repmat を使用して cell 配列の要素を定義するようにコードを書き換えることができます。たとえば、関数 cell を使用して 1 行 (n*2) 列の cell 配列を作成する関数 repmatAssignmentError について考えます。repmatAssignmentErrorca の奇数要素に 1 を代入し、偶数要素に 0 を代入します。異なる for ループ内で初期値が cell 配列要素に代入されるため、またループ カウンターが 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 番目の要素が 1 で 2 番目の要素が 0 である一時的な 1 行 2 列の cell 配列を作成します。次に、関数 repmat を使用して、要素の値が 1 と 2 の間で交互に切り替わる 1 行 (n*2) 列の cell 配列を作成します。

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 を使用して、cell 配列要素に値を明示的に代入することなく、同種の cell 配列用にメモリを事前に割り当てることができます。一般に、同種の cell 配列には、doublestring など、すべて同じ型の要素が含まれています。しかし、特定の状況では、コード ジェネレーターは同じ cell 配列を、あるコンテキストでは同種として分類するが、他のコンテキストでは異種混合として分類することがあります。cell 配列のコード生成を参照してください。

coder.nullcopy を使用して同種の cell 配列を定義する場合、前述の認識されるコーディング パターンに従わなくてもコードを生成できます。たとえば、関数 nullcopyExample について考えます。この関数は coder.nullcopy を使用して同種の cell 配列 ca 用にメモリを事前に割り当てるため、2 つの別個のループで cell 配列に値を代入できます。

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 配列のすべての要素に値が代入されていることを確認する必要があります。初期化されていない cell 配列要素にアクセスした場合、結果は予測できません。

参考

| |

トピック