Fortran是世界上最早出现的计算机高级程序设计语言之一,最新版本为Fortran 2008,但其发展现状可谓惨淡,甚至连其擅长的科学与工程计算领域阵地也在渐渐地被C/C++、Python、Matlab等蚕食。笔者开此笔记主要有两方面的原因:
- 将笔者学习、工作中遇到的常见问题以及笔者的解决方案分享与坚守阵地的同行们;
- 笔者忘性比较好,简书也算是半个备忘录。
闲话不絮,上正菜。
字符串
program trim_test
implicit none
! Variables
character(16) s1,s2,s3,s4,s5
! Body of trim_test
s1 = ' abc '
s2 = adjustl(s1)
s3 = trim(s1)
s4 = s1//s1
s5 = trim(s1)//s1
print *, s1
print *, s2
print *, s3
print *, s4
print *, s5
end program trim_test
- adjustl()的作用是删掉字符串左端空格,trim()的作用是将字符串末尾(即右端)的空格删掉。
- //代表连接字符串
- 打印出来的s4和s5结果是比较有意思的地方:s5的打印结果很好理解,s4彷佛只打印出了' abc '。其实不然,s1的字符串长度被定义为16,事实上‘ abc’之后共有12个空格,在与s1拼接后,也只保留前16位字符,即s1本身。
变量属性
save属性的变量在下次调用函数时,依然保留上一次的值。
optional属性表示在函数调用时,此参数可以省略不传入,如:
integer,optional::a !optional 表示参数a可以省略不传入
整(实)型与字符型转换
整(实)型与字符型转换,可借助Fortran语言的内部文件完成,即将一个字符串变量当作一个内部文件来看待,用一个例子来解释可能会更有质感。
program int_char
implicit none
! Variables
character(2) :: temp
integer :: ii, last
! Body of int_char
do ii = 1, 30
!integer --> string
write(temp,'(i2)') k
open (127, file = 'FileName_' // trim(adjustl(temp)) // '.dat')
...
close(127)
end do
!string --> integer
read(temp, '(i2)' ) last
end program int_char
虽不若Python等脚本语言同类接口那么犀利,但此方法是Fortran语言处理大量命名包含规律(如本例中“FileName_.dat”, 从1递增至30。)文档的利器。
数组声明与初始化
数组声明
在使用数组之前,它包含的元素类型和个数必须用类型声明语句来向编译器声明,以便编译器知道数据按怎样的顺序存储在数组中,以及需要多少内存来存储数组。例如,含有16个元素的实型数组vessel可以被如下声明:
REAL, DIMENSION(16) :: vessel
类型声明语句中的DIMENSION属性说明被定义数组的大小。数组vessel中的元素用vessel(1), vessel(2), ... ...等访问。相似地,50个长度为20位字符的数组变量可以用如下的语句声明:
CHARACTER (len = 20), DIMENSION(50) :: vessel
数组vessel的每一个元素的长度都是20位字符的变量,同样地,元素将用vessel(1), vessel(2), ... ...等访问。
除此以外,FORTRAN语法还提供了另外两种数组声明方式:
- 直接在数组变量后面括号内声明数组的维度和大小;
REAL vessel(16)
- 先声明数组变量,再声明数组的维度和大小。
REAL vessel
DIMENSION vessel(16)
数组初始化
笔者在这里简要介绍四种常见的FORTRAN数组初始化方式:用赋值语句初始化数组;编译时在类型声明语句中初始化数组;利用DATA语句初始化数组;利用READ语句初始化数组。
- 用赋值语句初始化数组
初始值可以使用赋值语句赋给数组元素,可以利用循环或者数组构造器逐个初始化数组。例如,下面DO循环将初始化数组元素array的值为0.0, 1.0, 2.0等等。
REAL, DIMENSION(10) :: array
DO i = 1, 10
array(i) = REAL(i)
END DO
使用数组构造器也可以完成同样的功能,只是看起来有点傻瓜。
REAL, DIMENSION(10) :: array
array(i) = (/1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0/)
还可以只对数组部分元素赋值:
!将上述数组array的最后三个元素分别赋值为8.0, 9.0和10.0
array(8:10) = (/8.0, 9.0, 10.0/)
!将上述数组array的最后三个元素均赋值为10.0
array(8:10) =3*10.0
- 在类型声明语句中初始化数组
通过在类型声明语句中声明他们的值,可在编译时把初始值加载到数组中。为在类型声明语句中初始化数组,可以用数组构建器声明相应语句中数组的初始值。
INTEGER, DIMENSION(5) :: array = (/1, 2, 3, 4, 5/)
这个方法很适于初始化小尺度的数组,但是数组有100甚至更多的元素时,全部写出这些数组元素显然是不可行的,此时可以利用隐循环来初始化数组。隐循环的常规形式是:
(arg1, arg2, ... , index = istart, iend, incr)
这里arg1和arg2等式每次循环执行时估算的值,index, istart, iend和incr函数完全精确地用同样的方法来执行普通的DO循环。所以上述声明语句完全可以用隐循环代替:
INTEGER, DIMENSION(5) :: array = (/ (i, i = 1, 5) /)
隐循环还可以与常量嵌套或混合使用,例如下面的数值初始化中,所有不能被5整除的元素都为0,能被5整除的元素全部被初始化为array元素数据。
INTEGER, DIMENSION(25) :: array = (/ ((0, i = 1, 4) 5*j, j = 1, 5) /)
对于每一个外层循环,都完整地执行一遍内层循环(0, i = 1, 4),于是得到了初始化后的结果为:
0, 0, 0, 0, 5, 0, 0, 0, 0, 10, 0, 0, 0, 0, 10, ... ...
值得注意的是,利用隐循环在类型声明语句中初始化数组时,数组中的每一个元素都必须被赋值,而利用DATA语句初始化中隐循环则可以只对部分数组元素赋值。毫无疑问,也可以在类型说明语句中将数组所有元素全部初始化为某一常数值。在下面例子中,数组array的所有元素全部被初始化为1:
INTEGER, DIMENSION(25) :: array = 1
- 利用DATA语句初始化数组
DATA语句的一般形式如下:
<blockquote>DATA 变量表 /初值表/, 变量表/初值表/, ... ...
变量表中可以出现变量名、数组名、数组元素名、隐循环;初值表中只允许出现常量,不允许出现任何形式的表达式;变量表中的变量个数与对应初始值表中常量的个数必须相同,类型从左到右的顺序一一对应相同,当变量表中出现数组名时,初值表中常量的个数必须与数组元素的个数相同;变量表中变量之间、初值表中初值之间都用逗号隔开。</blockquote>
笔者贸然地将其总结为两种用法,并使用示例说明如下:
- 直接赋值初始化
INTEGER, DIMENSION(5) :: array
!将array数组中5个元素分别初始化为1, 2, 3, 4, 5
DATA array /1, 2, 3, 4, 5/
!将array数组中5个元素全部初始化为数值 1
DATA array /5*1/
- 利用DATA语句中的隐循环初始化
INTEGER, DIMENSION(100) :: array
!将array数组中前50个元素分别初始化为1,后50个元素初始化为-1。
DATA(NUM(i), i = 1, 50) /50*1/, (NUM(i), i = 51, 100) /50*-1/
需要注意的是,程序是在编译时给变量初始化,因此程序中每个变量只可能有一个初值,如果程序中有多个DATA语句给同一个变量赋初值,那么以最后一条DATA语句所赋初值为准。而且在程序执行期间DATA语句不起任何作用。
- 利用READ语句初始化数组
笔者写论文写累了,不想写了,自己参悟吧。。。。。。