购买
下载掌阅APP,畅读海量书库
立即打开
畅读海量书库
扫码下载掌阅APP

第4章
MATLAB编程基础

本章导读

在MATLAB中,除了可以在命令窗口中输入命令逐句执行外,也可以和其他形式的C、Fortran等高级语言一样采用编程的方式,称为M文件编程。

读者首先应掌握MATLAB程序设计的基本方法,不断实践,逐步将其强大的功能应用到科学计算及其他领域的学习和应用中去。

4.1 MATLAB编程概述

MATLAB不仅是一种功能强大的高级语言,而且是一个集成的交互式开发环境,用户可以通过MATLAB提供的编辑调试器编写和调试MATLAB代码。

MATLAB提供了代码书写和调试的集成开发环境,用户可以在MATLAB的代码编辑调试器中完成书写和调试过程。单击MATLAB主界面的“新建”工具按钮或者单击文件菜单(File)“新建子菜单”(New)的“M-File”项,就可以打开MATLAB代码编辑-调试器,其空白界面如图4-1所示。

用户也可以在命令窗口通过edit filename命令打开已存在的M文件进行编辑调试。

从图4-1可见,MATLAB能够根据M文件内容区别是脚本M文件还是函数M文件,并且在整个编辑过程中追踪光标位置(如图4-1底部的“Ln 1 Col 1”表示当前光标处在第一行的第一列),这对于准确快速定位当前编辑和修改位置是很方便有用的。

开发MATLAB程序一般需要经历代码编写、调试、优化几个阶段。

在编写代码时,要及时保存阶段性成果,可以通过File菜单的Save项或者保存工具按钮保存当前的M文件。

完成代码书写之后,要试运行代码看看有没有运行错误,然后根据针对性的错误提示对程序进行修改。

运行脚本M文件,只需要在命令窗口中输入其文件名,然后按回车键,或通过Debug菜单的Save file and Run项,或按快捷键“F5”完成。

运行函数M文件,需要通过命令窗口传递输入参数来调用。除了一些很简单的代码,大部分情况下用户都可能遇到程序报错的问题,这就需要对代码进行调试纠错,一般需要通过Debug菜单下的子项辅助完成,包括设置断点、逐步运行等项。

当程序运行无误后,还要考虑程序性能是否可以改进。

MATLAB提供了M-Lint和Profiler工具,能够辅助用户分析代码运行中时间消耗的细节和可能需要改变的编程细节,如循环赋值前没有预定义数组,用循环去实现可以用数组函数实现的运算等。这些工具都在Tools菜单下设置了子菜单。

图4-1 MATLAB代码编辑-调试器

4.2 MATLAB程序设计原则

MATLAB程序的基本设计原则如下所述:

●百分号“%”后面的内容是程序的注解,要善于运用注解使程序更具可读性;

●养成在主程序开头用clear指令清除变量的习惯,以消除工作空间中其他变量对程序运行的影响,但注意在子程序中不要用clear;

●参数值要集中放在程序的开始部分,以便维护。要充分利用MATLAB工具箱提供的指令来执行所要进行的运算,在语句行之后输入分号使其及中间结果不在屏幕上显示,以提高执行速度;

●input指令可以用来输入一些临时的数据;而对于大量参数,则通过建立一个存储参数的子程序,在主程序中通过子程序的名称来调用;

●程序尽量模块化,即采用主程序调用子程序的方法,将所有子程序合并在一起来执行全部的操作;

●充分利用Debugger来进行程序的调试(设置断点、单步执行、连续执行),并利用其他工具箱或图形用户界面(GUI)的设计技巧,将设计结果集成到一起;

●设置好MATLAB的工作路径,以便程序运行;

●MATLAB程序的基本组成结构如下所示。

当然,更复杂的程序还需要调用子程序,或与其他应用程序相结合。

4.3 M文件

M文件是包含MATLAB代码的文件。

1.M文件的类型

M文件按其内容和功能可以分为脚本M文件和函数M文件两大类。

(1)脚本M文件

它是许多MATLAB代码按顺序组成的命令序列集合,不接受参数的输入和输出,与MATLAB工作空间共享变量空间。

它一般用来实现一个相对独立的功能,比如对某个数据集进行某种分析、绘图,求解某个已知条件下的微分方程等。用户可以通过在命令窗口中直接输入文件名来运行脚本M文件。

通过脚本M文件,用户可以把为实现一个具体功能的一系列MATLAB代码书写在一个M文件中,每次只需要输入文件名即可运行脚本M文件中的所有代码。

(2)函数M文件

它也是为了实现一个单独功能的代码块,但与脚本M文件不同的是函数M文件需要接受参数输入和输出,函数M文件中的代码一般只处理输入参数传递的数据,并把处理结果作为函数输出参数返回给MATLAB工作空间中的指定接收变量。

