搜档网
当前位置:搜档网 › 第六部分 M文件和面向对象编程

第六部分 M文件和面向对象编程

第六部分  M文件和面向对象编程
第六部分  M文件和面向对象编程

第六部分 M 文件和面向对象编程

假如读者想灵活运用MA TLAB 去解决实际问题,想充分调动MATLAB ——科学技术资源,想理解MA TLAB 版本升级所依仗的基础,那么本章内容将十分有用。 本章将涉及比较深层的MATLAB 内容:脚本;函数(一般函数、内联函数、子函数、私用函数、方法函数);函数句柄的创建和使用;程序调试和剖析;数据结构(类、对象);重载和继承;面向对象编程。本章配备了许多精心设计的算例。这些算例是完整的,可直接演练的。读者通过这些算例,将真切感受到抽象概念的内涵、各指令间的协调,将从感知上领悟到面向对象编程的优越和至关要领。 本章新增了第6.7节,专门阐述函数句柄的创建和使用,它适用于MA TLAB6.x 版;(而新增的第6.9.3节中关于程序性能优化的内容,则仅适用于MA TLAB6.5以后版。)

6.1入门

本小节将通过编写脚本文件和函数文件来解决一个具体问题。通过这两个文件,可以初步了解M 文件。对于其中所涉及到的各种语言结构将在后面做详细介绍。 【例6.1-1】通过M 脚本文件,画出下列分段函数所表示的曲面。

???

?

