M文件编程
标签:
杂谈 |
1.4 M文件编程
MATLAB 作为一种应用广泛的科学计算软件,不仅可以通过直接交互的指令和操作方式进行强大的数值计算、绘图等,还可以像 C、C++ 等高级程序语言一样,根据自己的语法规则来进行程序设计。编写的程序文件以 .m 作为扩展名,称之为M文件。通过编写M文件,用户可以像编写批处理命令一样,将多个MATLAB命令集中在一个文件中,既能方便地进行调用,又便于修改;还可以根据用户自身的情况,编写用于解决特定问题的M文件,这样就实现了结构化程序设计,并降低代码重用率。实际上,MATLAB 自带的许多函数就是M函数文件。MATLAB 提供的编辑器可以使用户方便地进行M文件的编写。
1.4.1 M文件通用格式及分类
简单地说,M文件就是用户把要实现的命令写在一个以 .m作为文件扩展名的文件中,然后由MATLAB系统进行解释,运行出结果,实际上M文件是一个命令集,因此,MATLAB具有强大的可开发性与可扩展性。MATLAB中的许多函数本身都是由M文件扩展而成的,而用户也可以利用M文件来生成和扩充自己的函数库。
下面给出一个M文件的代码,简单介绍M文件的基本组成部分。
function f =
fact(n)
% FACT
Factorial.
% FACT (N) returns the factorial of N,
H!
% usually denoted by N!
% Put simply ,FACT(N) is PROD(1,N).
f =
prod(1,n);
M 文件由以下四部分组成。
●
●
●
●
M 文件有两种类型:M 脚本文件(M-Script)和M 函数文件(M-Function)。它们的扩展名相同,都是“.m”。M 脚本文件中包含一组有MATLAB语言所支持的语句,类似于 DOS 下的批处理文件。执行方式也非常简单,用户只需要在MATLAB的提示符下输入该M文件的文件名,MATLAB 就会自动执行该M文件的各条语句,并将结果直接返回到MATLAB的工作空间。在运行过程中产生的所有变量都是全局变量。
脚本文件与函数文件的区别在于脚本文件没有函数定义行,且一般没有注释信息,当然也可以添加注释信息,即以 % 开头的内容。在使用方法、变量生存周期中也存在差异,如表1-8所示。
表1-8
|
比 较 项 目 |
M脚本文件 |
M函数文件 |
|
输入/输出参数 |
不接收输入参数,也不返回输出参数 |
接收输入参数,也可以返回参数 |
|
变量情况 |
处理工作空间中的变量 |
默认内部变量为局部变量,工作区间不能访问 |
|
适用情况 |
常用于需多次执行的一系列命令 |
常用于需多次执行且需要输入/输出参数的命令集合,常作为MATLAB 应用程序的扩展编程使用 |
脚本文件和函数文件适用于不同的情况,有时需要把脚本文件转换为函数文件。转换方法实际上非常简单,只需要在脚本文件前面添加必要的函数定义行和注释信息(可以省略)即可。
1.4.2 M文件编辑器
M文件的编辑和调试是在 MATLAB的M文件编辑器(M-file Editor)中进行的,若需要对M文件进行调试,还需要 MATLAB运行环境的支持,该编辑器既为基本文本文件的编辑提供了图形用户界面,又可以用于其他文本文件的编辑,同时还可以进行M文件的调试工作。可以在下面的情况下启动M文件编辑器。
●
●
●
打开后的界面如图1-1所示。
图1-1
http://s13/middle/6377a3104824b061d2a7c&690
1.4.3 流程控制
程序设计一般可分为三种结构:顺序结构、循环结构、分支结构。与大多数程序设计语言一样,MATLAB 也提供了丰富的程序流程控制语句用来完成具体的程序设计。下面分别对这三种结构进行介绍。
1.顺序结构
程序设计中最简单的语法结构,就是顺次执行程序的各条语句。下面给出一个简单的顺序结构M文件的例子,假设其文件名为 array_ex1.m:
A = [2,1,0;-2,5,-1;7,9,6];
B =
rot90(A);
C = A*3;
B
C
在MATLAB 命令窗口中执行该文件,将得到如下结果。
>> array_ex1
B =
|
0 |
-1 |
6 |
|
1 |
5 |
9 |
|
2 |
-2 |
7 |
C =
2.循环结构
MATLAB中的循环语句包括:For循环和While循环。它们各有特点,根据需要进行选择,在某些时候可以相互替换。需要注意的是,MATLAB 解释器对循环结构的执行效率比较低,在M 文件中尽量不采用。
1) For循环
For循环是以固定或已知次数重复执行一组命令。For 循环的一般形式如下:
for 变量=表达式
在for和end之间的执行语句是按矩阵(或数组)中的每一列执行一次,即在每一次循环中,矩阵(或数组)元素一个一个地被赋予变量,然后执行循环体的语句。
% 文件名为 array_ex2
% 控制循环的变量 n 的初始值为1,终止值为10,步长为1
n = 0:1:10;
for i = 1:10
Y(i) = sin(n(i));
end
Y
在MATLAB命令行输入该文件的文件名:
>> array_ex2
Y =
|
|
0.8415 |
0.9093 |
0.1411 |
-0.7568 |
-0.9589 |
|
|
0.6570 |
0.9894 |
0.4121 |
使用for循环语句需注意以下问题:
●
●
2) While循环
While循环则是以不定的次数执行一组命令来求值,其一般形式如下:
while 表达式
执行语句组;
end
只要表达式中的元素为真,就执行while和end语句之间的命令。通常,表达式给出的是一个标量值,但是数组(或矩阵)同样有效。若为数组(或矩阵),则要求所有元素必须都为真。
执行函数文件array_ex3,结果如下:
>> array_ex3
a =
2.4619
若要退出while循环可以使用break语句。
一个while条件表达式为一个空矩阵时,该表达式代表了一个假的条件,如下
s1
end
当矩阵A为空矩阵时,将不会执行表达式s1。
3.分支结构
在MATLAB中分支结构包括 if-else-end 语句与switch-case-otherwise语句,根据一定条件来执行不同的语句。
1) if-else-end 选择语句
最简单的if-else-end语句如下:
if
end
如果表达式中的值为真,执行if和end之间的语句,否则就跳过该分支结构。如果有两个选择,则if-else-end语句为如下格式:
if
else
end
如果有两个选择,则if-else-end语句就是如下格式:
if
else
else
…
else
end
例如编写如下的M文件:
function s = ex3(n)
if n < 60
else if n < 70
else if n < 80
else if n < 90
else
end
上例中,逻辑表达式是一个标量,若逻辑表达式是一个向量,那么若判断表达式成立,则必须向量的每个元素都不为0,例如,X是一个矩阵,则
if
end
就等同于表达式
if
end
2) switch–case-otherwise分支语句
MATLAB 中的分支选择语句switch–case-otherwise,是特别为方便熟悉C语言等高级语言的用户编写M文件而专门添加的。switch–case-otherwise 语句的通用格式如下。
switch 表达式
case 数据1
case 数据2
…
otherwise
end
其中,switch表达式给出了开关条件,当某个case后的数据与之匹配时,就执行其后面的语句;否则就执行otherwise后面的语句。在执行过程中只有一个case命令被执行,当执行完命令后,程序就跳出分支结构,执行end下面的语句。例如:
Function s = switch(n)
switch n
case 1
case 2
case 3
otherwise
end
switch 开关语句能够在同一个 case 表达式中处理多个条件值,只须将多个条件值以一个单元阵列的形式括起来,如下例:
switch var
case
case
case
otherwise
end
4. 控制语句
在许多程序设计语言中,经常会碰到提前终止循环,跳出子程序、显示出错信息等情况,这时就要用到一些用于控制程序流的语句,在MATLAB中提供的控制语句有continue、break、try-catch、return语句。
1) continue 命令和break语句
continue命令经常与for或while语句一起使用,其作用是结束本次循环,即跳过循环体后面尚未执行的语句,接着进行下一次是否执行循环的判断。
break命令也经常与for或while语句一起使用,其作用是终止本次循环,即跳出最内的循环。使用break命令可以在循环结构中,根据条件推出循环,在许多情况下是必需的。
2) try…catch语句
通过以下这个例子具体了解try…catch语句的使用。
n = 4; A =
magic(3);
try
catch
end
程序运行结果如下:
>> ex4
A_end =
try…catch命令是对异常进行处理的语句,把可能引起异常的语句放在try模块中,当try控制模块中的语句引起异常时,catch 控制模块就可以捕获到,并针对不同的错误类型,进行不同的处理。
3) return语句
return语句使当前正在运行的函数正常退出,并返回调用它的函数,继续运行。这个语句经常用于函数的末尾,用以正常结束函数的运行。也可以用于函数的其他地方,对于某条件进行判断,如果条件不符合要求,调用return语句终止当前运行,并返回调用它的函数环境。
1.4.4 M脚本文件
脚本文件是M文件中最简单的一种,它既没有输入变量,也没有输出变量,因此它常常用于需要自动执行一系列MATLAB命令的情况下,比如需要重复多次执行的操作,就可以把它作为一个M脚本文件存储起来,在需要执行时,直接执行该脚本文件即可。特别需要注意的是,虽然脚本文件没有返回输出变量,但是在脚本文件中的变量在文件执行完毕后仍然保存在工作区间之中,可以将其用于将来的计算。
下面给出一个简单的M脚本文件的例子:
% An Script M-file to find a new value using linear interpolation
>>
x1=2.0;
x2=2.7;
y1=20;
y2=25;
x=2.4;
newvalue=y1+((y2-y1)/(x2-x1))*(x-x1)
这个脚本文件的功能是计算在通过(x1,y1)和(x2, y2)的直线上,任意一点 x 处所对应的 y 的值。假设给这个文件命名为 line_val.m,在MATLAB命令行输入该文件的文件名(不包括后缀名),就可以运行该程序了,得到如下运行结果:
newvalue =
虽然脚本文件没有输入/输出变量,但在程序执行完毕后,文件中的变量仍然保存在工作区间中,可以通过whos命令来查看一下:
>> whos
1.4.5 M函数文件
M函数文件与M脚本文件是不同的,它可以接收输入变量,也可以返回输出变量,接下来看一个简单的M函数文件的例子:
Function y = average(x)
% AVERAGE Mean of vector elements.
% AVERAGE(X), where X is a vector, is the mean of vector elements.
% Nonvector input results in an error .
[m,n]=size(x);
if (~((m==1)|(n==1))|(m==1 & n==1))
end
y = sum (x)/length(x);
该函数以文件名ave.m保存,用于计算一个向量中所有元素的平均值,有一个输入变量和一个输出变量。执行该函数可以通过执行以下操作:
>> a=1:2:100;
>> ave(a)
可以得到计算向量a的平均值的结果:
ans =
1. M函数文件的组成部分
一个M函数文件由以下几部分组成:
●
●
●
●
●
(1) 函数定义行:定义了函数名称、输入和输出变量信息。函数名以字母开头,由数字、字母或下划线组成。为了便于代码在不同平台间转移,一般使用小写字母。调用该函数文件只需要在命令行输入函数名即可。若函数有多个输出变量,则用中括号将他们括起来,若有多个输入变量则使用小括号将他们括起来。
Function [x, y, z]=sphere(theta, phi, rho)
如果没有变量输出,则将输出部分省去或者使用空的中括号。
(2) 函数信息行:又称为H1行,是紧跟在函数定义行后的一个注释行,以百分号开头。
% AVERAGE Mean of vector elements.
H1行用于指示当前函数的功能,代表了帮助文件的简要信息。当用户使用help命令来查看函数时,H1行和帮助文本就会显示出来。
(3) 函数体:M文件的主体部分,由一系列MATLAB命令组成,通过它实现函数的功能。
(4) 注释行:在函数体中起解释作用,也以百分号开头。
2.M脚本文件转为M函数文件
M脚本文件和M函数文件可以转换,而且通常更需要M函数文件,因此需要将M脚本文件转换为M函数文件。转换方法是在M脚本文件前面添加必要的函数定义行和注释信息。但是将M脚本文件转换为M函数文件时注意参数的设置。
下面我们就对1.4.4中的脚本文件进行转换。
如果不需要计算其他x点处的y值,可以简单地改为(设函数文件名为 l_val.m ):
function newvalue=l_val()
x1=2.0;
x2=2.7;
y1=20;
y2=25;
x=2.4;
newvalue=y1+((y2-y1)/(x2-x1))*(x-x1);
函数l_val仅能得到x = 2.4处的y值,只增加了函数定义行,仍然没有输入参数,只是形式上转换成了函数文件,但在功能上没有变化。可以通过将x改为输入参数来增加灵活性。假设新的函数文件为l_valx.m,则函数文件可以写成:
function newvalue=l_valx(x)
x1=2.0;
x2=2.7;
y1=20;
y2=25;
newvalue=y1+((y2-y1)/(x2-x1))*(x-x1);
要计算点x= 2.4处的y值,可以在MATLAB命令行输入l_valx(2.4),结果是一样的。
>> l_valx(2.4)
ans =
函数l_valx实现的是在已知的两点(x1,y1)和(x2,y2)的直线上,根据给出的任意一点的 x值,求出所对应的y值。这里进一步增加函数的通用性,即求过任意两点的直线上,任意给出的x值所对应的y值。假设新函数名为l_valxy.m,则函数l_valxy 可改写为:
function newvalue=l_valxy(x1,x2,y1,y2,x)
% parameters:
% input:
%
%
%
% output:
%
newvalue=y1+((y2-y1)/(x2-x1))*(x-x1);
函数l_valxy 增加了参数说明,是因为当参数较多时,进行参数说明可以方便用户使用函数文件。
通过以上脚本文件向函数文件的转换可以看出,脚本文件转换为函数文件并不是唯一的,而是根据问题对函数的灵活性要求进行适当的转换,转换时,要特别注意其中的输入和输出参数。
3.子函数
一个M文件可以包含多个函数,其中第一个函数为主函数,其函数名就是调用M文件的文件名,而同一个文件中的其他函数则为子函数,这些子函数只对同一个文件中的主函数和其他子函数有效。
Function
[avg,med]=newstats(u)
%NEWSTATS Find mean and median with internal functions.
n = length(u);
avg = mean(u.n);
med = median(u,n);
function a =
mean(v,n)
% 计算平均值
a = sum(v)/n;
functionM=
median(v,n)
% 计算中位值
w = sort(v);
if rem (n,2) == 1
else
end
当一个M文件中包含多个子函数时,则它们的顺序可以是任意的,但是主函数一定要位于第一行。
1.4.6 编写M文件需要注意的问题
在编写M文件时,应当注重M文件的正确性、规范性和通用性。
(1) 函数名和函数文件名必须相同。例如,函数avg就存储在avg.m文件中。
(2) 变量的名字最好能够反映它们的意义,这一点对于一般语言程序设计都是适用的。
(3) 结构体的命名应该以一个大写字母开头,帮助区别结构体和普通变量。
(4) MATLAB第一次执行一个M文件时,将打开相应的文本文件并将命令编辑成存储器的内部表示,以加速执行以后的调用。如果函数包含了对其他M文件函数的调用,它们也将同样被编译到存储器中。普通的脚本M文件不被编译,即使它们是从函数M文件调用;每次打开脚本M 文件时,都逐行进行注释。
(5) MATLAB采用结构化的程序设计。编写一个大型的程序时,最好将它划分为一些小的模块,通常采用函数的方式,以增强程序的可读性和可测试性。
(6) MATLAB支持函数间的相互调用。M文件可以包含两个以上的函数,其中第一个函数为主函数,其他的为子函数,子函数的顺序可以任意排列。调用子函数只需要通过M函数文件的名字即可,子函数只能被本文件中的主函数和其他子函数调用。
(7) 自己编写的函数最好在编写时就添加注释,包括输入和输出参数、解释用法以及可能需要的改进等信息,以增强函数的可读性和可用性。
(8) 函数可以没有输入和输出参数,也可以具有一个或多个输入和输出参数。
(9) 函数可以按少于规定的输入和输出变量进行调用,但不能多于函数所规定的输入和输出变量数目。如果调用时输入和输出变量数目多于函数M文件中所规定的数目,则会返回一个错误信息。
(10) 函数有自己的专用工作区,它与MATLAB的工作空间是分开的。函数内变量与MATLAB工作空间唯一的联系是函数的输入和输出变量。如果函数的任意一个输入变量值发生变化,则其变化仅在函数内出现,不影响MATLAB工作空间的变量。函数内所创建的变量只驻留在函数的工作空间,而且只在函数执行期间临时存在,以后就消失了。因此,从一个调用到下一个调用,在函数工作空间存储信息是不可能的,然而使用全局变量就可以。
(11) 如果变量说明是全局变量,函数可以与其他函数、MATLAB工作空间和递归调用本身共享变量。为了在函数内或MATLAB工作空间中访问全局变量,在每一个所希望的工作空间,变量必须说明是全局的。但是在实际编程时应尽量避免使用全局变量,因为在函数中的全局变量,在定义该变量函数或工作空间内都可被改变,所以可能会得到与预期不同的结果,或者是毫无意义的结果。如果确实需要用全局变量,建议全局变量要长,可包含所有的大写字母,并有选择地以首次出现的M文件的名字开头,将全局变量之间不必要的相互作用减至最小。
(12) 函数可以递归调用,也就是说,M函数文件可以调用它们本身,也可以调用脚本文件。函数每调用一次脚本文件,脚本文件就被打开和解释一次,这样对函数的执行会略有减慢。

加载中…