因此,函数M文件具有独立的内部变量空间。在执行函数M文件时,要指定输入参数的实际取值,而且一般要指定接收输出结果的工作空间变量。

MATLAB提供的许多函数就是用函数M文件编写的,尤其是各种工具箱中的函数,用户可以打开这些M文件来查看。实际上,对于特殊应用领域的用户,如果积累了充分的专业领域应用的函数,就可以组建自己的专业领域工具箱了。

通过函数M文件,用户可以把实现一个抽象功能的MATLAB代码封装成一个函数接口,在以后的应用中重复调用。

2.M文件的结构

图4-2显示的是MATLAB提供的一个函数M文件的全部内容,图中清楚地显示了一般的M文件包括的各部分结构。

从图4-2可以看到,MATLAB中M文件一般包括以下五部分结构。

●函数声明行(Function Definition Line),这一行只出现在函数M文件的第一行,通过function关键字表明此文件是一个函数M文件,并指定函数名、输入和输出参数,如图4-2中的第1行。

●H1行,这是帮助文字的第一行(the first help text line),给出M文件帮助最关键的信息。当用lookfor查找某个单词相关的函数时,lookfor只在H1行中搜索是否出现指定单词,如图4-2中的第2行。

图4-2 M文件的一般结构

●帮助文字,这部分对M文件有更加详细的说明,经常解释M文件实现的功能,M文件中出现的各变量、参数的意义,以及创作版权信息等。如图4-2中的第13行。当获取一个M文件的帮助时,H1行和帮助文字部分同时显示。

●M文件正文,这是M文件实现功能的MATLAB代码部分,通常包括运算、赋值等指令。图4-2的例子中只有第16行,但一般都由多行组成。

●注释部分,这部分出现的位置比较灵活,主要是用来注释M文件正文的具体运行过程,以方便阅读和修改,经常穿插在M文件正文中间。

图4-2的例子中的第15行就是注释说明正文第16行的意义。注释一般都是针对接下来的一段正文代码的,常见的M文件中也经常包括多行注释。

3.M文件的创建

虽然一般脚本M文件可以包括上述五部分结构中除去“函数声明行”以外的四部分,但在实际应用中,脚本M文件经常仅仅由M文件正文和注释部分构成。正文部分实现功能,注释部分则给出每一块代码的功能说明。下面通过实例讲述脚本M文件的创建。

【例4-1】 M文件创建实例。建立一个命令文件,将变量a,b的值互换。

解: 首先打开M文件编辑器,输入以下程序:

然后保存文件名为“41.m”即完成了文件的建立。

在MATLAB的命令窗口中输入41,将会执行该命令文件。

函数M文件的命名一般习惯和函数名一致,比如图4-2中函数声明行function T=now(),表明函数名为now,因此此函数M文件一般保存为now.m,可以通过now()语句调用该文件;否则,如果函数名和文件名不一致时,函数调用就需要通过文件名和与函数声明中对应的参数列表来实现。

编写好的函数M文件,相当于MATLAB提供的命令,可以在命令行进行函数调用。但要注意,要求被调用的函数对应的.m文件必须在MATLAB路径下。

4.4 MATLAB程序流程控制

和各种常见的高级语言一样,MATLAB也提供了多种经典的程序结构控制语句。MATLAB中的程序流程控制语句有:分支控制语句(if结构和switch结构)、循环控制语句(for循环、while循环、continue语句和break语句)和程序终止语句(return语句),下面分别进行介绍。

1.程序分支控制语句

分支语句可以使程序中的一段代码只在满足一定条件时才执行,因此也成为分支选择。MATLAB中的分支语句有两类:if语句和switch语句。

●if与else或elseif连用,偏向于是非选择,当某个逻辑条件满足时执行if后的语句,否则执行else语句。

●switch和case、otherwise连用,偏向于情况的列举,当表达式结果为某个或某些值时,执行特定case指定的语句段,否则执行otherwise语句。

if结构的语法形式如下所示:

其中elseif和else语句都是可选语句。if、elseif和else构成的各项分支里面,哪一个的条件满足(逻辑表达式logical_expression的结果为真),就执行哪一个分支后面紧跟的程序语句。因此,各个分支条件满足的情况应该是互斥的和完全的,就是被选的条件在一个分支中成立,而且只能在一个分支中成立。

当然,省略了elseif和else分支的语句,就不必要求分支条件满足的情况具备完全性了。

if结构中条件判断除了可以用逻辑表达式外,还可以用数组A,这时判断相当于逻辑表达式all(A),即当数组A的所有元素都为非零值时,才执行该条件后的分支代码。

特别地,当数组A为空数组时,相当于该条件判断为假。

switch结构的语法形式如下所示:

执行中,先计算表达式expression的值,当结果等于某个case的value时,就执行该case紧随的语句。如果所有case的value都和expression计算结果不相等,则执行otherwise后面紧随的语句。

