彭国伦Fortran95学习笔记(一)第八章至第十六章

基本读了一遍,把剩下的总结完。

第八章 函数

基本的调用不解释了,这里强调一些细节。

  1. 可以在子函数最后使用end表示函数的结束,不过实际上使用retrun是标准的用法,并且可以选择让函数在执行到一般的时候return返回。
  2. Fortran77不支持递归,但是Fortran90支持,不过递归的调用需要额外的命令,由于递归调用的计算速度很慢,科学计算中并不推荐使用,参考Page195
  3. Fortran中的传参均为传址调用(by reference),subroutinefunction均是传址,但是一般不建议function中改变传参的值。如果所传的参数值发生改变,那么主程序中的变量值也会变化,这点和matlab或者C中均不同。
  4. subroutinefunction的区别,后者有返回值,另外function调用之前要先声明real,external :: add。注意这里的externcal不是C语言中防止重复编译的关键字,是声明add是一个函数而不是变量。另外函数声明也可以不用单独写出,例如下面
program ex0808
 implicit none
 real a,b
 real add
 add(a,b) = a+b
 write(*,*) add(a,3.0)
end 

全局变量common

基本的用法不讲了,这里强调一下全局变量是根据相对位置关系对应的,一个common中写入多个变量时,一定要注意顺序

int a,b,c
common /abc/ a,b,c

common的赋初值不能直接像数组一样使用DATA,而是要用block data,具体用法如下,类似subroutine一样写在程序之外单独一块:

program ex0812
...
end

block data
implicit none
  integer a,b,c
  common /abc/ a,b,c
  data a,b,c /1,2,3/
end block data

实际上在程序中用一般的赋值方式对common中的变量赋值就可以进行初始化,这一用法的用意除了标准化之外,执行顺序也不同。block data中声明的初值,在主程序调用之前就已经赋值了。所以这也要求程序中只能有声明和赋初值相关,不能出现程序命令,而且附加parameter在这里赋初值也不支持。

函数中的变量

实际上也属于函数本身的注意事项,但是和变量特别相关。

  1. 函数所传的参数类型一定要对应,否则可能出现float类型的变量被以int类型的编码读取,数据是错误的。
  2. 如果所传参数为数组,则传输的是数组的第一个元素的地址。例如传递一个4个元素的一维数组a时,子函数中的变量定义可以是integer ainteger a(3)integer a(4)integer a(2,2)。甚至子函数中数组长度超过主程序中,语法都是允许的,数据读取时是根据连续内存读取的,所以并不影响使用。但是编译在这里不进行长度校对,如果编程人员不小心,就会出现很多数组操作改变其他变量值的问题,即下标溢出(实操吃过很多次亏,这个一定要小心)

变量的生存周期

即一般子函数中变量的生存周期,只维持到当前子函数执行结束。如果希望像C中静态变量一样,子函数退出后,变量中的值保留,就需要使用关键字save

integer :: cout = 1
save cout

而不使用save关键字的子函数,在有的编译器中,变量也不会在每次执行后清零。

传递函数

即子函数传参时,传递的参数中含有子函数名,这样的子函数,需要在主程序中声明,例如

real,external :: func !声明func是一个自定义函数
real,intrinsic :: sin !声明sin是库函数

call ExecFunc(func)

特殊参数的使用方法

两种技巧,

  • 设置赋值参数属性,设置某些参数是只读不能改变的
integer,intent(in) :: a   !子函数中值不允许改变
integer,intent(out):: b   !子函数中值必须改变

这种技巧可以帮助我们进行语法检查,减少调试时的工作量

  • 不指定个数的参数传递,这个并不对应重载,而是C语言里面传参的默认值,这里要用到option这个关键字
subroutine sub(a,b)
...
integer :: a
integer,optional :: b
if (present(b)) then
   ....
else
  ...
end if
return
end

而其中的present(b)表示是否传入了参数b。从而实现不定个数传递

函数接口interface其实是命令语句之前的声明语句,在特殊返回值或者调用参数不固定时会被要求使用,具体参阅书中Page189,这里只给出一个例子:

program ex0825
implicit none
  interface 
    function reandom10(lbound,ubound)
    implicit none
    real :: lbound,ubound
    real :: reandom10(10) !函数的返回值为数组
    end function
  end interface
  ...
