Почему 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.