第六章Maple程序设计_第1页
第六章Maple程序设计_第2页
第六章Maple程序设计_第3页
第六章Maple程序设计_第4页
第六章Maple程序设计_第5页
已阅读5页,还剩16页未读, 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

/第六章Maple程序设计教学目的:学习并驾驭计算机代数系统Maple下的算法设计和程序设计原理和方法,包括基本程序结构、子程序求值、程序的嵌套以及程序的调试。教学要求:驾驭算法设计和程序设计原理和基本方法。重点内容:算法设计,程序设计。难点内容:算法设计,程序设计。前面,我们运用的是Maple的交互式叮嘱环境.所谓交互式叮嘱环境,就是一次输入一条或几条叮嘱,然后按回车,这些叮嘱就被执行了,执行的结果显示在同一个可执行块中.对于大多数用户来说,利用交互式叮嘱环境解决问题已经足够了,但假如要解决一系列同一类型的问题或者希望利用Maple编写须要的解决特定问题的函数和程序,以期更加充分地利用Maple的强大功能,提高大规模问题的计算效率,进行确定的程序设计是必要的.程序设计主要包括两个方面:行为特性的设计和结构特性的设计.所谓行为特性的设计,通常是指将解决问题的过程的每一个微小环节精确地加以定义,并且还应当将全部的解题过程用某种工具完整地描述出来,这一过程也称为算法的设计.而结构特性设计是指为问题的解决确定合适的数据结构.幸运的是,Maple自身供应了一套编程工具,即Maple语言.Maple语言事实上是由Maple各种叮嘱以及一些简洁的过程限制语句组成的.1编程基础1.1算子所谓算子,是从一个抽象空间到另一个抽象空间的函数.在数学上算子的含义通常是函数到函数的映射.在Maple中,算子常用“箭头”记号定义(也称箭头操作符):>f:=x->a*x*exp(x);>g:=(x,y)->a*x*y*exp(x^2+y^2);另外,函数unapply也可以从表达式建立算子:>unapply(x^2+1,x);>unapply(x^2+y^2,x,y);当我们依次把算子f作用到参数0,a,x^2+a时即可得平常意义上的函数值:>f:=t->t*sin(t);>f(0);>f(a);>f(x^2+a);上述结果是函数作用的例子.而最终一个结果事实上是算子f和算子g:=t->t^2+a复合后再作用到参数x的结果.从数学上讲,作用和复合是不同的,它们产生的结果是有区分的,但在运用它们时,两者还是有些重叠的.在Maple中,可以依靠于语法把它们区分开:当复合两个算子时,结果仍是算子,两个算子的定义域必需是相容的;当把一个算子作用于一个参数(参数必需在算子的定义域中)时,结果是一个表达式;在Maple中,函数作用的语法是运用括号(),如函数f作用到参数u写作f(u).而复合算子的符号是@,多重复合时运用符号@@.通过进一步的例子可以清楚区分作用和复合的功能:f和g复合的结果是算子,而把这个算子作用到参数x得到表达式f(g(x)).例如,,则是一个算子,而是一个表达式,因为x是一个实数.试比较下述两例:>D(g@f);>D(g*h);另外一个应引起留意的问题是算子(函数)和表达式的异同,在第一章中曾探讨过函数和表达式的区分,这里再通过几个例子说明其中的微妙差异:>f1:=x^2+1;>f2:=y^2+1;>f3:=f1+f2;再看下面的例子:>g1:=x->x^2+1;>g2:=y->y^2+1;>g3:=g1+g2;和前面例子不同的是,两个算子(函数)g1,g2相加的结果照旧是函数名g3,出现这个问题的主要缘由是g1和g2分别为x,y的函数,Maple认为它们的定义域不相容.要得到和前例的结果,只需稍作改动:>g3:=g1(x)+g2(y);下面的例子想说明生成Maple函数的两种方式“箭头操作符”及“unapply”之间微妙的差异:>restart:a:=1:b:=2:c:=3:>a*x^2+b*x+c;>f:=unapply(a*x^2+b*x+c,x);>g:=x->a*x^2+b*x+c;由此可见,f中的a,b,c已经作了代换,而g中则显含a,b,c。再看下面试验:>f(x);g(x);f和g两者相同,再对其微分:>D(f);D(g);再变更常数c的值,视察f和g的变更:>c:=15;>f(x);g(x);由此可见,在利用Maple进行函数探讨时,对同一问题应当用不同方法加以校验,而这一切的支撑是数学基础!1.2编程初体验利用算子可以生成最简洁的函数—单个语句的函数,但严格意义上讲它并非程序设计,它所生成的数据对象是子程序.所谓子程序,简洁地说,就是一组预先编好的函数叮嘱,我们由下面的简洁程序来看看Maple程序的结构:>plus:=proc(x,y)x+y;end;这个程序只有2个参数,在程序内部它的名称是x,y,这是Maple最简洁的程序结构,仅仅在proc()和end中间加上在计算中须要的一条或者多条叮嘱即可,Maple会把最终一个语句的结果作为整个子程序的返回结果,这一点须要引起留意.再看下例:>P:=proc(x,y)x-y;x*y;x+y;end:>P(3,4);明显,尽管程序P有三条计算叮嘱,但返回的只是最终一个语句x+y;的结果.要想输出全部的计算结果,须要在程序中增加print语句:>P:=proc(x,y)print(x-y);print(x*y);print(x+y);end:>P(3,4);再看下面几个例子:>forifrom2to6doexpand((x+y)^i);od;>F:=proc(n::integer)ifnmod12=0thentrueelsefalsefiend:>F(123^123),F(1234567890^9);从上面几个简洁的例子可以看出Maple子程序主要包含以下一些内容:把定义的子程序赋值给程序名procname,以后就可以用子程序名procname来调用程序;子程序一律以proc()开头,括号里是程序的输入参数,假如括号中什么都没有,表示这个子程序没有任何输入参数;子程序中的每一个语句都用分号(或冒号)分开(这一点不是主要的,程序设计时,在可能的时候—过程当中的最终一个语句、for-循环、if语句中的最终一个语句省略终结标点也是允许的,这并不是为了懒散,而是因为在终结语句后面插入一个语句产生的影响要比仅仅执行一个新语句产生的影响大);在定义完子程序之后,Maple会显示它对该子程序的说明(除非在end后用冒号结束),它的说明和你的定义是等价的,但形式上不愿定完全相同;Maple会自动地把除了参数以外的变量都作为局部变量(localvariable),这就是说,它们仅仅在这个子程序的定义中有效,和子程序以外的任何同名变量无关.在定义了一个子程序以后,执行它的方法和执行任何Maple系统子程序一样—程序名再加上一对圆括号(),括号中包含要调用的参数,假如子程序没有参数,括号也是不能省略的.除了上面给出的程序设计方法外,在Maple中还可以干脆由“->”(箭头)生成程序,如下例:>f:=x->ifx>0thenxelse-xfi;>f(-5),f(5);甚至于程序名也可以省略,这种状况通常会在运用函数map时遇到:>map(x->ifx>0thenxelse-xfi,[-4,-3,-2,0,1]);假如须要察看一个已经定义好的子程序的过程,用eval叮嘱,查看Maple中源程序(如factor函数)运用下述组合叮嘱:interface(verboseproc=2);print(factor);1.3局部变量和全局变量Maple中的全局变量,是指那些在交互式叮嘱环境中定义和运用的变量,前面所运用的变量几乎都属于全局变量.而在编写子程序时,须要定义一些只在子程序内部运用的变量,称其为局部变量.当Maple执行子程序时,全部和局部变量同名的全局变量都保持不变,而不管在子程序中给局部变量赐予了何值.假如要把局部变量定义为全局变量,须要用关键词global在程序最起先加以声明,而局部变量则用local声明,虽然这是不必要的,但在程序设计时,声明变量是有确定好处的.下面通过实例演示局部变量和全局变量的不同.为了更清楚地视察子程序对全局变量的影响,在子程序外先设定一个变量a的值:>a:=1;>f:=proc()locala;a:=12345678/4321;evalf(a/2);end;>f();>a;>g:=proc()globala;a:=12345678/4321;evalf(a/2);end;>g();>a;明显,在前一个程序中,由于在子程序外已经赋值给a,a是全局变量,它的值不受子程序中同名局部变量的影响;而在后一个子程序中,由于重新把a定义为全局变量,所以子程序外的a随着子程序中的a值的变更而变更.子程序中的输入参数,它既不是全局的,也不是局部的.在子程序内部,它是形式参数,也就是说,它的详细取值尚未被确定,它在程序调用时会被替换成真正的参数值.而在子程序外部,它们仅仅表示子程序接受的参数的多少,而对于详细的参数值没有关系.1.4变量nargs,args和procname在全部程序中都有三个有用的变量:nargs,args和procname.前两个给出关于调用参量的信息:nargs变量是调用的实际参量的个数,args变量是包含参量的表达式序列,args的子序列通过范围或数字的参量选取.例如,第i个参量被调用的格式为:args[i].nargs,args变量通常在含有可选择参量的程序中运用.下面看一个例子:>p:=proc()locali;RETURN(nargs,[seq(i^3,i=args)])endproc:>p(1,2,3,4,5,6,7,8,9,10);该程序利用Maple函数RETURN返回了输入参量的个数以及参量序列的立方列表,RETURN函数运用时必需在其后加圆括号,即使无结果返回时也得如此。下面是一个关于求随意序列最大值的困难程序:>maximum:=proc()localr,i;r:=args[1];forifrom2tonargsdoifargs[i]>rthenr:=args[i]endifenddo;r;endproc:>maximum(12^4,5^7,69^3,10^4+4.9*1000);假如变量procname(程序被调用的名字)存在的话,它可以用来干脆访问该程序,通常用procname(args)完成调用:>f:=proc(a)ifa>0thenRETURN(a^(1/2))elseRETURN('procname(args)')endifendproc:>f(100-3^6),f(10^2+90-9*4); 2基本程序结构全部的高级程序设计语言都具有程序结构,因为为了完成一项困难的任务,仅仅依据语句依次依次执行是远远不够的,更须要程序在特定的地方能够跳转、分叉、循环,……,和此同时,程序结构就产生了.2.1分支结构(条件语句)所谓分支结构,是指程序在执行时,依据不同的条件,分别执行两个或多个不同的程序块,所以常常称为条件语句.if条件结构的语法为:if逻辑表达式1(条件1)then程序块1elif逻辑表达式2(条件2)then程序块2else程序块3fi;其中,fi是条件语句的结束标记,也可以写作endif.下面是一个推断两个数中较大者的例子:>bigger:=proc(a,b)ifa>=bthenaelsebfiend;再如下例:>ABS:=proc(x)ifx<0then-x

