为什么 MATLAB 对结构数组赋值中的字段顺序敏感?
Why is MATLAB sensitive to order of fields in a struct array assignment?
首先我将 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
分配给 B
和 C
是可行的,这是我期望结构的行为 - 元素的顺序无关紧要。
现在我将 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 突然报错:
subscripted assignment between dissimilar structures
有谁知道为什么会发生这种情况以及如何以优雅的方式解决它?
这很可能发生,因为源结构和目标结构的内置 subsasgn
call for the struct
probably just compares the output of fieldnames
(取决于字段顺序)并且在比较之前不执行任何排序(可能是因为性能下降为每个分配排序两个元胞数组)。如果存在差异(如您所示的情况),则会抛出错误并中止分配。
解决此问题的最简单方法是在源结构上使用 orderfields
并使用第二个输入参数指定您希望顺序与目标结构相匹配。
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
输入自己做这件事,因为操作相对较快(因为没有排序和[=15的基础数据=] 未被复制)但允许更灵活的 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);
更新
我刚刚注意到您在原始 post 中显示以下内容有效,因为顺序无关紧要。
A = B;
之所以可行,是因为此赋值 不依赖于 A
的数据类型。在这种情况下,MATLAB 删除 A
在分配之前引用的数据,然后 重新分配 A
它到了 B
的地步。我们甚至可以使 A
成为元胞数组并毫无问题地执行上述赋值。
A = cell(2);
B = struct('y', 21, 'x', 22);
%// No errors here!
A = B;
此赋值不会调用 subsasgn
(仅处理下标赋值),因此不会出现您遇到的问题。
我过去解决这个问题的一种方法是创建结构的 "null" 版本,类似于为对象创建构造函数。
%% 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;
然后,您可以按照您喜欢的任何顺序填充结构。您甚至可以将 "empty" 结构传递给函数,并允许函数填充值,并且函数不必注意值的分配顺序。
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!
初始化数据结构是很好的编程习惯,对于大型结构数组,预先进行初始化实际上可以节省很多时间。即使您必须初始化比需要更多的元素,并在末尾截断数组,这通常也是值得的。
编辑:错误解释:
MATLAB 在您的原始示例中抛出错误的原因是在内部(在 C 代码后端),MATLAB 将字段名称存储在有序的字符串数组中,并将名称映射到相应的字段索引。当您执行
之类的作业时
A = C;
MATLAB 首先检查两个字段名列表是否匹配,这要求列表相同,包括顺序相同。如果是,则它按从 rhs 到 lhs 的顺序映射字段值。
首先我将 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
分配给 B
和 C
是可行的,这是我期望结构的行为 - 元素的顺序无关紧要。
现在我将 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 突然报错:
subscripted assignment between dissimilar structures
有谁知道为什么会发生这种情况以及如何以优雅的方式解决它?
这很可能发生,因为源结构和目标结构的内置 subsasgn
call for the struct
probably just compares the output of fieldnames
(取决于字段顺序)并且在比较之前不执行任何排序(可能是因为性能下降为每个分配排序两个元胞数组)。如果存在差异(如您所示的情况),则会抛出错误并中止分配。
解决此问题的最简单方法是在源结构上使用 orderfields
并使用第二个输入参数指定您希望顺序与目标结构相匹配。
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
输入自己做这件事,因为操作相对较快(因为没有排序和[=15的基础数据=] 未被复制)但允许更灵活的 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);
更新
我刚刚注意到您在原始 post 中显示以下内容有效,因为顺序无关紧要。
A = B;
之所以可行,是因为此赋值 不依赖于 A
的数据类型。在这种情况下,MATLAB 删除 A
在分配之前引用的数据,然后 重新分配 A
它到了 B
的地步。我们甚至可以使 A
成为元胞数组并毫无问题地执行上述赋值。
A = cell(2);
B = struct('y', 21, 'x', 22);
%// No errors here!
A = B;
此赋值不会调用 subsasgn
(仅处理下标赋值),因此不会出现您遇到的问题。
我过去解决这个问题的一种方法是创建结构的 "null" 版本,类似于为对象创建构造函数。
%% 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;
然后,您可以按照您喜欢的任何顺序填充结构。您甚至可以将 "empty" 结构传递给函数,并允许函数填充值,并且函数不必注意值的分配顺序。
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!
初始化数据结构是很好的编程习惯,对于大型结构数组,预先进行初始化实际上可以节省很多时间。即使您必须初始化比需要更多的元素,并在末尾截断数组,这通常也是值得的。
编辑:错误解释: MATLAB 在您的原始示例中抛出错误的原因是在内部(在 C 代码后端),MATLAB 将字段名称存储在有序的字符串数组中,并将名称映射到相应的字段索引。当您执行
之类的作业时A = C;
MATLAB 首先检查两个字段名列表是否匹配,这要求列表相同,包括顺序相同。如果是,则它按从 rhs 到 lhs 的顺序映射字段值。