MATLAB 学习笔记——3. 元胞数组和结构体数组
在编程实践中,仅仅只靠浮点数数组还是不够的,我们还需要其他更灵活的数据结构(运行效率也会更慢),最常见的数据结构是元胞数组和结构体数组。
元胞数组
元胞数组是一种包含名为cell的索引数据容器的数据类型,其中的每个cell都可以包含任意类型的数据。
元胞数组可以包含文本列表、文本和数字的组合或者不同大小的矩阵等,通常用于打包一组矩阵或多组矩阵。
通过将索引括在圆括号()
中可以引用cell,使用花括号{}
进行索引来直接访问cell的内容,两者区别见下文。
元胞数组的创建方式如下,元胞数组通常使用二维的结构,m行n列,每一个元素是一个矩阵或其他对象,元胞数组也支持多维的定义和操作。
1
2
3C1 = {}; % 空的元胞数组
C2 = {1,2,3;
'text',rand(5,10,2),{11; 22; 33}} % 2*3的元胞数组
还可以定义指定尺寸的,每一个元素均为空矩阵的元胞数组:
C = cell(n)
,返回由空矩阵构成的\(n\times n\)元胞数组。C = cell(s1,...,sn)
返回由空矩阵构成的 \(s_1 \times \dots \times s_n\) 元胞数组。例如,cell(2,3)
返回一个 \(2\times 3\) 元胞数组。C = cell(a)
返回由空矩阵构成的元胞数组,并由行向量a来定义尺寸,例如,cell([2 3])
返回一个 \(2\times 3\) 元胞数组。(使用列向量是非法操作)
元胞数组和普通矩阵一样,可以在赋值的同时直接进行扩充,并且与矩阵不同,元胞数组支持不同的数据组成cell
1
2
3
4
5C = {'2017-08-16',[56 67 78]}; % size 1*2
% append
C(2,:) = {'2017-08-17',[58 69 79]}; % size 2*2
C(3,:) = {'2017-08-18',[60 68 81]}; % size 3*2
此时()
和{}
索引得到的结果是不同的
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15>> C(1,1)
ans =
1×1 cell array
{'2017-08-16'}
>> C{1,1}
ans =
'2017-08-16'
>> C(1,2)
ans =
1×1 cell array
{[56 67 78]}
>> C{1,2}
ans =
56 67 78
前者仍然是一个\(1\times 1\)的元胞数组,后者得到的则是实际的矩阵或字符串。
基于花括号{}
索引获取分量或矩阵之后,就可以直接赋值和修改了,略。
结构体数组
虽然MATLAB官方将其称为结构体数组,但是实际上不管是从使用还是实现的角度,将其称为字典都更合适。在C++的语境中,结构体约等于自定义类型,而对于MATLAB,自定义类型和结构体数组是截然不同的。
结构体数组(struct)是 MATLAB 中一种非常灵活的具名数据类型,可以将不同类型的数据直接组合在一起,每个字段具有名称,可以包含不同类型和大小的数据,结构体数组使得组织和管理复杂数据更加方便。
结构体数组的使用非常灵活,可以使用点符号直接创建 1
2
3
4
5% 此前person未定义
% 创建一个结构体数组并添加字段
person.name = 'John Doe';
person.age = 30;
person.height = 1.75;
注意:这里person
变量尚未定义,这个语法糖导致我们可能无意之间写出错误的语句——创建了一个新的结构体数组,
而非对已有的结构体数组操作,这个错误还很难检查。
我们还可以使用struct
函数创建,一次性添加多个字段以及对应的数据
1
person = struct('name', 'Jane Doe', 'age', 28, 'height', 1.65);
结构体数组并没有明确完整的创建过程,我们可以在任何时间自由地向结构体数组中添加字段,或对现有字段进行访问和修改。
每一个结构体数组都是完全独立的,对于这个结构体数组可以有(或者必须有)哪些字段没有任何要求
1
2
3disp(person.name); % 输出:John Doe
disp(person.age); % 输出:30
disp(person.height); % 输出:1.75
尝试访问不存在的字段会导致错误。
我们可以使用内置函数isfield
来检查结构体数组是否具有某个字段
1
2
3% 检查字段
hasAge = isfield(person, 'age'); % 返回 true
hasHeight = isfield(person, 'height'); % 返回 false
我们可以使用内置函数rmfield
来移除结构体数组的字段,需要用返回值重新赋值
1
2
3
4
5% 删除字段
person = rmfield(person, 'height');
% 尝试访问已删除的字段将会导致错误
% disp(person.height); % 错误
使用内置函数filedsname
可以基于结构体数组的所有字段名,创建为一个\(n \times 1\)的元胞数组,例如
1
2
3
4
5
6>> fieldnames(person);
ans =
3×1 cell array
{'name' }
{'age' }
{'height'}
与之对应的是,使用内置函数struct2cell
可以基于结构体数组所有字段的值,创建为一个\(n \times 1\)的元胞数组,例如
1
2
3
4
5
6>> struct2cell(person)
ans =
3×1 cell array
{'Jane Doe'}
{[ 28]}
{[ 1.6500]}
使用内置函数cell2struct
可以通过存储字段名和值的元胞数组,创建对应的结构体数组
1
2
3
4
5
6
7cellArray = {'John Doe', 30, 1.75};
fieldNames = {'name', 'age', 'height'};
% 将单元数组转换为结构体数组
person = cell2struct(cellArray, fieldNames, 2);
disp(person);
需要注明的是,第三个参数是创建结构体数组所使用的维度,1
代表使用的是列维度,这里使用的2
代表行维度。
不同的结构体数组也可以进一步组成数组,结构体数组还可以相互嵌套,略。
从上面的介绍可以看出,结构体数组实际上就是一个键为字符串的字典,语法非常灵活,易与后文中的面向对象的语法产生冲突,因此决定尽量避免使用结构体数组,转而使用更安全的面向对象的语法!