otherwise语句是可选的,当没有otherwise语句时,如果所有case的value都和expression计算结果不相等,则跳过switch-case语句段,直接执行后续代码。

相等的意义,对于数值类型来说,相当于判断“if result==value”,对于字符串类型来说,相当于判断“if strcmp(result,value)”。

由此可见,switch-case语句实际上可以被if-elseif-else语句等效替换,不过两种结构各有更便利的地方,读者在以后的例子中会逐渐体会到。

学过C语言的读者需要注意,MATLAB中的switch-case结构,只执行表达式结果匹配的第一个case分支,然后就跳出switch-case结构。因此,在每一个case语句中不需要用break语句跳出。

在一条case语句后可以列举多个值,只需要以元胞数组的形式列举多个值,就是用花括号把用逗号或空格分隔的多个值括起来即可。

2.程序循环控制语句

循环控制语句能够使得某段程序代码多次重复执行,MATLAB中提供了两类循环语句,分别是for循环和while循环:

●for循环一般用在已知循环执行次数的情况;

●while循环则用在已知循环退出条件的情况。

MATLAB还提供了continue和break语句,用于更精细地控制循环结构:

●continue语句使得当前次循环不向下执行,直接进入下一次循环;

●break语句则是退出该循环。

(1)for循环

for循环用于知道循环次数的情况,其语法格式如下所示:

index为循环变量,increment为增量,end用于判断循环是否应该终止。增量increment默认值为1,可以自由设置;当增量为正数时,循环开始先将index赋值为start,然后判断index是否小于等于end。若是,则执行循环语句,执行完后,对index累加一个增量increment;再判断index是否小于等于end,若是,则继续执行循环语句,继续对index累加,循环往复,直到index大于end时退出循环。

增量increment也可以设置为负整数,表示每次循环执行后对循环变量index进行递减,当index小于end时,退出循环。

MATLAB中,循环的执行效率很低,提高程序效率的一个办法就是多采用数组结构和MATLAB内联函数。

for循环中的循环变量index也可以赋值为数组A,那么在第一次循环中index就被赋值为A(:,1),即A的第一列元素,第二次循环index被赋值为A(:,2),依次类推;若A有n列元素,则循环执行n次,第n次循环时,循环变量index被赋值为A(:,n)。

(2)while循环

while循环用于已知循环退出条件的情况,其语法形式如下所示:

当表达式expression的结果为真时,就执行循环语句,直到表达式expression的结果为假,才退出循环。

如果表达式expression是一个数组A,则相当于判断all(A)。特别地,空数组则被当作逻辑假,循环停止。

(3)continue语句

continue语句用在循环中,表示当前次循环不再继续向下执行,而是直接对循环变量进行递增,进入下一次循环。

(4)break语句

break语句用于退出循环。

3.程序终止控制语句

一般程序代码都是按流程执行完毕后正常退出,但当遇到某些特殊情况,程序需要立即退出时,就可以用return语句提前终止程序运行。

【例4-2】 return语句使用实例。

本例中当变量n取值为负数时,通过return直接退出,不执行if后的代码。其运行结果是:

若去掉其中的return语句,则运行结果变为:

return语句更多地用在MATLAB函数M文件中。

4.5 MATLAB中的函数及调用

4.5.1 函数类型

MATLAB中的函数可以分为匿名函数、M文件主函数、嵌套函数、子函数、私有函数和重载函数。

1.匿名函数

匿名函数通常是很简单的函数,它是面向命令行代码的函数,通常只由一句很简单的声明语句组成。

匿名函数也可以接受多个输入和输出参数。使用匿名函数的优点是不需要维护一个M文件,而只需要一句非常简单的语句,就可以在命令窗口或M文件中调用函数,这对于那些函数内容非常简单的情况是很方便的。

创建匿名函数的标准格式如下所示:

其中,

(1)“expr”通常是一个简单的MATLAB变量表达式,实现函数的功能,比如x+x.^2、sin(x).*cos(x)等;

(2)“arglist”是参数列表,它指定函数的输入参数列表,对于多个输入参数的情况,通常要用逗号分隔各个参数;

(3)符号“@”是MATLAB中创建函数句柄的操作符,表示对由输入参数列表arglist和表达式expr确定的函数创建句柄,并把这个函数句柄返回给变量fhandle,这样,以后就可以通过fhandle来调用定义好的这个函数。

例如定义函数:

表示创建了一个匿名函数,它有一个输入参数x,它实现的功能是x+x.^2,并把这个函数句柄保存在变量“myfunhd”中,以后就可以通过“myfunhd(a)”来计算当“x=a”的时候的函数值。

需要注意的是,匿名函数的参数列表arglist中可以包含一个参数或多个参数,这样调用的时候就要按顺序给出这些参数的实际取值。

但arglist也可以不包含参数,即留空,这种情况下调用函数时还是需要通过fhandle()的形式来调用的,即要在函数句柄后紧跟一个空的括号。否则,只显示fhandle句柄对应的函数形式。

