Почему MATLAB чувствителен к порядку полей в распределении массива структуры?

Сначала я указываю A как структуру и две другие структуры: B с одинаковым порядком элементов, а C имеет другой порядок элементов.

A.x = 11;
A.y = 11;

B.x = 21;
B.y = 22;

C.y = 31;   %// Note that I am specifying
C.x = 32;   %// y first and x second

A = B;      %// Works fine
A = C;      %// Works fine

Присвоение A to B и C работает, это поведение, которое я ожидаю от structs - порядок элементов не должен иметь значения.

Теперь я указываю A как структурный массив вместо структуры и пытаюсь назначить один из его элементов B и C соответственно:

clear;

A(1).x = 11;
A(1).y = 12;

B.x = 21;
B.y = 22;

C.y = 31;   %// Note that I am specifying
C.x = 32;   %// y first and x second

A(1) = B;   %// Works fine
A(1) = C;   %// Error!

Внезапно MATLAB жалуется на ошибку:

подстрочное присвоение между разнородными структурами

Кто-нибудь знает, почему это происходит и как исправить это элегантным способом?

Ответ 1

Вероятно, это происходит потому, что встроенный вызов subsasgn для struct, вероятно, просто сравнивает вывод fieldnames (который зависит от порядка полей) для исходной и целевой структур и не выполняет сортировку до сравнения (вероятно, из-за хита производительности сортировка двух массивов ячеек для каждого назначения). Если есть разница (как в случае, если вы показали), тогда возникает ошибка и назначение прерывается.

Самый простой способ обойти это - использовать orderfields в структуре источника и указать, что вы хотите, чтобы заказ соответствовал назначению struct, используя второй входной аргумент.

A = struct('x', 11, 'y', 12);
B = struct('y', 21, 'x', 22);

%// Ensure that the fields of B are in the same order as they are in A
A(1) = orderfields(B, A);

По моему личному мнению, я думаю, что subsasgn должен делать это сам для входов struct, потому что операция относительно быстрая (поскольку сортировка отсутствует, а исходные данные struct не копируются), но позволяет более гибко назначать struct.

Если, с другой стороны, вы не выполняете прямое назначение, а просто хотите добавить два structs, порядок полей не имеет значения, а порядок полей наследуется от первого struct, который встречается.

%// Uses the ordering of the fields in A
C = cat(1, A, B);

%// Uses the ordering of the fields in B
D = cat(1, B, A);

Обновление

Я только заметил, что вы показали в своем оригинальном посте, что следующее работало, потому что заказ не имел значения.

A = B;

Это работает, потому что это назначение не зависит от типа данных A. В этом случае MATLAB удаляет данные, которые A указывал перед назначением, а затем повторно назначает A его точке B. Мы могли бы даже сделать A массив ячеек и выполнить указанное назначение без проблем.

A = cell(2);
B = struct('y', 21, 'x', 22);

%// No errors here!
A = B; 

Это назначение не вызывает вызов subsasgn (который имеет дело только с присвоением индекса), и поэтому не ожидается, что вы столкнетесь с проблемой, с которой вы столкнулись.

Ответ 2

Один из способов, который я решил в прошлом, - создать "нулевую" версию структуры, аналогичную созданию конструктора для объекта.

%% define null struct
null_struct.x = 0;
null_struct.y = 0;

%% Now, initialize all structs with it
A=null_struct;
B=null_struct;
C=null_struct;

%% You can even initialize large arrays
Z(1:1000, 1:1000) = null_struct;

Затем вы можете заполнить структуры в любом порядке. Вы даже можете передать "пустые" структуры в функцию и позволить функции заполнять значения, и функция не должна быть осторожна относительно порядка, которому присваиваются значения.

A(1).x = 11;
A(1).y = 12;

B.x = 21;
B.y = 22;

C.y = 31;   % Note that I'm specifying
C.x = 32;   % y first and x second

A(1) = B;   % Works fine
A(1) = C;   % Also works fine!

Инициализация ваших структур данных - очень хорошая практика программирования, а для больших массивов массивов это фактически экономит много времени, чтобы выполнить инициализацию. Даже если вам нужно инициализировать больше элементов, чем нужно, и обрезать массив в конце, это вообще стоит.

EDIT: Объяснение для вашей ошибки: Причина, по которой MATLAB вызывает ошибку в вашем исходном примере, заключается в том, что внутри (в бэкэнде C-кода) MATLAB хранит имена полей в упорядоченном массиве символьных строк и сопоставляет имена с соответствующими индексами полей. Когда вы выполняете задание типа

A = C;

MATLAB сначала проверяет соответствие двух списков имен полей, что требует идентичности списков, в том числе в том же порядке. Если они есть, то он отображает значения полей в порядке от rhs до lhs.