Fortran程序设计第七讲过程(一)回顾第六讲数组对数组使用内置函数基本函数查询函数变换函数WHERE与FORALL结构MASK及其意义可变大小数组的定义文件的打开、关闭、使用第七讲内容子程序的使用自定义函数的使用过程的参数7.1过程自顶向下的程序设计方法的一个核心步骤:将算法分解为若干个称为子任务的逻辑子部分,再把这些逻辑子部分分解成许多更小的块,每个块完成一个简单的、易于理解的工作。在Fortran里面,把每个子任务作为独立的程序单元来编码,这些程序单元可以独立开发和调试。这些独立的程序单元称为外部过程(externalprocedure)Fortran有两种外部过程子程序(subroutine)函数子程序(functionsubprogram)在程序设计中引入过程的意义:独立子任务的测试可重用代码孤立无目的的副作用7.1子程序SUBROUTINEdisplay(var)REAL::varWRITE(*,*)varENDSUBROUTINEPROGRAMtest_subREAL::a=1CALLdisplay(a)ENDPROGRAM通用格式SUBROUTINEsubroutine_name(argument_list)...(声明部分)…(执行部分)…RETURNENDSUBROUTINE[name]参数列表argument_list调用程序通过参数列表所列的参数将变量的值传递给子程序子程序的结构和任何Fortran程序一样子程序必须包含声明部分和执行部分声明部分对子程序中所使用的变量、数组进行声明同样,声明部分必须在子程序的第一条执行语句之前完成调用机制当调用程序调用子程序时调用程序的执行被暂时挂起子程序的执行部分开始运行运行到子程序的RETURN或ENDSUBROUTINE时,调用程序开始运行调用子程序语句下面的程序代码例7.1SUBROUTINEsolve(s,a,b,n)IMPLICITNONEREAL(KIND=8)::s,a,b,h,tINTEGER::n,ks=0d0h=(b-a)/ns=(a**2+dsin(a))+(b**2+dsin(b))dok=1,n-1t=a+k*hs=s+2d0*(t**2+dsin(t))ENDDOs=s*h/2d0ENDSUBROUTINEsolve调用PROGRAMtest_subREAL(KIND=8)::result,a=-2D0,b=2D0INTEGER::n1=200,n2=2000CALLsolve(result,a,b,n1)WRITE(*,*)resultCALLsolve(result,a,b,n2)WRITE(*,*)resultENDPROGRAM7.2自定义函数如果某个过程的结果是单个的数值、逻辑值、字符串或数组,可以写成函数形式。FUNCTIONname(argument_list)…声明部分…执行部分…name=exprRETURNENDFUNCTION[name]例7.2FUNCTIONfunc1(x)REAL(KIND=8)::func1,xfunc1=x**2+DSIN(x)ENDFUNCTIONPROGRAMtest_subIMPLICITNONEREAL(KIND=8),EXTERNAL::func1WRITE(*,*)func1(10D0)ENDPROGRAM子程序和函数的分别使用自定义函数之前,必须要声明自定义函数会返回一个值,由函数的名字带回调用函数不需要CALL,直接用类似内置函数的方式调用,可以直接写入表达式函数也可以写成子程序的形式函数应用举例REALFUNCTIONF1(x)REAL::xF1=(x+1)/(x-1)ENDFUNCTION函数应用举例REALFUNCTIONF2(a,b,c,x)REAL::a,b,c,xF2=a*x*x+b*x+cENDPROGRAM7.3过程的参数传递7.3.1Fortran过程的参数传递是地址传递过程在被调用时,形参通过实参得到确定的值这个值是怎么得来的?当调用过程时,主程序传递一个指针来指向实参表中各个参数的存储位置。过程查找调用程序所指向的内存位置,以获得它需要的实参值REAL(KIND=8)::result,a,bINTEGER::n1,n2CALLsolve(result,a,b,n1)SUBROUTINEsolve(s,a,b,n)实参result的地址传递给形参s,其余三个参数同理要注意的问题过程调用的参数传递要求:调用中的参数要与过程中声明的参数在个数、类型和位置摆放次序上保持一致。如果不一致,一般会提示编译错误7.3.2变量的局部性不做特别声明的情况下:过程中所使用的变量仅在该过程中有效因此,过程中使用的变量名可以和其它过程或主程序的变量名同名过程与调用者之间仅仅通过参数列表来共享内存过程对于共享内存的内容修改,在过程结束后仍然有效在例7.1中,虽然过程中和主程序中都声明变量a,b,但它们并不会冲突比如在过程中,主程序中,都会采用变量名为i,j之类的变量来进行循环控制,它们都只在各自的过程中有效,不会发生冲突SUBROUTINEfactorial(result,n)INTEGER::result,n,iresult=1DOi=1,nresult=result*iENDDOENDSUBROUTINEPROGRAMvar_testINTEGER::i,facDOi=1,5CALLfactorial(fac,i)WRITE(*,*)‘factorialof&‘,i,’is’,facENDDOENDPROGRAM7.3.4数组作为参数数组作为过程的参数,非常灵活,暂时我们只了解最稳妥的办法SUBROUTINEexample_dim(dim1,dim2,n)INTEGER::nREAL::dim1(n),dim2(n)dim1=dim1+dim2ENDSUBROUTINE7.3.5字符变量作为参数当字符变量作为过程的形参时,用*来声明字符变量的长度SUBROUTINEsample(string)CHARACTER(len=*)::stringWRITE(*,’(1X,A,I3)’)‘Lengthofvariable=‘,LEN(string)ENDSUBROUTINE7.3.6过程作为其它过程的参数问题的提出:例7.1是一个复合梯形积分的子程序,可以对形如的函数计算在某一区间内的积分。可不可以对任一函数都可以调用这个函数来计算积分呢?把函数形式作为参数传递进去就可以了)sin()(2xxxf自定义函数作为参数传递SUBROUTINEsolve(func,s,a,b,n)IMPLICITNONEREAL(KIND=8),EXTERNAL::funcREAL(KIND=8)::s,a,b,h,tINTEGER::n,ks=0d0h=(b-a)/ns=func(a)+func(b)dok=1,n-1t=a+k*hs=s+2d0*func(t)ENDDOs=s*h/2d0ENDSUBROUTINEsolve例7.3改进例7.111)(2)()(2)(nibaihafbfafhdxxfFUNCTIONfunc1(x)REAL(KIND=8)::func1,xfunc1=x**2+dsin(x)ENDFUNCTIONPROGRAMtest_subIMPLICITNONEREAL(KIND=8),EXTERNAL::func1REAL(KIND=8)::resultcallsolve(func1,result,-2d0,2d0,20000)WRITE(*,*)resultendprogram7.4INTENT属性地址传递的参数传递方式实参的值在过程中可能会被修改有些实参用户并没有意图去修改程序书写错误导致实参改变且错误难以发现引入INTENT属性规定形参的类别SUBROUTINEsub1(input,output)IMPLICITNONEREAL,INTENT(IN)::inputREAL,INTENT(OUT)::outputoutput=2.*inputinput=-1.ENDSUBROUTINE试图对IN属性的形参操作,编译器会报错INTENT属性有三种取值INTENT(IN)形参仅用于向过程传递输入数据INTENT(OUT)形参用于将结果返回给调用程序INTENT(INOUT)或INTENT(INOUT)既传递输入数据也返回结果事实上,我们更多也基本上只用INTENT(IN)属性来确保不希望修改的参数不会被错误的代码所修改7.5用模块共享数据问题的提出:一些过程都需要使用同样的常量有些变量需要在多个过程中使用(全局变量)难点:前面讲过,过程与过程之间的数据仅能通过形参列表来传递怎么实现的?如果是常量,在每个过程中都定义如果是变量,需要列在参数列表中传递进去缺点:在所有的过程中重复添加常量的声明—繁琐而且容易出错当需要使用的变量很多时,参数列表太长例:!在子程序中和主程序中定义相同常量的例子SUBROUTINEsub1(input_data,n,output)IMPLICITNONEINTEGER::nREAL,PARAMETER::PI=3.14159,h=6.626E-34REAL::input_data(n),output…执行语句ENDSUBROUTINEPROGRAMdata_testREAL,PARAMETER::PI=3.14159,h=6.626E-34INTEGER,PARAMETER::nn=100REAL::source_data(nn),out…CALLsub1(source_data,nn,out)…ENDPROGRAM解决的办法旧的Fortran版本使用COMMON语句COMMON语句在课本P169-176有讲,请自行查阅现在通行的方法是用模块来共享数据模块的定义模块共享数据的使用MODULEshared_dataIMPLICITNONESAVE!在SAVE之后定义的变量都是全局变量INTEGER,PARAMETER::DIM_SUM=100REAL,PARAMETER::PI=3.14159,h=6.626E-34REAL::input_data(DIM_NUM)ENDMODULE模块的使用SUBROUTINEsub1(input_data,ouput)USEshared_dataIMPLICITNONEREAL::input_data(DIM_NUM),output…执行语句ENDSUBROUTINEPROGRAMdata_testUSEshared_dataREAL::source_data(DIM_NUM),out…CALLsub1(source_data,out)…ENDPROGRAMUSEMODULE_name必须出现在程序单元中的其它语句之前,在它之前只可能有PROGRAM或SUBROUTINE语句或注释语句例题写一个过程,用来求任一函数在某点的一阶导数