4.5 脚本和函数(1)
4.5.1 脚本
对于比较简单的运算过程,从命令行窗口中直接输入指令并运行计算是非常方便的。随着指令行的增加,或是运算逻辑复杂度的增加,以及重复计算要求的提出,再直接从命令行窗口中进行运算就十分不明智了。
在这种情况下,使用脚本文件最为适宜。“脚本”本身反映这样一个事实:MATLAB是按照文件中所输入的指令执行的,这种文件的构成比较简单,其主要特点如下:
● 文件只是一串按照用户意愿排列而成的MATLAB指令集合。
● 脚本文件运行后,其运算过程中所产生的所有变量都自动保留在MATLAB工作区(Base工作区)中,除非用户关闭MATLAB运行界面,或是使用clear指令对工作区中的变量加以清理,否则这些变量将一直保存在基本的工作区中。基本空间随着MATLAB的启动而产生,只有关闭MATLAB界面时,该基本空间才会被删除。
● 当调用一个脚本时,MATLAB会简单地执行文件中找到的命令。脚本可以运行工作区中存在的数据,或者创建新数据来运行。
● 虽然脚本不能返回输出变量,但是所有创建的变量都将保留在工作区中,供后面的计算使用。另外,脚本能提供图形输出,就像使用图形输出函数plot()一样。
例4-15:脚本文件的简单运用示例。
创建M文件并命名为ex4_15.m,利用M文件编辑器在M文件中输入:
array = zeros(1,32); for n = 3 : 32 array(n) = rank(magic(n)); end array bar(array) % 用柱状图输出结果
运行M文件,结果如图4-4所示。
图4-4 脚本文件运行结果
4.5.2 函数
如果M文件的第一个可执行语句以function开始,该文件就是函数文件,每一个函数文件都定义一个函数。事实上,MATLAB 提供的函数命令大部分都是由函数文件定义的,这足以说明函数文件的重要性。
从使用的角度来看,函数是一个“黑箱”,把一些数据送进去,经加工处理,把结果送出来。从形式上看,函数文件区别于脚本文件之处在于脚本文件的变量为命令工作空间变量,在文件执行完成后保留在命令工作空间中;而函数文件内定义的变量为局部变量,只在函数文件内部起作用,当函数文件执行完后,这些局部变量将被清除。
例4-16:编写函数average()用于计算向量元素的平均值。创建M文件并命名为average.m(文件名与函数名相同),利用M文件编辑器在M文件中输入:
function y = average(x) [a , b] = size(x); if ~((a == 1) | (b == 1)) | ((a == 1) & (b == 1)) error('必须输入向量。') end y = sum(x) / length(x);
保存M文件,函数average()接收一个输入参数并返回一个输出参数,该函数的用法与其他MATLAB函数一样。在MATLAB命令行窗口中运行以下语句,便可求得1~9的平均值。
>> x = 1 : 9 x = 1 2 3 4 5 6 7 8 9 >> average(x) ans = 5
通常函数文件由以下几个基本部分组成。
(1)函数定义行:函数定义行由关键字function引导,指明这是一个函数文件,并定义函数名、输入参数和输出参数。函数定义行必须为文件的第一个可执行语句,函数名与文件名相同,可以是MATLAB中任何合法的字符。
函数文件可以带有多个输入参数和输出参数,如:
function [x , y , z] = sphere(theta , phi , rho)
也可以没有输出参数,如:
function printresults(x)
(2)H1行:H1行就是帮助文本的第一行,是函数定义行下的第一个注释行,是供lookfor查询时使用的。一般来说,为了充分利用MATLAB的搜索功能,在编制M文件时,应在H1行中尽可能多地包含该函数的特征信息。由于在搜索路径上包含average的函数很多,因此用lookfor average语句可能会查询到多个有关的命令。如:
>> lookfor average_2
(3)帮助文本:在函数定义行后面,连续的注释行不仅可以起到解释与提示作用,更重要的是为用户自己的函数文件建立在线查询信息,以供help命令在线查询时使用。如:
>> help average_2
函数average_2(x)用以计算向量元素的平均值。
输入参数x为输入向量,输出参数y为计算的平均值。非向量输入将导致错误。
(4)函数体:函数体包含了全部的用于完成计算及给输出参数赋值等工作的语句,这些语句可以是调用函数、流程控制、交互式输入/输出、计算、赋值、注释和空行。
(5)注释:以%起始到行尾结束的部分为注释部分,MATLAB的注释可以放置在程序的任何位置,可以单独占一行,也可以在一个语句之后。如:
% 非向量输入将导致错误 [m , n] = size(x); % 判断输入量的大小
4.5.3 M文件的一般结构
从结构上来看,脚本文件只比函数文件少了一个“函数声明行”,除此之外,二者的语法及构架等均相同。于是将典型规范的M文件函数的结构总结如下。
● 函数声明行:位于函数文件的首行,以MATLAB关键字function开头,定义函数名及函数的输入/输出变量。脚本文件无须函数声明行。
● H1行:紧随函数声明行之后的以“%”开头的第一注释行。H1行包括大写的函数文件名和运用关键词简要描述的函数功能。该行将提供lookfor命令作为关键词时查询使用。
● 在线帮助文本区:H1行及其后的连续以“%”开头的注释行,通常包括函数输入/输出变量的含义调用说明。
● 编写和修改记录:与在线帮助文本区应以一个“空”行相隔;该行以“%”开头,记录了编写及修改M文件的所有作者、日期及版本号,以方便后来的使用者查询、修改和使用。
● 函数主体:规范化的写法应与编写和修改记录以一个“空”行相隔;这部分内容包括所有实现该M函数文件功能的MATLAB指令、接收输入变量、进行程序流控制。
说明:
(1)函数定义名应和文件保存名一致。当两者不一致时,MATLAB将忽视文件首行的函数定义名,而以文件保存名为准。
(2)MATLAB中的函数文件名必须以字母开头,可以是字母、下画线及数字的任意组合,但不可以超过31个字符。
(3)建议读者在编写H1行注释时,尽量采用英文表述方式,这是为了之后在使用过程中关键词检索方便。
例4-17:完整的M文件示范。
创建M文件并命名为spirallength.m,利用M文件编辑器在M文件中输入:
function spir_len = spirallength(d, n, lcolor) % CIRCLE plot a circle of radius as r in the provided color and calculate its area % d : 螺旋的旋距; n : 螺旋的圈数; lcolor:画图线的颜色; spir_len:螺旋的周长 % spirallength(d,n):利用蓝色以预设参数的螺旋线 % spirallength(d,n,lcolor):利用1color颜色以预设参数的螺旋线 % spir_len = spirallength(d,n):计算螺旋线的周长,并用蓝色填充螺旋线 % spir_len = spirallength(d,n,lcolor):计算螺旋线的周长,并用lcolor颜色填充螺旋线 if nargin > 3 error('输入变量过多!'); elseif nargin == 2 lcolor = 'b'; end j= sqrt(-1); phi = 0: pi / 1000 : n * 2 * pi; amp =0: d/2000 : n * d; spir = amp .* exp(j * phi); if nargout == 1 spir_len =sum(abs(diff (spir))); fill(real(spir), img(spir), lcolor) elseif nargout == 0 plot (spir, lcolor) else error('输出变量过多!'); end axis('square')
在命令行窗口中输入:
spirallength(0.25,4,'k:')
输出结果如图4-5所示。
图4-5 spirallength.m运行结果图
M文件函数参数指令集如表4-5所示。
表4-5 M文件函数参数指令集
4.5.4 匿名函数、子函数、私有函数与私有目录
1.匿名函数
匿名函数没有函数名,也不是函数M文件,只包含一个表达式和输入/输出参数。用户可以通过在命令行窗口中输入代码来创建匿名函数。匿名函数的创建方法为:
f = @(input1,input2,…) expression
f为创建的函数句柄。函数句柄是一种间接访问函数的途径,可以使用户调用函数过程变得简单,减少了程序设计中的繁杂,而且可以在执行函数调用过程中保存相关信息。
例4-18:当给定实数x、y的具体数值后要求计算表达式xy+3xy的结果,可以通过创建匿名函数的方式来解决。
在命令行窗口中输入:
>> Fxy = @(x,y) x.^y + 3 * x * y
MATLAB将创建一个名为Fxy的函数句柄:
Fxy = 包含以下值的 function_handle: @(x,y)x.^y+3*x*y
调用whos函数,可以查看变量Fxy的信息:
>> whos Fxy Name Size Bytes Class Attributes Fxy 1x1 32 function_handle
分别求当x=2、y=5及x=1、y=9时表达式的值:
>> Fxy(2,5) ans = 62 >> Fxy(1,9) ans = 28
2.子函数
在MATLAB中,多个函数的代码可以同时写到一个M函数文件中。其中,出现的第一个函数称为主函数(Primary Function),该文件中的其他函数称为子函数(Sub Function)。保存时所用的函数文件名应当与主函数定义名相同,外部程序只能对主函数进行调用。
子函数的书写规范有如下几条:
(1)每个子函数的第一行是其函数声明行。
(2)在M函数文件中,主函数的位置不能改变,但是多个子函数的排列顺序可以任意改变。
(3)子函数只能被处于同一M文件中的主函数或其他子函数调用。
(4)在M函数文件中,在任何指令通过“名称”对函数进行调用时,子函数的优先级仅次于MATLAB内置函数。
(5)同一M文件的主函数、子函数的工作区都是彼此独立的。各个函数间的信息传递可以通过输入/输出变量、全局变量或跨空间指令来实现。
(6)help、lookfor等帮助指令都不能显示一个M文件中的子函数的任何相关信息。
例4-19:M文件中的子函数示例。
创建M文件并命名为ex4_19.m,利用M文件编辑器在M文件中输入:
function F = ex4_19(n) A = 1;w = 2;phi = pi / 2; signal = createsig(A,w,phi); F = signal .^ n; % ----------创建子函数---------- function signal = createsig(A,w,phi); x = 0 : pi / 100 : pi * 2; signal = A * sin(w * x + phi);
3.私有函数与私有目录
所谓私有函数,是指位于私有目录private下的M函数文件,它的主要性质有如下几条。
(1)私有函数的构造与普通M函数完全相同。
(2)关于私有函数的调用:私有函数只能被private直接父目录下的M文件所调用,而不能被其他目录下的任何M文件或MATLAB指令窗中的命令所调用。
(3)在M文件中,任何指令通过“名称”对函数进行调用时,私有函数的优先级仅次于MATLAB内置函数和子函数。
(4)help、lookfor等帮助指令都不能显示一个私有函数文件的任何相关信息。
4.5.5 重载函数
“重载”是计算机编程中非常重要的概念,经常用于处理功能类似但变量属性不同的函数。例如实现两个相同的计算功能,输入的变量数量相同,不同的是其中一个输入变量类型为双精度浮点数类型,另一个输入变量类型为整型。这时,用户就可以编写两个同名函数,分别处理这两种不同的情况。当用户实际调用函数时,MATLAB就会根据实际传递的变量类型选择执行哪一个函数。
MATLAB的内置函数中就有许多重载函数,放置在不同的文件路径下,文件夹通常命名为“@+代表MATLAB数据类型的字符”。例如@int16路径下的重载函数的输入变量应为16位整型变量,而@double路径下的重载函数的输入变量应为双精度浮点数类型。
4.5.6 eval和feval函数
1.eval函数
eval函数可以与文本变量一起使用,实现有力的文本宏工具。其调用格式如下。
● eval(s):该指令的功能为使用MATLAB的注释器求表达式的值或执行包含文本字符串s的语句。
例4-20:eval函数的简单运用示例。
本例展示了利用eval函数分别计算4种不同类型的语句字符串,分别是:
● “表达式”字符串。
● “指令语句”字符串。
● “备选指令语句”字符串。
● “组合”字符串。
(1)创建M文件并命名为eval_exp1.m,利用M文件编辑器在M文件中输入:
Array = 1 : 5; String = '[Array * 2;Array / 2;2 .^ Array]'; Output = eval(String)
输出结果:
Output = 2.0000 4.0000 6.0000 8.0000 10.0000 0.5000 1.0000 1.5000 2.0000 2.5000 2.0000 4.0000 8.0000 16.0000 32.0000
(2)创建M文件并命名为eval_exp2.m,利用M文件编辑器在M文件中输入:
theta = pi; eval('Output = exp(sin(theta))'); who
运行M文件,得到结果:
Output = 1.0000 您的变量为: Array Output String theta
(3)创建M文件并命名为eval_exp3.m,利用M文件编辑器在M文件中输入:
Matrix = magic(3) Array = eval('Matrix(5,:)' , 'Matrix(3,:)') errmessage = 'lasterr'
运行M文件,得到结果:
Matrix = 8 1 6 3 5 7 4 9 2 Array = 4 9 2 errmessage = 'lasterr'
(4)创建M文件并命名为eval_exp4.m,利用M文件编辑器在M文件中输入:
Expression = {'zeros','ones','rand','magic'}; Num = 2; Output = []; for i = 1 : length(Expression) Output = [Output eval([Expression{i},'(',num2str(Num),')'])]; end Output
运行M文件,得到如下结果:
Output = 0 0 1.0000 1.0000 0.9649 0.9706 1.0000 3.0000 0 0 1.0000 1.0000 0.1576 0.9572 4.0000 2.0000
2.feval函数
feval函数的具体句法形式如下:
[y1 , y2 , …] = feval(‘FN’ , arg1 , arg2 , …)
该指令的功能为用变量arg1,arg2,…来执行FN函数指定的计算。
说明:
(1)在此FN为函数名。
(2)在eval函数与feval函数通用的情况下(使用这两个函数均可以解决问题),feval函数的运行效率比eval函数高。
(3)feval函数主要用来构造“泛函”型M函数文件。
例4-21:feval函数的简单运用示例。
(1)示例说明:feval和eval函数运行区别之一是feval函数的FN不可以是表达式。
创建M文件并命名为feval_exp1.m,利用M文件编辑器在M文件中输入:
Array = 1 : 5; String = '[Array * 2;Array / 2;2 .^ Array]'; Outpute = eval(String) % 使用eval函数运行表达式 Outputf = feval(String) % 使用feval函数运行表达式
运行M文件,得到结果:
Outpute = 2.0000 4.0000 6.0000 8.0000 10.0000 0.5000 1.0000 1.5000 2.0000 2.5000 2.0000 4.0000 8.0000 16.0000 32.0000 错误使用 feval 函数名称 '[Array * 2;Array / 2;2 .^ Array]' 无效。 出错 feval_exp1 (line 4) Outputf = feval(String) % 使用feval函数运行表达式
(2)示例说明:feval函数中的FN只接收函数名,不能接收表达式。
创建M文件并命名为feval_exp2.m,利用M文件编辑器在M文件中输入:
j = sqrt(-1); Z = exp(j * (-pi : pi / 100 : pi)); eval('plot(Z)'); set(gcf , 'units' , 'normalized' , 'position' , [0.2,0.3,0.2,0.2]) title('Result by eval'); axis('square') figure set(gcf , 'units' , 'normalized' , 'position' , [0.2,0.3,0.2,0.2]) feval('plot' , Z); title('Result by feval'); axis('square')
运行M文件,结果如图4-6所示。
图4-6 feval_exp2.m文件的运行结果