2.1 数据类型
MATLAB中的基础数据类型主要包括数值类型、字符串、结构、单元数组和函数句柄等数据类型。本小节主要介绍这些基础数据类型及其相关的基础操作。
2.1.1 数值类型
数值类型按数值在计算机中存储与表达的基本方式进行分类,主要有整数、单精度浮点数和双精度浮点数三类,如表2.1所示。在默认情况下,MATLAB对所有数值按照双精度浮点数类型进行存储等操作。
表2.1 数值类型分类
相对于双精度浮点数类型数据,整数型与单精度浮点型数据的优点在于节省变量占用的内存空间;在满足精度要求的情况下,可以考虑优先采用。需要时,可以指定系统按照整数型或单精度浮点型对指定的数字或数组进行存储、运算等操作。
提示
MATLAB自动进行内存的分配和回收,因此操作数据相对C/C++等编程语言而言简单了很多,有利于专心于程序算法的编制。
下面介绍这三种数值类型及衍生出来的复数类型。
1.整数类型
MATLAB中提供了4种有符号整数类型和4种无符号整数类型;有符号整数类型可以表示整数和负数,而无符号整数类型仅能表示负数。这8种类型的存储占用位数、能表示的数值范围和转换函数均不相同,如表2.2所示。
表2.2 MATLAB中的关系操作符
提示
不同的整数类型所占用的位数不同,因此能够表示的数值范围也不同。在实际应用中,应根据实际需要合理选择合适的整数类型。
由于MATLAB中数值的默认存储类型是双精度浮点类型,因此在将变量设置为整数类型时,需要使用表2.3中所示的转换函数,将双精度浮点数转换为指定的整数类型。
表2.3 MATLAB中的取整函数
2.浮点数类型
MATLAB中提供了单精度浮点数类型和双精度浮点数类型,其存储位宽、能够表示的数值范围、数值精度各方面均不相同,具体如表2.4所示。
表2.4 MATLAB中的取整函数
MATLAB中的默认数值类型为双精度浮点类型,但可以通过转换函数来创建单精度浮点类型。
双精度浮点数参与运算时,返回值的类型依赖于参与运算的其他数据类型。参与运算的其他数据为逻辑形、字符型时,返回结果为双精度浮点型;其他数据为整数型时,返回结果为相应的整数类型;其他数据为单精度浮点型时,返回结果为相应的单精度浮点型。
注意
在MATLAB中,单精度浮点类型不能与整数类型直接进行算术运算;例如,在命令行窗口输入:
a=uint32(1); b=single(22.809); ab=a*b;
输出结果如下:
Error using .* Integers can only be combined with integers of the same class, or scalar doubles.
下面通过示例说明浮点类型数据的相关操作。
例2-1,使用realmax和realmin函数求浮点类型数据可表达范围。
在命令行窗口输入:
str1 = ’双精度浮点数的范围为:\n\t%g到 %g\t和 \t%g到 %g'; sprintf(str1, -realmax, -realmin, realmin, realmax) str2 = ’单精度浮点数的范围为:\n\t%g到 %g\t和 \t%g到 %g'; sprintf(str2, -realmax('single'), -realmin('single'), realmin('single'), realmax('s ingle'))
得到的结果如下所示:
ans = 双精度浮点数的范围为: -1.79769e+308 到 -2.22507e-308 和 2.22507e-308 到 1.79769e+308 ans = 单精度浮点数的范围为: -3.40282e+38 到 -1.17549e-38 和 1.17549e-38 到 3.40282e+38
例2-2,查看双精度数与其他类型数的求解结果类型。
在命令行窗口输入:
a=uint32(1); b=single(1.0); c=1.0; a1=a*c; b1=b*c; c1=c*c; whos
输出结果如下所示:
Name Size Bytes Class Attributes a 1x1 4 uint32 a1 1x1 4 uint32 b 1x1 4 single b1 1x1 4 single c 1x1 8 double c1 1x1 8 double
提示
该结果表明双精度数与其他类型数的求解结果由其他数据类型决定。
由于浮点数只占用一定的存储位宽,其中只有有限位分别用来存储指数部分和小数部分,所以浮点类型能够表示的实际数值是有限且离散的,即任何两个最近相邻的浮点数之间都有间隙,而处在间隙中的数值都只能用这两个相邻的浮点数之一表示。
在MATLAB中,使用eps函数可以获取一个数值和最接近该数值的浮点数之间的间隙。
例2-3,浮点数的精度。
在命令行窗口输入:
format long; e1=eps(6), e2=eps(single(6)), format short
输出结果如下:
e1 = 8.881784197001252e-16 e2 = 4.7683716e-07
提示
MATLAB中eps可视为0,0附近的eps近似为2.2e-16,这种特殊表达在避免0作为分母时是很有用。
在MATLAB中几乎所有的计算都使用双精度浮点数,然而由于计算机所能处理的精度有限,有些时候这种局限性可能会导致错误。
例2-4,使用双精度浮点数进行计算时可能出现的错误。
在命令行窗口输入:
e1= 1-3*(4/3-1) a = 0.0; for i = 1:10, a = a + 0.1; end, e2=a-1.0 b = 1e-16 + 1-1e-16; c = 1e-16-1e-16 + 1; e3= b-c e4= (2^53 + 1) -2^53 e5=sin(pi) % pi为圆周率在MATLAB中的表达 e6= sqrt(1e-16 + 1)-1
输出结果如下:
e1 = 2.2204e-16 e2 = -1.1102e-16 e3 = -1.1102e-16 e4 = 0 e5 = 1.2246e-16 e6 = 0
例2-4中为使用MATLAB进行计算时常见的错误用法,虽然在很多时候这样用并不会造成太大的错误,但在使用中还是应该注意并加以避免。
3.复数
复数由实部和虚部两部分构成。在MATLAB中,字符i或j默认作为虚部标志。创建复数时,可以直接按照复数形式进行输入或者利用complex函数。
关于复数的相关函数如表2.5所示。
表2.5 MATLAB中的相关函数
例2-5,复数基本操作示例。
在命令行窗口输入:
c1=complex(3,5); c2=6+2i; c=c1-c2 r1=real(c) i1=imag(c) a1=abs(c) ag1=angle(c) cn1=conj(c)
输出结果如下:
c = -3.0000 + 3.0000i r1 = -3 i1 = 3 a1 = 4.2426 ag1 = 2.3562 cn1 = -3.0000-3.0000i
4.无穷量(Inf)和非数值量(NaN)
MATLAB中使用Inf和-Inf分别代表正无穷量和负无穷量,NaN表示非数值量。正负无穷量一般由于运算溢出产生,非数值量则是由于类似0/0或Inf/Inf类型的非正常运算产生。
MATLAB提供Inf函数和NaN函数来创建指定数值类型的无穷量和非数值量,生成结果默认为双精度浮点类型中还有一种特殊的指数类型的数据叫作非数,通常表示运算得到的数值结果超出了运算范围。非数的实部用NaN表示,虚部用InF表示。
例2-6,无穷量及非数值量的产生和性质。
在命令行窗口中输入:
a = 0 / 0, a1=1/0, b = log( 0 ), c =exp(1000), d=NaN- NaN whos
输出结果如下:
a = NaN a1 = Inf b = -Inf c = Inf d = NaN Name Size Bytes Class Attributes a 1x1 8 double a1 1x1 8 double b 1x1 8 double c 1x1 8 double d 1x1 8 double
2.1.2 字符与字符串
MATLAB将文本作为特征字符串或简单地当作字符串。这些字符串显示在屏幕上,也可以用来构成一些命令。字符串是存储在行向量中的文本,这行向量中的每一个元素代表一个字符。
实际上,元素中存放的是字符的内部代码,即ASCII码。但在屏幕上显示字符变量的值时,显示出来的是文本,而不是ASCII编码,这是因为在显示前已经对ASCII编码进行了输出处理。
字符串一般是ASCII值的数值数组,它作为字符串表达式进行显示。字符串可以通过其下标对其中的任一元素进行访问,也可以通过矩阵下标索引进行访问,但是矩阵的每行字符数必须相同。
例2-7,字符串属性示例。
在命令行窗口输入:
string ='good boy' whos s1=abs (string) s2=abs (string+'0')
输出结果如下:
string = good boy Name Size Bytes Class Attributes string 1x8 16 char s1 = 103 111 111 100 32 98 111 121 s2 = 151 159 159 148 80 146 159 169
2.1.3 结构
MATLAB的结构与C语言类似,一个结构可以通过字段存储多个不同类型的数据。结构相当于一个数据容器,可以把多个相关联的不同类型的数据封装在一个结构对象中。
一个结构中可以具有多个字段,每个字段又可以存储不同类型的数据,这样,通过这种方式就把多个不同类型的数据组织在了一个结构对象中。
如图2.1所示,结构patient中有3个字段,姓名字段name中存储了一个字符串类型的数据;账单字段billing中存储了一个浮点数值;成绩字段test中存储了三维浮点数矩阵。
图2.1 结构patient的示意图
下面通过示例来说明创建、访问和连接结构对象等基本操作。
1.创建结构对象
创建结构对象的方法有两种,可以直接通过赋值语句给结构的字段赋值,也可以使用struct函数创建结构。两种方法的具体操作步骤如下:
(1)通过字段赋值创建结构
在对结构的字段进行赋值时,赋值表达式的变量名使用“结构名称.字段名称”的形式书写,对同一个结构可以进行多个字段的赋值。
例2-8,通过赋值创建结构。
在命令行窗口输入:
patient.name = 'John Doe'; patient.billing = 127.00; patient.test = [79, 75, 73; 180, 178, 177.5; 220, 210, 205]; patient whos
输出结果如下:
patient = name: 'John Doe' billing: 127 test: [3x3 double] Name Size Bytes Class Attributes patient 1x1 468 struct
例2-8中,通过对3个字段赋值,创建了结构对象patient,然后用whos函数分析出patient是一个1×1的结构数组。
提示
进行赋值操作时,对于没有明确赋值的字段,MATLAB默认赋值为空数组。通过圆括号索引进行字段赋值,还可以创建任意尺寸的结构数组。另外,同一个结构数组中的所有结构对象具有相同的字段组合。
(2)利用struct函数创建结构
例2-9,通过struct函数创建结构。
在命令行窗口输入:
patient=struct('name', 'John Doe', 'billing',127.00, 'test', [79,75,73; 180,178,177.5; 220,210,205]) whos
输出结果如下:
patient = name: 'John Doe' billing: 127 test: [3x3 double] Name Size Bytes Class Attributes patient 1x1 468 struct
2.访问结构对象
通过对结构对象的字段和其在结构对象组中的位置可以访问结构对象。
例2-10,访问结构对象。
在命令行窗口输入:
patient(1)=struct('name', 'John Doe', 'billing',127.00, 'test', [79,75,73;180,178,177.5; 220,210,205]); patient(2).name = ' Tim Burg '; patient(2).billing = 335.00; patient(2).test = [89, 80, 72; 183, 175, 172.5; 221, 211, 204]; p1=patient(1), p2= patient(2), p1name=patient(1).name, p2name=patient(2).name
输出结果如下:
p1 = name: 'John Doe' billing: 127 test: [3x3 double] p2 = name: 'Tim Burg' billing: 335 test: [3x3 double] p1name =John Doe p2name =Tim Burg
3.连接结构对象
使用直接连接的方式就可以将结构对象连接起来。
例2-11,连接结构对象。
在命令行窗口输入:
patient1=struct('name', 'John Doe', 'billing',127.00, 'test', [79,75,73;180,178,177.5; 220,210,205]); patient2=struct('name', 'Tim Burg', 'billing',128.00, 'test', [79,75,73;180,178,177.5; 220,210,205]); patient=[ patient1, patient2]; whos
输出结果如下:
Name Size Bytes Class Attributes patient 1x2 1056 struct patient1 1x1 624 struct patient2 1x1 624 struct
从结果中可以看出,patient结构对象由patient1和patient2连接而成。
2.1.4 单元数组
单元数组(Cell Arrays)是一种广义矩阵。每一个单元可以包括一个任意数组,如数值数组、字符串数组、结构体数组或另外一个单元数组,因而每一个单元可以具有不同的尺寸和内存占用空间。
1.创建单元数组
单元数组的创建有两种方法:通过赋值语句或cell函数创建。
(1)使用赋值语句创建单元数组:单元数组使用花括号“{}”来创建,使用逗号“, ”或空格分隔单元,使用分号“; ”来分行。
(2)使用cell函数创建空单元数组。
例2-12,创建单元数组。
在命令行窗口输入:
A= {'x', [2;3;6];10,2*pi} B = cell(2,2) whos
输出结果如下:
A = 'x' [3x1 double] [10] [ 6.2832] B = [] [] [] [] Name Size Bytes Class Attributes A 2x2 282 cell B 2x2 16 cell
提示
使用cell函数创建空单元数组主要是为该单元数组预先分配连续的存储空间,提高执行效率。
2.访问单元数组
在单元数组中,单元和单元中的内容属于不同范畴,这意味着寻访单元和单元中的内容是两个不同的操作。MATLAB为上述两种操作设计了相对应的操作对象:单元外标识(cell indexing)和单元内编址(content indexing)。
单元外标识使用圆括号进行操作,对于单元数组C, C(m, n)指的是单元数组中第m行第n列的单元;单元内编址使用大括号进行操作,对于单元数组C, C{m, n}指的是单元数组中第m行第n列的单元中的内容。
例2-13,单元数组的访问。
在命令行窗口输入:
A= {'x', [2;3;6];10,2*pi}; b= A(1,2) C=A{1,2}
输出结果如下:
b = [3× 1 double] C = 2 3
3.单元数组的操作
单元数组的操作包括:合并、删除单元数组中的指定单元和改变单元数组的形状等。
(1)单元数组的合并
例2-14,单元数组的合并。
在命令行窗口输入:
A= {'x', [2;3;6];10,2*pi}; B= {'Jan'} C = {A B} whos
输出结果如下:
B = 'Jan' C = {2x2 cell} {1x1 cell} Name Size Bytes Class Attributes A 2x2 282 cell B 1x1 66 cell C 1x2 468 cell
(2)删除单元数组中指定单元
如果要删除单元数组中指定的某个单元,只需要将空矩阵赋给该单元,即:C{ m, n} = []。
例2-15,删除单元数组中指定单元。
在命令行窗口输入:
A= {'x', [2;3;6];10,2*pi}; A{ 1,2}=[]; A1=A whos
输出结果如下:
A1 ='x' [] [10] [6.2832] Name Size Bytes Class Attributes A 2x2 258 cell A1 2x2 258 cell
(3)使用reshape函数改变单元数组的形状。
例2-16,改变单元数组的形状。
在命令行窗口输入:
A= {'x', [2;3;6];10,2*pi} newA = reshape(A,1,4) whos
输出结果如下:
A = 'x' [3x1 double] [10] [ 6.2832] newA = 'x' [10] [3x1 double] [6.2832] Name Size Bytes Class Attributes A 2x2 282 cell newA 1x4 282 cell
2.1.5 函数句柄
在MATLAB中,可以实现对函数的间接调用,这归功于函数句柄提供了一种间接调用函数的方法。
创建函数句柄需要使用到操作符@,对MATLAB库函数中提供的各种M文件中的函数和使用者自主编写的程序中的内部函数,也都可以创建函数句柄,进而通过函数句柄来实现对这些函数的间接调用。
创建函数句柄的一般句法格式为:
Function_Handle = @Function_Filename;
其中,
● Function_Filename是函数所对应的M文件的名称或MATLAB内部函数的名称;
● @是句柄创建操作符;
● Function_Handle变量保存了这一函数句柄,并在后续的运算中作为数据流进行传递。
例2-17,函数句柄的创建与调用。
在命令行窗口输入:
F_Handle = @sin x = 0 : 0.25 * pi : pi; F_Handle( x ) %通过函数句柄调用函数
输出结果如下:
F_Handle = @sin ans = 0 0.7071 1.0000 0.7071 0.0000
MATLAB库函数提供了大量处理函数句柄的操作函数,将函数句柄的功能与其他数据类型联系起来;这扩展了函数句柄的应用。函数句柄的简单操作函数如表2.6所示。
表2.6 函数句柄的操作函数
例2-18,函数句柄的基本操作。
在命令行窗口输入:
F_Handle=@sin f1=functions( F_Handle ) t= func2str(F_Handle) F_Handle1 = str2func(t) f2=functions( F_Handle1)
输出结果如下:
F_Handle = @sin f1 = function: 'sin' type: 'simple' file: '' t = sin F_Handle1 = @sin f2 = function: 'sin' type: 'simple' file: ''
2.1.6 映射容器
映射容器(Map Containers,也叫Map对象)可以将一个量映射到另一个量。例如,将一个字符串映射为一个数值,则相应字符串就是映射的键(key),相应值就是映射的数据(value)。可以将Map容器理解为一种快速键查找数据结构。
对一个Map元素进行寻访的索引称为“键”,一个“键”可以是以下任何一种数据类型:
(1)1×N字符串;
(2)单精度或双精度实数标量;
(3)有符号或无符号标量整数。
键和其对应的数据存储在映射容器中,存在一一对应的关系。映射容器p中存储的数据可以是任何类型,包括数值类型、字符或字符串类型、结构体类型、单元类型或其他映射容器。
单个映射容器对象是MATLAB Map类的对象。Map类的所有对象具有3种属性,如表2.7所示。用户不能直接对这些属性进行修改,但可以通过Map类操作函数来进行修改。
表2.7 Map类的属性
属性的查看方法为:Map名+“.”+Map的属性名。例如,为了查看mapObj对象包括的数据类型,可以使用:
mapObj.ValueType
下面将讨论Map对象的创建、读取和编辑等内容。
1.创建Map对象
Map构造方法如下:
mapObj = containers.Map({key1, key2, …}, {val1, val2, …})
当键和值是字符串时,需要对上述语法稍作变更,即
mapObj = containers.Map({'keystr1', 'keystr2', …}, {val1, val2, …})
例2-19,创建Map对象。
在命令行窗口输入:
k = {'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec', 'Annual'}; v = {327.2, 368.2, 197.6, 178.4, 100.0, 69.9, 32.3, 37.3, 19.0, 37.0, 73.2, 110.9, 1551.0}; rainfallMap = containers.Map(k, v) whos rainfallMap
输出结果如下:
rainfallMap = containers.Map handle Package: containers Properties: Count: 13 KeyType: 'char' ValueType: 'double' Methods, Events, Superclasses Name Size Bytes Class Attributes rainfallMap 13x1 60 containers.Map
此外,Map对象的创建可以分为两个步骤:首先创建一个空Map对象;然后使用keys和Value方法对其进行内容补充。空Map对象的创建方法如下:
newMap = containers.Map()
输入上述命令,得到的结果如下所示:
newMap = containers.Map handle Package: containers Properties: Count: 0 KeyType: 'char' ValueType: 'any' Methods, Events, Superclasses
2.查看和读取Map对象
Map对象中的每个条目包括两个部分:唯一的键及其对应的值。可以通过使用keys函数查看Map对象中包含的所有键;通过values函数查看所有的值。
例2-20,查看Map对象。
在命令行窗口输入:
k = {'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec', 'Annual'}; v = {327.2, 368.2, 197.6, 178.4, 100.0, 69.9, 32.3, 37.3, 19.0, 37.0, 73.2, 110.9, 1551.0}; rainfallMap = containers.Map(k, v); kv=keys(rainfallMap) vv=values(rainfallMap)
输出结果如下:
kv = 'Annual' 'Apr' 'Aug' 'Dec' 'Feb' 'Jan' 'Jul' 'Jun' 'Mar' 'May' 'Nov' 'Oct' 'Sep' vv = [1551] [178.4000] [37.3000] [110.9000] [368.2000] [327.2000] [32.3000] [69.9000] [197.6000] [100] [73.2000] [37] [19]
用户可以对Map对象进行数据的寻访。寻访指定键(keyName)所对应的值(valueName)使用的格式如下:
valueName = mapName(keyName)
当键名是一个字符串时,需使用单引号将键名括起来。
例2-21, Map对象数据寻访。
在命令行窗口输入:
k = {'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec', 'Annual'}; v = {327.2, 368.2, 197.6, 178.4, 100.0, 69.9, 32.3, 37.3, 19.0, 37.0, 73.2, 110.9, 1551.0}; rainfallMap = containers.Map(k, v); v5= rainfallMap ('May')
输出结果如下:
v5 = 100
如果需要对多个键进行访问,可以使用values函数,例如输入:
vs=values(rainfallMap, {'Jan', 'Dec', 'Annual'})
得到的结果为:
vs = [327.2000] [110.9000] [1551]
注意
在对多个键进行访问时,不能像在其他数据类型中那样使用冒号“:”,否则将导致错误的产生。
3.编辑Map对象
(1)删除keys/values对
可以使用remove函数从Map对象中删除keys/values对。
例2-22,删除keys/values对。
在命令行窗口依次输入:
k = {'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun'}; v = {327.2, 368.2, 197.6, 178.4, 100.0, 69.9}; rainfallMap = containers.Map(k, v); remove(rainfallMap, 'Jan'); ks=keys(rainfallMap) vs=values(rainfallMap)
得到的结果为:
ks = 'Apr' 'Feb' 'Jun' 'Mar' 'May' vs = [178.4000] [368.2000] [69.9000] [197.6000] [100]
(2)添加keys/values对
可以直接向Map对象中删除keys/values对。
例2-23,添加keys/values对。
在命令行窗口依次输入:
k = {'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun'}; v = {327.2, 368.2, 197.6, 178.4, 100.0, 69.9}; rainfallMap = containers.Map(k, v); rainfallMap('Jul')=33.3; ks=keys(rainfallMap) vs=values(rainfallMap)
得到的结果如下:
ks = 'Apr' 'Feb' 'Jan' 'Jul' 'Jun' 'Mar' 'May' vs = [178.4000] [368.2000] [327.2000] [33.3000] [69.9000] [197.6000] [100]
(3)修改keys与values
如果在需要值不变的情况下对键名进行更改,首先要删除键名和对应的值,然后再添加一个有正确键名的新条目。通过赋值操作,覆盖原有的值,即可对Map对象中的值进行修改。
例2-24,修改keys与values。
在命令行窗口输入:
k = {'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun'}; v = {327.2, 368.2, 197.6, 178.4, 100.0, 69.9}; rainfallMap = containers.Map(k, v); remove(rainfallMap, 'Jan'); rainfallMap('JAN')= 327; rainfallMap(' Mar ')=33.3; ks=keys(rainfallMap) vs=values(rainfallMap)
程序运行结果为:
ks = ' Mar ' 'Apr' 'Feb' 'JAN' 'Jun' 'Mar' 'May' vs = [33.3000] [178.4000] [368.2000] [327] [69.9000] [197.6000] [100]
2.1.7 数据类型识别与转换
数据类型识别用以确定变量的数据类型,常用到的函数如表2.8所示。
表2.8 数据类型识别函数
MATLAB提供了如表2.9所示的函数用以完成不同数据类型间的转换。
表2.9 数据类型转换函数
例2-25,将字符串数转换为浮点数。
在命令行窗口输入:
c = {'37.294e-1'; '-58.375'; '13.796'} d = str2double(c)
得到的结果为:
c = '37.294e-1' '-58.375' '13.796' d = 3.7294 -58.3750 13.7960