

§6 程序设计
MATLAB作为一种高级语言,它不仅可以如前几节所介绍的那样,以一种人机交互式的命令行的方式工作,还可以像BASIC、FORTRAN、C等其他高级计算机语言一样进行控制流的程序设计,即编制一种以 . m为扩展名的MATLAB程序(简称M文件)。而且,由于MATLAB本身的一些特点,M文件的编制同上述几种高级语言比较起来,有许多无法比拟的优点。
6.1 M文件
所谓M文件就是由MATLAB语言编写的可在MATLAB语言环境下运行程序源代码文件。由于商用的MATLAB软件是用C语言编写而成。因此,M文件的语法与C语言十分相似。对广大参加建模竞赛且学过C语言的同学来说,M文件的编写是相当容易的。M文件可以分为脚本文件(Script)和函数文件(Function)两种。M文件不仅可以在MATLAB的程序编辑器中编写,也可以在其他的文本编辑器中编写,并以“m”为扩展名加以存储。
6.1.1 脚本文件
脚本类似于DOS下的批处理文件,不需要在其中输入参数,也不需要给出输出变量来接受处理结果,脚本仅是若干命令或函数的集合,用于执行特定的功能。脚本的操作对象为MATLAB工作空间内的变量,并且在脚本执行结束后,脚本中对变量的一切操作均会被保留。在MATLAB语言中也可以在脚本内部定义变量,并且该变量将会自动地被加入到当前的MATLAB工作空间中,并可以为其他的脚本或函数引用,直到MATLAB被关闭或采用一定的命令将其删除。
例如:
%命令窗口中定义矩阵a,b
a=pascal(3)
a=
1 1 1
1 2 3
1 3 6
b=magic(3)
b=
8 1 6
3 5 7
4 9 2
% 在编辑器中编写下述命令
a=a+b
b=a-b
a=a-b
在编辑器中编辑完上例的脚本文件后,保存至文件scripts—example中,然后在工作窗口中调用该脚本文件,
scripts—example
>> a
a=
8 1 6
3 5 7
4 9 2
>> b
b=
1 1 1
1 2 3
1 3 6
其中矩阵a、b均是在工作空间中已定义完毕的,脚本运行时直接使用该变量,并对其进行操作,然后在命令窗口中调用该脚本,可以看到变量a、b已经进行了相互交换。
6.1.2 函数文件
MATLAB语言中,相对于脚本文件而言,函数文件是较为复杂的。函数需要给定输入参数,并能够对输入变量进行若干操作,实现特定的功能,最后给出一定的输出结果或图形等,其操作对象为函数的输入变量和函数内的局部变量等。
MATLAB语言的函数文件包含如下5个部分。
(1) 函数题头:指函数的定义行,是函数语句的第一行,在该行中将定义函数名、输入变量列表及输出变量列表等。
(2) HI行:指函数帮助文本的第一行,为该函数文件的帮助主题,当使用lookfor命令时,可以查看到该行信息。
(3) 帮助信息:这部分提供了函数的完整的帮助信息,包括HI之后至第一个可执行行或空行为止的所有注释语句,通过MATLAB语言的帮助系统查看函数的帮助信息时,将显示该部分。
(4) 函数体;指函数代码段,也是函数的主体部分。
(5) 注释部分:指对函数体中各语句的解释和说明文本,注释语句是以%引导的。
例如:
function[output,output2]=function—example(input1,input2) % 函数题头
%This is function to exchange two matrices % HI行
%input1,input2 are input variables % 帮助信息
%output1,output2 are output variables % 帮助信息
output1=input2; % 函数体
output2=input1; % 函数体
%The end of this example function
[a,b]=function---example(a,b)
a=
8 1 6
3 5 7
4 9 2
b=
1 1 1
1 2 3
1 3 6
可以看到通过使用函数可以和上一节中的示例一样同等矩阵a、b进行了相互交换。在该函数题头中,“function”为MATLAB语言中函数的标示符,而function---example为函数名,input1、input2为输入变量,而output1、output2为输出变量,实际调用过程中,可以用有意义的变量替代使用。题头的定义是有一定的格式要求的,输出变量是由中括号标识的,而输入变量是由小括号标识的,各变量间用逗号间隔,应该注意到,函数的输入变量引用的只是该变量的值而非其他值,所以函数内部对输入变量的操作不会带回到工作空间中。
函数题头下的第一行注释语句为HI行,可以通过lookfor命令查看;函数的帮助信息可以通过help命令查看。
函数体是函数的主体部分,也是实现编程目的的核心所在,它包括所有可执行的一切MATLAB语言代码。
在函数体中“%”后的部分为注释语句,注释语句主要是对程序代码进行说明解释,使程序易于理解,也有利于程序的维护。MATLAB语言中将一行内百分号后所有文本均视为注释部分,在程序的执行过程中不被解释,并且百分号出现的位置也没有明确的规定,可以是一行的首位,这样,整行文本均为注释语句,也可以是在行中的某个位置,这样其后所有文本将被视为注释语句,这也展示了MATLAB语言在编程中的灵活性。
尽管在上文中介绍了函数文件的5个组成部分,但是并不是所有的函数文件都需要全部的这5个部分,实际上,5部分中只有函数题头是一个函数文件所必需的,而其他的4个部分均可省略。当然,如果没有函数体则为一空函数,不能产生任何作用。
在MATLAB语言中,存储M文件时文件名应当与文件内主函数名相一致,这是因为在调用M文件时,系统查询的相应的文件而不是函数名,如果两者不一致,则或者打不开目的文件,或者打开的是其他文件。鉴于这种查询文件的方式与以往程序设计语言不同,在其他的语言系统中,函数的调用都是指对函数名本身的,所以,建议在存储M文件时,应将文件名与主函数名统一起来,以便于理解和使用。
6.2 函数变量及变量作用域
在MATLAB语言的函数中,变量主要有输入变量、输出变量及函数内所使用的变量。输入变量相当于函数入口数据,是一个函数操作的主要对象。某种程度上讲,函数的作用就是对输入变量进行加工以实现一定的功能。如前节所述,函数的输入变量为形式参数,即只传递变量的值而不传递变量的地址,函数对输入变量的一切操作和修改如果不依靠输出变量传出的话,将不会影响工作空间中该变量的值。
MATLAB语言提供了函数nargin和函数varargin来控制输入变量的个数,以实现不定个数参数输入的操作。
函数对于函数变量而言,还应当指出的是其作用域的问题。在MATLAB语言中,函数内定义的变量均被视为局部变量,即不加载到工作空间中,如果希望使用全局变量,则应当使用命令global定义,而且在任何使用该全局变量的函数中都应加以定义。在命令窗口中也不例外。
例如:
% 这里一个全局变量的示例
function [num1,num2,num3]=text (varargin)
global firstlevel secondlevel %定义全局变量
num1=0;
num2=0;
num3=0;
list=zeros(nargin);
for i=1:nargin
list (i)=sum (varargin{i}(:));
list (i)=list (i) /length (varargin{i});
if list (i)>firstlevel
num1=num1+1
elseif list (i)>secondlevel
num2=num2+1;
else
num3=num3+1;
end
end
% 在命令窗口中也应定义相应的全局变量
>> global firstlevel secondlevel
>> firstlevel=85;
>> secondlevel=75;(程序运行结果略)
从该例中可以看到,定义全局变量时,与定义输入变量和输出变量不同,变量之间必须用空格分隔,而不能用逗号分隔,否则系统将不能识别逗号后的全局变量。
6.3 子函数与局部函数
在MATLAB语言中,与其他的程序设计语言类似,也可以定义子函数,以扩充函数的功能。在函数文件中题头中所定义的函数为主函数,而在函数体内定义的其他函数均被视为子函数。子函数只能被主函数或同一主函数下其他的子函数所调用。
在MATLAB语言中将放置在目录private下的函数称为局部函数,这些函数只能被private目录的父目录中函数调用,而不能被其他的目录的函数调用。
局部函数与子函数所不同的是局部函数可以被其父目录下的所有函数所调用,而子函数则只能为其所在的M文件的主函数所调用,所以局部函数可应用范围大于子函数;在函数编辑的结构上,局部函数与一般的函数文件的编辑相同,而子函数则只能在主函数文件中编辑。
当在MATLAB的M文件中调用函数时,首先将检测该函数是否为此文件的子函数;如果不是的话,再检测是否为可用的局部函数;当结果仍然为否定时,再检测该函数是否为MATLAB搜索路径上的其他M文件。
6.4 流程控制语句
如其他的程序设计语言一样,MATLAB语言也给出了丰富的流程控制语句,以实现具体的程序设计。在命令窗口中的操作虽然可以实现人面交互,但是所能实现的功能却相对简单,虽然也可以在命令窗口中使用流程控制语句,但是由于命令窗口中交互式的执行方式,使用得这样的操作极为不方便;而在M文件中,通过对流程控制语句的组合使用,可以实现多种复杂功能。MATLAB语言的流程控制语句主要有for、while、if-else-end及switch-case等4种语句。
6.4.1 for语句
for循环语句是流程控制语句中的基础,使用该循环语句可以以指定的次数重复执行循环体内的语句。
for循环语句的调用形式为:
for 循环控制变量=〈循环次数设定〉
循环体
end
例如:
for i=1:2:12
s=s+i;
end
在上例中,循环次数由数组1:2:12决定,设定循环次数的数组可以是已定义的数组,也可以在for循环语句中定义,此时定义的格式为:
〈初始值〉:〈步长〉:〈终值〉
初始值为循环变量的初始设定值,每执行循环体一次,循环控制变量将增加步长大小,直至循环控制变量的值大于终值时循环结束,这里步长是可以为负的。在for循环语句中,循环体内不能出现对循环控制变量的重新设置,否则将会出错,for循环允许嵌套使用。
6.4.2 while语句
while循环语句与for循环语句不同的是,前者是以条件的满足与否来判断循环是否结束的,而后者则是以执行次数是否达到指定值为判断的。
while循环语句的一般形式为:
while〈循环判断的语句〉
循环体
end
其中循环判断语句为某种形式的逻辑判断表达式,当该表达式的值为真时,就执行循环体内的语句;当表达式的逻辑值为假时,就退出当前的循环体。如果循环判断语句为矩阵时,当且仅当所有的矩阵元素非零时,逻辑表达式的值为真。
在while循环语句中,在语句内必须有可以修改循环控制变量的命令,否则该循环语言将陷入死循环中,除非循环语句中有控制退出循环的命令,如break语句。当程序流程运行至该命令时,则不论循环控制变量是否满足循环判断语句均将退出当前循环,执行循环后的其他语句。
与break语句对应,MATLAB还提供了continue命令用于控制循环,当程序流运行至该命令时会忽略其后的循环体操作转而执行下一层次的循环。当循环控制语句为一空矩阵时,将不执行循环体的操作而直接执行其后的其他命令语句,即空矩阵被认为是假。
6.4.3 if-else-end语句
条件判断语句也是程序设计语言中流程控制语句之一。使用该语句,可以选择执行指定的命令,MATLAB语言中的条件判断语句是if-else-end语句。
if-else-end语句的一般形式为:
if〈逻辑判断语句〉
逻辑值为“真”时执行的语句
else
逻辑值为“假”时执行的语句
end
当逻辑判断表达式为“真”时,将执行if与else语句间的命令,否则将执行else与end语句间的命令。
例如:
if a=1
a=a+1
else
a=a+2
end
在MATLAB语言的if-else-end语句中的eles子句是可选项,即语句中可以不包括else子句的条件判断。在程序设计中,也经常碰到需要进行多重逻辑选择的问题,这时可以采用if-else-end语句的嵌套形式:
if〈逻辑判断语句1〉
逻辑值1为“真”时的执行语句
elseif〈逻辑判断语句2〉
逻辑值2“真”时的执行语句
elseif〈逻辑判断语句3〉
……
else
当以上所有的逻辑值均为假时的执行语句
end
在以上的各层次的逻辑判断中,若其中任意一层逻辑判断为真,则将执行对应的执行语句,并跳出该条件判断语句,其后的逻辑判断语句均不进行检查。
6.4.4 switch-case语句
if-else-end语句所对应的是多重判断选择,而有时也会遇到多分支判断选择的问题。MATLAB语言为解决多分支判断选择提供了switch-case语句。
switch-case语句的一般表达形式为:
switch〈选择判断量〉
Case 选择判断值1
选择判断语句1
case 选择判断值2
选择判断语句2
……
otherwise
判断执行语句
end
与其他的程序设计语言的switch-case语句不同的是,在MATLAB语言中,当其中一个case语句后的条件为真时,switch-case语句不对其后的case语句进行判断,也就是说在MATLAB语言中,即使有多条case判断语句为真,也只执行所遇到的第一条为真的语句。这样就不必像C语言那样,在每条case语句后加上break语句以防止继续执行后面为真的case条件语句。