匿名函数可以嵌套,即在expr表达式中可以用函数来调用一个匿名函数句柄。

【例4-3】 匿名函数创建实例。

匿名函数可以保存在.mat文件中,上例中就可以通过“save myfunquad.mat myffhd”把匿名函数句柄“myffhd”保存在“myfunquad.mat”文件中,以后需要用到匿名函数“myffhd”时,只需要使用语句“load myfunquad.mat myffhd”就可以了。

2.M文件主函数

每一个函数M文件第一行定义的函数就是M文件主函数,一个M文件只能包含一个主函数,并通常习惯上将M文件名和M文件主函数名设为一致。

M文件主函数的说法是针对其内部嵌套函数和子函数而言的。一个M文件中除了一个主函数外,还可以编写多个嵌套函数或子函数,以便在主函数功能实现中进行调用。

3.嵌套函数

在一个函数内部,可以定义一个或多个函数,这种定义在其他函数内部的函数就被称为嵌套函数。嵌套可以多层发生,就是说一个函数内部可以嵌套多个函数,这些嵌套函数内部又可以继续嵌套其他函数。

嵌套函数的书写语法格式如下所示:

一般函数代码中结尾是不需要专门标明“end”的,但在使用嵌套函数时,无论嵌套函数还是嵌套函数的父函数(直接上一层函数)都要明确标出“end”表示函数结束。

嵌套函数的互相调用需要注意和嵌套的层次密切相关,如在下面一段代码中:

(1)外层的函数可以调用向内一层直接嵌套的函数(A可以调用B和D),而不能调用更深层的嵌套函数(A不可以调用C或E);

(2)嵌套函数可以调用与自己具有相同父函数的其他同层嵌套函数(B和D可以互相调用);

(3)嵌套函数也可以调用其父函数或与父函数具有相同父函数的其他嵌套函数(C可以调用B和D),但不能调用与其父函数具有相同父函数的其他嵌套函数内深层嵌套的函数。

4.子函数

一个M文件只能包含一个主函数,但一个M文件中可以包含多个函数,这些编写在主函数后面的函数都称为子函数。所有子函数只能被其所在M文件中的主函数或其他子函数调用。

所有子函数都有自己独立的声明和帮助、注释等结构,只需要在位置上处在主函数之后即可,而各个子函数的前后顺序都可以任意放置,和被调用的前后顺序无关。

M文件内部发生函数调用时,MATLAB首先检查该M文件中是否存在相应名称的子函数,然后检查这一M文件所在目录的子目录下是否存在同名的私有函数,然后按照MATLAB的路径检查是否存在同名的M文件或内部函数。

根据这一顺序,函数调用时首先查找相应的子函数,因此,可以通过编写同名子函数的方法实现M文件内部的函数重载。

子函数的帮助文件也可以通过help命令显示,如myfun.m文件中有名为myfun的主函数和名为mysubfun的子函数,那么可以通过help myfun>mysubfun命令来获取子函数mysubfun的帮助。

5.私有函数

私有函数是具有限制性访问权限的函数,它们对应的M文件需要保存在名为“private”的文件夹下,这些私有函数代码在编写上和普通的函数没有什么区别,也可以在一个M文件中编写一个主函数和多个子函数,以及嵌套函数。

但私有函数只能被private目录的直接父目录下的脚本M文件或M文件主函数调用。

通过help命令获取私有函数的帮助,也需要声明其私有特点,例如要获取私有函数myprifun的帮助,就要通过help private/myprivfun命令。

6.重载函数

“重载”是计算机编程中非常重要的概念,它经常用在处理功能类似但参数类型或个数不同的函数编写中。

例如现在要实现一个计算功能,一种情况下输入的几个参数都是双精度浮点类型,另一种情况是,输入的几个参数都是整型变量。这时候,用户就可以编写两个同名函数,一个用来处理双精度浮点类型的输入参数,另一个用来处理整型的输入参数,这样,当用户实际调用函数时,MATLAB就会根据实际传递的变量类型选择执行其中一个函数。

MATLAB中重载函数通常放置在不同的文件夹下,通常文件夹名称以符号@开头,然后跟一个代表MATLAB数据类型的字符。

例如“@double”目录下的重载函数的输入参数应该是双精度浮点型,而“@int32”目录下的重载函数的输入参数应该是32位整型。

4.5.2 函数参数传递

MATLAB中通过M文件编写函数时,只需要指定输入和输出的形式参数列表,只是在函数实际被调用的时候,才需要把具体的数值提供给函数声明中给出的输入参数。

MATLAB中参数传递过程是传值传递,也就是说,在函数调用过程中,MATLAB将传入的实际变量值赋给形式参数指定的变量名,这些变量都存储在函数的变量空间中,这和工作空间变量空间是独立的,每一个函数在调用中都有自己独立的函数空间。