end

内部函数,Pure函数,elemental函数

第一个为某个子函数为另外一个子函数专属调用时,可以使用contain直接写在函数体中。后面两个为并行使用。

Module

可以用来封装程序模块,功能和include有点像,但是并不是纯粹的添加代码段,而是具有一定的语法。主要用途有声明全局变量,声明type结构体,声明函数等。在后面第11章会有详细的介绍。

第九章 文件

writeread的使用不重复了,这里强调后面出现的新知识。

inquire(file=filename,exist=alive)

Page237,可以检查一个文件是否存在

open(unit = fileld, file=filename, access="direct", &
   form="formatted",recl=6,status="old")

Page254,使用direct模式读取文件,允许在文件中的读取位置进行跳跃,而非按顺序读取。

open(unit = fileld, file=filename, access="direct", form="unformatted",&
      access="direct",recl=1,status="replace")

Page258,读取二进制文件时,通过recl=1设置读取字段的单位长度,1代表为1bytes还是4bytes要看不同的编译器。

write(unit=string,fmt="(I2,'+',I2,'=',I2)") a,b,a+b
write(*,*) string

Page260,成为internal file(内部文件),将字符串生成并赋值给字符串变量,然后字符串可以正常输出。

integer :: a=1,b=2,c=3
namelist /na/ a,b,c
write(*,nml=na)

可以将na名下的所有变量按顺序输出,不用使用do循环。

第十章 指针

和C一样的指针的概念,但是具体的语法约定有很多的不同,先给出最基础的使用方式
方式一

integer, target:: a=1 !声明一个可以当成目标的变量
integer,pointer:: p  !声明一个可以指向整数的指针
p=>a !将指针指向变量a的地址
a=2  !改变变量的值
p=3 !改变地址的值,注意,是值,不是地址

方式二

integer,pointer :: p
allocate(p)
p=100

需要注意的就是fortran的指针变量赋值时,也是改变指向的地址的值,这点和C不同。另外就是使用动态数组记得使用deallocate(p)释放内存。为了方便使用,还有三个工具函数。

  • associated(pointer,[target]) 检查指针是否设置指向
  • p=>null()让指针指向一个空地址
  • nullify(pointer1,[pointer2,…])功能同上,fortran90以上支持

指针数组

这个蛮厉害的,感觉比C要强大

integer,target :: b(5)=(/1,2,3,4,5/)
integer,pointer :: a(:)

a=>b !数组b的所有值都给a
a=>b(1:3) !只把数组b的前三个元素给a
a=>b(1:5:2) !把第1,3,5个元素给a

或者使用动态数组

integer,pointer :: a(:)
allocate(a(5))
a=(/1,2,3,4,5/)
write(*,*) a
deallocate(a)

但注意allocate声明成指针数组时,生存周期不止在当前函数结束,一定要deallocate释放内存。

指针与数组

指针可以是数组的参数或者返回值,但是需要使用接口interface。另外指针参数声明时不需要intent关键字。给一个例子Page282

program ex1007
  ...
  integer,pointer :: p(:)
  interface 
    function getmin(p)
      integer,pointer :: p(:)
      integer,pointer :: getmin
    end function
  end interface
  ...
  getmin(p)
end 

指针的基本应用

指针在fortran中可以方便数组中固定元素的访址,以及较大数据的交换给出例子分别如下

b=>a(5,5,5)
a(5,5,5) = 1 !常规写法,但是找到这个元素需要经过地址计算
b=1   !速度更快
type person
...
end type

type(person):: a,b,temp
type(person),pointer :: pa,pb,pt
!正常交换交换的数据较多
temp = a
a = b
b = temp
!指针长只有4bytes,交换指针要比交换数据本身更快
!但是注意此时a,b本身是否交换了需要再验证一下
pt=>pa
pa=>pb
pb=>pt

指针的高级应用

其实还是蛮基础的,就是数据结构常见的内容,链表,双向链表和环形链表。

第十一章 Module及面向对象

基础的用法给出一个,

module bank
  implicit none
  private money
  public LoadMoney,SaveMoney,Report
  integer::money = 1000000
  contains
    subroutine LoadMoney(num)
    ...
    end subroutine
  ...

program main
use bank
...