???-≤+≤+<->+=+-------15457.0117575.015457.0),(215.175.375.0216215.175.375.02112122212212

122x x e x x e x x e x x p x x x x x x x x

(1)编写M 脚本文件的步骤

图 6.1-1

a. 打开文件编辑(如上),输入如下一段程序:

[exm0601_1.m] %exm0601_1.m

a=2;b=2; % <2>

clf;

x=-a:0.2:a;y=-b:0.2:b; for i=1:length(y) for j=1:length(x) if x(j)+y(i)>1

z(i,j)=0.5457*exp(-0.75*y(i)^2-3.75*x(j)^2-1.5*x(j)); elseif x(j)+y(i)<=-1

z(i,j)=0.5457*exp(-0.75*y(i)^2-3.75*x(j)^2+1.5*x(j)); else z(i,j)=0.7575*exp(-y(i)^2-6.*x(j)^2);

end

end

end

axis([-a,a,-b,b,min(min(z)),max(max(z))]);

colormap(flipud(winter));surf(x,y,z);

b.点击编辑调试器工具条的保存图标,键入新编文件名(如exm0601_1),完成文件的保存;

(2)运行文件

a.使exm0601_1所在目录成为当前目录,或让该目录处在matlab的搜索路径上;

b.然后在指令窗运行指令(文件名),便可得到图形。

exm0601_1

图 6.1.2

【例6.1-2】通过M函数文件画出上例分段函数的曲面。

整个编程步骤和前面的相同。这里将演示如何在exm0601_1.m的基础上产生函数文件exm0601_2.m。

(1)在编辑调试器中,把exm0601_1.m“另存为”exm0601_2.m;

(2)用下面4行指令代替原文件的第<1><2>条指令。

function exm0601_2(a,b)

%This is my second example.

%a Define the limit of variable x.

%b Define the limit of variable y.

(3)保存修改后的exm0601_2.m文件。

(4)在指令窗中,运行以下指令,就能产生与图6.1.2完全相同的图形。

exm0601_2(2,2)

6.2M文本编辑器

Matlab Editor/Debugger是一个集编辑与调试两种功能于一体的工具环境。利用它不仅可以完成基本的文件编辑操作,还可以对M文件进行调试。这里先介绍它的文件编辑功能,调试功能在后面介绍。

(1)为创建新M文件,启动编译器的3种操作方法:

a. 在指令窗中运行指令edit;

b. 点击指令窗工具条上的新建文件图标;

c. 利用指令窗的[File:New]子菜单,再从右拉菜单中选择“M_file”项。

(2)打开已经有的M文件的3种操作方法:

a. 在指令窗中运行指令edit filename。filename是待打开的文件名,可不带扩展名。

b. 通过指令窗工具条上的打开图标,再从对话框中点选所需打开的文件。

c. 利用指令窗的[File:Open]子菜单,再从对话框中点选所需打开的文件。

(3)文件保存方法(略)/

6.3 MATLAB 控制流

Matlab 提供了5种控制程序流的结构:for 循环结构、while 循环结构、if_else_end 分支结构、以及switch-case 结构,try-catch 结构。matlab 的这5种控制指令用法与其他语言的十分类似。

6.3.1 for 循环结构

for x=array

(commands) end

说明:for 指令后的变量x 称为循环变量,而for 与end 之间的组命令称为循环体。循环体被重复执行的次数是确定的,该次数由for 指令后面的数组array 的列数决定。换言之,循环变量依次取数组的各列,对于每个变量值,循环体被执行一次。 【例6.3.1-1】一个简单的for 循环示例。

for i=1:10; %i 依次取1,2,…,10

x(i)=i; %对每个I 值,重复执行由该指令构成的循环体 end;

x %要求显示运行后数组x 的值 x =

1 2 3 4 5 6 7 8 9 10

说明:

1. 在for 后面的表达式中的数组可以为任何合法的matlab 数组; 2. 循环结构可以嵌套使用;

3. 为了得到高效代码,应尽量提高代码的向量化程度,而避免使用循环结构; 4. 为了得到高效代码,在循环指令之前应尽量对数组进行预定义。

6.3.2 while 循环结构

while (expression )

(commands) end

【例6.3.2-1】Fibonacci 数组的元素满足Fibonacci 规则:12+++=k k k a a a ,),2,1( =k ;且121==a a 。现要求该数组中第一个大于10000的元素。

a(1)=1;a(2)=1;i=2; while a(i)<=10000 a(i+1)=a(i-1)+a(i); %当现有的元素仍小于10000时,求解下一个元素 i=i+1; end;

i,a(i), i = 21 ans =

10946

说明:

1. 一般情况下,表达式的值都是标量值,但是matlab 允许它为一个数组,此时只有当

该数组所有元素均为真时,matlab 才会执行循环体;

2. 如while 指令后的数组为空数组时,matlab 认为表达式值为假,而不执行循环体。

6.3.3 if-else-end 分支结构

if-else-end 指令有多种形式,其中最简单的用法是:

if expression

(commands)

end

说明:

1.当有两种选择时,采用下面的结构:

if expression

(commands1)

else (commands2)

end

2.如果选择多于两个,采用下面的结构:

if expression1

(commands)

if expression2

(commands)

……

else (commandsk)

end

3.表达式有时由多个逻辑子表达式组成,matlab将尽可能少地检测这些子表达式的值。例如,表达式为:(子表达式1|子表达式2),当matlab检测到子表达式1的值2为真时,它就认为表达式为真,而不再对子表达式2进行检测。又如,表达式:(子表达式1&子表达式2),当matlab检测到子表达式1的值为假时,它就认为表达式值为假的,从而跳过该结构。【例6.3.3-1】一个简单的分支结构。

cost=10;number=12;

if number>8

sums=number*0.95*cost;

end,sums

sums =

114.0000

【例6.3.3-2】用for循环指令来寻求Fibonacc数组中第一个大于10000的元素。

n=100;a=ones(1,n);

for i=3:n

a(i)=a(i-1)+a(i-2);

if a(i)>=10000

a(i),

break; %跳出所在的一级循环

end;

end,i

ans =

10946

i =

21

6.3.4switch-case结构

switch-case指令的一般语法结构形式如下:

switch ex %ex为一标量或字符串

case test1

(commands 1) %当ex等于test1时,执行组命令1,然后跳出该结构。

case test2

……

case testk

(commands k) %当ex等于testk时,执行组命令k,然后跳出该结构。

otherwise %otherwise指令可以不存在

(commands ) %表达式不等于前面所有检测值时,则执行该组命令。

end

说明:

1.switch指令后面的表达式应为一个标量或者为一个字符串。对于标量形式的表达式,比较这样进行:表达式= =检测值I。而对于字符串,matlab将调用函数strcmp来实现比较:strcmp (表达式,检测值i)。

2.case指令后面的检测值不仅可以为一个标量值或者为一个字符串。还可以为一个元胞数组。如果一个检测值是一个元胞数组,matlab将把表达式的值和该元胞数组中的所有元素进行比较,如果元胞数组中某个元素和表达式的值相等,matlab认为此次比较结果为真,从而执行与该检测值相应的一组命令。(见下例)

【例6.3.4-1】学生的成绩管理,用来演示switch结构的应用。

clear;

%划分区域:满分(100),优秀(90-99),良好(80-89),及格(60-79),不及格(<60)for i=1:10;a{i}=89+i;b{i}=79+i;c{i}=69+i;d{i}=59+i;end;c=[d,c];

Name={' Jack','Marry','Peter',' Rose',' Tom'};

Mark={72,83,56,94,100};Rank=cell(1,5); %3个元胞数组,且都是(1x5)维的%创建一个含5个元素的构架数组S,它有三个域。

S=struct('Name',Name,'Marks',Mark,'Rank',Rank);

%根据学生的分数,求出相应的等级。

for i=1:5

switch S(i).Marks

case 100

S(i).Rank='满分';

case a

S(i).Rank=' 优秀';

case b

S(i).Rank=' 良好';

case c

S(i).Rank=' 及格';

otherwise

S(i).Rank='不及格';

end

end

%将学生姓名,得分,等级等信息打印出来

disp(['学生姓名 ',' 得分 ',' 等级']);disp(' ')

for i=1:5;

disp([S(i).Name,blanks(6),num2str(S(i).Marks),blanks(6),S(i).Rank]); end;

学生姓名得分等级

Jack 72 及格

Marry 83 良好

Peter 56 不及格

Rose 94 优秀

Tom 100 满分

说明:matlab的switch指令不同于c语言的switch指令。当matlab检测到表达式与某个检测值相等而执行了相应的一组命令,执行完毕后,自动跳出switch结构,而无需使用break 指令。

6.3.5try-catch结构

try

(command1) %组命令1总被执行。若正确,则跳出此结构

catch

(command2) %仅当组命令1出现错误,组命令2才被执行

end

说明:

1.可以用lasterr函数查询出错原因。如果函数lasterr的运行结果为一个空串,则表明组命

令1被成功执行了。

2.当执行组命令2时又出错,matlab将终止该结构。

3( 魔方阵的行进行援引,当“行下标”超出魔【例6.3.5-1】try-catch结构应用实例:对)3

方阵的最大行数时,将改向对最后一行的援引,并显示“出错”警告。

clear,N=4;A=magic(3); %设置3行3列矩阵A

try

A_N=A(N,:) %取A 的第N行元素

catch

A_end=A(end,:) % 如果取A(N,:)出错,则改取A的最后一行

end

lasterr %显示出错原因

A_end =

4 9 2

ans =

Index exceeds matrix dimensions.

6.3.6控制程序流的其它常用指令

6.3.6.1return指令

通常,当被调函数执行完后,matlab会自动地把控制转至主调函数或者指令窗。如果在被调函数中插入了return命令,可以强制matlab结束执行该函数并把控制转出。

6.3.6.2input和keyboard指令

(1)input

input指令将matlab的“控制权”暂时交给用户。此后,用户通过键盘键入数值、字符串或者表达式,并经“回车”把键入内容输入工作空间,同时把“控制权”交还给matlab。

常用格式:

v=input(‘message’) 将用户键入的内容赋给变量v

v=input(‘message’,’s’) 将用户键入的内容作为字符串赋给变量v

说明:

1.对于第一种调用格式,用户可以输入数值、字符串、元胞数组等各种形式的数据;

2.对于第二种调用格式,不管键入什么,总以字符串形式赋给变量v。

(2)keyboard

keyboard指令将matlab的“控制权”暂时交给键盘。此后,用户可以通过键盘键入各种合法的matlab指令,只有当用户使用return指令结束输入后,“控制权”才交还给程序。说明:它与input不同在于,它允许输入任意多个matlab指令,而input只能输入赋给变量的“值”,即数值、字符串或元胞数组等。

6.3.6.3yesinput指令

yesinput指令是一个智能输入指令,它提供了一个缺省输入值,并可以对输入范围进行

检查。格式是:

v=yesinput(‘Prompt’,Default,Possib)

说明:

1.‘Prompt’为文字提示,Default为缺省设置“值”,Possib为可选“值”的范围;

2.当该指令运行后,如不输入任何“值”,则变量v将接受缺省“值”。

6.3.6.4pause指令

pause 暂停执行文件,等待用户按任意键继续

pause(n)在继续执行之前,暂停n秒

6.3.6.5break指令

break指令导致包含有该指令的while、for指令的终止。通过break指令,可不必等待循环的自然结束,而根据循环内部另设的某种条件是否满足,去决定是否退出循环。

6.3.6.6error和warning指令

在编写M文件时,常用的警示指令有:

error(‘message’) 显示出错信息message,终止程序。

errortrap 错误发生后,程序继续执行与否的双位开关。

lasterr 显示matlab自动判断的最新出错原因并终止程序。

warning(‘message’) 显示警告信息message,程序继续执行。

lastwarn 显示message自动给出的最新警告,程序继续执行。

6.4脚本文件和函数文件

6.4.1M脚本文件

随着指令数的增加,或随控制流复杂度的增加,以及重复计算要求的提出,直接从指令窗进行计算就显得烦琐。而此时脚本文件(Script file)最为适宜。“脚本”反映这样一个事实:matlab只是按文件所写的指令执行。脚本文件的特点是:

1.它只是一串按用户意图排列而成的(包括控制流向指令在内的)matlab指令集合;

2.脚本文件执行后,所产生的所有变量都驻留在matlab基本空间中。只要用户不使用clear

指令加以清除,且matlab指令窗不关闭这些变量就将一直保存在基本空间中。基本空间随matlab的启动而产生;只有关闭matlab时,该基本空间才被删除。

6.4.2M函数文件

与脚本文件不同,函数文件(Function file)犹如一个“黑箱”。从外界只看到传给它的输入量和送出来的计算结果,而内部运作是看不见的。函数文件的特点是:

1.形式上不同。如函数文件的第一行总是以“function”引导的“函数声明行”。该行还罗

列出函数与外界的联系的全部“标称”输入输出宗量。但对“输入输出宗量”的标称数目并没有限制,即可以完全没有输入输出宗量也可以是任意数目;

2.matlab允许使用比“标称数目”较少的输入输出宗量,实现对函数的调用;

3.运行上不同。每当函数文件运行,matlab就会专门为它开辟一个临时工作空间(Context

workspace)。该空间称之为函数工作空间(Function workspace)。所有中间变量都存放在函数工作空间中。当执行完文件最后一条指令时,或遇到return,就结束该函数文件的执行,同时该临时函数空间及其所有的中间变量就立即被清除。

4.函数空间随具体M函数文件的被调用而产生,随调用结束而删除。函数空间是相对基

本空间独立的、临时的。在matlab整个运行期间,可以产生任意多个临时函数空间。

5.假如在函数文件中,发生对某脚本文件的调用,那么该脚本文件运行产生的所有变量都

存放于那函数空间中,而不是存放在基本空间。

6.4.3局部变量和全局变量

1.局部(Local)变量

存在于函数空间内部的中间变量,产生于该函数的运行过程中,其影响范围仅限于该函数本身。

2.全局(Global)变量

通过global指令,matlab也允许几个不同的函数空间以及基本工作空间共享同一个变量,即全局变量。每个希望共享全局变量的函数或matlab基本工作空间,必须逐个用global 对具体变量加以专门定义。否则将无权享用全局变量。如果某个函数的运作使全局变量的内容发生了变化,那么其他函数空间以及基本工作空间中的同名变量也就随之变化。

除非与全局变量联系的所有工作空间都被删除,否则全局变量将一直存在。

说明:

1.对全局变量的定义必须在使用之前进行。一般建议是把全局变量的定义放在函数体的首

行位置;

2.虽然对全局变量的名字并没有任何限制,但是为了提高M文件的可读性,一般建议选

用大写字符命名全局变量。

3.全局变量损害函数的封装性,因此不提倡使用全局变量。

6.4.4M文件的一般结构

M文件一般包含以下5大部分:

1.函数声明行:以matlab关键字function开头,函数名以及函数的输入输出宗量都在这

一行进行定义。

2.H1行(The first help text line):紧随函数声明行之后一%开头的第一注释行。按matlab

自身文件的规则,H1行供lookfor关键词查询和help再线帮助使用。

3.在线帮助文本区:H1行及其之后的连续的以%开头的所有注释行构成整个在线帮助文

本。它通常包括:函数输入输出宗量的含义;调用格式说明。

4.编写和修改记录:它与在线帮助文本相隔一个“空”行;也以%开头。用来标志编写及

修改该M文件、的作者和日期;版本记录。它用作软件档案管理。

5.函数体:这部分内容由实现该M函数文件功能的matlab指令组成。它接收输入宗量,

进行程序流控制,得到输出宗量。若仅从运算角度看,只有“函数申明行”和“函数体”

两部分是构成M 函数文件所必部可少的。

【例6.4.4-1】M函数文件示例。本例演示:(A)编写一个画任意半径任意色彩线型的圆。(B)完整函数文件的基本结构。(C)函数文件各基本组成部分的作用。

[exm06044_1.m]

function sa = exm06044_1(r,s)

%CIRCLE

% r 指定半径的数值

% s 指定线色的字符串

% sa 圆面积

%

% exm06044_1(r) 利用蓝实线画半径为r的圆周线

% exm06044_1(r,s) 利用串s指定的线色画半径为r的圆周线

% sa=exm06044_1(r) 计算圆面积,并画半径为r的蓝色圆面

% sa=exm06044_1(r,s) 计算圆面积,并画半径为r的s色圆面

%编写于2004年5月30日,修改于2004年5月30日

if nargin>2

error('输入宗量太多。');

end;

if nargin==1

s='b';

end;

clf;

t=0:pi/100:2*pi;

x=r*exp(i*t);

if nargout==0

plot(x,s);

else

sa=pi*r*r;

fill(real(x),imag(x),s)

end

axis('square')

说明:

1.从结构上看,M脚本文件仅比M函数文件少一个“函数申明行”,其余各部分的构造和

作用都相同;

2.函数定义名和保存文件名一致。两者不一致时,matlab将忽视文件首行的函数定义名,

而以保存文件名为准;

3.函数文件的名字必须以字母开头,后面可字母、下划线以及数字的任意组合,但不得超

过31个字符;

4.建议在编写H1行时,如上例那样才用英文表达。这样处理是为了以后关键词检索方便。

6.4.5P码文件

6.4.5.1语法分析过程和伪代码

一个M文件首次被调用(运行文件名,或被M文本编辑器打开)时,matlab将首先对该M文件进行语法分析(Parse),并把生成的相应内部伪代码(Psedecode,简称P码)文件存放在内存中。此后,当再次调用该M文件时,将直接调用该文件在内存中的P码文件,而不会对原码文件重复进行语法分析。值得注意的是:matlab的分析器(Parse)总是把M 文件连同被它调用的所有函数M文件一起变换成P码文件的。

P码文件有与原码文件相同的文件名,但其扩展名是“.p”。本质上说,P码文件运行速度高于原码文件。

在matlab中,假如存在同名的P码和原码文件,那么当该文件名被调用时,被执行的肯定是P码文件。

6.4.5.2P码文件的预生成

P码文件不是仅当M文件被调用时才可产生。P码文件也可被预先生成。具体如下:pcode FunName 在当前目录上生成FunName.p

pcode FunName –inplace 在FunName.m所在目录上生成FunName.p

6.4.5.3内存中P码文件的列表和清除

inmem 罗列出内存中所有P码文件名

clear FunName 清除内存中的FunName.pP码文件

clear functions 清除内存中的所有P码文件

6.4.6MATLAB的搜索过程

假设matlab在一个文件中碰到指令cow时,它将按以下步骤逐步进行检索:

·matlab在内存中进行检查,看cow是不是变量;假如不是,进入下一步;

·检查cow是不是内建函数(Built-in Function);假如不是,再往下执行;

·检查cow是不是cow所在的M文件中的一个子函数;假如,再往下执行;

·检查cow是不是cow所在的M文件中的一个私用函数(先找cow.p,然后是cow.m);假如,再往下执行;

·检查cow是不是当前目录上的文件(先找cow.p,然后是cow.m);假如,再往下执行;

·在matlab搜索路径的其他目录上,检查是否有名为cow的M文件存在(先找cow.p,然后是cow.m)。

matlab将使用最先找到的那个cow。如果一直找不到cow,matlab就给出错误提示信息。

6.5变量的检测传递和限权使用函数

6.5.1输入输出宗量检测指令

nargin 在函数体内,用于获取实际输入宗量

nargout 在函数体内,用于获取实际输出宗量

nargin(‘fun’) 获取‘fun’指定函数的标称输入宗量数

nargout(‘fun’) 获取‘fun’指定函数的标称输出宗量数

inputname(n) 在函数体内使用,给出第n个输入宗量的实际调用变量名

说明:

1.在函数体内使用nargin、nargout的目的是:与程序流控制指令配合,对于不同数目的输

入输出宗量数,函数可完成不同的任务;

2.应注意:nargin、nargout、inputname本身都是函数,而不是变量,所以不能使用赋值指

令对它们进行处理。

6.5.2“变长度”输入输出宗量

在matlab中有相当一些函数,都具有接受“任意多输入”、返回“任意多输出”的能力。如前面已经使用过的plot绘图指令,就允许使用任意多的“属性名/属性值对”精细指定plot 绘图的用线。

为了使用户的自编函数也具备这种能力,matlab提供如下两个内状函数:

varargin “变长度”输入宗量列表

varargout “变长度”输出宗量列表

说明:

1.编写M函数文件时,函数申明行中的“变长度”宗量必须被放置在“普通”宗量之后;

2.varargin的工作机理:

(1)narargin本身是个元胞数组

(2)M文件被调用时,函数输入变量的分配规则是:首先,输入变量依先后次序逐个对应分配给M函数文件输入宗量列表中那些被明确定义的“普通”输入宗量;

然后,把剩余的输入变量依次逐个分配到varargin元胞数组的元胞中。因此,

varargin元胞数组的长度取决于分配到的输入变量数。

(3)所谓“变长度”,就是指:varargin的长度随分配到的输入变量数而变;

(4)narargout的工作机理、规则与narargin相同。

【例6.5.2-1】变长度宗量使用示例。本例演示:①编写程序,画两个同心圆环,其中一个半径由独立输入宗量定义,另一个由变长度输入宗量的第一个元素定义,可以用任意指定的色彩、线型绘制;②变长度输入输出宗量在程序中的构成和使用;③变长度输入输出宗量的对外使用表现。

(1)编写函数文件exm06052_1.m

[exm06052_1.m]

function varargout = exm06052_1(r,varargin)

%RINGZY Plot a ring and calculate the area of the ring.

%r 基圆半径

%调用格式

%[ x1,y1,x2,y2,s1,s2]=exm06052_1(r,r2,’PropertyName’,’PropertyValue’,…)

% (1)无输出时,绘圆回环

% (2)有输出时,不绘图

% (x1,y1),(x2,y2)分别是两个圆的坐标点

% s1是基圆面积

% s2为正值时,表示内环面积;为负值时,表示外环面积

vin=length(varargin);Nin=vin+1;%计算输入变量数目<11>

error(nargchk(1,Nin,nargin)) %检查输入变量数目是否合适

if nargout>6 %检查输出变量数目是否合适error('Too many output arguments')

end

t=0:pi/20:2*pi;x=r*exp(i*t);s=pi*r*r;

if nargout==0

switch Nin

case 1

plot(x,'b')

case 2

r2=varargin{1}; %<22> x2=r2*exp(i*t);

plot(x,'b');hold on ;plot(x2,'b');hold off

otherwise

r2=varargin{1}; %<26> x2=r2*exp(i*t);

plot(x,varargin{2:end});hold on %利用元胞叔祖设置对象属性 <28> plot(x2,varargin{2:end});hold off %利用元胞叔祖设置对象属性 <29> end;

axis('square')

else

varargout{1}=real(x);varargout{2}=imag(x); %<33> varargout{5}=pi*r*r;varargout{6}=[]; %<34> if Nin>1

r2=varargin{1}; %<36> x2=r2*exp(i*t);

varargout{3}=real(x2);varargout{4}=imag(x2); %<38> varargout{6}=pi*(r^2-r2^2); %<39> end;

end

(2)有输出情况:自己用plot(x1,y1,x2,y2)检验下列3个调用示例的运行结果

r1=1;r2=3;

[x1,y1,x2,y2,s1,s2]=exm06052_1(r1);

[x1,y1,x2,y2]=exm06052_1(r1,r2);

[x1,y1,x2,y2,s1,s2]=exm06052_1(r1,r2);

(3)无输出情况:为节省篇幅,在此给出了3个调用示例

r1=1;r2=0.6;

subplot(1,3,1),exm06052_1(r1,r2),

subplot(1,3,2),exm06052_1(r1,r2,'Marker','o')

说明:

应注意上例中标有号码的各条相应指令是如果调用varargin每个元胞的内容;以及如何向varargout每个元胞放置内容的。

6.5.3跨空间变量传递

6.5.3.1跨空间计算串表达式的值

前面已经介绍了实现不同工作空间之间变量传递的两种渠道:函数的输入输出宗量和全局变量。这里见介绍第3个传递渠道:跨空间计算串表达式值的指令。格式是:

evalin(‘worspace’,’expression’) 跨空间计算串表达式值

evalin(‘worspace’,’expression1’, ’expression2’) 跨空间计算替代串表达式值

说明:

1.‘worspace’可取两个值:’base’和’caller’;

2.第1种调用格式的执行机理是:

(1)当‘worspace’取’base’时,表示计算eval(’expression)时,将从基本工作空间获得变量值;

(2)当‘worspace’取’caller’时,表示计算eval(’expression)时,将从主调函数空间获得变量值。(主调函数是相对被调函数而言的。这里被调函数是指evalin所在的

函数。)

3. 第2种调用格式的执行机理是:

先从所在函数空间获取变量值,用eval(’expression1’)计算原串表达式;如若该计算失败,则再从’workspace’指定的(基本或主调函数)工作空间获取变量值,再通过eval(’expression2’)计算替代串表达式。

【例6.5.3.1-1】本例演示:(A)编写绘制正多边形或圆的程序。(B)子函数与(母)函数的关系。(C)各种不同的工作空间。(D)evalin运行机理与eval的异同。

(1)编写M函数文件

[exm060531_1.m]

function y1=exm060531_1(a,s)

t=(0:a)/a*2*pi;

y1=subevalinzzy(4,s);

%------------ subfunction -------------

function y2=subevalinzzy(a,s)

t=(0:a)/a*2*pi;ss='a*exp(i*t)';

case {'base','caller'}

y2=evalin(s,ss);

case 'self'

y2=eval(ss);

end

(2)在maltab指令窗中运行以下指令

clear,a=30;t=(0:a)/a*2*pi;sss={'base','caller','self'};

for k=1:3

y0=exm060531_1(8,sss{k});

subplot(1,3,k)

plot(real(y0),imag(y0),'r','LineWidth',3),axis square image

end

图 6.5-2 利用不同工作空间中的变量值计算

说明:

1.本例运行时,有3个空间:基本空间、主函数空间、子函数空间。在每个空间里,都有

各自不同的a,t值;

2.本例的3脏子图显示:虽eval('a*exp(i*t)')的计算,都在子函数subevalinzzy中进

行,但由于取不同工作空间的a,t值,所以产生的结果也不同。在基本空间中,产生圆;在主函数空间中,产生八边形;在子函数空间中,产生四边形。

6.5.3.2跨空间赋值

实现不同工作空间之间变量传递的第四个传递渠道:跨空间赋值指令。格式是:assignin(‘workspace’,’VN’,x) 跨空间向VN变量赋值

说明:

把当前工作空间内变量x的值赋给‘workspace’指定空间的名VN变量。

【例6.5.3.2-1】assignin运作机理示范。

(1)编写M函数文件

[exm060532_1.m]

function y=exm060532_1(x)

y=sqrt(x);t=x^2;

assignin('base','yy',t)

(2)在matlab指令窗中运行以下指令

clear;x=4;y=exm060532_1(x);

disp([blanks(5),'x',blanks(5),'y',blanks(4),'yy']),disp([x,y,yy])

x y yy

4 2 16

本例运行后,基本空间内的y变量值是通过函数exm060532_1.m的输出宗量送出的,而yy 变量却是由该函数中的跨空间赋值指令直接产生。

6.5.4子函数和私用函数

6.5.4.1子函数

matlab允许1个M函数文件包含多个函数的代码。其中,第一个出现的那个函数称为主函数,该文件中的其他函数则称为子函数。保存时所用文件名与主函数定义名相同。外部程序只能对主函数进行调用。

子函数的主要性质:

(1)在M函数文件内,主函数的位置不可改变,子函数的排列次序可以任意改变;

(2)子函数只能被处于同一文件的主函数或子函数调用;

(3)在M函数文件内,任何指令通过“名字”对函数进行调用时,子函数的优先级仅次于内装函数;

(4)同一文件的主函数、子函数的工作空间都是彼此独立的。各函数间的信息,或通过输入输出宗量传递,或通过全局变量传递,或通过跨空间指令传递;

(5)help,lookfor等帮助指令都不能提供关于子函数的任何帮助信息。

6.5.4.2私用函数

所谓私用函数,是指位于private目录上的M文件函数,性质是:

(1)私用函数的构造完全与普通M函数相同;

(2)私用函数只能被private直接父目录上的M文件所调用,而不能被其他目录上的任何M文件或matlab指令窗中的命令所调用;

(3)在M函数文件内,任何指令通过“名字”对函数进行调用时,私用函数的优先级虽低于内装函数和子函数,但高于其他任何目录上的函数;

(4)help,lookfor等帮助指令都不能提供关于私用函数的任何帮助信息。

6.6串演算函数

指令、表达式、语句、以及由它们综合组成的M文件,是用户为达到自己计算目的时所最常使用的形式。为提高计算的灵活性,matlab还提供了一种利用字符串进行计算的能力。利用字符串可以构成函数,可以在运行中改变所执行的指令,*可以被泛函数指令调用实现比较复杂的求零点、求极值等运算。

6.6.1eval

y=eval(‘CEM’) 执行CEM指定的计算

y=eval(‘CEM1’, ‘CEM2’) 先执行CEM1指定的计算,如果出错,就执行CEM2

[y1,y2,…]=eval(‘CEM’) 执行时对CEM代表的函数文件调用,并输出计算结果

说明:

1.eval指令的输入宗量必须是字符串;

2.构成字符串的CEM,可以是matlab任何合法的指令、表达式、语句或M文件名;

3.第3种格式中的CEM只能是(包含输入宗量在内的)M函数文件名。

【例6.6.1-1】计算“表达式”串,产生向量值。

clear,t=pi;cem='[t/2,t*2,sin(t)]';y=eval(cem)

y =

1.5708 6.2832 0.0000

【例6.6.1-2】计算“语句”串,创建变量。

clear,t=pi;eval('theta=t/2,y=sin(theta)');who

theta =

1.5708

y =

1

Your variables are:

t theta y

6.6.2feval

[y1,y2,…]=feval(‘FN’,arg1,arg2,…)用参量arg1,arg2等执行FN函数指定的计算

说明:

1.‘FN’只能是函数名;

2.在既可以使用eval,又可以使用feval的情况下,feval运行效率更高;

3.*feval主要用来构造“泛函”型M函数文件(如fmin,ezplot)。

4.

【例6.6.2-1】feval 和eval 运行区别之一:feval的FN绝对不能是表达式。

x=pi/4;Ve=eval('1+sin(x)')

Ve =

1.7071

Vf=feval('1+sin(x)',x)

??? Error using ==> feval

Invalid function name '1+sin(x)'.

【例6.6.2-2】feval 和eval 调用区别:feval 的FN只接受函数名。本例两种方法以后者为好。

randn('seed',1);A=rand(2,2);

[ue,de,ve]=eval('svd(A)');

disp('Results by eval');disp([ue,de,ve]);disp(blanks(1))

[uf,df,vf]=feval('svd',A);

disp('Results by feval');disp([uf,df,vf])

Results by eval

-0.9193 -0.3936 1.2212 0 -0.7897 -0.6135

-0.3936 0.9193 0 0.2633 -0.6135 0.7897

Results by feval

-0.9193 -0.3936 1.2212 0 -0.7897 -0.6135

-0.3936 0.9193 0 0.2633 -0.6135 0.7897

6.6.3内联函数

内联函数是matlab提供的一个对象。它的形状表现和函数文件一样,而内联函数的创建则比较容易。

6.6.3.1内联函数的创建

inline(‘CE’) 把串表达式转化为输入宗量自动生成的内联函数

inline(‘CE’,arg1,arg2,…) 把串表达式转化为arg1,arg2等指定输入宗量的内联函数inline(‘CE’,n) 把串表达式转化为x,P1,P2,…Pn等指定输入宗量的内联函数

说明:

1.‘CE’是字符串;CE必须是不包含赋值号“=”的表达式;

2.第1中调用格式将按一定规则自动对CE进行辩识;

3.第2种调用格式是创建内联函数的最稳妥、可靠的途径。输入宗量字符可以表达得更自

如;

4.第3种调用格式最简练,但对输入宗量的字符限制严格。输入宗量字符只能是x,

P1,P2,…Pn等。(注意:P是大写。)

5.内联函数也可以看做是沟通eval,feval两个不同指令的“桥梁”。凡是eval可以运作的

表达式,都可以通过inline转化为内联函数,而这种内联函数总可被feval使用。Matlab 的许多“泛函”函数,就是由于采用了inline,而具备了适应各种被处理函数形式的能力。

6.6.3.2涉及内联函数性质的指令

class(inline_fun) 给出内联函数类型

char(inline_fun) 给出内联函数计算公式

argnames(inline_fun) 给出内联函数的输入宗量

vectorize(inline_fun) 使内联函数适用“数组运算”规则

6.6.3.3内联函数创建和应用示例

【例6.6.3.3-1】演示:内联函数的第一种创建格式;使内联函数适于“数组运算”。clear,F1=inline('sin(rho)/rho') %第一种格式创建内联函数

F1 =

Inline function:

F1(rho) = sin(rho)/rho

f1=F1(2) %内联函数的一种使用方法

f1 =

0.4546

FF1=vectorize(F1) %产生适于“数组运算”的内联函数

xx=[0.5,1,1.5,2];ff1=FF1(xx)

FF1 =

Inline function:

FF1(rho) = sin(rho)./rho

ff1 =

0.9589 0.8415 0.6650 0.4546

【例6.6.3.3-2】演示:第一种内联函数创建格式的缺陷;含向量的多宗量输入的赋值。

G1=inline('a*exp(x(1))*cos(x(2))'),G1(2,[-1,pi/3])

G1 =

Inline function:

G1(a) = a*exp(x(1))*cos(x(2))

??? Error using ==> inline/subsref

Too many inputs to inline function.

G2=inline('a*exp(x(1))*cos(x(2))','a','x'),G2(2,[-1,pi/3])

G2 =

Inline function:

G2(a,x) = a*exp(x(1))*cos(x(2))

ans =

0.3679

【例6.6.3.3-3】演示:产生向量输入、向量输出的内联函数;这种向量函数的调用方法。Y2=inline('[x(1)^2;3*x(1)*sin(x(2))]')

argnames(Y2) %观察内联函数的输入宗量

Y2 =

Inline function:

Y2(x) = [x(1)^2;3*x(1)*sin(x(2))]

ans =

'x'

x=[4,pi/6]; %向量输入形式的赋值

y2=Y2(x) %获得向量输出

y2 =

16.0000

6.0000

【例6.6.3.3-4】演示:最简练格式创建内联函数;内联函数可被feval 指令调用。

Z2=inline('P1*x*sin(x^2+P2)',2)

Z2 =

Inline function:

Z2(x,P1,P2) = P1*x*sin(x^2+P2)

z2=Z2(2,2,3) %直接计算内联函数

fz2=feval(Z2,2,2,3) %注意:这里应写成Z2,不能写成’Z2’

z2 =

2.6279

fz2 =

2.6279

6.7函数句柄

函数句柄是matlab6.x版启用的新数据类型。它保存着为该函数创建句柄时的路径、视野、函数名,以及可能存在的重载方法。

引入函数句柄的目的是:使feval及借助于它的泛函指令工作更可靠;使“函数调用”象变量调用一样灵活方便;可迅速获得同名重载函数的位置、类型信息;可在更大范围内调用子函数和私有函数,提高软件重用性;提高函数调用速度,特别在反复调用情况下更显效率。

6.7.1函数句柄的创建和观察

函数句柄并不是伴随函数文件而自动形成的文件“属性”,它必须通过专门的定义才会生成。为一个函数定义句柄有两种方法:利用@符号或利用转换函数str2func。

对函数句柄的内涵观察需要借助专门指令functions实现。

对此,要特别强调的是:

(1)创建函数句柄时,被创建句柄的函数文件必须在当前视野范围内。所谓当前视野包括:当前目录、搜索路径、当前目录所包含的“私用文件夹”。此外,如果

创建函数句柄的指令在一个函数文件中,那么该文件包含的所有子函数,也在

当前视野内。

(2)假如被创建句柄的函数不在当前视野内,则所创建的函数句柄无效。对于这种无效创建,matlab既不会发布“出错”信息,也不会给出任何警告。

【例6.7.1-1】为MA TLAB的“内建”函数创建函数句柄,并观察其内涵。

(1)创建函数句柄

hsin=@sin;

(2)类型判别

class(hsin)

size(hsin)

ans =

function_handle

ans =

1 1

(3)借助指令functions观察句柄的内涵

CC=functions(hsin)

CC =

function: 'sin'

type: 'overloaded'

file: 'MATLAB built-in function'

methods: [1x1 struct]

(4)同名重载函数观察

CC.methods.sym

ans =

d:\matlab6p5\toolbox\symbolic\@sym\sin

说明:

1. 指令hsin=@sin 的功能可以用hsin= str2func(‘sin ’)替代;

2.定义函数句柄时,所指定的函数名不应该包括路径信息,也不应该包括扩展名。函数名最多只能包括63个字符;

3.本例对hsin 的检查表明:它是(1x1)的“函数句柄”数组;

5. 观察指令functions 只能接受(1x1)的函数句柄数组,并返回一个单构架。该返回构架

包含若干个域,记录着各种信息,详见下表:

6.7.2 函数句柄的基本用法

这里将介绍如何通过函数句柄执行相应函数的计算。假设一个函数的调用格式为: [argout1,argout2,…,argoutn]=Funname(argin1,argin2,…,arginn); 又假设该函数的句柄通过指令: Hfun=@FunName

获得,那么通过函数句柄实现函数运算的调用格式是:

[argout1,argout2,…,argoutn]=feval(Hfun,argin1,argin2,…,arginn)。

说明:

1. 一旦函数句柄被有效建立,无论名为FunName 的函数文件是否在当前搜索路径上,是

否在当前视野范围内,是否是子函数或私有函数,只要运用句柄Hfun 便总能被正确地执行;

2. 在FunName 存在重载函数时,借助句柄Hfun 的计算总能被“恰当地”执行。即是说,

它也会根据计算的数据类型,从其包含的所有重载含糊中选择相应的函数文件执行运算;

3. 在不使用函数句柄的情况下,对FunName 进行多次调用时,每次都要为该函数进行全

面的路径搜索,直接影响计算速度。借助Hfun 可完全避免这种无谓的时间消耗。 【例6.7.2-1】本例通过函数及其句柄演示若干基本用法。 (1)函数句柄的创建

fhandle=str2func('sin');

(2)函数句柄在)4

