上一篇:轻松掌握MATLAB - 2.4 数组的内存管理与矩阵思想
程序可以看作是由数据和对数据的处理(算法)构成的。在使用 MATLAB 编程解决问题的过程中,我们会用到多种类型的数据。例如:在数值计算中,数值型数据必不可少;在程序的提示或报错中,字符型数据不可或缺;在处理成绩单等这类数据集时,我们还需要更复杂的数据类型。为了满足编程的需求,MATLAB 预定义了17种基本数据类型。学习这些基本数据类型的使用和相互转换方法,是使用 MATLAB 的基础。
3.1.1 概述
如图3.1所示,MATLAB的基本数据类型共有17种,可以分为8组,分别为:逻辑型(logical)、字符型(char)、字符串型(string)、数值型(numeric, 共包括10种)、表(table)、元胞(cell)、结构体(struct)和函数句柄(function handle)。除函数句柄始终为标量外,其余数据类型均支持矩阵和多维数组,而且均支持稀疏矩阵。其中逻辑型、字符型、字符串型、数值型属于简单数据类型,这些类型的数组中元素的数据类型是单一的。而元胞和结构体属于容器型数据(复合数据类型),它们的数据单元可以是任意大小、任意数据类型(包括自身类型)的数组。需要说明的是,无论是简单数据类型还是容器型的数组,在任意一个二维面上都必须保持矩形,只是容器型数组允许各单元存放不同类型的数据而已。
MATLAB从R2008a版本开始支持面向对象编程(OOP),其自身也逐渐向OOP的编程思想转变。上述17种基本数据类型(Data Types),也被称为“基础类”(Fundamental Classes)。利用MATLAB对OOP的支持,基于这些基础类可以定义扩展类或自定义类。例如MATLAB中就预定义了处理时间和日期的datatime类、timetable类等,并定义了与之配套的函数。关于MATLAB面向对象编程和自定义类,将在第七章详述。
在MATLAB中,所有数据都是数组,所有的数组都属于某个确定的类。要查看一个数组所属的类,可以用whos函数查询。例如,
在whos函数的查询结果中,Class列所示即为查询对象所属的类名称。用class函数也可以直接查询指定变量的类名。例如,
【关于稀疏矩阵】
在数学上,如果一个矩阵中绝大多数的元素都是0,则该矩阵被称为稀疏矩阵(Sparse matrix);反之,则称为稠密矩阵(Dense matrix)或满矩阵(Full matrix)。对于大型的稀疏矩阵,如果仅保存其中非零元素的值和它们的位置,可以节省大量的内存,而且可以提高数据存取和运算效率。MATLAB支持按这种方式存储矩阵,并将按这种方式存储的矩阵,称为稀疏矩阵;反之,需要开辟一块连续的内存并按顺序来存储包括0在内的所有的元素的矩阵,称为满矩阵。
这里需要强调的是,MATLAB中的稀疏矩阵和满矩阵仅是指矩阵的存储方式。MATLAB不会自动识别一个矩阵是不是数学意义上的稀疏矩阵。默认情况下都会以满矩阵方式存储,稀疏矩阵需要用sparse函数来主动创建。运算中矩阵以何种方式存储更有利,需要编程者予以判断。在大型科学工程计算中,常常会涉及大量的稀疏矩阵,正确识别和合理使用稀疏矩阵,可以显著地提高程序的性能。
3.1.2 MATLAB的基本数据类型
下面详细介绍MATLAB的17种基本数据类型及不同数据类型间的相互转换。
1. 逻辑型
逻辑型数据仅有“真”和“假”两种状态,可用逻辑值 1 和 0 表示。在程序中,逻辑型数据主要用于条件判断、条件查找或数组索引等。逻辑型数据主要有 3 个来源:(1)逻辑运算或关系运算的结果;(2)MATLAB 中的判别函数(如以 is 开头的函数)返回逻辑型数据;(3)通过logical函数将数值型和字符型数据转化为逻辑值。
在 MATLAB 中,“真”和“假”两种状态分别用 true 和 false 表示。例如,
上述指令创建了一个具有两个元素的逻辑数组。除了表示逻辑状态,true和false本身也是两个函数,可以用来生成全“真”或全“假”的数组。例如,创建一个2x3x2的全真数组
用logical函数可以将数值型或字符型数组转换为逻辑数组。例如,
可以看出,logical函数将数值型矩阵B转换成了逻辑形矩阵LB。仔细观察转换结果可以发现,logical函数在转换过程中遵循的是“非0即true”的原则。也就是说,无论是正数还是负数,只要不是0,转换结果都是逻辑1。
字符型数据也可以被转化为逻辑型。例如,
用logical函数也可以将数值型空数组转换为逻辑型空数组,例如:
一个数组是否为逻辑型数组,可以用islogical函数进行判断。例如,
islogical函数是将数组作为一个整体来进行判断的,它仅能判断数组的数据类型,而无法判断数组中元素的真/假。
用all和any 函数可以判断数组中某一维的所有元素是否全为 true 或至少有一个 true。基本语法如下:
B = all(A) B = any(A)
B = all(A,dim) B = any(A,dim)
B = all(A,'all') B = any(A,'all')
可选参数dim用于指定判断的维度,默认为长度不等于1的最低维。用'all'参数来声明是对整个数组进行判断 (R2018b引入)。例如,
可以看出,前面我们创建的空逻辑数组epl被判断为了全“真”数组。这是因为,根据“非0即true”的原则,空数组不是0,所以判断结果为真。再如,
对矩阵LB而言,长度不等于1的最低维是1(列),所以默认dim=1,即按列操作。因为LB中只有第3列全为1,所以返回1,其余列返回0。按行判断需要指定dim,例如,
由于LB中每行都至少有一个1,因此按第2维(行)判断时any返回的结果均为1。all和any实际也是is*类函数,只是没有被命名为isalltrue或isnanytrue而已。要将数组作为整体进行any或all判断,可以用冒号操作符将数组转化为向量,或者用“all”参数。例如,
需要说明的是,any和all不仅可以用于逻辑数组,还可以用于数值型数组,分别判断在指定dim上是否有非零值和是否全为非零值。实质上,这种情况下可以看作是先自动将数值数组按照“非0即true“的原则转化为了逻辑数组,然后再进行地判断。例如,对前面创建的随机整数数组B
返回结果与前面all(LB)返回结果一致。这是因为逻辑型数组LB本来就是由数值型数组B转化来的。对数值型数组,any和all函数可以理解为检测数组中是否有非零元素(is any none zero)和数组中所有元素是否均为非零元素(is all none zero)。
2. 字符与字符串
MATLAB中有两种数据类型可以用来表达文本数据:字符型(char)和字符串型(string)。
(1)字符型
字符型数组(Character array)是以单个字符为元素的数组,其定义符为一对单引号''。例如,
字符型数组也可以通过下标或序号进行索引。例如,
由于MATLAB中任何类型的数组都必须遵循“矩形原则”,字符型数组如果有多行,则各行上的字符个数(包括空格)必须相等。例如,
在这个例子中,第一行额外添加了空格,以确保其与后面的两行长度相等。否则,MATLAB会报错。显然,这种限制会给字符型数组的使用带来不便。因此,字符型数组通常是以行向量的形式来存储文本信息,用于在程序中动态生成提示、错误、警告信息等。例如,
【说明】
- warning函数用于生成格式化的警告信息。在默认情况下,这些警告信息在命令行会以橙色字符显示。
- 上述例子中的char函数会将输入参数转换为字符。对于介于0到65535之间的整数,char函数返回对应的Unicode字符。例如,8451对应的字符是摄氏度的符号℃。
- 常用的格式化输出函数有fprintf、sprintf、warning、error、assert等。请doc函数名,熟悉它们的基本用法。
用upper和lower函数可以实现字符的大小写转换。例如,
这两个函数常用来消除字符数组比较时的大小写敏感。
用length、size、numel函数可以分别获取字符数组的长度、大小和总字符个数。例如,
以上函数返回的统计信息是计算空格的。用deblank函数可以将字符向量末尾的空白字符删除。例如,
用ischar函数可以判断输入参数是否为字符型数组。
isletter和isspace函数可分别判断字符数组中各个元素是否为字母和空格。例如,
可以发现,在isletter(chr)返回的结果中,空格和末尾的标点位置都返回了0,这说明空格和标点都不被视为字母;而在isspace(chr)的返回结果中,只有空格的位置返回的是1,末尾的标点位置也返回了0。
(2)字符串型
如前所述,字符型数组在使用中存在诸多不便,为了便于文本信息的存储,MATLAB自 R2016b 版本起引入了一种新的数据类型——字符串数组(String array)。字符串数组以一对双引号""作为定义符,例如,
字符串数组的元素是字符串,而不是字符。在构成数组时无需关心每个字符串的字符个数,因此比char数组更便于应用。例如,通过拼接法将str扩展为了一个2x3的字符串数组:
虽然每个元素中的字符数量并不相同,甚至有些元素为空,但这并不违背“矩形原则”,因为各行的字符串数量是相同,各列的字符串数量也是相同的。值得一提的是,我们还在此顺便演示了两种定义空字符串的方法。其中,missing是一个特殊函数,用来定义暂缺值。在字符串数组中,missing被转化为
因为字符串数组以字符串为元素,所以下标索引时,返回的是对应位置的整个字符串。例如,
返回的是str中第一个元素,返回结果也是一个字符串。如果要对字符串中的某个元素进行部分修改,需要用双重索引。例如,将this的首字母改为大写
在上述指令中,花括号{ }用于提取字符串数组str的第一个元素的内容,其结果是一个char型数组。随后的小括号( )则用于对该char型数组进行索引。(在稍后学习元胞数组时我们将看到,这是典型的对元胞数组元素内容的索引方法,说明字符串数组本质上是一种特殊的元胞数组,其每个cell中放的是char型向量。)
对于比较大的字符串数组,可以用strings函数为其预分配内存,然后再通过索引赋值填入内容。例如,
用isstring函数可以判断输入参数是否为字符串数组。
isstrprop函数可以判断字符串数组中每个字符的成分。例如,
可以看到,str第1行第3列中字符为数字的位置返回逻辑1,非数字位置返回逻辑0。通过改变第2个参数,isstrprop函数可鉴别字符串的各种属性。详情请doc isstrprop。
length函数可以返回字符串数组最长维的元素个数,而strlength函数则可以返回字符串数组中每个单元的字符个数。例如,
注意:空字符串""的位置返回了0,而
在编程中,我们经常会遇到数字与字符之间的转换。MATLAB提供的相关的内置函数有string、num2str、str2num、str2double等。举例如下:
注意这三个函数的区别:char函数会将数值取整,并返回该整数对应的 ASCII 码或 Unicode 码;string函数则直接将数值转换为字符串;而num2str函数则将数值转化为char型数组。num2str还可以同时指定转换精度。例如,
在MATLAB中,字符向量和字符串标量在许多情况下是可以混用的。例如,在上面这条指令中,通常用单引号来指定输出格式,但换成双引号也是可以的。然而,当string和char共同构成数组时,char元素会被自动转化为string。例如,
可以看出,得到的数组strchr的类型为string。
str2double和str2num函数都可以将char型或string型数据转化为数值型数据。它们的主要区别在于,str2num只能转换标量string或char向量,而str2double可以转换char向量和任意大小的string数组。例如,
在上面的例子中,这两个函数是不可互换的。此外,从这两个例子的返回结果还可以看出,str2num可以识别特殊的常量,例如pi,Inf,eps等,而str2double则会将部分特殊常量识别为字母,导致转换错误。碰到无法转换的文本时,str2num返回[],而str2double返回NaN。
在二者均可使用的情况下,MATLAB推荐用str2double,因为它的效率要更高一些。
【说明】
- MATLAB提供了几十个文本处理函数,详情请doc Characters and Strings。利用这些函数,我们可以对文本内容进行大小写转换、文本比较,通过构建复杂的正则表达式来对文本进行查找与统计,甚至可以用这些函数来开发字处理软件。MATLAB帮助文档中提供了一个有趣的例子,它利用文本处理函数统计了莎士比亚十四行诗中单词的出现概率,感兴趣的读者可以doc Analyze Text Data with String Arrays来查看和学习。