elsex;fi;end;明显,这个确定值函数的简洁程序,对于随意实数范围内的数值型变量计算都是没有问题的,但对于非数值型的参数则无能为力.为此,我们须要在程序中添加一条推断参数是否为数值型变量的语句:>ABS:=proc(x)iftype(x,numeric)thenifx<0then-xelsex;fi;else'ABS'(x);fi;end:这里,我们用到一个if语句的嵌套,也就是一个if语句处于另一个if语句当中.这样的结构可以用来推断困难的条件.我们还可以用多重嵌套的条件结构来构造更为困难的函数,例如下面的分段函数:>HAT:=proc(x)iftype(x,numeric)thenifx<=0then0;elseifx<=1thenx;else0;fi;fi;else'HAT'(x);fi;end;尽管在上面的程序中我们用了不同的缩进来表示不同的层次关系,这段程序还是很难懂.对于这种多分支结构,可以用另一种方式书写,只需在其次层的if语句中运用elif,这样就可以把多个分支形式上写在同一个层次中,以便于阅读.>HAT:=proc(x)iftype(x,numeric)thenifx<=0then0;elifx<=1thenx;else0;fi;else'HAT'(x);fi;end;和许多高级语言一样,这种多重分支结构理论上可以多到随意多重.再来看看前面的确定值函数的程序,似乎是完备的,但是,对于乘积的确定值则没有方法,下面用map叮嘱来修正,留意其中的type(…,`*`)是用来推断表达式是否为乘积,进而对其进行化简.>ABS:=proc(x)iftype(x,numeric)thenifx<0then-xelsexfi;eliftype(x,`*`)thenmap(ABS,x);else'ABS'(x);fi;end:>ABS(-1/2*b);分段函数是一类重要的函数,条件语句在定义分段函数时具有明显的优越性,下面学习几个实例:(1)>f:=proc(x)ifx<0thenx^2+1elsesin(Pi*x)fi;end;(2)>g:=proc(x)ifx<=0thenx^2+xelseifx<3*Pithensin(x)elsex^2-6*x*Pi+9*Pi^2-x+3*Pififiend;2.2for循环在程序设计中,常常须要把相同或者类似的语句连续执行多次,此时,通过for循环结构可以更便捷地编写程序.试看下面几个例子:①求1至5自然数的和:>total:=0:forifrom1to5dototal:=total+i:od;②列示2,4,6,8,10及其平方数、立方数:>forifrom2to10by2do'i'=i,'i^2'=2^i,'i^3'=i^3;od;③列示第100到第108个素数(第1个素数是2):>forjfrom100to108doprime[j]=ithprime(j);od;④列示5到10的阶乘及其因数分解:>forninseq(i!,i=5..10)don=ifactor(n);od;⑤一个困难的幂指函数生成:>f:=proc(x,n)locali,t;t:=1;forifrom1tondot:=x^(t^x);od;t;end:>f(x,10);通过上述例子可以看出,Maple的for循环结构很接近于英语语法.在上面例子中,i称为循环变量,用于循环计数,do后面的部分,称为循环体,就是须要反复执行的语句,可以是一条语句,也可以是多条语句.在循环体中,也可以引用循环变量.循环体的结束标记,也就是整个for循环结构的结束标记,是字母“od”(也可以写作enddo).for循环结构的语法可总结为:for循环变量from初始值to终止值(by步长)do循环体od在for循环结构中,初始值和终了值必需是数值型的,而且必需是实数,假如初始值是1,可以省略不写,而步长用by引出.对于上面程序中的重复循环似乎可有可无,但实际科学计算中,重复次数往往成千上万,或者重复的次数无法确定,循环就必不行少了.下面再看一个好用程序—从1到n的自然数的求和程序:>SUM:=proc(n)locali,total;total:=0;forifrom1tondototal:=total+i;od;end;>SUM(1000);该程序仅仅具有练习例举的意义,因为在Maple中像这样一个问题只须要一个简洁语句就可以完成:>Sum(n,n=1..1000)=sum(n,n=1..1000);更一般地,对于确定的求和可用add叮嘱(该叮嘱无惰性函数形式):>add(n,n=1..1000);再看下面一个动画制作例子(图略去):>forifrom-20to30dop||i:=plots[implicitplot](x^3+y^3-5*x*y=1-i/8,x=-3..3,y=-3..3,numpoints=800,tickmarks=[2,2])od:>plots[display](p||(-20..30),insequence=true);2.3while循环for循环在那些已知循环次数,或者循环次数可以用简洁表达式计算的状况下比较适用.但有时循环次数并不能简洁地给出,我们要通过计算,推断一个条件来确定是否接着循环,这时,可以运用while循环.while循环标准结构为:while条件表达式do循环体odMaple首先推断条件是否成立,假如成立,就一遍遍地执行循环体,直到条件不成立为止.下面看一个简洁的程序,是用辗转相除法计算两个自然数的最大公约数(Euclidean算法).>GCD:=proc(a::posint,b::posint)localp,q,r;p:=max(a,b);q:=min(a,b);r:=irem(p,q);whiler<>0dop:=q;q:=r;r:=irem(p,q);od;q;end:>GCD(123456789,987654321);在上面程序中的参数a、b后面的双冒号“::”指定了输入的参数类型.若类型不匹配时输出错误信息.再看下面一个扩展Euclidean算法的例子:>mygcdex:=proc(a::nonnegint,b::nonnegint,x::name,y::name)locala1,a2,a3,x1,x2,x3,y1,y2,y3,q;a1:=a;a2:=b;x1:=1;y1:=0;x2:=0;y2:=1;while(a2<>0)doa3:=a1moda2;q:=floor(a1/a2);x3:=x1-q*x2;y3:=y1-q*y2;a1:=a2;a2:=a3;x1:=x2;x2:=x3;y1:=y2;y2:=y3;od;x:=x1;y:=y1;RETURN(a1)end:>mygcdex(2^10,6^50,'x','y');2.4递归子程序正如在一个子程序中我们可以调用其他的子程序一样(比如系统内部函数,或者已经定义好的子程序),一个子程序也可以在它的内部调用它自己,这样的子程序我们称为递归子程序.关于递归算法及其设计将在下节详述.在数学中,用递归方式定义的例子许多,如Fibonacci数列:下面我们在Maple中利用递归子程序求Fibonacci数列的第n项:>F:=proc(n::nonnegint)::listifn<=1thenn;elseF(n-1)+F(n-2);fi;end:>seq(F(i),i=0..19);>F(20);但是,应用上述程序计算F(2000)时则不能进行,缘由是要计算F(2000),就先得计算F(1999)、F(1998)、……,无限制地递归会耗尽系统的堆栈空间而导致错误.由数据结构有关理论知道,递归程序理论上都可以用循环实现,比如我们重写上面的程序如下:>F:=proc(n::nonnegint)localtemp,fnew,fold,i;ifn<=1thenn;elsefold:=0;fnew:=1;forifrom2tondotemp:=fnew+fold;fold:=fnew;fnew:=temp;od;fi;end:>F(2000):>time(F(2000));利用循环,程序不像前面那么易懂了,但同时带来的好处也是不行忽视的,循环结构不仅不会受到堆栈的限制,而且计算速度得到了很大的提高.我们知道,Maple子程序默认状况下把最终一条语句的结果作为子程序的结果返回.我们可以用RETURN(或return)叮嘱来显式地返回结果,比如前面的递归结果可等价地写成:>F:=proc(n::nonnegint)optionremember;ifn<=1thenRETURN(n);fi;F(n-1)+F(n-2);end:程序进入n<=1的分支中,执行到RETURN叮嘱就跳出程序,而不会接着执行后面的语句了.另外,运用RETURN叮嘱在确定程度上可以增加程序的可读性.在其次章中曾提到Maple的自动求导功能,下面通过实例说明。第一个例子是关于分段函数求导问题:>F:=x->ifx>0thensin(x)elsearctan(x)fi;>Fp:=D(F);将分段函数及其导数的曲线绘制在同一个坐标系中,实线为F,虚线为Fp:>plot({F,Fp},-3*Pi..3*Pi,linestyle=[1,4],color=black);其次个例子试图说明Maple能够完成对多个函数的自动求导:>f:=proc(x)locals,t;s:=ln(x);t:=x^2;s*t+3*t;end;>fp:=D(f);程序fp是怎样构造的呢?对比f和fp的源代码可以帮助我们了解Maple自动求导机理:在fp的源代码中,每一条赋值语句V:=f(v1,v2,…vn)之前是Vx:=fp(v1,v2,…vn),此处vi是局部变量或形式参数,fp(v1,v2,…vn)是对f(v1,v2,…vn)在形式上的微分,用该赋值语句的的导数取代最终一条语句或返回值。这种方法被称为向前自动微分法。在Maple内部对这种方法有一些限制:不能有递归程序,不能有记忆选项,不能对全局变量赋值,只能有常数的循环变量,等。下面一个例子说明自动求导在计算时间和存储空间上的优越性,这是一个利用递归定义的的幂函数的自动求导问题:>f:=proc(x,n)locali,t;t:=1;foritondot:=x^tod;tend;>f1_3:=D[1$3](f):#求f对x的三阶导数>setbytes:=kernelopts(bytesused):#运用寄存器内存量>settime:=time():#起先计时>f1_3(1.1,22);>cpu_time:=(time()-settime)*seconds;#计算时间>memory_used:=evalf((kernelopts(bytesused)-setbytes)/1024*kbytes,5);#存储空间大小再利用符号微分重复上述步骤:>f22:=unapply(f(x,22),x):>setbytes:=kernelopts(bytesused):>settime():>(D@@3)(f22)(1.1);>cpu_time:=(time()-settime)*seconds;>memory_used:=evalf((kernelopts(bytesused)-setbytes)/1024*kbytes,5);明显Maple自动求导在计算时间和存储空间上具有明显的优越性。奇异利用自动微分解决较困难的函数求导问题有特殊现实的意义。3算法及其设计算法(algorithm)这一术语是对9世纪巴格达数学家al-Khowarizmi(阿尔-花拉子米)这一名字的讹用,他写的书《Kitabal-jabrw’almuqabalah》(代数学)演化成为现在中学的代数教科书,al-Khowarizmi强调求解问题的有条理的步骤.最初,讹用的algorism表示运用十进制做算术运算的规则.18世纪时algorism演化为algorithm.随着计算机的发展,算法的概念有了更广的含义.所谓算法,简洁地说,是一组严谨地定义运算依次的规则,并且每一个规则都是有效的且是明确的,此依次将在有限的次数下终止.作为一个算法,应具有四个特征:1)有效性(effectiveness):即算法中的每一个步骤必需是能够实现的,算法执行的结果要能达到预期的目的;2)确定性(definiteness):算法中的每一个步骤都必需是有明确定义的,不允许有模棱两可的说明,也不允许有多义性;3)有穷性(finiteness):算法必需在有限个步骤之后终止,且还应具有合理的执行时间;4)通用性(currency):算法过程应适用于要求形式的全部问题,而不只是用于一组特定的输入值.对于一个问题,假如可以通过一个计算机程序,在有限的存储空间内运行有限长的时间而得到正确的结果,则称该问题是算法可解的.但算法不等于程序,也不等于计算方法.程序可以作为算法的一种描述,但程序通常还需考虑许多和方法和分析无关的微小环节问题,这是因为在编写程序时要受到计算机系统运行环境的限制.通常,程序设计的质量不行能优于算法的设计.算法的设计方法许多,下面介绍几种常用的算法设计方法.3.1枚举法枚举法(也称列举法,穷举法)的基本思想是依据提出的问题,列举全部可能状况,并检验那些满意问题中提出的条件,那些不满意.因此,枚举法常用于“是否存在”或“有多少种可能”等类型的问题.枚举法是重要而好用的算法之一.枚举法的特点是算法比较简洁,但当列举的可能状况较多时,执行列举算法的工作量将会很大.因此,在用列举法设计算法时,应当留意使方案优化,尽量削减运算工作量.通常,只要对实际问题作详细的分析,将和问题及关的学问条理化,完备化,系统化,从中找出规律,或对全部可能的状况进行分类,引出一些有用的信息,列举量是可以削减的.例1(百钱买百鸡问题):用100元钱买100只鸡,大公鸡5元钱1只,大母鸡3元钱1只,小鸡1元钱3只,问如何买法?这是一个不定方程求解问题.事实上,设大公鸡,大母鸡,小鸡的数量分别为x,y,z,则:算法1)完全枚举法>baiji:=proc()localx,y,z;forxfrom1to100doforyfrom1to100doforzfrom1to100doifx+y+z=100and5*x+3*y+z/3=100thenprint(`COCK`=x,`HEN`=y,`CHICK`=z)fi;od;od;od;end:在算法1)中,各层循环均需100次,总循环次数1003次,明显计算量太大.对问题稍做思索,则可知x最大值不超过19,其次层循环中y的最大值为33-5x/3,z的值自然等于100-x-y,即第三层循环已经没有必要.由此,可得改进算法.算法2)改进算法>baiji2:=proc()localx,y,z;forxfrom1to19doforyfrom1to33-5*x/3doz:=100-x-y;if5*x+3*y+z/3=100thenprint(`COCK`=x,`HEN`=y,`CHICK`=z)fi;od;od;end:不难分析,算法2的总循环次数大为降低,仅为310次.枚举原理是计算机应用领域中特殊重要的原理.许多实际问题,若接受人工列举是不行想象的,但由于计算机运算速度快,擅长重复操作,因此,枚举法是计算机算法中的一个基础算法.例如,为了证明一个命题为真,则只需一一列举每一种状况均为真即可.枚举法是比较笨拙而原始的方法,它最大的缺点是运算量大,但在实际问题中,局部运用枚举法却是有效的.为了更进一步说明枚举法的运用,下面再看一个例子:例2(均分问题):有三个人均分油,共有7满桶,7半桶,7空桶,但不许打开桶倒油,如何分法?>sever_oil:=proc()locala1,a2,a3,b1,b2,b3,k1,k2,k3,k,kk,n,i;kk:=[];fora1from1to4dofora2from1to6doif2*a1+a2=7thena3:=7-a1-a2;forb1from1to6doforb2from1to6doif2*b1+b2=7anda1+b1<7anda2+b2<7thenb3:=7-b1-b2;if((7-a1-b1)+(7-a2-b2)+(7-a3-b3)=7)and(2*(7-a1-b1)+(7-a2-b2)=7)thenk1:=[a1,a2,a3];k2:=[b1,b2,b3];k3:=[(7-a1-b1),(7-a2-b2),(7-a3-b3)];k:={k1,k2,k3};n:=nops(kk);ifn=0thenkk:=[[k1,k2,k3]];elseforifrom1tondoif{op(kk[i])}=kthenbreak;endif;enddo;ifi>nthenkk:=[op(kk),[k1,k2,k3]];endif;endif;endif;endif;enddo;enddo;endif;enddo;enddo;n:=nops(kk);forifrom1tondoprintf("第%a种状况\n",i);printf("第一家:%a个满桶%a个半桶%a个空桶\n",kk[i,1,1],kk[i,1,2],kk[i,1,3]);printf("其次家:%a个满桶%a个半桶%a个空桶\n",kk[i,2,1],kk[i,2,2],kk[i,2,3]);printf("第三家:%a个满桶%a个半桶%a个空桶\n\n",kk[i,3,1],kk[i,3,2],kk[i,3,3]);enddo;endproc:>sever_oil();第一种状况第一家:1个满桶,5个半桶,1个空桶其次家:3个满桶,1个半桶,3个空桶第三家:3个满桶,1个半桶,3个空桶其次种状况第一家:3个满桶,1个半桶,3个空桶其次家:2个满桶,3个半桶,2个空桶第三家:2个满桶,3个半桶,2个空桶在该程序中,为了给出可读性的输出,大量运用了printf语句格式.3.2归纳法归纳法的基本思想是通过列举少量的特殊状况,经过分析,最终找出一般关系.明显,归纳法要比枚举法更能反映问题的本质.但是,要从一个实际问题中总结归纳出一般的关系却并非易事,尤其要归纳出其数学模型更难.从本质上讲,归纳就是通过视察一些简洁而特殊的状况,最终总结出有用的结论或解决问题的有效途径.归纳是一种抽象,即从特殊现象中找出一般关系.由于在归纳过程中不行能对全部可能的状况进行列举,因而最终得到的结论还只是一种揣测(即归纳假设).所以,对于归纳假设还必需加以严格的证明,通常接受数学归纳法.下面是一个归纳法的典型例子。例3:求前n个自然数学的平方和:明显,有:从上述几个值很难看出Sn和n之间的关系.G.Polya曾经提出下表所示的归纳公式:n123456…1+2+3+…+n136101521…12+22+32+…+n21514305591…(12+22+32+…+n2)/(1+2+3+…+n)3/35/37/39/311/313/3…由此即可揣测:而于是:要证明上式是否正确,需用数学归纳法对其加以证明.3.3递归法若一个算法通过把问题连续地归纳到带更小输入的相同问题,据此来解决原来的问题,则这个算法称为递归的.例4:给出计算an的递归算法,其中a是非0实数而n是非负整数.>A:=proc(a::nonnegative,n::nonnegint)ifn=0then1;elseA(a,n):=a*A(a,n-1);fi;end:例5:把线性搜寻算法表达成递归过程.>search1:=proc(a::list,i::nonnegint,j::nonnegint,x)ifa[i]=xtheni;elifi=jthen0;elsesearch1(a,i+1,j,x);fi;end:例6:构造对分搜寻算法的递归形式.>search2:=proc(a::set,i,j,x)localm;m:=trunc((i+j)/2);ifx=a[m]thenreturnm;elifx<a[m]andi<mthenreturnsearch2(a,i,m-1,x);;elifx>a[m]andj>mthenreturnsearch2(a,m+1,j,x);elsereturn0;fi;end:下面的例子想说明递归和迭代的微妙关系.例7:n!的计算.>fact:=proc(n::posint)ifn=1then1;elsefact(n):=n*fact(n-1);fi;end:明显,这是递归算法.而假如是下述程序,则为迭代算法:>fact1:=proc(n::posint)localx,i;x:=1;forifrom1tondox:=x*i;od;end:3.4递推法所谓递推,是指从已知条件动身,逐次推出所要求的各中间结果及最终结果.其中,初始条件或是问题本身已经给定,或是通过对问题的分析和化简后确定.递推本质上也属于归纳法.下面通过一个著名的难题的递推来说明该方法.例8(汉诺塔问题):19世纪后期一个著名的游戏叫做汉诺塔,它是由安装在一个板上的3根柱子和若干大小不同的盘子构成。起先时,这些盘子依据大小次序放在第一根柱子上,使得大盘子在底下。游戏的规则是:每次把1个盘子从一根柱子移动到另个根柱子,但是不允许这个盘子放在比它小的盘子上面。游戏的目标是把全部的盘子依据大小次序都放在其次根柱子上,并且将最大的盘子放在底部。令Hn表示解n个盘子的汉诺塔问题所须要的移动次数。建立一个关于序列{Hn}的递推关系。设初始时刻n个盘子均在柱1。依据游戏规则我们可以用Hn-1次移动将上边的n-1个盘子移到柱3。在这些移动中保留最大的盘子不动。然后,用一次移动将最大的盘子移到其次根柱子上。我们可以再运用Hn-1次移动将柱3上的n-1个盘子移到柱2,把它们放到最大的盘子上面,这个最大的盘子始终放在柱2的底部。简洁看出,运用更少的步数是不行能求解这个难题的。这就证明白Hn=2Hn-1+1初始条件是H1=1,因为依照规则一个盘子可以用一次移动从柱1移到柱2。我们可以运用迭代方法求解这个递推关系。留意H=2H+1=2(2H+1)+1=2H+2+1=2(2H+1)+2+1=2H+2+2+1……=2H+2+2+……+2+1=2+2+……+2+1=上述公式可以用数学归纳法证明。一个古老的传闻告知我们,汉诺塔里的僧侣依据这个游戏规则从一个柱子到另一个柱子移动64个金盘子。他们1秒钟移动1个盘子,据说当他们结束游戏时世界就到了末日。这个世界将在僧侣起先移动盘子多久以后终结?依据公式,僧侣须要=18446744073709551615次移动来搬这些盘子。每次移动须要1秒钟,他们将用5849亿年来求解这个难题,因此这个世界的寿命应当比它已有的寿命更长。例9(编码字的枚举):一个计算机系统把一个十进制数字串作为一个编码字,假如它包含偶数个0就是有效的,否则就不是有效的.设an是有效的n位编码字的个数,试找出an的递推关系.留意到a1=9(因为串0是无效的).本问题只需通过考虑由n-1位数字串构成n位数字串的方式来解决.而该方式共有两种:第一种:在一个n-1位有效数字串后加上一个非0数字,而加这个数字的方式有9种,此时,an=9an-1;其次种:在一个n-1位无效数字串后加上0数字,这样做的方式数等于无效n-1位数字串的个数,因为共存在10n-1个n-1位有效数字,此时,an=10n-1-an-1.从而,有:an=9an-1+(10n-1-an-1)=10n-1+8an-1>a:=proc(n::posint)ifn=1then9;else10^(n-1)+8*a(n-1);fi;end;有些实际问题,既可以用递推法也可以用递归法求解。但递归和递推算法的实现方法是不一样的。递推是从初始条件动身,逐次推出所需求的结果;而递归则是从算法本身到达递归边界。通常,递归算法要比递推算法更清楚易读,其结构也比较简练。特殊是在许多比较困难的问题中,很难找到从初始条件推出所需结果的全过程,此时,设计递归算法要比递推算法简洁得多。但递归算法也有一个致命的缺点,即执行的效率比较低。3.5回溯法回溯法也称为摸索法,该方法首先短暂放弃规模大小的限制,并将问题的候选按某种依次逐一枚举和检验.当发觉当前问题选解不行能是解时,就选择下一个候选解;倘如当前候选解除了还不满意规模要求外,满意全部其他要求时,接着扩大当前候选解的规模,并接着摸索.假如当前候选解满意规模在内的全部要求时,该候选解就是问题的一个解.在回溯法中,放弃当前候选解,找寻下一个候选解的过程称为回溯.扩大当前候选解的规模,并接着摸索的过程称为向前摸索.例10(皇后问题):由n2个方块排成n行n列的正方形,称之为“n元棋盘”.假如两个皇后位于n元棋盘上的同一行、或同一列、或同一对角线上,则称它们在相互攻击。现要求找出访n元棋盘上的n个皇后互不攻击的布局。n个皇后在n元棋盘上的布局共有nn种为了从中找出互不攻击的布局,须要对此nn种方案逐个进行检查,将有攻击的布局删掉。这是一种列举法。但这种方法对于较大的n,其工作量会急剧增加。而事实上,从互不攻击的布局的要求可分析出逐一列举也是没有必要的。例如:每一行只能放一个皇后。又如,假如第一行上的皇后已经在某一列,则其它行上的皇后就不行能在该列。下面用回溯法来设计求解这个问题的算法。首先,将问题归结为在n元棋盘的每一行上安置一个皇后,并假设第i个皇后被安置在第i行上。定义一个一维数组A(1:n),其中每一个元素A(i)用于在安置皇后过程中随时记录第i行上的皇后所在的列号。由此可知,这种状况下,事实上已经剔除了两个皇后在同一行上的可能性。因此,在安置每一行上的皇后时,只需考虑每两个皇后不能在同一列或同一对角线上即可。简洁验证,第i行上的皇后和第k行上的皇后正好在同一列的充要条件为:A(i)-A(k)=0正好同在某一对角线上的充要条件为:|A(i)-A(k)|-|i-k|=0初始时,将每一行的皇后均放在第一列,即初始状态为A(i)=1,i=1,2,….n.然后,从第一行(即i=1)起先进行以下过程。设前i-1行上的皇后已经布局好,即它们互不攻击。现考虑支配第i行上的皇后的位置,使之和前i-1行的皇后也都互不攻击。为了实现这一点,可以从第i行皇后的当前位置起先向右搜寻:(1)若A(i)<=n,则需检查第i-1行皇后是否互不攻击。若无攻击,则考虑支配下一行皇后的位置;若有攻击,则将第i行皇后右移一个位置,重新进行这个过程。(2)若A(i)>n,则说明在前i-1行皇后的当前布局下,第i行皇后已无法安置。此时将第*行皇后重新放在第一列,且回退一行,再考虑第i-1行皇后和前i-2行皇后均互不攻击的下一个位置。在这种状况下,假如已退到第0行(n元棋盘不存在第0行),则过程结束。(3)若当前安置好的皇后是在最终一行(即第n行),则说明已找到了n个皇后互不攻击的一个布局,将这个布局打印输出。然后将第n行的皇后右移一个位置,重新进行这个过程,以便进一步找出另外的布局。综合以上过程,可以形象地概括成一句话:“向前走,碰壁回头”。这种方法也称为深度优先搜寻技术DFS(DepthFirstSearch)。>Q:=proc(col::integer,n::integer,Row,Lbias,Rbias,result::array)locali,j,A,m;forifrom1tondoifRow[i]=1andLbias[col+i]=1andRbias[n+col-i]=1thenresult[col]:=i;Row[i]:=0:Lbias[col+i]:=0:Rbias[n+col-i]:=0;ifcol=nthenA:=matrix(n,n);forjfrom1tondoformfrom1tondoA[j,m]:=0;enddo;enddo;forjfrom1tondoA[result[j],j]:=Q;enddo;print(A);elseQ(col+1,n,Row,Lbias,Rbias,result);endif;Row[i]:=1:Lbias[col+i]:=1:Rbias[n+col-i]:=1;endif;enddo;endproc:>Queen:=proc(n::integer)locali,Row,Lbias,Rbias,result;ifn<4thenprint(²»Äܵõ½½á¹û);return;endif;Row:=array(1..n);result:=array(1..n);Lbias:=array(1..(2*n));Rbias:=array(1..(2*n));forifrom1tondoRow[i]:=1;enddo;forifrom1to2*ndoLbias[i]:=1:Rbias[i]:=1;enddo;Q(1,n,Row,Lbias,Rbias,result);return;endproc:>Queen(4);例11:在9()个方格中填入1到N(N≥10)内的某9个数字,每个方格填一个整数,使全部相邻两个方格内的数字和为质数.试求出全部满意这个要求的各种数字填法.可用摸索法找到问题的解.即从第一个方格起先,为当前方格找寻一个合理的整数填入,并在当前位置正确填入后,为下一个方格找寻填入的合理整数,如不能为当前方格找到一个合理的可填整数,就要回退到前一方格,调整前一方格的填入数.当为第9格也填入了合理的整数后,就找到了一个解,将该解输出,并调整第9格的填数去找下一个解.为了进一步说明回溯法及其程序设计的本质,下面的程序给出的是求解类似问题的通用形式,即个方格中中填数的问题.其中check子程序是为判定两数和为质数而设计的.>check:=proc(s::array,top::integer,n::integer)locali,j,m;iftop=1thenreturntrue;endif;forifrom1totopdoforjfrom1totopdoifi<>jands[i]=s[j]thenreturnfalse;endif;enddo;enddo;m:=(n-1)*n;forifrom1totop-1doif(imodn)<>0andisprime(s[i]+s[i+1])=falsethenreturnfalse;endif;ifi<=mandtop>n+iandisprime(s[i]+s[i+n])=falsethenreturnfalse;endif;enddo;returntrue;endproc:>Get:=proc(number::integer,n::integer)localresult,top,i,ok,A;result:=array(1..(n*n));top:=1;i:=1;whiletruedoresult[top]:=i;ok:=check(result,top,n);ifokthentop:=top+1;i:=1;elseifi<numbertheni:=i+1;elsetop:=top-1;i:=result[top]+1;whilei>=numberandtop>1dotop:=top-1;i:=result[top]+1;enddo;endif;endif;iftop>n*nthenA:=matrix(n,n,result);print(A);top:=top-1;i:=result[top]+1;whilei>=numberandtop>1dotop:=top-1;i:=result[top]+1;enddo;endif;iftop<=1andresult[top]>=numberthenreturn;endif;enddo;endproc:要运行该程序,只需输入最大的N及方格的行数n即可:>Get(10,3);3.6贪欲法贪欲法是一种不追求最优解,只希望得到较为满意的解的方法.贪欲法一般可以快速得到满意的解,因为它省去了为找最优解要穷尽全部可能而必需耗费的大量时间.贪欲法常以当前状况为基础作最优选择,而不考虑整体状况,所以贪欲法不如回溯法.例如去商场购物找钱时,为使找回的零钱数最少,不考虑找零钱的全部可能的方案,而是从最大面值的币种起先,按递减的依次考虑各币种,先尽量用大面值的币种,当不足大面额时才去考虑下一种较小面值的币种.这就是贪欲法.例12(装箱问题):设有编号为0,1,2,…,n-1的n种物品,体积分别为v0,v1,…,vn-1,将这n种物品装到容量为V的箱子里.约定这n种物品的体积不超过V.不同的装箱方案所须要的箱子数可能不同,求装尽这n种物品的箱子数最少是多少?>count:=proc(cargo::list,volume::float)localm,n,box,i,j,box_count,cargos;m:=nops(cargo);cargos:=sort(cargo);box:=[];box_count:=0;forifrom1tomdon:=nops(box);forjfrom1tondoifvolume-value(convert(box[j],`+`))>cargos[i]thenbreak;endif;enddo;ifj=0thenbox:=[[cargos[i]]];elifj<=nthenbox[j]:=[op(box[j]),cargos[i]];elsebox:=[op(box),[cargos[i]]];box_count:=box_count+1;endif;enddo;return[box_count,box];endproc:>count([1,1.1,1.2,2,2.5,3.4,3,4,6,7.9,8,5],10.0);3.7数字模拟法数字模拟也称数字仿真或计算机仿真,也就是用计算机模拟实物系统,对实物系统的结构和行为进行动态演示,以评价或预料系统的行为效果,为决策供应信息.一般分为两类:确定性模拟和随机性模拟.在实际问题的求解中,当遇到很难建立数学模型或算法的问题时,通常接受模拟法.例13(战舰问题):一敌舰在某海疆内沿正北方向航行时,我方战舰恰位于敌舰的正西方向1nmile处,我舰向敌舰放射制导鱼雷,敌舰速度为0.42nmile/min,鱼雷的速度为敌舰速度的2倍.假设鱼雷和敌舰距离<0.02nmile时敌舰被击中,试写出敌舰被击中的Maple仿真程序.建立直角坐标系(图1),设敌舰为动点Q,鱼雷为动点P,Q点的初始位置为Q(1,0),P点的初始位置为P(0,0).为了计算出追击过程中每一时刻P点和Q点的详细位置,需分别描P、Q两点运动的方向、速度及位置变更规律。由于Q点从初始点动身沿y轴方向运动且速度为常数v,故Q点在t=tk时刻的位置为Q(1,v0tk)。由于P点的运动方向始终指向Q,设在t=tk时刻,P点位置是P(xk,yk),则向量,此时P点的运动方向可由其方向余弦表示:其重量表示为:P点运动速度为常数v=2v。取时间步长t=2s,设在t=t时刻,P点的位置为P(x,y),于是P点的位置变更规律为:据此,可写出其计算机仿真程序如下:>Digits:=4:with(plots):>f:=proc()locald,x,y,x1,y1,dt,v0,v1,t,i,j,px,py,qx,qy,F,G:i:=0:dt:=2:v0:=0.42/60:v1:=2*v0:x:=0:y:=0:d:=1:t:=0:whiled>0.02doi:=i+1:d:=sqrt((1-x)^2+(v0*t-y)^2):x1:=x+v1*dt*(1-x)/d:y1:=y+v1*dt*(v0*t-y)/d:x:=x1:y:=y1:t:=t+dt:px[i]:=x:py[i]:=y:qx[i]:=1:qy[i]:=v0*t:od:printf("击中敌舰的时间t=%as\n",t);printf("[%12a,%12a]%12a\n",px,py,qx,qy);forjfrom1toidoprintf("[%12a,%12a]%12a\n",px[j],py[j],qy[j]);od:F:=plot([seq([px[j],py[j]],j=1..i)],style=point,color=red);G:=plot([seq([qx[j],qy[j]],j=1..i)],style=point,color=blue);display({F,G},scaling=constrained,title='Figure1');endproc:>f();击中敌舰的时间t=96s数字模拟总体来说是一种近似方法.在上述问题的解决过程中,接受逐点扫描法将各个时刻的P,Q坐标标示出来,并推断P,Q两点间的距离而得结论.因此,时间步长,相应的最小距离值的是确定该问题解的精度的主要参数.给参数赋不同的值将会得到不同的结果.当然,该问题还可通过数学分析的方法求解.下面的内容想进一步说明仿真法和数学分析法的异同.设敌舰的速度为常数v0,追曲线为y=y(x)。即在时刻t,鱼雷的位置在点P(x,y)处,这时敌舰的位置在Q(1,vt)处。由于鱼雷在追击过程中始终指向敌舰,而鱼雷运动方向沿曲线的切线方向,所以有:即:两边对x求导,得:即:由已知鱼雷的速度为2v,即:*因为所以,即代入(*)式得曲线满意的微分方程模型令方程化为:分别变量,积分并代入初始条件计算得:即而两式相减得干脆积分并代入初始条件得:这就是鱼雷追击曲线的方程.因鱼雷击中敌舰时,它的横坐标x=1,代入曲线方程得y=.即敌舰航行至nmile处时将被击中,这段航程所须要的时间是95.2381s.毫无疑问,随着计算机技术的进一步普及,计算机模拟在科学探讨中将会充当更为重要的角色.为了进一步展示计算机模拟的作用和地位,下面的例子是古典概型中一个典型问题—掷骰子问题的仿真:>ludo:=proc()localm,s,i,S,bool,dice,budeng;dice:=[seq(rand(1..6)(),i=1..10^5)]:print(`Theprobabilityofludoisasfollow:`);print(evalf(add(i,i=dice)/nops(dice)));s:=[]:print(`Theprobabilityofiofdiceisasfollow:`);formfrom1to6dos:=[op(s),evalf(nops(select(has,dice,m))/nops(dice))]:od:s:S:=s;forifrom1tonops(s)doprintf("s[%a]=%a\n",i,s[i]);S[i]:=evalf(ceil(s[i]*100)/100,2);od:budeng:=[];print(`So,wewillhavetheresultasfollows:`);printf("");forifrom1to6doifevalf(1/6,2)=evalf(s[i],2)thenprintf("s[%a]=",i);elsebudeng:=[op(budeng),i];endif;enddo;printf("0%a\n",evalf(1/6,2));ifbudeng<>[]thenprintf("");forifrom1tonops(budeng)doprintf("s[%a]=0%a",budeng[i],S[budeng[i]]);enddo;endif;return;endproc:>ludo();s[1]=.1664000000s[2]=.1683300000s[3]=.1674300000s[4]=.1651900000s[5]=.1651200000s[6]=.1675300000s[1]=s[2]=s[3]=s[4]=s[5]=s[6]=0.174子程序求值在Maple子程序中的语句,求值的方式和交互环境中的求值有所不同,这在某种程度上是为了提高子程序的执行效率而考虑的.在交互式环境中,Maple对于一般的变量和表达式都进行完全求值(除了数组、映射表等数据结构外).比如先将b赋给a,再将c赋给b,则最终a的值也指向c.>a:=b:b:=c:a+1;但在子程序内部状况就不一样了,试看下述试验:>f:=proc()locala,b;a:=b;b:=c;a+1;end:结果尽然是:>f();这是因为a和b都是局部变量,Maple对于子程序中的局部变量只进行一层的求值.下面,我们针对子程序中的不同的变量,系统介绍其求值机制.4.1参数子程序中的参数,就是那些在proc()的括号中的变量.参数是一类特殊的变量,在调用子程序时,会把它们替换成实际的参数.>sqrt1:=proc(x::anything,y::name)y:=x^2;end:>sqrt1(d,ans);>ans;我们再来试试别的参数,比如前面赋值过的a作为第一个参数,其次个参数仍用ans,只不过加了单引号:>sqrt1(a,'ans');>ans;事实上,Maple在进入子程序以前就已经对参数进行了求值,因为是在子程序外进行求值,所以求值规则听从调用时所在的环境.上面是在交互式环境下调用sqrt1得到的结果,作为比照看看在子程序内部调用上面的sqrt1子程序会有什么不同.>g:=proc()locala,b,ans;a:=b;b:=c;sqrt1(a,ans);end:>g();因为这次调用是在程序内部,所以只进行第一层求值,进入sqrt1的参数值为b.Maple对于子程序的参数,都只在调用之前进行一次求值,而在子程序内部出现的地方都用这次求值的结果替换,而不再重新进行求值.如前所述之sqrt1:>sqrt1:=proc(x::anything,y::name)y:=x^2;y+1;end:>sqrt1(d,'ans');可见,对参数赋值的作用只有一个—返回确定的信息.因为在子程序内部,恒久不会对参数求值.我们可以认为,参数是一个0层求值的变量.4.2局部变量和全局变量Maple对于局部变量只进行一层求值,也就相当于用eval(a,1)得到的结果.这种求值机制不仅可以提高效率,而且还有着更重要的作用.比如在程序中,我们往往须要把两个变量的值交换,一般地,我们运用一个中间变量来完成这样的交换.但假如求值机制和交互式环境中一样,将达不到我们的目的.试看,在交互式环境下,我们企图用这样的方法交换两个未被赋值的变量会有什么后果:>temp:=p:p:=q:q:=temp;不管在交互式环境下还是在程序中,Maple对于几乎每一个全局变量都进行完全求值,而对于数组、映射表和子程序,Maple接受赋值链中的上一名称来求值.除了上面这些特殊数据对象及下面一个特例外,总结起来,Maple对于子程序的参数进行0层求值,对于局部变量进行1层求值,而对于全局变量,则进行完全求值.对于上面说的求值规则,在Maple中还有个特例,就是“同上”操作符“%”须要引起留意.就其作用来说,它应当属于局部变量.在进入一个子程序时,Maple会自动地把该子程序中的%设置成NULL(空).但是,对于这个“局部变量”,Maple不遵循上面的规则,无论在那儿都会将其完全求值.我们下面通过一个例子说明它在子程序中的求值机制:>f:=proc()locala,b,c,d;print("Atthebeginning,[%]is",%);a:=b;b:=c;c:=d;a+1;print("Now[%]is",%);end:>f();可以看出,尽管在子程序内部,局部变量a仅进行一层求值,但指代a+1的同上操作符却进行了完全求值,得到d+1的结果.4.3环境变量所谓环境变量就是从一个程序退出时系统自动设置的全局变量.Maple中有几个内建的环境变量,如Digits,Normalizer,Testzero,mod,printlevel等.从作用域来看,它们应当算作是全局变量.在求值上,它们也像其他全局变量一样,始终都是完全求值.但是假如在子程序中间对环境变量进行了设置,那么,在退出该子程序时,系统会自动地将其复原成进入程序时的状态,以保证当前行时的环境.正因如此,我们称其为环境变量.试看下面程序:>f:=proc()print("Enteringf.Digitsis",Digits);Digits:=20;print("NowDigitshasbecome",Digits);end:>g:=proc()print("Enteringg.Digitsis",Digits);Digits:=100;print("NowDigitsis",Digits);f();print("Backingfromf,Digitsis",Digits);end:>f();>g();>Digits;可以看出,从子程序g中返回时,Digits又被自动地设成了原来的值(10).假如你须要

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论