sin(

“数值计算”中的情况

ys=sin(pi/4) %函数直接调用

yfold=feval('sin',pi/4) %feval 指令的“老式”调用 yfnew=feval(fhandle,pi/4) %feval 指令的“新式”调用 ys =

0.7071 yfold = 0.7071 yfnew =

0.7071

(3)函数句柄在“符号计算”中的情况

Alpha=sym('pi/4'); %定义符号常数

yss=sin(Alpha) %函数直接调用

yfold=feval('sin',Alpha) % feval指令的“老式”调用

ynews=feval(fhandle,Alpha) % feval指令的“新式”调用

yss =

1/2*2^(1/2)

yfold =

1/2*2^(1/2)

ynews =

1/2*2^(1/2)

2,0[ 间求sin函数的极小值

(4)*函数句柄在泛函指令中的用法:在]

xold=fminbnd('sin',0,2*pi) %泛函指令的“老式”调用

xnew=fminbnd(fhandle,0,2*pi) %泛函指令的“新式”调用

xold =

4.7124

xnew =

4.7124

*【例6.7.2-2】本例演示:如何避免创建“无效函数句柄“问题。

(1)

Hy2=@fhzzy %fhzzy.m是随书光盘mfiles文件夹上的一个函数文件。Hy2 =

@fhzzy

(2)

class(Hy2)

size(Hy2)

ans =

function_handle

ans =

1 1

(3)

feval(Hy2,'line');

??? Error using ==> feval

Undefined function 'fhzzy'.

*【例6.7.2-3】自建函数及其句柄的使用。

(1)

[fhzzy.m]

function Hr=fhzzy(flag )

% fhzzy

%

%

t=(0:100)/100*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','LineWidth',2)

case 'circle'

plot(x,y,'g.','MarkerSize',30),

axis square off

otherwise

error('输入宗量只能取''line'' 或''circle'' !')

end

shg

(2)

Hy3=@fhzzy

fhzzy('line');

Hy3 =

@fhzzy

(3)

which('fhzzy')

fhzzy not found.

(4)

fhzzy('line')

feval('fhzzy','line')

??? Undefined function or variable 'fhzzy'. (5)

feval(Hy3,'line');

*【例6.7.2-4】子函数句柄的创建与使用。

(1)

HCL=fhzzy('circle')

HCL =

@cirline

图6.7-2

(2)

第11章 数据的导入和导出

第11章数据的导入和导出 通过本章内容学习,可以使读者掌握Access与其他类型数据的共享与交互方法,能够熟练地对数据进行导入和导出操作,并且能够根据实际需求选用合适的导入和导出类型。此外,用户还能对Office软件的协同工作有了基本的认识和了解。 本章重点: 外部数据介绍 数据的导入 数据的导出 Office软件的协作

11.1外部数据介绍 Access作为一种典型的开放型数据库,能够支持与其他类型的数据库文件进行数据的交换和共享,同时也支持与其他Windows程序创建的数据文件进行数据交换。 当数据进行交换时,就需要进行数据的导入、导出操作。Access具有很强的导入和导出功能,而且,在Access中所包含的4个默认的选项卡中,有关数据导出和导入的【外部数据】选项卡就是其中的一个。由此可见,数据的共享和交换在数据库工具中起到的作用。 和旧版本的Access软件相比较,Access 2010停止了对数据访问页对象的支持,反而大幅度地提高了网络协同工作的能力。利用SharePoint网站,事先数据的共享和交换,利用Office中的Outlook邮件收发软件,加强了开发人员的协同工作等。在下面的各节中将带大家一起体会Access2010强大的协同工作能力。 11.2数据的导入 一般情况下,Access数据库获得数据的方法主要有两种,一种方法是在数据表或者窗体中直接输入数据,另一种方法是利用Access的数据导入功能,将外部数据导入到当前使用的数据库中。 简单地讲,数据的导入就是将其他格式的数据合并到Access数据库中,并实现对导入数据的调用。被导入的数据是将外部数据作为源数据,在Access中建立一个对源数据的备份,这个备份是以Access的数据结构储存的,因此备份中的数据也是单独存在的,它与原来的数据是分开使用的。 数据的各种导入操作都是在【外部数据】选项卡中的【导入并连接】选项组中实现的,其中的各个选项如图11-1所示。

第11章 文件

第11章文件 本章要点 ●文件的打开与关闭 ●文件的读写 ●文件的定位 本章难点 ●fopen函数和fclose函数的用法 ●fread函数、fwrite函数、fprintf函数和fscanf函数的用法 11.1 C文件概述 以前各章节中所用到的原始数据都是通过键盘输入的,并将输入的数据放入到指定的变量或数组中,若要处理这些数据,可以从指定的变量或数组中取出并进行处理。但是当重新执行程序时,这些输入的数据都将丢失。如果数据量很庞大时,一旦某个数据输入错误,则全部数据都要重新输入。另外,一些程序运行后会产生大量输出结果,对这些结果有时需要反复查看或使用,因此,需要将这些输出结果保存起来。C语言中引入了文件,将程序运行时所需要的和所产生的数据(原始的、中间的、最终的)独立在源程序文件之外,以“数据文件”的形式存储到计算机外存,以备计算机需要时调入内存。这种“数据文件”就是磁操作系统管理下的“文件”。 文件(file)是程序设计中的一个重要概念。所谓“文件”一般是指存储在外部介质上数据的集合。一批文件是以数据的形式存放在外部介质(如磁盘)上的。操作系统是以文件为单位对数据进行管理的。如果想找存在外部介质上的数据,必须先按文件名找到指定的文件,然后再从该文件中读取数据。要向外部介质上存储数据也必须先建立一个文件(以文件名标识),才能向它输出数据。 C语言将文件看作是一个字符(字节)的序列,即一个一个字符(字节)的数据顺序组成。根据数据的组成形式,可分为ASCII文件和二进制文件。ASCII文件又称文本(text)文件,它的每一个字节可放一个ASCII码,代表一个字符。二进制文件是把内存中的数据按其在内存中的存储形式按原样输出到磁盘上存放。因而一个C文件就是一个字节流或二进制流。 C语言中,要调用一个文件,一般需要该文件的一些信息,例如:文件的名字,文件当前的读写位置,文件的操作方式等。缓冲文件系统会为每一个文件系统开辟这样一个“文件信息区”,包含在头文件stdio.h中,它被定义为FILE类型数据。 typedef struct { short level; /*缓冲区“满”或“空”的程度*/ unsigned flags; /*文件状态标志*/

第11章 文件

第十一章 文件 一、 C 文件概述 1、文件:文件指存储在外部介质(如磁盘磁带)上数据的集合。 2、文件的分类 按数据的组织形式: ASCII 文件(文本文件):每一个字节放一个ASCII 代码 二进制文件:把内存中的数据按其在内存中的存储形式原样输出到磁盘上存放。 例:整数10000在内存中的存储形式以及分别按ASCII 码形式和二进制形式输出如下图所示: ASCII 文件和二进制文件的比较: ASCII 文件便于对字符进行逐个处理,也便于输出字符。但一般占存储空间较多,而且要花费转换时间。 二进制文件可以节省外存空间和转换时间,但一个字节并不对应一个字符,不能直接输出字符形式。 一般中间结果数据需要暂时保存在外存上,以后又需要输入内存的,常用二进制文件保存。 3、 C 语言对文件的处理方法: 缓冲文件系统:系统自动地在内存区为每一个正在使用的文件开辟一个缓冲区。用缓冲文件系统进行的输入输出又称为高级磁盘输入输出。 非缓冲文件系统:系统不自动开辟确定大小的缓冲区,而由程序为每个文件设定缓冲区。用非缓冲文件系统进行的输入输出又称为低级输入输出系统。

二、文件的打开与关闭 1、文件的打开(fopen函数) 函数调用: FILE *fp; fp=fopen(文件名,使用文件方式); ①需要打开的文件名,也就是准备访问的文件的名字; ②使用文件的方式(“读”还是“写”等); ③让哪一个指针变量指向被打开的文件。 ④打开文件的方式: r:以只读方式打开一个已存在的文本文件, w:以只写的方式打开一个文本文件,若文件存在则删除之,然后重新建立, a:以在文件末尾追加的方式打开一个文本文件,(属于只写方式) rb: 以只读方式打开一个已存在的二进制文件 wb: 以只写的方式打开一个二进制文件,若文件存在则删除之,然后重新建立 ab: 以在文件末尾追加的方式打开一个二进制文件,(属于只写方式) r+:以读写方式打开一个已存在的文本文件(可读可写) w+:以读写方式打开一个文本文件(可读可写),若文件存在则删除之,然后重新建立a+:以在文件末尾追加的方式打开一个文本文件,(可读可写) rb+:以读写方式打开一个已存在的二进制文件(可读可写) wb+:以读写方式打开一个二进制文件(可读可写),若文件存在则删除之,然后重新建立 ab+:以在文件末尾追加的方式打开一个二进制文件,(可读可写) 2、文件的关闭(fclose函数) fclose( 文件指针); 返回值: 关闭成功返回值为0;否则返回EOF(-1) 。 三、文件的读写 1、fputc函数和fgetc函数 fputc函数 作用是把一个字符写到磁盘文件上去。 一般形式为: fputc(ch , fp); 函数功能: 将字符(ch的值)输出到fp所指向的文件中去。 返回值: 如果输出成功,则返回值就是输出的字符; 如果输出失败,则返回一个EOF。 fgetc函数 作用是从指定文件读入一个字符,该文件必须是以读或读写方式打开的。fgetc函数的调用形式如下:

相关主题