例如编写函数:

在命令窗口通过a=myfun(3,2,0.5)调用此函数,那么MATLAB首先会建立myfun函数的变量空间,把3赋值给x,把2赋值给y,把0.5赋值给z,然后执行函数实现的代码;在执行完毕后,把myfun函数返回的参数y的值传递给工作空间变量a,调用过程结束后,函数变量空间被清除。

1.输入和输出参数的数目

MATLAB的函数可以具有多个输入或输出参数。通常在调用时,需要给出和函数声明语句中一一对应的输入参数;而输出参数个数可以按参数列表对应指定,也可以不指定。不指定输出参数调用函数时,MATLAB默认把输出参数列表中第一个参数的值返回给工作空间变量“ans”。

MATLAB中可以通过nargin和nargout函数确定函数调用时实际传递的输入和输出参数个数,结合条件分支语句,就可以处理函数调用中指定不同数目的输入输出参数的情况。

【例4-4】 显示函数输入和输出参数的数目实例。

这个函数可以处理一个或两个输入参数、一个或两个输出参数的情况。当只有一个输入参数x1和一个输出参数y1时,把x1赋值给y1;当有1个输入参数x1和两个输出参数y1、y2时,把x1赋值给y1和y2;当有两个输入参数x1、x2和一个输出参数y1时,把x1+x2的计算结果赋值给y1;当有两个输入参数x1、x2和两个输出参数y1、y2时,把x1赋值给y1,并把x2赋值给y2。函数调用结果如下所示:

指定了输入和输出参数个数的情况比较好理解,只要对应函数M文件中对应的if分支项即可;而不指定输出参数个数的调用情况,MATLAB是按照指定了所有输出参数的调用格式对函数进行调用的,不过在输出时只是把第一个输出参数对应的变量值赋给工作空间变量ans。

例如“mytestnio(5,7)”这句函数调用中,实际上是按照“[y1,y2]=mytestnio(x1,x2)”这种形式调用的,在函数变量空间中x1被赋值为5,x2被赋值为7,y1计算结果为5,y2计算结果为7,但函数只把输出参数列表中第一个输出变量(即y1)的取值返回给工作空间变量ans,因此,ans取值为5。

2.可变数目的参数传递

函数nargin和nargout结合条件分支语句,可以处理可能具有不同数目的输入和输出参数的函数调用,但这要求对每一种输入参数数目和输出参数数目的组合分别进行代码编写。

有些情况下,用户可能并不能确定具体调用中传递的输入参数或输出参数的个数,即具有可变数目的传递参数,MATLAB中可以通过varargin和varargout函数实现可变数目的参数传递,使用这两个函数对于处理具有复杂的输入输出参数个数组合的情况也是便利的。

函数varargin和varargout把实际的函数调用时传递的参数值封装成一个元胞数组,因此,在函数实现部分的代码编写中,就要用访问元胞数组的方法访问封装在varargin或varargout中的元胞或元胞内的变量。

【例4-5】 可变数目的参数传递实例。

本例中的函数mytestvario以varargin为输入参数,从而可以接受可变数目的输入参数。函数实现部分首先计算了各个输入参数(可能是标量、一维数组或二维数组)的均值,然后计算这些均值的均值。调用结果如下所示:

对于“mytestvario(4,[1 3],[1 23;23 1],magic(4))”这句函数调用,在函数变量区,varargin首先被赋值为一个元胞数组“{4,[1 3],[1 23;23 1],magic(4)}”,即varargin有1行4列个元胞,各个元胞中分别存储了一个标量数值、一维行数组、2行2列的二维数组和4行4列的魔方数组;在函数实现部分,首先创建中间变量temp,并初始化赋值为零(用来存储各个元胞中数据均值的总和),然后计算每一个元胞中所有数据的均值并将结果累加到temp上;最后通过“y=temp/length(varargin)”计算这些均值的均值。

函数varargin和varargout也可以放置在参数列表中某些必然出现的参数之后,其语法格式有如下几种形式。

1)function[out1,out2]=example1(a,b,varargin),表示函数example1可以接受大于等于两个输入参数,返回两个输出参数;两个必选的输入参数是a和b,其他更多的输入参数被封装在varargin中。

2)function[i,j,varargout]=example2(x,y),表示函数example2接受两个输入参数x和y,返回大于等于两个输出参数,前两个输出参数为i和j,其他更多的输出参数封装在varargin中。

函数varargout和varargin的用法类似,只需要注意访问时应按照访问元胞数组的方法,这里就不再举例了。

3.返回被修改的输入参数

MATLAB函数有独立于MATLAB工作空间的自己的变量空间,因此,输入参数在函数内部的修改都只具有和函数变量空间相同的生命期,如果不指定将修改后的输入参数值返回到工作空间,那么在函数调用结束后这些修改后的值将被自动清除。

