第六章 M文件和函数句柄 —— matlab语言称为第四代编程语言,程序简洁、可读性很强而且调试十分容易。
本章内容 §1 matlab控制流 §2 脚本文件和函数文件 §3 函数类别 §4 函数句柄
6.1 MATLAB控制流 (1) 顺序结构 —— 依次顺序执行程序的各条语句 (2) 分支结构——根据一定条件来执行各条语句。 if — else — end条件控制语句有3种形式 单分支 、双分支、多分支 switch — case — otherwise — end结构 仅执行满足条件的分支 (3) 循环结构 —— 被重复执行的一组语句。 循环语句主要有: for — end while — end
6.1 MATLAB控制流 6.1.1 if — else — end条件控制 分支控制有3种形式 多分支 if 表达式1 单分支 语句1 elseif 表达式2 语句2 elseif 表达式3 语句3 · else 语句n end 单分支 if 表达式 执行语句 end 双分支 语句1 else 语句2
在条件表达式中,通常用 关系操作符: >, < ,<=, >=, = =, ~= 逻辑操作符: &,| , ~ 逻辑函数:isequal —— 若是相等则为真 isempty ——若是空矩阵则为真 isstr —— 若是字符串则为真
x (x<-1) y = x3 (-1≤x<1) e-x+1 (x≥1) function y=abc(x) % function calculate of y n=length(x); for k=1:n if x(k)<-1 y(k)=x(k); elseif x(k)>=1 y(k)=exp(1-x(k)); else y(k)=x(k)^3; end 例6.1-1:编写m函数,对于任意一组输入x值,计算函数值 x (x<-1) y = x3 (-1≤x<1) e-x+1 (x≥1) x=[-2,-1.2,-0.4,0.8,1,6] y=abc (x) x = -2.0000 -1.2000 -0.4000 0.8000 1.0000 6.0000 y = -2.0000 -1.2000 -0.0640 0.5120 1.0000 0.0067
switch-case语法结构: switch expr case value1 (commands 1) case value2 …… case vlauek (commands k) otherwise (commands) end
●当遇到switch结构时, matlab将表达式的值依次和各个case指令后面的检测值进行比较 ●当遇到switch结构时, matlab将表达式的值依次和各个case指令后面的检测值进行比较.如果比较结果为假,则取下一个检测值再比较,一旦比较结果为真, matlab将执行相应的一组命令,然后跳出该结构。如果所有的比较结果都为假,即表达式和所有的检测值都不等, matlab将执行otherwise后面的一组命令.可见该结构保证至少有一组命令会得到执行。 ●switch指令后面的表达式为一个标量或者一个字符串。对于标量形式的表达式,比较这样进行:表达式==检测值i.对于字符串,将调用函数strcmp来实现比较。 ●case指令后面的检测值不仅可以是一个标量值或字符串,还可以是一个单元数组。如果一个检测值是一个单元数组,matlab将把表达式的值和该单元数组中的所有元素进行比较,如果单元数组中某个元素和表达式的值相等,就认为此次比较结果为真。 ●当matlab检测到某个检测值和表达式相等时,将执行相应的一组命令,执行完毕,自动跳出swith结构,无需使用break指令。
例6.1-2:根据学生百分制分数进行等级成绩划分. for i=1:10 a{i}=89+i; b{i}=79+i; c{i}=69+i; d{i}=59+i; end %用胞元数组 c=[d,c]; A=cell(2,5); A(1,:)={'Jack','Marry','Peter','Rose','Tom'}; A(2,:)={72,83,56,94,100}; %用胞元数组 for k=1:5 switch A{2,k} case 100, A{3,k}= '满分'; case a, A{3,k}= '优秀'; case b, A{3,k}= '良好'; case c, A{3,k}= '及格'; otherwise, A{3,k}= '不及格'; end A 'Jack' 'Marry' 'Peter' 'Rose' 'Tom' [ 72] [ 83] [ 56] [ 94] [ 100] '及格‘ '良好' '不及格' '优秀‘ '满分'
6.1.3 for循环和while循环 N=5; n=repmat(1:N,N,1); m=n'; format rat for i = 表达式 (表达式为一个向量) 可执行语句 end 例6.1-3:用for循环生成Hilbert方阵 ( ) 循环法: K=5; A=zeros(K,K);%预配置内存空间 for m=1:K for n=1:K A(m,n)=1/(m+n-1); end format rat A 向量法: N=5; n=repmat(1:N,N,1); m=n'; format rat A3=1./(n+m-1)
while — end 循环结构语法为: while 表达式 循环体语句 end 表达式一般是由逻辑运算和关系运算以及一般运算组成的,以判断循环的进行和停止;只要表达式的值非0,继续循环;直到表达式值为0,循环停止。可借助break指令退出循环。
function [S,N]=exm060104(epsilon) k=0;s=0;d=inf;S=0; 例6.1-4:计算 function [S,N]=exm060104(epsilon) k=0;s=0;d=inf;S=0; while d>epsilon k=k+1; s=s+k; %sum(1:k) d=1/s; S=S+d; end N=k; 符号法验证结果: N=141; SN=vpa(symsum(1/symsum(k,1,n),n,1,N)) %N项(141项)和 SN=1.9859154929577464788732394366197 syms k n N SINF=limit(symsum(1/symsum(k,1,n),n,1,N),N,inf) SINF=2 调用: >>[S,N]=exm060104(0.0001) S = 1.9859, N = 141
【例6.1-5】创建n阶魔方矩阵,限定条件是n为能被4整除的偶数。 (1)所谓魔方矩阵(Magic matrix),是指那矩阵由1到n2的正整数按照一定规则排列而成,并且每列、每行、每条对角线元素的和都等于n(n2+1)/2 。就生成规则而言,魔方矩阵可分成三类:一,n为奇数;二,n为不能被4整除的偶数;三,n为能被4整除的偶数。 clear;clc; while 1 n=input('请输入一个能被4整除的正整数! n = '); if mod(n,4)==0 break end G=logical(eye(4,4)+rot90(eye(4,4))); m=n/4;K=repmat(G,m,m); N=n^2;A=reshape(1:N,n,n); A(K)=N-A(K)+1
s0=round(n*(n*n+1)/2); disp([int2str(n),' 阶魔方矩阵的标称和是 ',int2str(s0)]) Ns0=round(2*(n+1));B=A';SC=sum(A); SR=sum(B); Sd=sum(diag(A)); Sdi=sum(diag(B)); LS=[SC,SR,Sd,Sdi]==s0; NS=round(sum(LS)); if NS==Ns0 disp('经验证,A是魔方矩阵。') else disp('经验证,A不是魔方矩阵。') end 12 阶魔方矩阵的标称和是 870 经验证,A是魔方矩阵。
6.1.3 控制程序流的其他常用指令见表6.1-4 input keyboard break continue pause(n) return
控制程序程序流的其他常用指令 1) v=input('msg') v=input('msg', 's' ) 该指令执行时,控制权交给键盘,待输入结束,按Enter键,控制权交换matlab, msg是提示用的字符串,告诉用户输入什么. 第一种格式用于键入数值,字符串等数据,最后v是一个数值(包括数组)或者一个字符串. 第二种格式,不管键入什么,总以字符串形式赋给变量v。
2) keyboard 从M文件中激活键盘,遇到该命令时,将控制权交给键盘,用户可以从键盘输入各种matlab指令,仅当用户输入return指令后,控制权才交还给程序。与input指令的区别是:该指令允许输入任意多个matlab指令, 而input只能输入赋给变量的值。该指令在调试M文件时非常有用。
3) continue a=ones(1,12);count=0;s=0; for k=1:12 if rem(k,3)==0 跳过位于其后的循环中的其他指令,执行循环的下一个迭代 例 更改数组中的值 a=ones(1,12);count=0;s=0; for k=1:12 if rem(k,3)==0 continue else s=s+k; end count = count + 1; break count = 8
4) break 5) pause, pause(n) 6) return 终止while,for循环,也可以在if-end, switch-case, try-catch中导致中断 5) pause, pause(n) 第一种格式使程序暂停执行,等待用户按任意键继续,第二种格式使程序暂停n秒后,再继续执行 6) return 结束return指令所在函数的执行,把控制转至主调函数或指令 窗.否则,只有整个被调函数执行完毕才转出
6.2 脚本文件 和函数文件 m文件的语法类似于c语言,但又有其自身特点。它只是一个简单的ASCII码文本文件,执行程序时逐行解释运行程序。 m文件有两类独立的m文件 — 称脚本文件 可调用m文件 — 称函数文件 6.2.1 脚本文件 脚本文件 — 简单的m文件 脚本文件实际上是一串指令的集合,与在命令窗口逐行执行文件中的所有指令,其结果是一样的。没有输入输出参数。 运行产生的所有变量驻留在基本工作空间。
6.2.2 M函数文件— 需要输入变量,返回输出变量 函数m文件的格式: function 返回变量=函数名(输入变量) %注释说明语句段 程序语句段 特定规则: 函数m文件第一行必须以单词function作为引导词,必须遵循如下形式: function <因变量>=<函数名>(<自变量>) m文件的文件名最好是<函数名> .m。 程序中的变量均为局部变量,不保存在工作空间中。 其变量只在函数运行期间有效。
6.2.3 局部变量和全局变量 局部(local)变量 它存在于函数空间内部的中间变量,产生于该函数的运行过程中,其影响范围也仅限于该函数本身。 全局(global)变量 通过global指令定义变量,Matlab也允许几个不同的函数空间以及基本工作空间共享同一个变量。这种被共享的变量称为全局变量。每个希望共享全局变量的函数或Matlab基本工作空间,必须逐个用 global对具体变量加以专门定义。没有采用global定义的函数或基本工作空间,将无权享用全局变量。
关于全局变量的说明 如果某个函数的运作使全局变量的内容发生了变化,那么其他函数空间以及基本工作空间中的同名变量也就随之变化。 除非与全局变量联系的所有工作空间都被删除,否则全局变量依然存在。 对全局变量的定义必须在该变量被使用之前进行。建议把全局变量的定义放在函数体的首行位置。 虽然Matlab对全局变量的名字没有任何特别的限制,但是为了提高 M文件的可读性,建议选用大写字符命名全局变量。 由于全局变量损害函数的封装性,因此不提倡使用全局变量。
6.2.4 函数文件一般结构: 举例vdp1,ode45 1.函数申明行 :位于函数文件的首行,以关键字 function开头,函数名以及函数的输入输出参量都在这一行定义。函数名和文件名必须相同。 2.H1行:紧随函数申明行之后以 %开头的第一行注释行,包括大写体的函数文件名;运用关键词要描述的函数功能。 3.在线帮助文件文本 :H1行及其之后的连续 %开头的所有注释行构成整个在线帮助文本。它通常包括:函数输入输出参量的含义,调用格式说明。
6.2.4 函数文件一般结构 4.编写和修改记录:与在线帮助文本区相隔一个“空”行,也以%开头。标志编写及修改该文件的作者、日期和版本记录。它用于软件档案管理。 5.函数体:这部分内容由实现该 M函数文件功能的Matlab指令组成。它接收输入参量,进行程序流控制,得到输出参量。
例6.2-1:根据指定边数N、半径、线型色彩,绘出圆周线(正多边形)或填色圆 (正多边形) 。 function [S,L]=exm060201(N,R,str) switch nargin case 0, N=100;R=1;str='-b'; case 1, R=1;str='-b'; case 2, str='-b'; case 3 ; otherwise, error('输入量太多'); end; t=0:2*pi/N:2*pi; x=R*sin(t);y=R*cos(t); if nargout==0,plot(x,y,str) elseif nargout>2,error('输出量太多'); else S=N*R*R*sin(2*pi/N) /2; L=2*N*R*sin(pi/N); fill(x,y,str) end axis equal square,box on,shg %将该函数M文件存于matlab搜索路径上 调用: [S,L]=exm060201(6,2,'-g')
例6.1-2存为M函数文件, 百分成绩转换为五级制成绩。 function grade=score2grade(score); % TRANS 将学生分数 score 转换为五级制成绩grade % 级别,90-100, A; 80-89, B; 70-79,C; 60-69,D; others, E; for i=1:10 a{i}=89+i; b{i}=79+i; c{i}=69+i; d{i}=59+i; end for k=1:length(score) switch score(k) case 100, grade{k}= ‘A'; case a, grade{k}= ' A '; case b, grade{k}= ‘B'; case c, grade{k}= ‘C'; case d, grade{k}= ‘D'; otherwise, grade{k}= ‘E';
6.3 MATLAB 的函数类别 (1)主函数:由第一个function引出的函数 各函数间通过输入输出宗量或全局变量传递 (3)匿名函数:不以文件形式驻留在文件夹中 创建:FH=@(arglis)expr 直接调用:FH(arglis) 用函数句柄演算函数间接调用:feval(FH,arglis) 例:匿名函数的调用 y=@(x)(x.^2+x-20) feval(y,2) 或 y(2)
6.3 MATLAB 的函数类别 6.3.1 主函数 通常每个 M 文件中的第一个函数为主函数,主函数可以被该文件之外的其他函数调用,而子函数只能被该文件内的函数调用。主函数的调用通过存储该函数的 M 文件的文件名调用。我们在前面几节中编写的实验程序,大多为主函数。因此,这里不再过多介绍。
6.3.2 子函数 一个 M 文件中可以包括多个函数,除主函数之外的其他函数称为子函数。子函数只能被主函数或该文件内的其他子函数调用。每个子函数以函数定义语句开头,直至下一个函数的定义或文件的结尾。 当函数中调用函数时,系统判断其函数类型的顺序为:首先判断是否为子函数,然后判断是否为私有函数,最后判断其是否为当前目录下的 M 文件函数或者系统内置函数。由于子函数具有最高的优先级别,因此,在定义子函数时,可以采用已有的其他外部函数的名称。
子函数 每个子函数的第一行是其自己的函数申明行; 主函数的位置不可改变,但子函数的排列次序可以任意改变; 子函数只能被处于同一文件的主函数或其他子函数调用; 在M函数文件中,任何指令通过“名字”对函数进行调用时,子函数的优先级仅次于内装函数。 同一文件的主函数、子函数的工作空间都是彼此独立的。各函数间的信息,或通过输入输出宗量传递,或通过全局变量传递,或通过跨空间指令传递。 help,lookfor等帮助指令都不提供关于子函数的任何帮助信息。
第四章习题六用主、子函数实现 function y0_5=main( ) tspan=[0, 0.5]; y0=[1;0]; [tt,yy]=ode45(@DyDt,tspan,y0); y0_5=yy(end,1); function ydot=DyDt (t,y) mu=3; ydot=[y(2);mu*y(2)-2*y(1)+1]; 在命令行输入y0_5=main,即可得到结果
例6.3-1: 编写一个内含子函数的函数M文件 %主调函数 exm060301('line') HH=exm060301('circle') function Hr=exm060301(flag) %------prime function----------- t=(0:50)/50*2*pi; x=sin(t);y=cos(t); Hr=@cirline; feval(Hr,flag,x,y,t) %------subfunction----------- function cirline(wd,x,y,t) switch wd case 'line' plot(t,x,'b',t,y,'r'); case 'circle' plot(x,y,'-g') otherwise error('输入宗量错!') end axis square %主调函数 exm060301('line') HH=exm060301('circle') %绘图并返回子函数句柄 t=(0:2*pi/5:2*pi); x=cos(t); y=sin(t); HH('circle',x,y,t) %通过子函数句柄直接调用子函数
6.3.3 匿名函数 匿名函数提供了一种创建简单程序的方法,使用它用户可以不必每次都编写 M 文件。用户可以在 MATLAB 的命令窗口或是其他任意 M 文件和脚本文件中使用匿名函数。 匿名函数的创建格式为: FH = @(arglist) expr 其中 FH 是为该函数创建的函数句柄;@ 符号用于创建函数句柄;arglist 为用逗号分隔的参数列表;expr 为函数主体,为 MATLAB 表达式。 匿名函数的调用格式为: FH (arglist) %直接调用 feval(FH, arglist) %间接调用
嵌套函数 一个函数内部可以定义其他的函数,这种内部的函数称作嵌套函数。 1.嵌套函数的书写 定义嵌套函数时,只要在一个函数内部直接定义嵌套函数即可。需要注意的是当一个 M文件中存在嵌套函数时,该文件内的所有函数必须以 end 结尾。
私用函数 私用函数的构造与普通 M函数完全相同; 私用函数只能被 private目录的直接父目录上的 M函数文件所调用。它不能被其他目录上的任何M函数、M脚本文件或 Matlab指令窗中的命令所调用,也不能被直接父目录上的 M脚本文件调用。 M文件中,任何指令通过“名字”对函数进行调用时,私用函数的优先级虽低于内装函数和子函数,但高于其他任何目录上的函数 help, lookfor等指令都不能提供关于私用函数的任何帮助信息。
串演算函数 一种是串演算函数eval,它具有对字符串表达式进行计算的能力; 另一种是“函数句柄演算函数”feval,它具有对函数句柄进行操作的能力。
构成字符串CEM,可以是Matlab任何合法的指令、表达式、语句或M文件名; 第二种格式中的CEM只能是(包含输入参量在内的)M函数文件名; y=eval('CEM') 执行CEM指定的计算 [y1,y2,…]=eval('CEM') 执行CEM代表的函数文件,并输出结果 eval指令的输入参量必须是字符串; 构成字符串CEM,可以是Matlab任何合法的指令、表达式、语句或M文件名; 第二种格式中的CEM只能是(包含输入参量在内的)M函数文件名;
6.4 函数句柄 6.4.1 函数句柄的创建和观察 函数句柄是matlab的一种数据类型,携带着“相应函数创建句柄时的路径、视野(Scope)、函数名及重载方法”。 优点: 使函数调用像变量一样灵活方便 拓宽子函数包括局部函数的使用范围 提高函数调用的可靠性和软件的重用性 减少程序设计中的冗余 提高重复执行的效率
file: 'MATLAB built-in function‘ 6.4.1 函数句柄的创建和观察 函数句柄的定义有两种:(1)在提示符 @ 后添加相应函数的函数名;(2)利用转换函数str2func。 f_h=@plot %与f_h=str2func('plot')等价 则: f_h = @plot 函数句柄内涵的观察通过functions实现 functions(f_h) ans = function: 'plot' type: 'simple‘ file: 'MATLAB built-in function‘
6.4.2函数句柄的基本用法 如果一个函数的调用格式为: 假设该函数的句柄为,那么通过函数句柄实现函数运算的调用格式为: [argout1,argout2,…argoutn]=FunName(argin1,argin2,…,arginn) 假设该函数的句柄为,那么通过函数句柄实现函数运算的调用格式为: (1) 直接调用格式 [argout1,argout2,…,argoutn]=hfun(argin1,argin2,..,arginn) (2) 间接调用格式 [argout1,argout2,…,argoutn]=feval(hfun,argin1,argin2,..,arginn)
6.4.2 函数句柄的调用 (1) 直接调用 例: f_h1=@peaks %f_h1=str2func(‘peaks’) f_h1(50) (2) 间接调用: feval(函数句柄,参数列表) 例: f_h=@plot %f_h=str2func(‘plot’) feval(f_h,0:10,0:10)
例6.2-1:根据指定不同的半径、色彩、线型,绘出圆周线或圆。 调用: [S,L]=exm060201(8,2,'-g') [S,L]=exm060201(7,3) [S,L]=exm060201(5) [S,L]=exm060201 exm060201(8,2,'-g') function [S,L]=exm060201(N,R,str) switch nargin case 0, N=100;R=1;str='-b'; case 1, R=1;str='-b'; case 2, str='-b'; case 3 ; otherwise, error('输入量太多'); end; t=0:2*pi/N:2*pi; x=R*sin(t);y=R*cos(t); if nargout==0,plot(x,y,str) elseif nargout>2,error('输出量太多'); else S=N*R*R*sin(2*pi/N/2); L=2*N*R*sin(pi/N); fill(x,y,str) end axis equal square,box on,shg %将该函数M文件存于c:\ 函数句柄调用: H=@exm060201 [S,L]=H(7,3) 改变路径为d:\再调用: [S,L]=H(5,2) exm060201(8,2,‘-g’)
域名 说 明 function 句柄所代表的函数名 type 函 数 类 型 simple 无重载的内建函数,M,P函数文件或无法判断类型的函数 subfunction 子函数 private 私用函数 constructor matlab类的对象构造函数 overloaded 有重载的函数 file 非重载数据类型所对应的源代码及路径,若有内建函数,总列在此栏 methods 仅当函数类型为重载时,返回结构才有该域
小 结 matlab的程序结构: 顺序、分支、循环结构 程序M文件,函数M文件,函数文件一般结构 函数类别,主、子函数,匿名函数 小 结 matlab的程序结构: 顺序、分支、循环结构 程序M文件,函数M文件,函数文件一般结构 函数类别,主、子函数,匿名函数 函数句柄: @plot, str2func(‘plot’)
习题5 (Page226) 1,4,5,7 题