《MatLab编程中文版第五章.doc》由会员分享,可在线阅读,更多相关《MatLab编程中文版第五章.doc(39页珍藏版)》请在淘文阁 - 分享文档赚钱的网站上搜索。
1、目录第五章 自定义函数15.1 MATLAB函数简介15.2 在MATLAB中传递变量:按值传递机制6例5.375.3 选择性参数14测试5.1165.4 用全局内存分享数据17例5.4185.5 在函数调用两次之间本地数据的存储22例5.5 运行平均数225.6 函数的函数(function functions),26例5.6275.7 子函数和私有函数295.8 总结295.9 练习305.1305.2305.3315.4315.5315.6315.7315.8315.9315.10315.11315.12325.13325.14325.15335.16335.17335.18345.19
2、355.20355.21355.22355.23365.24365.25375.2637第五章 自定义函数在第三章中,我们强调了好的编程习惯的重要性。我们进行开发的基本手段是自上而下的编程方法。在自上而下的编程方法中,它开始于对所要解决问题的精确陈述和定义输入量和输出量。下一步,我们在大面上进行算法的描述,然后把算法分解成一个一个的子问题。再然后,程序员把这一个个子问题进行再一次的分解,直到分解成简单而且能够清晰理解的伪代码。最后把伪代码转化为MATLAB代码。尽管我们在前面的例子中,按照上面的步骤进行了编程。但是产生的结果在某种程度上还是受限制的。因为我们必须把每一个子问题产生的MATLAB
3、代码嵌入到一个单独的大程序中。在嵌入之前我们无法对每一次子问题的代码进行独立地验证和测试。幸运的是,MATLAB有一个专门的机制,在建立最终的程序之前用于独立地开发与调试每一个子程序。每一个子程序都可以独立函数的形式进行编程,在这个程序中,每一个函数都能独立地检测与调试,而不受其他子程序的影响。良好的函数可以大大提高编程的效率。它的好处如下:1.子程序的独立检测每一个子程序都可以当作一个独立的单元来编写。在把子程序联合成一个的大程序之前,我们必须检测每一个子程序以保证它运转的正确性。这一步就是我们熟知的单元检测。在最后的程序建立之前,它排除了大量的问题。2.代码的可复用性在许多的情况下,一个基
4、本的子程序可应用在程序的许多地方。例如,在一个程序的许多地方,要求对一系列按由低到高的顺序进行排序。你可以编一个函数进行排序,然后当再次需要排序时可以调用这个函数。可重用性代码有两大好处:它大大提高了整体编程效率,它更易于调试,因为上面的排序函数只需要调试一次。3.远离意外副作用函数通过输入参数列表(input argument list)从程序中读取输入值,通过输出参数列表(output argument list)给程序返回结果。程序中,只有在输入参数列表中的变量才能被函数利用。函数中,只有输出参数列表中的变量才能被程序利用。这是非常重要的,因为在一个函数中的突发性编程错误只会发生错误的函
5、数的变量。一旦一个大程序编写并发行,它还要面临的问题就是维护。程序的维护包括修补错误,修改程序以适应新或未知的环境。作维护工作的程序员在一般情况下不会是程序的原作者。如果程序编写的不好,改动一处代码就可能对程序全局产生负面影响。这种情况的发生,可能是因为变量在其他部分被重新定义或利用。如果程序员改变这个变量,可能会导致后面的程序无法使用。好的函数的应用可以通过数据隐藏使问题最小化。在主函数中的变量在函数中是不可见的(除了在输入变量列表中的变量),在主程序中的变量不能被函数任意修改。所以在函数中改变变量或发生错误不会在程序的其他部分发生意外的副作用。好的编程习惯把大的程序分解成函数,有很多的好处
6、,例如,程序部分的独立检测,代码的可复用性,避免意想不到的错误。5.1 MATLAB函数简介到目前为止,我们看到的所有的M文件都是脚本文件。脚本文件只是用于存储MATLAB语句。当一个脚本文件被执行时,和直接在命令窗口中直接键入MATLAB语句所产生的结果是一样的。脚本文件分享命令窗口中的工作区,所以所有的在脚本文件运行之前定义的变量都可以在脚本文件中运行,所有在脚本文件中创建的变量在脚本文件运行之后仍然存在工作区。一个脚本文件没有输入参数,也不返回结果。但是所有脚本文件可以通过存于工作区中的数据进行交互。相对地,MATLAB函数是一种特殊形式的M文件,它运行在独立的工作区。它通过输入参数列表
7、接受输入数据,它通过输出参数列表返回结果给输出参数列表。MATLAB函数的基本形式如下:function outarg1, outarg2, . = fname(inarg1, inarg2, .)%H1 comment line%Other comment lines.(Executable code).(return)function语句标志着这个函数的开始。它指定了函数的名称和输入输出列表。输入函数列表显示在函数名后面的括号中。输出函数列表显示在等号左边的中括号中。(如果只有一个输出参数,中括号可以省略。)输入参数列表是名字的列表,这些名字代表从调用者到函数的值。这些名字被称作形参。当函
8、数被调用时,它们只是从调用者得来实际变量的占位符而已。相似地,输出参数列表也形参组成,当函数结束运行时,这些形参是返回到调用者的值的占位符。在一个表达式中,调用一个函数需要用到实参列表。在命令窗口直接(或在脚本文件中,另一个函数中)键入函数的名字就可以调用这个函数了。当调用一个函数时,第一个实参的值用在第一个形参的位置,而且其余的形参和实参都一一对应。函数的执行从函数的顶部开始,结束于return语句或函数的终点。因为在函数执行到结尾就会结束,所以return语句在大部分的程序中没有必要使用。在输出参数列表中每一个项目都必须出现在function语句中的左边。当函数返回时,存储于输出函数列表的
9、值就会返回给调用者,用于下一步的运算。在一个函数中的初始注释行有特定的目的。在function语句的第一个行注释被称为H1注释行。它应当是对本函数功能的总结。这一行的重要性在于,通过lookfor命令它能被搜索到并显示出来。从H1注释行到第一个空行或第一个可执行性语句可以通过help命令或帮助窗口搜索到。它们则应包含如何使用这个函数的简单总结。下面是一个自定义函数的简单例子。函数dist2用于计算笛卡尔坐标系中点(x1,y1)与点(x2,y2)之间的距离。(把以下代码保存成dist2.m文件)function distance = dist2 (x1, y1, x2, y2)%DIST2 Ca
10、lculate the distance between two point% Function DIST2 calculates the distance between% two points (x1, y1) and (x2,y2) in a cartesian% coordinate system.% Calling sequence:% res = dist2(x1, y1, x2, y2)% Define variables:% x1-x-position of point 1% y1-y-position of point 1% x2-x-position of point 2%
11、 y2-y-position of point 2% distance -Distance between points% Record of revisions:%Date PragrammerDescription of change% =%12/15/98S.J.ChapmanOriginal code% Calculate distance.distance = sqrt(x2-x1).2 + (y2-y1).2);这个函数有4个输入参数各和1个输出参数。一个简单的利用这个函数的例子显示如下:%Script file: test_dist2.m%Purpose:%This progra
12、m test2 function dist2.%Record of revisions:%Date PragrammerDescription of change% =%12/15/98S.J.ChapmanOriginal code%Define variables:%ax-x-position of point a%ay-y-position of point a%bx-x-position of point b%by-x-position of point b%Get input data.disp(Calculate the distance between two points:);
13、ax = input (Enter x value of point a:);ay = input (Enter y value of point a:);bx = input (Enter x value of point b:);by = input (Enter y value of point b:);%Evaluate functionresult = dist2 (ax, ay, bx, by);%Write out result.fprintf(The distance between points a and b is %f n, result);当脚本文件被执行时,它的结果显
14、示如下: test_dist2Calculate the distance between two points:Enter x value of point a:1Enter y value of point a:1Enter x value of point b:4Enter y value of point b:5The distance between points a and b is 5.000000通过手动运算我们可知程序运算的结果是正确的。函数dist2也支持MATLAB帮助子系统。如果你键入“help dist2”,将会得到的结果是: help dist2 DIST2 Cal
15、culate the distance between two point Function DIST2 calculates the distance between two points (x1, y1) and (x2,y2) in a cartesian coordinate system. Calling sequence: res = dist2(x1, y1, x2, y2) Define variables: x1-x-position of point 1 y1-y-position of point 1 x2-x-position of point 2 y2-y-posit
16、ion of point 2 distance -Distance between points Record of revisions: Date PragrammerDescription of change = 12/15/98S.J.ChapmanOriginal code Calculate distance.相似地,键入“lookfor dist2”后将会产生如下的结果: lookfor dist2DIST2 Calculate the distance between two pointtest_dist2.m: %Script file: test_dist2.m lookfo
17、r distanceDIST2 Calculate the distance between two point为了仔细观察工作区在函数执行前后的变化,我们将在MATLAB调试器中加载函数dist2和脚本文件test_dist2。在函数加载前,加载中,加载后设置断点(如图5.1所示)。当程序中止在函数调用之前的断点,它的工作区如图5.2(a)所示。注意工作区中只有变量ax,ay,bx和by。当程序中止在函数调用过程中的断点,它的工作区如图5.2(b)所示。注意工作区中只有变量x1,x2,y1,y2和distance。当程序中止在函数调用后的断点,它的工作区如图5.2(c)所示。注意工作区中原来
18、的变量又重复出现,再加上函数返回的变量result。这些图显示了MATLAB调用M文件的过程中工作区的变化。图5.1 M文件和函数dist2将会被加载到调试器,在函数调用前,调用过程中,调用后设置合适断点图5.2(a)图5.2(b)图5.2(c)图5.2(a)在函数调用之前的工作区(b)函数调用过程中的工作区(c)函数调用之后的工作区5.2 在MATLAB中传递变量:按值传递机制maltab程序与它们函数之间的交互用是按值传递机制。当一个函数调用发生时,MATLAB将会复制实参生成一个副本,然后把它们传递给函数。这次复制是非常重要的,因为它意味着虽然函数修改了输入参数,但它并没有影响到调用者的
19、原值。这个特性防止了因函数修改变量而导致的意想不到的严重错误。这一特性将在下面的函数中得到说明。这个函数中有两个输入参数:a和b。在它的计算中,它修改了变量的值:function out = sample(a, b)fprintf(In Sample: a = %f, b = %f %fn,a,b);a = b(1) + 2*a;b = a .* b;out = a + b(1);fprintf(In Sample: a = %f, b = %f %f n,a,b);下面是调用这个函数的检测程序:a = 2; b = 6 4;fprintf(Before sample: a = %f, b =
20、 %f %fn, a, b);out = sample(a, b);fprintf(After sample: a = %f, b = %f %fn,a,b);fprintf(After sample: out = %f n, out);当这个程序被执行将产生如下的结果: test_sampleBefore sample: a = 2.000000, b = 6.000000 4.000000In Sample: a = 2.000000, b = 6.000000 4.000000In Sample: a = 10.000000, b = 60.000000 40.000000 After
21、sample: a = 2.000000, b = 6.000000 4.000000After sample: out = 70.000000注意,a和b在函数sample内都改变了,但这些改变对调用函数中的值并没有任何的影响。C语言的使用者对按值传递机制比较熟悉,因为C应用它把标量值传递给函数。尽管C语言不能用按值传递机制传递数组,所以对在C语言函数中的形参数组进行意想不到的修改将会导致在调用程序时产生错误。MATLAB改进了按值传递机制,既适于标量,又适应于数组(在MATLAB中参数传递过程中的执行要远比上面讨论中指出的要复杂的多。正如上面指出的,与按值传递相联系的复制将花去大量的时间,
22、但是保护程序以至于不产生错误。实际上,MATLAB用了两种方法,它先对函数的每一个参数进行分析,确定函数的那些参数进行了修改。如果函数没有修改这个参数,它将不会对此参数进行复制,而是简单地指向程序外面的外面的变量,如果函数修改了这个参数,那么这个复制就会被执行)。xyPxyrO图5.3 在笛卡尔平面内的一点P,既可以用直角坐标系来描述,又可以有极坐标来描述例5.3直角坐标与极坐标的转换在笛卡尔平面上的一点的坐标既可以通过直角坐标(x,y)来描述,也可以通过极坐标(r,)来描述,如图5.3所示。两套坐标体系的关系如下式所示:x = r cos(5.1)y = r sin(5.2)(5.3)(5.
23、4)编写两个函数rect2polar和polar2rect,用来实现两坐标体系的转换。其中单位于为度。答案:我们现在应用标准的问题解决方法来创建函数。注意matab的trigonometric(三角)函数的参数的单位是弧度。所以在解决这个问题时,我们必须把度转化为弧度,反之亦然。基本的度与弧度的转换关系如下:180o = radians(5.5)1.陈述问题对这个问题的简单陈述为:编写一个函数,把直角坐标系描的笛卡尔平面内的一个坐标转化对应的极坐标,角的单位为度。反之,把极坐标系内的一个坐标转化对直角坐标。角的单位也为度。2.定义输入输出量函数rect2polar的输入量是直角坐标系(x,y)
24、中的一个点。这个函数的输出量是极坐标(r,)中的一个点。函数polar2rect的输入量是极坐标(r,)中的一个点。此函数的输出量是直角坐标系(x,y)中的一个点。3.定义算法这些函数是非常简单的,所以我们能直接的写出它们的伪代码。函数polar2rect的伪代码如下:x r * cos(theta * pi/180)y r * sin(theta * pi/180)函数rect2polar的伪代码将会用到函数atan2,因为函数的取值范围覆盖了笛卡尔平面的所有象限。(通过MATLAB的帮助系统查找这个函数。)4.把算法转化为MATLAB语句函数polar2rect的MATLAB代码如下所示f
25、unction x, y = polar2rect(r, theta)%POLAR2RECT Convert rectangular to polar coordinates%Function POLAR2RECT accepts the polar coordinates%(r, theta), where theta is expressed in degrees,% and converts them into the rectangular coordinates (x, y)%Calling sequence:%x, y = polar2rect(r, theta)%Define v
26、ariables:%r-Length of polar vector%theta-Angle of vector in degrees%x-x-position of point%y-y-position of point%Record of revisions:%DateProgrammer Description of change%=%09/19/00S.J.ChapmanOriginal codex = r * cos(theta * pi/180);y = r * sin(theta * pi/180);函数rect2polar的MATLAB代码如下所示function r, the
27、ta = rect2polar(x, y)%RECT2POLAR Convert rectangular to polar coordinates%Function RECT2POLAR accept the rectangular coordinates%(x, y) and converts them into the polar coordinates%(r, theta), where theta is expressed in degrees.%Calling sequence:%r, theta = rect2polar(x, y)%Define variables:%r-Leng
28、th of polar vector%theta-Angle of vector in degrees%x-x-position of point%y-y-position of point%Record of revisions:%DateProgrammerDescriptoin of change%=%09/19/00S.J.ChapmanOriginal coder = sqrt( x .2 + y .2);theta = 180/pi * atan2(y, x);注意这两个函数中都包含了帮助信息,所以在应用MATLAB的帮助子系统中,或使用lookfor命令中它们将正常的运作。5.检
29、测程序为了检测这些程序,我们将在MATLAB命令窗口中直接运行它们。我们将用边长分别为3,4,5的三角进行检测,这个三角在初中我们就非常的熟悉了。在这个三角形中最小的角约为36。87度。我们将在四个象限对函数进行检测,以保证在任何情况下转换都是正确的。 r, theta=rect2polar(4,3)r = 5theta = 36.8699 r, theta=rect2polar(-4,3)r = 5theta = 143.1301 r, theta=rect2polar(-4,-3)r = 5theta = -143.1301 r, theta=rect2polar(4,-3)r = 5th
30、eta = -36.8699 x, y= polar2rect(5,36.8699)x = 4.0000y = 3.0000 x, y= polar2rect(5,-143.1301)x = -4.0000y = -3.0000 x, y= polar2rect(5,143.1301)x = -4.0000y = 3.0000 x, y= polar2rect(5,-36.8699)x = 4.0000y = -3.0000在笛卡尔坐标的四个象限内得到的结果均是正确的。例5.2数据排序在许多的科研和工程应用中,随机输入一组数据并对它进行由低到高排序或由高到低进行排序是十分必要的。假设你是一个动
31、物学家,你正在研究大量的动物,并想要鉴定这些动物最大的5%。解决这个问题的最直接的方法是对这些动物的大小按照降序进行排列,取其前5%。对数据进行升序或降序排列似乎是一件非常容易的工作。毕竟,我们经常作这样的事。有一个简单的例子,把数据(10,3,6,4,9)按升序排列成(3,4,6,9,10)。我们应当怎样做呢。我们首先应当浏览整个输入数据列表(10,3,6,4,9)找出其中的最小值(3),然后浏览剩下的输入数据(10,6,4,9)并找到下一个最小值(4),然后继续重复上面的步骤,直到所有的列表中的所有数都能排完。实际上,排序是一个非常困难的工作。当值的个数增加时,用上面简单的排序方法进行运算
32、所消耗的时间将会迅速增加,因为每排一个数就要浏览一个遍输入值。对于大的数据集合,这个方法因太耗时,而不用。更糟糕的是,如果有大量的数据占有计算机的大部分内存我们将如何排序。开发大数据集合的高效排序技术是一个相当活跃的研究领域,它已经成为了一个新的科目。在这个例子中,我们将尽可能简单的算法来说明排序的内容。这个最简单的算法叫做选择性排序(selection sort)。它只是对应上面描述方法的计算机执行。选择性排序的基本算法如下:1.浏览被排序数的列表,并找出其中的最小值。把最小值与排在最前面的数进行交换。如要排在最前面的数就是这个数表最小值,什么也不用做。2.从这个数据列表的第二个数开始浏览找
33、到第二个最小的数。把这个数与当前排在第二个数进行交换。如果当前排在第二位的数就是下一个最小值,那么什么也不用做。3.从数据列表的第三个数开始找到第三个最小的数。把这个数与当前排在第三个数进行交换。如果当前排在第三位的数就是第三个最小值,那么什么也不用做。4.重复以上步骤直至最后一位置排完。当最后一个位置排完后,排序结束。注意:如果我们要对N个数进行排序,这个排序算法要进行N1次浏览才能完成排序。这个步骤的说明如图5.4所示。因为有5个数进行排序,所以要对数据进行4次浏览。首先对整个数据列表进行浏览,得到最小值3,把3置于第一位,故与10进行交换。从第二位开始浏览,得到第二个最小值4,与10交换
34、。从第三位进行浏览,得到最小值6,6恰在第三位上,不用交换。从第四位开始浏览,得到最小值9,与排在第4位的10交换。排序结束。性能提示选择性编程算法是一种极易理解的编程算法,但它的效率是极低的。我们绝不能用它进行大数据集合的排序(例如含有1000个元素的数组)。这个几年里,计算机专家已经发展了许多高效的排序算法。内置于MATLAB的sort和sortrows函数是非常高效的,在实际工作中我们应当应用这些函数。图5.4选择性排序的一个简单例子。我们将开发一个程序,读取从命令窗口读取一个数据集,对它进行升序排列,并出排序后的结果。这个排序将会由独立的自定义函数来完成。答案:这个程序必须提示使用者提
35、供输入数据,对其进行排序,并输出排序结要。这个程序的设计过程如下:1.陈述问题我们刚才没有指定要排序的数据类型。如果数据是数字,那么问题的陈述如下。开发一个程序,它能够读取在命令窗口中输入的任意类型的数字,用独立的自定义函数对读取的值进行排序,并在命令窗口写出排序结果。2.定义输入输出量这个程序的输入值是在命令窗口键入的数字值。这个程序的输出量是写在命令窗口中的排序结果。3.设计算法这个问题可以分解为三大步:Read the input data into an arraySort the data in ascending orderWrite the sorted data第一大步是读取数
36、据。我们必须提示使用者输入输入数据的个数,然后读取数据。因为我们知道所要读取的数的确切个数,所以可以用for循环主读取合适的数据。它的伪代码如下:Prompt user for the number of data valuesRead the number of data valuesPreallocate an input arrayfor ii = 1:number of valuesPrompt for next valueRead valueend下一步,我们必须要用独立的函数对数据进行排序。我们需要对数据进行naval1次浏览,每一次找出一个最小值。我们将用一个指针来寻找每一次浏览
37、的最小值。一量最小值被找到,如果它不在列表的顶端,它就与列表顶端的元素进行交换。伪代码如下:for ii = 1:nvals -1% Find the minimum value in a(ii) through a(nvals) iptr ii for jj = ii +1 to nvals if a(jj) a(iptr) iptr a(iptr) end end% iptr now points to the min value, so swap a(iptr)% with a(ii) if iptr = ii. if ii = iptr temp a(ii) a(ii) a(iptr)
38、 a(iptr) temp endend最后一步是输出排序结果。这个步骤的伪代码不需要重复。最终的伪代码是这三大步伪代码的联合。4.把伪代码转化为MATLAB语言选择性排序的MATLAB代码如下所示:function out = ssort(a)%SSORT Selection sort data in ascending order% Function SSORT sorts a numeric data set into% ascending order. Note that the selection sort% is relatively inefficient. DO NOT USE
39、 THIS% FUNCTION FOR LARGE DATA SETS. Use MATLABs% sort function instead.% Define variables:% a -Input array to sort% ii -Index variable% iptr -Pointer to min value% jj -Index variable% nvals -Number of values in a% out -Sorted output array% temp -Temp variable for swapping% Record of revisions:% Dat
40、e Programmer Description of change% = = =% 12/19/98 S. J. Chapman Original code% Get the length of the array to sortnvals = size(a,2);% Sort the input arrayfor ii = 1:nvals-1% Find the minimum value in a(ii) through a(n) iptr = ii; for jj = ii+1:nvals if a(jj) a(iptr) iptr = jj; end end% iptr now po
41、ints to the minimum value, so swap a(iptr)% with a(ii) if ii = iptr. if ii = iptr temp = a(ii); a(ii) = a(iptr); a(iptr) = temp; endend% Pass data back to callerout = a;调用选择性排序函数的程序如下:% Script file: test_ssort.m% Purpose:% To read in an input data set, sort it into ascending% order using the selecti
42、on sort algorithm, and to% write the sorted data to the Command window. This% program calls function ssort to do the actual% sorting.% Record of revisions:% Date Programmer Description of change% = = =% 12/19/98 S. J. Chapman Original code% Define variables:% array -Input data array% ii -Index varia
43、ble% nvals -Numberof input values% sorted -Sorted data array% Prompt for the number of values in the data setnvals = input(Enter number of values to sort: );% Preallocate arrayarray = zeros(1,nvals);% Get input valuesfor ii = 1:nvals %Prompt for next value string = Enter value int2str(ii) : ; array(ii)=input(string);end% Now sort the datasorted = ssort(array);% Display the sorted result.fprintf(nSorted data:n);for ii = 1:nvals fprintf( %8.4f n,sorted(ii);end5.检测程序为了检测这个程序,我们应当创建一个输入数据集,并运行这个程序
限制150内