MATLAB中结构体和空单元数组的“bug”(或非常奇怪的行为)

7

我不知道这里发生了什么。我使用的是R2006b版本。在我提交错误报告之前,有没有人能测试一下他们是否会遇到相同的问题,尤其是使用更新的版本?

代码: (bug1.m)

function bug1
S = struct('nothing',{},'something',{});
add_something(S, 'boing');          % does what I expect
add_something(S.something,'test');  % weird behavior
end

function add_something(X,str)
    disp('X=');
    disp(X);
    disp('str=');
    disp(str);
end

输出:

>> bug1
X=
str=
boing
X=
test
str=
??? Input argument "str" is undefined.

Error in ==> bug1>add_something at 11
    disp(str);

Error in ==> bug1 at 4
add_something(S.something,'test');

似乎S.something的空值/无内容状态可以使其转移函数调用的参数。这似乎是非常糟糕的行为。在短期内,我想找到一个解决方法(我正在尝试编写一个函数,将项目添加到最初为空的单元格数组中,该数组是结构体的成员)。
附带问题:那么没有办法构造包含任何空单元格数组的struct文字吗?
4个回答

15

如你自己已经发现的那样,这不是一个错误而是一个“特性”。换句话说,这是STRUCT函数的正常行为。如果你将空单元数组作为字段值传递给STRUCT函数,它会假定你想要一个具有给定字段名称的空结构体数组。

>> s=struct('a',{},'b',{})

s = 

0x0 struct array with fields:
    a
    b

要将空的cell数组传递作为实际字段值,您需要执行以下操作:

>> s = struct('a',{{}},'b',{{}})

s = 

    a: {}
    b: {}

顺便提一下,任何时候你想使用STRUCT将字段的值设置为一个单元格数组,都需要将其包含在另一个单元格数组中。例如,这将创建一个包含单元格数组和向量的字段的单个结构元素:

>> s = struct('strings',{{'hello','yes'}},'lengths',[5 3])

s = 

    strings: {'hello'  'yes'}
    lengths: [5 3]

但是这会创建一个包含两个结构体元素的数组,它会分配单元格数组但是复制向量:

>> s = struct('strings',{'hello','yes'},'lengths',[5 3])

s = 

1x2 struct array with fields:
    strings
    lengths

>> s(1)

ans = 

    strings: 'hello'
    lengths: [5 3]

>> s(2)

ans = 

    strings: 'yes'
    lengths: [5 3]

AH:谢谢 - 我没有看到结构函数中关于在想要传递单元数组时使用额外大括号的注释。真烦人。 - Jason S

2

啊...我想我找到答案了。struct()有多种行为,包括:

注意 如果任何值字段是空单元数组{}, MATLAB软件将创建一个空结构数组,其中所有字段也为空。

显然,如果您将0x0结构的成员作为参数传递,它就像一种空幽灵,不会真正出现在参数列表中。(这仍然可能是一个bug)

bug2.m:

function bug2(arg1, arg2)
disp(sprintf('number of arguments = %d\narg1 = ', nargin));
disp(arg1);

测试用例:

>> nothing = struct('something',{})

nothing = 

0x0 struct array with fields:
    something

>> bug2(nothing,'there')
number of arguments = 2
arg1 = 
>> bug2(nothing.something,'there')
number of arguments = 1
arg1 = 
there

2
这种行为在2008b版本中仍然存在,实际上并不是一个真正的错误(尽管我不认为设计师们有意这样做):当你进入add_something(S,'boing')并观察第一个参数(比如选择它并按F9),你会得到与空结构S相关的一些输出。进入add_something(S.something,'test')并观察第一个参数,你会发现它实际上被解释为“test”! 语法struct.fieldname旨在返回类型为“逗号分隔列表”的对象。Matlab中的函数被设计为接收这个精确类型的对象:参数名被赋给列表中的值,按照它们传递的顺序。在你的情况下,由于第一个参数是一个空列表,函数收到的逗号分隔列表实际上从你传递的第二个值开始——也就是说,“test”。

实际上,设计师们确实打算让这种行为发生。传递参数给STRUCT函数的语法是这样设计的,你可以根据输入参数的单元数组封装的使用方式创建包含数组的结构或结构数组。 - gnovice
此外,逗号分隔的列表 (CSL) 行为也是预期的。输入和输出参数列表被设计成 CSL,并且以下语法创建 CSL:structureArray.fieldName、cellArray{:}。 - gnovice
当然,你所描述的这两种行为都是经过设计的。然而,正如刚才看到的 - 当多个参数传递到一个函数中,其中间的一个为空的CSL时,会导致一定会引起混淆的设计后果。这种情况(在我看来)证明了需要单独进行设计。至少应该发出运行时警告。 - Ofek Shilon

1

在R2008b中输出是相同的:

>> bug1
X=
str=
boing
X=
test
str=
??? Input argument "str" is undefined.

Error in ==> bug1>add_something at 11
    disp(str);

Error in ==> bug1 at 4
add_something(S.something,'test');  % weird behavior

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接