许多时候希望将特定的算法写成函数的形式,以提高程序的可重用性和程序设计的效率。
函数文件定义了输出参数和输入参数的对应关系,以方便外部调用。事实上
MATLAB提供的标准函数都是由函数文件定义的。
函数文件由function语句引导,其基本结构如下:
function 输出形参表 = 函数名(输入形参表)
注释说明部分
函数体语句
其中,以 function开头的一行为引导行,表示定义一个函数。函数名的命名规则与变量名相同。在函数定义时,输入输出参数没有分配存储空间,所以称为形式参数,简称形参。当有多个形参时,形参之间用逗号分隔:组成形参表。当输出形参多于一个时,则应该用方括号括起来,构成一个输出矩阵。
说明:
(1)关于函数文件名。函数文件名通常由函数名再加上扩展名.m组成,不过函数文件名与函数名也可以不相同。当两者不同时,MATLAB将忽略函数名,调用时使用函数文件名。为理解和记忆的方便,一般建议函数文件名和函数名统一。
(2)关于注释说明部分。注释说明包括如下3部分内容。
- 紧随函数文件引导行之后以%开头的第一注释行。这一行一般包括大写的函数文件名和函数功能简要描述,供 lookfor关键词查询和help在线帮助用。
- 第一注释行及之后连续的注释行。通常包括函数输入输出参数的含义及调用格式说明等信息,构成全部在线帮助文本。
- 与在线帮助文本相隔一空行的注释行。包括函数文件编写和修改的信息,如作者、修改日期、版本等内容,用于软件档案管理。
(3)关于return 语句。如果在函数文件中插入了return语句,则执行到该语句就结束函数的执行,程序流程转至调用该函数的位置。通常,在函数文件中也可不使用returmn语句,这时在被调用函数执行完成后自动返回。
示例——编写函数文件,求半径为r的圆的面积和周长。
function [s , p] = fcircle(r)
% r 圆半径
% s 圆面积
% p 圆周长
s = pi * r * r;
p = 2 * pi * r;
调用函数
>> [s,p] = fcircle(10)
s =
314.1593
p =
62.8319
采用help命令或lookfor 命令可以显示出注释说明部分的内容,其功能和一般MATLAB函数的帮助信息是一致的。
>> help fcircle
r 圆半径
s 圆面积
p 圆周长
也可以用 lookfor命令在 MATLAB的搜索路径中寻找并列出所有第一注释行包括指定关键词的文件。
函数调用的格式
函数文件建立好后,就可以调用该函数了,调用格式如下:
[输出实参表] = 函数名(输入实参表)
在调用函数时,函数输入输出参数称为实际参数,简称实参。要注意的是,函数调用时各实参出现的顺序、个数,应与函数定义时形参的顺序、个数一致,否则会出错。函数调用时,先将实参传递给相应的形参,从而实现参数传递,然后再执行函数的功能。
示例——利用函数文件,实现直角坐标(x , y)和极坐标(p , θ)之间的转换。转换公式:
编写函数文件tran.m,再在main1.m脚本文件中调用函数
%函数
function [rho , theta] = tran(x , y)
rho = sqrt(x * x + y * y);
theta = atan(y / x);
%脚本
x = input('Please input x = ');
y = input('Please input y = ');
[rho , the] = tran(x , y);
disp(['rho = ',num2str(rho)])
disp(['the = ',num2str(the)])
运行脚本
>> main1
Please input x = 45
Please input y = 45
rho = 63.6396
the = 0.7854
实际上,MATLAB提供了直角坐标与极坐标之间转换的函数,分别如下:
(1)[th,r]=cart2pol(x,y):将直角坐标转换为极坐标。
(2) [x,y]=pol2cart(th,r):将极坐标转换为直角坐标。
>>
>> [rho , the] = cart2pol(45 ,45)
rho =
0.7854
the =
63.6396
函数的递归调用
在 MATLAB 中,函数可以嵌套调用,即一个函数可以调用其他函数,甚至调用它自身。一个函数调用它自身称为函数的递归调用。
示例——利用函数的递归调用,求n的阶乘。
函数文件factor.m
function f = factor(n)
if n <= 1
f = 1;
else
f = factor(n - 1) * n; % 递归调用
end
脚本文件
% 求 s = 1! + 2! + 3! + 4! + 5!
s = 0;
n = input('Please input n = ');
for i = 1 : n
s = s + factor(i);
end
disp(['1到',num2str(n),'的阶乘和为:',num2str(s)])
运行脚本文件
K>> main2
Please input n = 5
1到5的阶乘和为:153
示例——任意排列问题。MATLAB提供的函数randperm(n)可以产生一个从整数1到整数n的任意排列。编写一个函数来实现 randperm(n)函数的功能,即给出一个由任意数组成的行向量,然后产生这个行向量元素的任意排列。
rndprm1.m
function Y = rndprm1(X)
[m , n] = size(X);
if m > 1
error('RNDPRM1 accepts as inputs only vectors');
end
Y = [];
l = n;
for i = 1 : n
k = 1 + fix(1 * rand);
x = X(k);
Y = [Y , x];
X(k) = [];
l = l - 1;
end
rndprm2.m
function Y = rndprm2(X)
[m , n] = size(X);
l = n;
if m > 1
error('RNDPRM2 accepts as inputs only vectors')
end
if n <= 1
Y = X;
else
k = 1 + fix(1 * rand);
x = X(k);
X(k) = [];
Z = rndprm2(X);
Y = [Z , x];
l = l - 1;
end
调用函数
K>> rndprm1([34,6,3,54,2,5,454])
ans =
34 6 3 54 2 5 454
K>> rndprm2([34,6,3,54,2,5,454])
ans =
454 5 2 54 3 6 34
MATLAB在函数调用上有一个特点,就是函数所传递参数数目的可调性。凭借这一点,一个函数可完成多种功能。
在调用函数时,MATLAB用两个预定义变量nargin和nargout分别记录调用该函数时的输入实参和输出实参的个数。只要在函数文件中包含这两个变量,就可以准确地知道该函数文件被调用时的输入/输出参数个数,从而决定函数如何进行处理。
nargin用法示例
函数文件charray.m
function fout = charray(a , b ,c)
if nargin == 1
fout = a;
elseif nargin == 2
fout = a + b;
elseif nargin == 3
fout = (a * b * c) / 2;
end
脚本文件mydemo.m
a = 1 : 3;
b = a';
x = charray(a);
y = charray(a,b');
z = charray(a , b ,3);
disp(['x = ',num2str(x)])
disp(['y = ',num2str(y)])
disp(['z = ',num2str(z)])
运行脚本
>> mydemo
x = 1 2 3
y = 2 4 6
z = 21
>>
在脚本文件 mydemo.m中,3次调用函数文件charray.m,因输入参数的个数分别是1、2,3,从而执行不同的操作,返回不同的函数值。
在MATLAB中,函数文件中的变量是局部的,与其他函数文件及 MATLAB工作空间相互隔离,即在一个函数文件中定义的变量不能被另一个函数文件引用。如果在若干函数中,都把某一变量定义为全局变量,那么这些函数将共用这个变量。全局变量的作用域是整个MATLAB工作空间,即全程有效,所有的函数都可以对它进行存取和修改,因此,定义全局变量是函数间传递信息的一种手段。
全局变量用global命令定义,格式如下:
global变量名
示例
函数文件wadd.m,该函数将输入的参数加权相加
function f = wadd(x , y)
global ALPHA BETA
f = ALPHA * x + BETA * y;
命令行调用
>> global ALPHA BETA
>> ALPHA = 1;
>> BETA = 2;
>> s = wadd(1,2)
s =
5
由于在函数wadd和基本工作空间中都把 ALPHA 和 BETA两个变量定义为全局变量,所以只要在命令行窗口中改变 ALPHA 和 BETA 的值,就可改变函数中x、y的权值,而无须修改wadd.m文件。
在实际程序设计时,可在所有需要调用全局变量的函数中定义全局变量,这样就可实现数据共享。在函数文件中,全局变量的定义语句应放在变量使用之前,为了便于了解所有的全局变量,一般把全局变量的定义语句放在文件的前部。为了在工作空间中使用全局变量,也要定义全局变量。
值得指出的是,在程序设计中,全局变量固然可以带来某些方便,但却破坏了函数对变量的封装,降低了程序的可读性,因此,在结构化程序设计中,全局变量是不受欢迎的,尤其当程序较大,子程序较多时,全局变量将给程序调试和维护带来不便,故不提倡使用全局变量。如果一定要用全局变量,最好给它起一个能反映变量含义的名字,以免和其他变量混淆。