Chapter 8 函数

子程序 SUBROUTINE

把经常需要用到的程序封装成为一个子程序,通过 call 进行调用 call sub1(para1, para2)

subroutine sub1()......return
end subroutine sub1 ! 这里的sub1作为子函数名也可以省去

其中最后一行一般是 return 指返回到先前调用子程序的位置,不写的话算作默认添加,当然return也可以放在任何地方。其次,关于子程序的变量行代码是与主程序相互独立的,利用行代码进行跳转的时候主程序只会跳转到主程序里相应行代码的位置,子程序亦然。

子程序传参用的是传址调用,传递出去的参数和接受的参数用的是同一个地址,在子程序中修改变量也会修改主程序的值

subroutine add(first,second)
implicit none
integer :: first, second
! 用来接受参数的变量还是要声明类型,变量的初值就等于传递进来的数值

自定义函数FUNCTION

自定义函数的用法与子程序基本一致,但有几点不一样:

  • 调用自定义函数前要先声明
  • 自定义函数执行后仅返回一个数值
  • 不再需要用 call 来调用,可以直接调用并赋值
    参考一段示例代码:
program ex0807
implicit nonereal :: a=1real :: b=2real, external :: addwrite(*,*) add(a,b)stop
endfunction add(a,b)
implicit nonereal :: a,b ! 传入的参数real :: add ! add跟函数名称一样,这边不是用来声明变量,! 是声明这个函数会返回的数值类型add = a+breturn
end

特别注意在主程序中需要用external声明,然后在函数里面real :: add 表示返回值是real类型

全局变量 COMMON

全局变量只是定义了一块共享内存空间,取全局变量的时候是根据声明时候相对位置关系来做对应,而非用变量名称

program main
integer a,b,c,d,e,f
common a,b,c,d,e,f......
end program mainsubroutine sub()
integer n1,n2,n3,n4,n5,n6
common n1,n2,n3,n4,n5,n6......
end subroutine sub

假如子程序仅用到第六个共享变量也必须要全部声明出来才行(STUPID!),为此给出一种对变量归类的方法简化:

integer :: a,b
common /group1/ a ! 将a放在group1这个区间
common /group2/ b ! 将b放在group2这个区间! 调用时候
integer num1, num2
common /group1/ num1
common /group2/ num2

还有一种设置共享变量的方法就是用 BLOCK DATA

program ex0812
implicit noneinteger :: a,bcommon a,b          ! a,b放在不具名的全局变量空间中integer :: c,dcommon /group1/ c,d ! c,d放在group1的全局变量空间中integer :: e,fcommon /group2/ e,f ! e,f放在group2的全局变量空间中write(*,"(6I4)") a,b,c,d,e,fstop
endblock data
implicit noneinteger a,bcommon  a,b          ! a,b放在不具名的全局变量空间中data    a,b /1,2/    ! 设定a,b的初值integer c,dcommon  /group1/ c,d ! c,d放在group1的全局变量空间中data    c,d /3,4/    ! 设定c,d的初值integer e,fcommon  /group2/ e,f ! e,f放在group2的全局变量空间中data    e,f /5,6/    ! 设定e,f的初值
end block data

BLOCK DATA 这一段程序很像子程序,可以独立运行,甚至在编译过程中是先于主程序运行的,不需要别人的调用即可执行,不过他的功能只在于设置共享变量的初值,不能有执行命令。还有就是,全局变量不能声明为常量,即不能有PARAMETER出现。
最后还要强调的是,声明全局变量的时候不要改变他的数据类型,否则很容易出错。

函数中的变量

传递参数给函数时候最重要的是类型一定要正确,因为Fortran是传递变量的内存地址,不同数据类型在解读内存内容方法上会不一样。函数所接收的参数都是已经配置好内存空间的。

! 比如这里有一个数组 a
call Show(a)    ! 输入a第1个元素内存地址
call Show(a(2)) ! 输入a第2个元素内存地址

在实际应用过程中,传递数组参数时候比较灵活,重要的还是程序员自己判断是否超过数组范围,传递后的数组可以灵活的更改数组维度、坐标范围。多维数组在传递的时候,只有最后一维数组可以不赋值,其他维度都必须赋值。这是因为在取用数组元素的时候,会根据坐标计算元素内存的所在位置,在计算过程中一般是不涉及最后一维的。比如假设数组声明为A(L,M,N),对于元素A(x,y,z)所在的位置就是1+(x-1)+(y-1)L+(z-1)LM。

一般传递的参数变量生存周期是随着子程序或者函数的结束而结束,但是如果需要继续保存数据的话可以用命令 SAVE,比如下面这个计算调用多少次子程序的操作:

subroutine sub()implicit noneinteger :: count = 1save countcount = count + 1return
end subroutine sub

特别注意的是,在这里变量count 的初始化只进行一次(因为已经被save下来了),再次进入该子程序的时候并不会再进行初始化!!!

如果需要将函数作为参数传递的话,需要特别用external, intrinsic声明一下:

......
real, external :: func ! 声明func是自定义函数
real, intrinsic :: sin ! 声明sin是库函数
call ExecFunc(func)
call ExecFunc(sin)
......subroutine ExecFunc(f)real, external :: f ! 声明参数f是个函数return
end subroutine ExecFunc

特殊参数的使用方法

由于在传递参数的时候是传递地址,参数intent可以确保该参数是不可以改变的:
integer, intent(in) :: a ! 指定a不可修改 只读参数
integer, intent(out) :: b ! 指定b可重新设置数值

INTREFACE是一段程序模块,用于说清楚要调用的函数类型及返回值类型,在以下情况写这样的使用接口是必要的

  • 函数返回值为数组
  • 指定参数位置来传递参数
  • 所调用的函数参数数目不固定
  • 输入指标参数
  • 函数返回值为指针
    举个函数接口的例子:
Interfacefunction funcimplicit nonereal ......   ! 仅能说明参数或传回值类型integer ......end function funcsubroutine subimplicite noneinteger ......end subroutine sub
end interface

如果某个参数是不确定是否一定会传递的,即需要用到不定个数的参数传递,这时候可以声明时调用参数optional说明该参数是可选项,另外可调用参数 present 确定该参数是否传递过来,返回bool类型。如果多个参数传递,需要确保每一个参数仅传给对应的变量,可以作特别指定:

integer, optional :: a,b,c,d,e,f
call sub(b=3,f=5) ! 仅给参数b f传递值

特殊的函数类型

在Fortran函数,需要递归使用的函数必须加上关键词recursive
recursive integer function fact(n) result(ans)
最后的result(ans)是用来在程序代码中用另一个名字设置函数传回值,这里用ans来代替原来的fact,使用:ans = n*fact(n-1)
内部函数可以作为某个函数的归属,仅在这个函数内调用

program main......contains   ! contains后声明的函数仅在里面使用subroutine localsub......end subroutine localsubfunction localfunc......end function localfunc
end program

MODULE

MODULE 一般用于把有相关功能的函数及变量封装在一起,比如可以把全局变量封装在一起,然后调用这个module即可:

moudle globalimplicit noneinteger a,bcommon a,b
end moduleprogram mainuse global    ! a b变量已经声明过不用再声明a=1b=2......
end program

一般声明为module后的变量如果不是去全局变量的话只是在使用函数内的局部变量,如果需要传递参数则需要加上关键词SAVE 这样效果很全局变量差不多

moudle globalimplicit noneinteger,save :: a,b
end module

module 还能用于存放自定义类型的数据以及函数

module module_name......
containssubroutine sub.......end subroutine subfunction func......end function func
end module

一般在编写大型程序的时候都需要对程序做模块化处理,把特定功能的函数放在一个模块里,一个模块里有个好处就是变量是公用的,调用起来比较方便,且一个模块内的函数相互调用也不用经过声明。

module constantimplicit nonereal, parameter :: PI = 3.14159real, parameter :: G  = 9.81
end modulemodule typedefimplicit nonetype playerreal :: anglereal :: speedreal :: distanceend type
end modulemodule shootuse constantuse typedefimplicit none
contains! 由角度、切线速度来计算投射距离subroutine Get_Distance( person )implicit nonetype(player) :: personreal rad, Vx, timerad  = Angle_TO_Rad( person%angle )      ! 单位转换Vx   = person%speed * cos(rad)           ! 水平方向速度time = 2.0 * person%speed * sin(rad) / G ! 在空中飞行时间person%distance = Vx * time              ! 距离 = 水平方向速度 * 飞行时间returnend subroutine! 把0~360的角度转换成0~2PI的弧度real function Angle_TO_Rad( angle )implicit nonereal angleAngle_TO_Rad = angle*pi/180.0returnend functionend moduleprogram ex0837use shootimplicit noneinteger, parameter :: players = 5type(player) :: people(players) = (/ player(30.0, 25.0, 0.0),&player(45.0, 20.0, 0.0),& player(35.0, 21.0, 0.0),&player(50.0, 27.0, 0.0),&player(40.0, 22.0, 0.0) &/)integer :: Ido I=1, playerscall Get_Distance( people(I) )write(*,"('Player ',I1,' =',F8.2)") I, people(I)%distanceend dostop
end

其他的功能

entry 能创建一个新的函数入口,调用入口时候会跳过进入点之前的程序。

program ex0838implicit nonecall sub()call mid()stop
end subroutine sub()implicit nonewrite(*,*) "Hello."entry mid()write(*,*) "Good morning."return
end

运行结果是:

Hello.
Good morning.
Good morning.

第一次调用函数sub正常运行,出现两行输出;第二次调用mid则仅有后面那一行输出。

如果需要调用其他文件里面的内容(变量或者函数)可以用关键词 include来引入新的文件,使用的位置比较随意,在编译的时候会在此处展开。
include 'ex0840.f90'
但其实这种方法并不很推荐,因为这样的速度还是很慢,还是相当于加载所有的文件