【例4-6】 函数内部的输入参数修改实例。

本例中的mytest函数内部,首先修改了输入参数x的值(x=x+5),然后以修改后的x的值计算输出参数y的值(y=x*2)。调用结果如下所示:

由此结果可见,调用结束后,函数变量区中的x在函数调用中被修改,但此修改只在函数变量区有效,这并没有影响到MATLAB工作空间变量空间中的变量x的值。函数调用前后,MATLAB工作空间中的变量x始终取值为3。

那么,如果用户希望函数内部对输入参数的修改也对MATLAB工作空间的变量有效,就需要在函数输出参数列表中返回此输入参数。

对于本例中的函数,则需要把函数修改为“function[y,x]=mytest(x)”,而在调用时也要通过“[y,x]=mytest(x)”这种形式。

【例4-7】 函数参数传递实例。将修改后的输入参数返回给MATLAB工作空间。

MATLAB工作空间中的调用结果如下所示:

通过本例可见,函数调用后,MATLAB工作空间中的变量x取值从3变为8(3+5),可见通过[y,x]=mynewtest(x)调用,实现了函数对MATLAB工作空间变量的修改。

4.全局变量

通过返回修改后的输入参数,可以实现函数内部对MATLAB工作空间变量的修改。而另一种殊途同归的方法则是使用全局变量。声明全局变量需要用到global关键词,语法格式为“global variable”。

通过全局变量可以实现MATLAB工作空间变量空间和多个函数的函数空间的共享,这样,多个使用全局变量的函数和MATLAB工作空间共同维护这一全局变量,任何一处对全局变量的修改,都会直接改变此全局变量的取值。

在应用全局变量时,通常在各个函数内部通过global variable语句声明,在命令窗口或脚本M文件中也要先通过global声明,然后进行赋值和调用。

【例4-8】 全局变量使用实例。

在命令窗口中声明全局变量然后赋值调用:

通过本例可见,用global将T声明为全局变量后,函数内部对T的修改也会直接作用到MATLAB工作空间中。函数myprocess调用一次后,T的值从0.3变为0.6(0.3*2)。

4.6 函数句柄

函数句柄实际上提供了一种间接调用函数的方法。创建函数句柄需要用到操作符@。前面已经讲过,匿名函数实际上就是一种函数句柄,而对MATLAB提供的各种M文件函数和内部函数,也都可以创建函数句柄,从而可以通过函数句柄对这些函数实现间接调用。

函数句柄的优点如下:

(1)方便地实现函数间的互相调用;

(2)兼容函数加载的所有方式;

(3)拓宽子函数,包括局部函数的使用范围;

(4)提高函数调用的可靠性;

(5)减少程序设计中的冗余;

(6)提高重复执行的效率。

创建函数句柄的一般语法格式如下所示:

其中,

“funciont_filename”是函数所对应的M文件的名称或MATLAB内部函数的名称;

“@”是句柄创建操作符;

“fhandle”变量保存这一函数句柄。

例如fhandle=@sin就创建了MATLAB内部函数sin的句柄,并将其保存在fhandle变量中,以后就可以通过fhandle(x)来实现sin(x)的功能。

通过函数句柄调用函数时,也需要指定函数的输入参数,比如可以通过fhandle(arg1,arg2,...,argn)这样的调用格式来调用具有多个输入参数的函数。对于那些没有输入参数的函数,在使用句柄调用时,要在句柄变量后加上空的圆括号,即fhandle()。

【例4-9】 函数句柄创建和调用实例。

MATLAB中提供了丰富的处理函数句柄的函数,如表4.1所示。

表4.1 处理函数句柄的函数

【例4-10】 处理函数句柄的函数使用实例。

4.7 MATLAB程序调试

MATLAB程序调试主要是来发现和纠正程序中的错误。

4.7.1 常见程序错误

MATLAB程序常见的错误有以下几类。

1.矩阵运算方面的错误

(1)矩阵下标索引使用错误

MATLAB的计算元素是矩阵,即使是一个一维数组在MATLAB软件中,所有的计算都是以矩阵为单元进行的,矩阵是MATLAB的核心。需要非常注意的是:与C语言等编程语言的习惯不一样,MATLAB的语法规定矩阵的索引从1开始。因此,在访问矩阵(包括向量、二维矩阵、多维数组)的过程中,下标索引从0开始,或者出现负数,就会报错。

例如,在命令窗口输入:

输出报错为:

分析:如果改为A(1,1),则输出为1,表示访问A的第1行第1列的元素。同类的常见错误还有:在引用矩阵元素的时候,索引值超出矩阵应有的范围。

(2)矩阵运算对象维数不匹配的错误

