基本读了一遍,把剩下的总结完。
第八章 函数
基本的调用不解释了,这里强调一些细节。
- 可以在子函数最后使用
end
表示函数的结束,不过实际上使用retrun
是标准的用法,并且可以选择让函数在执行到一般的时候return
返回。 - Fortran77不支持递归,但是Fortran90支持,不过递归的调用需要额外的命令,由于递归调用的计算速度很慢,科学计算中并不推荐使用,参考Page195
- Fortran中的传参均为传址调用(by reference),
subroutine
和function
均是传址,但是一般不建议function
中改变传参的值。如果所传的参数值发生改变,那么主程序中的变量值也会变化,这点和matlab或者C中均不同。 subroutine
和function
的区别,后者有返回值,另外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
在这里赋初值也不支持。
函数中的变量
实际上也属于函数本身的注意事项,但是和变量特别相关。
- 函数所传的参数类型一定要对应,否则可能出现
float
类型的变量被以int
类型的编码读取,数据是错误的。 - 如果所传参数为数组,则传输的是数组的第一个元素的地址。例如传递一个4个元素的一维数组
a
时,子函数中的变量定义可以是integer a
,integer 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章会有详细的介绍。
第九章 文件
write
和read
的使用不重复了,这里强调后面出现的新知识。
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了,但是仅仅是能够运行。接下来花一点时间将程序改写得更加结构化一些。