《Fortran 95 程序设计》阅读笔记三相关推荐

  1. 06软件构架实践阅读笔记之六

    06软件构架实践阅读笔记之六 今天阅读的是第五章实现质量属性,在书中,存在这样的一句话:如果不顾及所有的质量属性,每一个好的质量属性都是有害的.在我认为,这句话就是最好的说明. 在上一章中,聊到的是系 ...

  2. 软件构架实践读书笔记二

    什么是软件构架 1.构架定义了元素 2.系统可能而且确实由多个结构组成 3.具有软件的每个计算系统都有一个软件构架 4.某个元素的行为就是构架的内容 构架是一个高层的.系统的总体结构.构架的内容就是构 ...

  3. 软件需求模式阅读笔记三

    阅读的章节是基础需求模式和信息模式.从现在开始,到了本书的重点,介绍了多种需求模式. 基础需求模式:其中包括系统间接口需求模式,系统间交互需求模式,技术需求模式,遵从标准需求模式,参考需求需求模式和文 ...

  4. 《软件构架实践》阅读笔记03

    <实现质量属性>这一章为我们提供了每个质量属性的构架方面的指导.一个好的设计需要实现一些质量属性,实现这些质量属性依赖于基本的设计决策,战术就是影响质量属性响应控制的设计决策. 了解到的第 ...

  5. 《软件构架实践》7-9章读后感

    今天,我阅读了<软件构架实践>7-9章. 第7章主要有4个主题,生命周期的构架:设计构架:形成团队结构及其与架构的关系:创建构架系统.首先讲了生命周期中的构架,把构架作为软件开发过程基础的 ...

  6. 《软件构架实践》阅读笔记三

    这一章主要是通过ISSS系统的构架来分析交通管制系统的实际解决方案. 首先从物理视图的角度来分析,物理视图主要是一些硬件方面的视图,通过它我们可以清楚的看到各个硬件之间相互关联关系,使系统的物理分布显 ...

  7. 软件构架实践(第2版)学习笔记

    一.软件架构.架构模式.参考模型.参考架构 1.对于软件架构定义有很多种,通用的定义是:某个软件或计算机系统的软件架构是该系统的一个或多个结构,他们由软件元素,这些元素的外部可见属性以及这些元素之间的 ...

  8. 软件构架实践 第2版 学习笔记

    分享一下我老师大神的人工智能教程!零基础,通俗易懂!http://blog.csdn.net/jiangjunshow 也欢迎大家转载本篇文章.分享知识,造福人民,实现我们中华民族伟大复兴! 一.软件 ...

  9. 《构架之美》阅读笔记三

    为什么说软件构架很重要? 主要有以下3个基本原因: (1)涉众之间的交流:软件构架是一种常见的对系统的抽象,绝大多数系统的涉众都以此作为彼此理解.协商.达成共识或相互沟通的基础. (2)早期设计决策: ...

  10. 信用卡葵花宝典 阅读笔记(三)

    <信用卡葵花宝典>第三篇阅读笔记是关于收单业务的基础知识以及风险管理.银行卡业务从大的概念上可以分为发卡业务和收单业务.收单业务通过为商户提供银行卡支付结算服务来获取商户回佣收入,同时通过 ...

最新文章

  1. hadoop集群配置与启动
  2. 全球及中国智能隐形眼镜行业供需现状与营销策略建议报告2022-2028年
  3. 2018南京java工资_2018年Java开发工程师的待遇如何?
  4. 闭包、执行环境、作用域链
  5. 中小型研发团队架构实践:集中式日志ELK
  6. 数组中查找並返回数组_java数组查找常见情况
  7. 前端学习(1880)vue之电商管理系统电商系统之获取左侧菜单数据
  8. 基于虚拟帐号的邮件系统extmail(1)
  9. 会议家用多面手!智能微型投影设备导购
  10. 烤烟发病叶片高光谱特征分析
  11. 利用C++的stringstream对各种数据类型转换
  12. Android视频加密
  13. Window驱动开发
  14. 在Ubuntu20.04(Linux Mint)中同步通达信软件的自选股和画线数据
  15. 微波雷达感应模块,人体存在感应雷达技术,广告屏智能感应显示
  16. 神经网络参数初始化方法
  17. python turtle setheading_Python turtle.right与turtle.setheading的区别
  18. 【中秋快乐】如何用three.js实现我的太空遐想3D网页
  19. 拼多多校招内推编程题2 大整数乘法
  20. C#环形缓冲区(队列)完全实现(转)

热门文章

  1. 《模式分类》原书(中文版)及课后习题答案(英文版)分享
  2. 【视频开发】【Live555】摄像头采集,264编码,live555直播
  3. iphone 计算机乱码,苹果电脑Word变乱码怎么办
  4. CodeProject上的两个简单绘图程序
  5. 基于SpringBoot校园疫情防控管理系统 校园出入登记系统
  6. redis 过期删除策略和淘汰策略 -redis设计与实现笔记
  7. 大众点评后端项目解析
  8. jQuery源码解读一
  9. 你觉得jQuery源码中有哪些写得好的地方?
  10. Android显示实时帧率工具