进行矩阵运算时,运算符(=+-/*等)两边的运算对象维数必须匹配。

例如,在命令窗口输入:

输出报错为:

分析:A是3*2的矩阵,B是3*3的矩阵,A*B是矩阵维数不匹配,故报错。如果对A取转置,然后再相乘,则不会出错。

(3)元素与矩阵运算的错误

MATLAB通过“.”来区分矩阵运算和元素运算,当元素与矩阵进行运算时,容易忽略“.”。

例如,在命令窗口输入:

输出报错为:

分析:其实,写为“B=6.*A”进行运算也是正确的,由于习惯的原因,这个“.”通常省略,在乘法时不会报错,但在除法时,就错了。改为以下语句就不会出错。

2.函数方面的错误

(1)函数没有定义的错误

在命令窗口中可以运行MATLAB自带的函数以及用户自己定义的函数,如果不是这两类函数,运行时会报错。

例如,在命令窗口中输入:

输出报错为:

分析:可能的出错原因有:程序文件名错、文件名大小写错、该文件不在搜索路径中。

解决办法:核对文件名、检查大小写,统一命名风格、将该文件复制到或包含路径下。

(2)函数输出变量赋值的错误

在函数中,如果有一个或多个输出变量没有被赋值,调用该函数时,会报错。

分析:函数如果带有输出变量,则每个输出在返回的时候都必须被赋值。容易出现这个错误的两个地方是:

①在部分条件判断语句(如if)中没有考虑到输出变量的返回值;

②在循环迭代过程中部分变量的维数发生了变化。

解决办法是:调试程序,仔细查看函数返回时各输出变量的值。更好的方法是:在条件判断或者执行循环之前对所使用的变量赋初值。

(3)在命令窗口定义函数的错误

在MATLAB中,不能在命令窗口或者M文件编辑器中定义函数。例如,在命令窗口输入:

输出报错为:

分析:在命令窗口写function c=myfun(a,b),此错误就会出现,因为函数只能定义在M文件中。关于脚本文件和M文件的区别请查阅MATLAB基础书,简言之:

①如果写成function的形式,那么必须写在M文件中,且以function开头(即function语句前不能包含其他语句,所有语句必须放在function中,当然,function的定义可以有多个,各function之间是并列关系,不能嵌套);

②如果写成脚本的形式,则既可以写在命令窗口中,也可以写在M文件中,但两者均不能包含function语句(即不能进行函数的定义)。

解决办法是:新建一个M文件,然后再进行函数的定义

总之,对于一些格式错误,如函数名的格式错误、缺括号等,MATLAB可在运行时检测出大多数的格式错误,并显示出错信息和位置,这类错误可很容易找到,并进行纠正。

对于算法错误,逻辑上的错误,不易查找,遇到此类错误时需耐心。一般可考虑如下方法:

●删除句尾分号“;”,注意变量值的变化;将每步执行结果输出到命令窗口,显示中间结果;

●在适当位置加上keyboard语句,当程序执行到这条语句时,MATLAB会暂停执行,并将控制权交给用户,这时可检查和修改局部工作空间的内容,从中找出错误的线索,利用return命令可恢复程序执行;

●在函数定义行之前加上%,注释掉,使之变成脚本语言;或者选用“Text”菜单的“Comment”命令,注释掉可疑的代码部分;这样,程序运行出错时便可查看M文件中产生的变量;

●使用MATLAB调试器,设置断点,或单步执行,使用一些调试和分析工具。

下面讲述程序调试的一些工具及调试方法,熟练掌握并运用这些工具及调试方法,能提高编程的效率。

4.7.2 调试方法

MATLAB程序有直接调试法和工具调试法这两种调试方法。

(1)直接调试法

直接调试法就是在M文件中,将某些语句后面的分号去掉,迫使M文件输出一些中间计算结构,以便发现可能的错误。常用的做法有:

①在适当位置,添加显示某些关键变量值的语句;

②利用echo指令,使运行时在屏幕上逐行显示文件内容,echo on能显示M脚本文件;echo FunName On能显示名为FunName的M函数文件;

③在原M脚本或函数文件的适当位置,添加指令keyboard,keyboard语句可以设置程序的断点;

④通过将原M函数文件的函数声明行注释掉,可使一个中间变量难于观察的M函数文件变为一个所有变量都保存在基本工作空间中的M脚本文件。

(2)工具调试法

工具调试法就是在程序中设置一些断点,利用调试菜单(Debug)中的一些选项进行调试。

MATLAB提供了进行代码调试和代码分析优化的工具,这些工具,一般的MATLAB用户都应该有所了解。尤其是断点调试部分的内容,建议读者尽量以自己的程序代码为例,多加练习,熟练掌握。

4.7.3 调试工具

当完成MATLAB代码编写后,用户就可以在命令窗口中运行代码(脚本或函数文件)。对于比较简单的代码,一般只要编程习惯较好,都可以一次通过。但对于很多比较复杂的情况,或者用户初学MATLAB编程,一些常见的错误还不能避免,就容易在运行时出现错误。这时候,就需要利用MATLAB的调试工具对出现错误的代码进行调试纠错。

MATLAB的代码编辑调试器是一个综合了代码编写、调试的集成开发环境。MATLAB代码调试过程,主要是通过MATLAB代码编辑-调试器的Debug菜单下的子项进行的,如图4-3所示。

Debug菜单用于程序调试,需要与Breakpoints菜单项配合使用。MATLAB R2008b的Debug菜单中的菜单项介绍如下。

(1)Open M-Files when Debugging:用于调试时打开M文件。

(2)Step:在调试模式下,执行M文件的当前行,对应的快捷键是F10。

(3)Step In:在调试模式下,执行M文件的当前行,如果M文件当前行调用了另一个函数,那么进入该函数内部,对应的快捷键是F11。

图4-3 MATLAB代码编辑-调试器的Debug菜单

(4)Step Out:当在调试模式下执行Step In进入某个函数内部之后,执行Step Out可以完成函数剩余部分的所有代码,并退出函数,暂停在进入函数内部前的M文件所在行末尾。

(5)Save File and Run:运行当前M文件,快捷键是F5;当前M文件设置了断点时,运行到断点处暂停。

(6)Run configuration for:运行调试配置文件。打开需调试的M文件后,点击工具栏的 按钮,将弹出一个如图4-4所示的该M文件的运行配置文件的编辑窗口。

在该编辑窗口,用户可以添加一些便于调试的代码、变量赋值、输入参数、中间变量结果等。

在如图4-4所示的例子中,在该配置文件中,在M文件运行之前对其中的参数进行了赋值,运行之后对其中的参数进行了运算。

有了该配置文件后,程序的运行结果c=-18。读者可自己体验一下,加强掌握。

(7)Go Until Cursor:运行当前M文件到在光标所在行的行尾。

需要注意,以上这些调试项,除了Run(运行),首先都需要在M文件中设置断点,然后运行到断点位置后,这些调试项才可启用。

(8)Set/Clear Breakpoint:在光标所在行开头设置或清除断点。

(9)Set/Modify Conditional Breakpoint…:在光标所在行开头设置或修改条件断点,选择此子项,会打开“条件断点设置”对话框,如图4-5所示,用于设置在满足什么条件时,此处断点有效。

图4-4 运行配置文件的编辑窗口

图4-5“条件断点设置”对话框

(10)Enable/Disable Breakpoint:将当前行的断点设置为有效或无效。

(11)Clear Breakpoints in All Files:清除所有M文件中的断点。

(12)Stop if Errors/Warnings…:设置出现某种运行错误或警告时,停止程序运行,选择此子项,会打开“错误/警告设置”对话框,如图4-6所示。

(13)Exit Debug Mode:退出调试模式。

上面逐项讲述了Debug菜单下每一个子项的意义,实际上,很多子项都有对应的快捷工具按钮。MATLAB代码编辑-调试器中,如图4-7所示的部分工具按钮就是用于M文件调试的。

图4-7中的各个工具按钮,从左向右依次对应于Set/Clear Breakpoint、Clear Breakpoints in All Files、Step、Step In、Step Out、Run、Exit Debug Mode等菜单子项。

图4-6 设置出现某种运行错误或警告则停止程序运行

图4-7 调试工具按钮

通常的调试步骤是:

【步骤1】:先运行(Run)一遍M文件,针对具体的出错信息,在适当的地方设置断点或条件断点;

【步骤2】:再次运行(Run)到断点位置(如图4-8所示),此时MATLAB把运行控制权交给键盘;

【步骤3】:此时命令窗口出现“K>>”提示符(如图4-9所示);可以在命令窗口中查询M文件运行过程中的所有变量,包括函数运行时的中间变量;

【步骤4】:运行到断点位置后,用户可以选择“Step/Step Into/Step Out”等调试运行方式,逐行运行并适时查询变量取值,从而逐渐找到错误所在并排除。

图4-8 设置断点后运行(Run)到断点所在位置

图4-9 调试模式时MATLAB命令窗口把控制权交给键盘

4.8 本章小结

本章围绕数值计算介绍了MATLAB程序设计的基础知识,包括MATLAB的基本操作和编程技巧,这些都是后面内容的基础。

MATLAB拥有众多的内置函数,学习MATLAB时,读者不要试图完全记住或者掌握它们,需要学会使用help、lookfor等命令查找所需的命令或函数。

在编写MATLAB程序,尤其是大型、复杂的MATLAB程序时,要多从用户角度考虑,力求让程序的例外处理机制完美,具有更好的可读性,但同时也要考虑算法的执行效率,找到这两方面的一个较好的平衡。 A9/pMGEb3ScCL8m4miEo0miSkEBAtoTDiaxLGBG2GIBnzv3FFVe7wvH9WVEfQmTX

点击中间区域
呼出菜单
上一章
目录
下一章
×