和C语言中的类不同,不需要实例,use module之后直接调用里面的函数就可以了,这一点其实更加直观。而关键字private表示只有module内部可以使用,public表示主程序中也可以调用,并且关键字对函数和变量都可以使用。另外module中可以通过contain添加函数。module中的变量默认的生存周期为整个函数,所以money变量在每次调用之后值改变但不会清零。

另外强调的一点是,module中函数之外声明的变量,函数本身可以使用。即money变量可以在子函数LoadMoney中使用,这不需要传参。这和C语言中的类又很像。

use的使用细节

直接给实例

program main
  use A, aa=>va !把module A中的变量aa改名为va使用
  use B, only:vc !只用module B中的变量vc
  use C, only:bb=>bc !前两者的组合

另外,module是支持嵌套的,这和类的继承思想类似,但private声明的部分不会被继承。

再论interface接口

除去前面函数的参数或者返回值比较特殊时需要使用interface之外,该命令还可以被用来实现重载(overload,2333骨王)和自定义操作符,直接给出两者例子

module MA
interface show
   module procedure show_int
   module procedure show_character
end interface   
contains
   !两个子函数的具体实现
   ...   
end module

program main
...
call show(1)
call show("abc")
...
end
module MA
  ...
  interface operator(+)
    module procedure add
  end interface
contain
   integer function add(a,b)
   ...
   end function
end module

program main
...
type(ta) a,b,c
c = a+b
...
end

就是说自己定义了结构体情形下+号代表的意义,就可以很方便的使用了,另外这个操作符可以是任意符号,不一定非得是当前存在的。

第十二章 编辑器的高级使用

首先讲了一下Visual Fortran如何进行调试,这部分略过。后半部分讲了优化,这是需要留意的知识。提升执行速度书中罗列了几种,避免重复运算等算法相关的优化这里不提。

  • 表达式的选择,加法比乘法快,乘法比乘幂快
2*A=A+A
A**2=A*A
X**2+2*X+3 = ((X+2)*X)+3
  • 整数与浮点数运算的选择,整数运算比浮点数快,整数浮点的纯运算比整数浮点混起来快
I = 1+1
F= 1.0+1.0

E = (real(a)/c)*(real(b)/d)
E = real(a*b)/(c*d)
  • 访问速度,常量数字以及parameter访存比一般的变量要快,数组访存需要经过额外的运算
integer,parameter :: d = 2.0
c = 2.0
a = b/2.0
a = b/c !这个最慢
a = b/d
  • 利用cache,提高cache访存预测的命中率,需要使用尽可能连续的内存空间,do循环数组时从第一个下标开始向后,C语言则反过来
do k = 1,kmax
do j = 1,jmax
do i = 1,imax
    s = s+ A(i,j,k)
end do
end do
end do
  • 减少程序代码的跳转、转向,就是尽量少用条件和循环语句。

最后说下,提高运算速度是好事,但是可读性有时候更加重要,占主要运算时间的语句进行执行速度优化即可。

与其他语言的链接

思想就是将代码中添加注释,并且要调整函数使两者的函数编码等匹配。不过书中主要是将win下如何混编介绍了,linux下未讲。不太用,写了也记不住,真的需要的时候回来补充。

第十三章 计算机绘图

跳过,作图用MATLAB它不香吗,Tecplot它不香吗,ParaView它不香吗。

第十四章 数值方法

跳过,参考数学系大一必修的计算方法课程,或者一般研究生学的数值方法,要比这里详细。

第十五章 算法与数据结构

跳过,C语言版本的学过,Fortran版本的现在真的有地方会用嘛…

第十六章 IMSL函数库

新知识,IMSL是一个module模板,如果当前编译器内置的话,直接在main函数中use即可。它包含几个模块

  • 线性代数,矩阵的相乘转置求逆等
  • 矩阵函数,各种分解,求行列式,秩
  • 求解线性系统,lin_sol_gen(A,B,X)求A*X=B,但是具体算法未给出
  • 求解非线性方程
  • 求微积分
  • 求常微分方程
  • 插值与曲线近似
  • 最小二乘

结束!!!终于!!!还是有点长的。

最近完成了一个后台阶的算例是从.f文件改写成.f90了,但是仅仅是能够运行。接下来花一点时间将程序改写得更加结构化一些。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值