基本语法
格式
固定格式(Fixed Format):Fortran77 程序需要满足一种特定的格式
自由格式(Free Format):Fortran90之后程序书写形式更为自由
数据类型
数据类型 | 关键词 | C中对应 |
---|---|---|
整型 | integer | int/long … |
浮点型 | real | float/double … |
复数 | complex | <complex.h> |
字符/字符串 | character | char … |
逻辑判断 | logical | bool … |
表达式与逻辑运算
!Fortran 95支持逻辑符号
+、-,*、/:加减乘除
** :乘方
==:等于
/=:不等于
>:大于
>=:大于等于
<:小与
<=:小于等于
逻辑判断式 | 含义 |
---|---|
.AND. | 且,两边都成立时,表达式成立 |
.OR. | 或 |
.NOT. | 非 |
.EQV. | 两边相等时为true |
.NEQV. | 两边不等时为true |
注意:浮点型的逻辑判断,需要考虑有效位数截断的问题,例如,2.0000001不等于2,需要使用 a b s ( a − 2 ) < 2 e − 5 abs(a-2)<2e-5 abs(a−2)<2e−5,这种方式来作判定
代码结构
Fortran语言不区分大小写
program main !自定义程序名称 main
write(*,*) "Hello World" !输出字符
stop !程序停止
end !main程序结束
输出与输入
输出
write(*,*) "Hello World" !输出字符
write(*,*)中,第一个*表示默认输出位置,第二个*表示默认输出格式,完整表述为以下格式
write(unit=6,fmt=*) "Hello World"
unit=6 表述输出至屏幕,等效于unit=*。
输出
program main
integer a,b,c
write(*,*) "please input 3 number"
read(*,*) a,b,c
write(*,*) a+b+c
end
read(*,*)与write(*,*)表示意义基本一致,read(*,*)等效于
read(unit=5,fmt=*) a,b,c
unit=5 表示由键盘输入,等效于unit=*。
格式化输入与输出
《Fortran95教程》4-4节
program main
integer a
real b
complex c
logical d
character(len=20) e
a=10
b=12.34
c=(1,2)
d=.true.
e="Fortran"
write(*,"(1X,I5)") a
write(*,"(1X,F6.3)") b
write(*,"(1X,F4.1,F4.1)") c
write(*,"(1X,L3)") d
write(*,"(1X,A10)") e
end
声明常规数据类型
声明整型数据进行运算
program main
integer a,b
a=3
b=4
write(*,*) "a=",a
write(*,*) "b=",b
write(*,*) "a+b=",a+b
write(*,*) "a-b=",a-b
write(*,*) "a*b=",a*b
write(*,*) "a/b=",a/b
write(*,*) "a**b=",a**b
stop
end
注意整型除法的截断问题
integer默认使用长整型进行声明,可以自定义数据类型存储长度,例如:
integer(kind=4) a
定义整型变量存储空间大小为4。
声明浮点型数据进行运算
program main
real :: a ,b
a=2.5
b=3.5
write(*,*) "a=",a
write(*,*) "b=",b
write(*,*) "a+b=",a+b
write(*,*) "a-b=",a-b
write(*,*) "a*b=",a*b
write(*,*) "a/b=",a/b
write(*,*) "a**b=",a**b
stop
end
real自定义数据长度同上
real(kind=8) a
声明复数型数据进行运算
program main
complex :: a ,b
a=(1,1)
b=(1,-1)
write(*,*) "a=",a
write(*,*) "b=",b
write(*,*) "a+b=",a+b
write(*,*) "a-b=",a-b
write(*,*) "a*b=",a*b
write(*,*) "a/b=",a/b
stop
end
复数运算符合相应运算规则
声明字符与字符串
声明字符与字符串
program main
character :: a
character(20) :: b
a="H"
b="Hello World"
write(*,*) a
write(*,*) b
stop
end
字符串操作:数组索引、切片操作
program main
character(len=30) :: first,second
first="Good morning"
second=first(1:4)
write(*,*) first
write(*,*) second
first(6:)="evening"
write(*,*) first
write(*,*) first//second
stop
end
字符串相关函数
函数 | 功能 |
---|---|
CHAR(num) | 返回num对应的字符 |
ICHAR(char) | 返回char字符对应的字符编号 |
LEN(string) | 返回字符串string声明空间长度 |
LEN_TRIM(string) | 返回字符串除去尾端空格后的字符长度 |
INDEX(string,key) | 返回key在string中第一次出现的位置 |
Trim(string) | 返回清楚尾端空格后的字符串 |
program main
character(len=20) :: string
character(len=5) :: substring
string="Have a nice day!"
substring="nice"
write(*,*) ichar("A")
write(*,*) char(65)
write(*,*) len(string)
write(*,*) len_trim(string)
write(*,*) index(string,substring)
stop
end
逻辑变量
true与false分别为.true.
、.false.
program main
logical a,b
a=.true.
b=.false.
write(*,*) "a=",a,"b=",b
stop
end
声明变量注意事项
变量名前缀必须是英文字母
IMPLICIT命令
implicit integer(A,B,C) !A,B,C开头变量视为整型
implicit integer(A-F) !A-F开头变量视为整型
implicit real(M-P) !M-P开头变量视为浮点型
implicit none !关闭默认类型,所有变量都需要声明
其他变量声明
常数声明方法(PARAMETER)
program main
implicit none
real pi
parameter(pi=3.1415926535)
write(*,"(F6.4)") sin(pi/6)
end
还可以等效成以下声明语句
real,parameter :: pi=3.14159
::表示类型申明的形容词已经添加完毕,后面紧随变量名称
设置变量初始值
Fortran95 可以直接在变量声明后进行赋值,而Fortran77需要使用DATA命令进行赋值
program main
implicit none
integer :: a=1
real :: b=2.0
complex :: c=(1,2.0)
character(len=20) :: str="Fortran95"
write(*,*) a,b,c,str
end
等价申明(EQUIVALENCE)
两个以上的变量使用同一块内存,类似于多个指针指向同一块内存,可以用来精简代码,节省内存。
integer :: a,b
equivalence(a,b) !a,b两个变量名共享同一块内存
声明语句在程序中的位置
声明语句必须放置在可执行操作语句之前,也就意味着,程序中所有使用的变量必须首先声明。
变量类型转换
program main
implicit none
integer :: a=1,b=2
write(*,*) a/b
write(*,*) real(a)/b
end
real()函数可以将变量转化为浮点型变量
自定义数据类型
结构体,type定义结构体,%获得结构体数据成员
program main
implicit none
type:: person
character(len=30) :: name
integer :: age
real :: weight
end type
type(person) :: a
write(*,*) "name:"
read(*,*) a%name
write(*,*) "age:"
read(*,*) a%age
write(*,*) "weight:"
read(*,*) a%weight
write(*,100) a%name,a%age,a%weight
100 format(/,"name:",A10/,&
&/,"age:",I10/,&
&/,"weight:",F5.2)
end
流控制语句
IF语句
IF (逻辑表达式) THEN
...
ELSE
...
END IF
SELECT-CASE语句
类似于C类语言中的switch-case语句
select case(变量)
case(数值1)
...
case(数值2)
...
case(数值n)
...
case default
...
end select
例:
program main
implicit none
integer :: i=1
select case(i)
case(1)
write(*,*) "1111"
case(2)
write(*,*) "2222"
case(3)
write(*,*) "3333"
case default
write(*,*) "0"
end select
end
GOTO语句
跳转至任意行,可读性差,不建议使用
DO语句
program main
implicit none
integer :: counter
do counter=1,10,1
write(*,*) counter
end do
end
counter由1到10,增量为1
DO-WHILE语句
program main
implicit none
integer :: counter=1
do while(counter<=10)
write(*,*) counter
counter=counter+1
end do
end
循环控制
Fortran语言中的CYCLE
语句可以跳过接下来的语句,跳转至循环开头,类似于C类语言中,continue
语句的功能。
EXIT
可以直接跳出当前循环,类似于C类语言中的break
语句
数组(Array)
定义数组
一维数组
program main
implicit none
integer :: i
integer :: a(10)
do i=1,10,1
a(i)=i*i;
end do
do i=1,10,1
write(*,*) "i=",i,"i*i=",a(i)
end do
end
数组申明格式:
Datatype name(size)
注意,size必须为常数,数组索引默认从1开始
多维数组
最多可以声明7维数组,声明格式
integer :: a(D1,D2,D3,...,Dn) !n维数组
其他数组声明方式
没有特别赋值的情况下,数组的默认索引是从1开始的,例如
integer :: a(3) !包含a(1),a(2),a(3)
自定义范围声明
integer :: a(0:2) !一维数组包含a(0),a(1),a(2)
integer :: b(2:3,-1:1)!二维数组b(2~3,-1~1)都可以使用
赋初值
直接赋初值
integer :: a(row,col)=0
program main
implicit none
integer ,parameter:: row=10
integer ,parameter:: col=10
integer :: a(row,col)=0
integer :: i,j
do i=1,row,1
do j=1,col,1
write(*,*) a(i,j)
end do
end do
end
对数组的操作
Fortran 95拥有强大的数组操作方法,简化许多for循环操作
等号赋值
integer :: a(10),b(10)
!!数组赋值!!
a=10
!等效于
do i=1,10,1
a(i)=10
end do
!!数组复制!!
b=a
!等效于
do i=1,10,1
b(i)=a(i)
end do
矩阵基本运算
+ , − , ∗ , / +,-,*,/ +,−,∗,/基本四则运算与Matlab中点乘、点除方法相同,均为数组对应元素进行四则运算
数组操作 | 意义 |
---|---|
a = b + c a=b+c a=b+c | a ( i , j ) = b ( i , j ) + c ( i , j ) a(i,j)=b(i,j)+c(i,j) a(i,j)=b(i,j)+c(i,j)矩阵对应位置数值相加 |
a = b − c a=b-c a=b−c | a ( i , j ) = b ( i , j ) − c ( i , j ) a(i,j)=b(i,j)-c(i,j) a(i,j)=b(i,j)−c(i,j)矩阵对应位置数值相减 |
a = b ∗ c a=b*c a=b∗c | a ( i , j ) = b ( i , j ) ∗ c ( i , j ) a(i,j)=b(i,j)*c(i,j) a(i,j)=b(i,j)∗c(i,j)矩阵对应位置数值相乘 |
a = b / c a=b/c a=b/c | a ( i , j ) = b ( i , j ) / c ( i , j ) a(i,j)=b(i,j)/c(i,j) a(i,j)=b(i,j)/c(i,j)矩阵对应位置数值相除以 |
program main
implicit none
integer ,parameter:: row=10
integer ,parameter:: col=10
integer :: a(row,col)
real :: b(row,col),c(row,col)
integer :: i,j
a=10
b=.5
c=a*b
do i=1,row,1
do j=1,col,1
write(*,*) c(i,j)
end do
end do
end
矩阵切片运算
可以通过对矩阵中部分数据进行赋值操作,Matlab中矩阵切片方式继承了Fortran语言这一形式。
a(3,5)=5 !a(3),a(4),a(5)数值设置为5
a(3:)=5 !a(3)之后的数值设置为5
a(3:5)=(/3,4,5/) !a(3),a(4),a(5)数值分别设为3,4,5
a(1:3)=b(4:6) !a,b赋值
a(1:5:2)=3 !a(1),a(3),a(5)赋值为3,1~5增量为2
a(1:10)=a(10:1:-1) !翻转a
integer :: a(5),b(5,5)
a(:)=b(:,2) !b中第二列赋值给a
integer :: a(5,5),b(5,5,5)
a(:,:)=b(:,:,1)
注意在进行数组操作时,需要保证两边数组数目一致
WHERE 语句
where是Fortran95添加的功能,可以通过逻辑判断,对数组部分元素进行操作。例如以下例子,可以将a中<3的数组元素赋值给b
program main
implicit none
integer :: i
integer :: a(5)=(/(i,i=1,5)/)
integer :: b(5)=0
where(a<3)
b=a
end where
write(*,*) "a=",a
write(*,*) "b=",b
stop
end
!输出
a= 1 2 3 4 5
b= 1 2 0 0 0
where也可以作嵌套,elsewhere(逻辑判断)
program main
implicit none
integer :: i
integer :: a(5)=(/(i,i=1,5)/)
integer :: b(5)=0
where(a<3)
b=1
elsewhere(a>4)
b=2
elsewhere
b=3
end where
write(*,*) "a=",a
write(*,*) "b=",b
end
!输出
a= 1 2 3 4 5
b= 1 1 3 3 2
FORALL语句
forall是Fortran95中添加的功能
program main
implicit none
integer :: i
integer :: a(5)=0
forall(i=1:5:2) !1~5,增量为2
a(i)=5
end forall
write(*,*) "a=",a
forall(i=1:5)
a(i)=i
end forall
write(*,*) "a=",a
stop
end
!输出
a= 5 0 5 0 5
a= 1 2 3 4 5
对于二维数组,有相同的操作
program main
implicit none
integer :: i,j
integer :: a(5,5)=0
forall(i=1:5:1,j=1:5:1)
a(i,j)=i+j
end forall
do i=1,5,1
write(*,*) a(:,i)
end do
stop
end
!输出
2 3 4 5 6
3 4 5 6 7
4 5 6 7 8
5 6 7 8 9
6 7 8 9 10
还可以在forall中添加条件判断,语法为
forall(索引1,索引2,逻辑判断)
program main
implicit none
integer :: i,j
integer,parameter :: N = 5
integer :: a(N,N)=0
forall(i=1:N,j=1:N,i>j) a(i,j)=1 !上三角
forall(i=1:N,j=1:N,i==j) a(i,j)=2 !对角线
forall(i=1:N,j=1:N,i<j) a(i,j)=3 !下三角
write(*,"(5(5I5,/))") a
stop
end
!输出
2 1 1 1 1
3 2 1 1 1
3 3 2 1 1
3 3 3 2 1
3 3 3 3 2
forall可以嵌套多层,forall中可以使用where,但是where中不能使用forall,例如
program main
implicit none
integer :: i,j
integer,parameter :: N = 5
integer :: a(N,N)=0
forall(i=1:N)
forall(j=1:N)
a(i,j)=i+j
end forall
end forall
write(*,"(5(5I5,/))") a
forall(i=1:N)
where(a(i,:)/=6)
a(i,:)=0
end where
end forall
write(*,"(5(5I5,/))") a
stop
end
!输出
2 3 4 5 6
3 4 5 6 7
4 5 6 7 8
5 6 7 8 9
6 7 8 9 10
0 0 0 0 6
0 0 0 6 0
0 0 6 0 0
0 6 0 0 0
6 0 0 0 0
数组的存储规则
可变大小的数组
利用allocate与deallocate分配与释放内存
program main
implicit none
integer :: students
integer,allocatable :: a(:)
integer :: i
write(*,*) "How many students:"
read(*,*) students
allocate(a(students)) !分配内存
write(*,*) "size of array = ",size(a)
deallocate(a) !释放内存
stop
end
测试程序(测试当前计算机所能分配的最大内存)
program main
implicit none
integer :: size_N=0,error=0
integer,parameter :: one_mb=1024*1024
character,allocatable :: a(:)
do while(.true.)
size_N=size_N+one_mb
allocate(a(size_N),stat=error) !分配内存
if(error/=0) then
exit
end if
write(*,"('Allocate ',I10,'bytes')") size_N
write(*,"(F10.2,'MB used')") real(size_N)/one_mb
deallocate(a) !释放内存
end do
stop
end
数组应用实例
冒泡排序
program main
implicit none
integer :: a(5)=(/10,2,5,4,6/)
integer :: i,j,temp
write(*,"(5(I5))") a
do i=1,5
do j=i+1,5
if(a(i)>a(j)) then
temp=a(i)
a(i)=a(j)
a(j)=temp
end if
end do
end do
write(*,"(5(I5))") a
stop
end program
!输出
10 2 5 4 6
2 4 5 6 10
函数(Subroutine/Function)
子程序(subroutine)
将一段经常使用的代码独立出来,封装成为一段子程序SUBROUTINE
,通过CALL
语句调用执行该段代码,例如:
program main
implicit none
call message()
call message()
stop
end
subroutine message()
implicit none
write(*,*) "Hello World!"
return !返回至主程序中,继续执行
end subroutine
子程序可以由主程序调用,子程序之间也可以相互调用。同时,主程序与子程序之间拥有各自的变量,即使变量名相同,但也是相互独立的。即,子程序可以视作不同的程序,拥有各自的变量与代码定义。
program main
implicit none
call sub1()
stop
end
subroutine sub1()
implicit none
write(*,*) "this is subroutine1"
call sub2()
return
end subroutine
subroutine sub2()
implicit none
write(*,*) "this is subroutine2"
return
end subroutine
!输出
this is subroutine1
this is subroutine2
子程序中可以接收参数,传递参数通过*传递调用
*的方式实现,主程序传递的参数
与子程序中接受到的参数
拥有同一块存储地址。在子程序中修改变量会引起主程序变量的同步修改,例如:
program main
implicit none
integer :: num=1
call output(num)
call addone(num)
call output(num)
call addone(num)
call output(num)
call addone(num)
stop
end
subroutine addone(a)
implicit none
integer :: a
a=a+1
return
end subroutine
subroutine output(num)
implicit none
integer :: num
write(*,"('a=',I5)") num
return
end subroutine
!输出
a= 1
a= 2
a= 3
自定义函数(FUNCTION)
自定义函数function
与子程序subroutine
大致上是相同的,但是function
需要首先声明才可以调用,function
参数传递的方法与subroutine
相同,和subroutine
只有两点不同
- 1、调用自定义函数前要先声明。
- 2、自定义函数执行后会返回一个数值。
例如
program main
implicit none
real :: a=1.,b=2.
real,external :: add !声明add函数
write(*,*) add(a,b) !调用函数时不需要使用call
stop
end
function add(a,b)
implicit none
real :: a,b
real :: add !声明add函数返回值类型
add = a+b
return
end
对于较为简单的函数,可以直接在程序中定义,不需要单独定义function
program main
implicit none
real :: a,b
real:: add !定义函数返回值类型
add(a,b)=a+b !定义函数形式
write(*,*) add(1.,2.)
stop
end
全局变量(COMMON)
common定义
不同的程序函数之间,可以通过船体参数来共享变量,也可以通过定义全局变量的方式来实现共享变量。
program main
implicit none
integer :: a,b
common a,b !定义第一、二个全局变量为a、b
a=1
b=2
call ShowCommon()
stop
end program
subroutine ShowCommon()
implicit none
integer :: num1,num2
common num1,num2 !定义第一、二个全局变量为num1、num2
!分别与a,b对应
write(*,*) num1,num2
return
end subroutine
common变量归类
全局变量的局限性:
program main
integer a,b,c,d,e,f
common a,b,c,d,e,f !将6个变量作为全局变量
end
subroutine sub()
implicit none
integer n1,n2,n3,n4,n5,f
common n1,n2,n3,n4,n5,f !只有当前5个变量声明,才能使用第6个变量
end
当全局变量过多时,全局变量的定义变得繁琐。使用group
关键词将变量归类,彼此放置在独立的COMMON
空间中
program main
implicit none
integer :: a,b
common /group1/a !设置全局变量于group1
common /group2/b !设置全局变量于group2
a=1
b=2
call ShowGroup1()
call ShowGroup2()
stop
end program
subroutine ShowGroup1()
implicit none
integer :: num1
common /group1/ num1 !设置全局变量于group1
write(*,*) num1
return
end subroutine
subroutine ShowGroup2()
implicit none
integer :: num1
common /group2/ num1 !设置全局变量于group2
write(*,*) num1
return
end subroutine
BLOCK DATA变量初始化
common
变量直接在声明变量时初始化,如
program main
implicit none
integer :: a=1,b=2
common a,b
integer :: c=3,d=4
common /group1/ c,d
integer :: e=5,f=6
common /group2/ e,f
call writecommon()
stop
end program
subroutine writecommon()
implicit none
integer :: n1,n2,n3,n4,n5,n6
common n1,n2
common /group1/ n3,n4
common /group2/ n5,n6
write(*,"(6I4)") n1,n2,n3,n4,n5,n6
end subroutine
也可以在block data中设置初始值,例如:
block data 没啥用,还麻烦,就不写了
函数中的变量
函数中参数的传递,函数中变量的生存周期。
传递变量类型一致
由于在Fortran中传递的参数为变量地址(与C类语言中形参不一样),所以传递的数据类型在函数内外要一致,否则数据读取会出现难以预料的结果。
program main
implicit none
real :: a=1.0
call ShowInteger(a)
call ShowReal(a)
stop
end program
subroutine ShowInteger(num)
implicit none
integer :: num
write(*,*) num
return
end subroutine
subroutine ShowReal(num)
implicit none
Real :: num
write(*,*) num
return
end subroutine
!输出
1065353216
1.00000000
数组参数
函数中传递的实际上是数组的内存地址
program main
implicit none
real :: a(10)=1.0
call ShowArray(a) !传递数组名称
stop
end program
subroutine ShowArray(num)
implicit none
real :: num(10) !使用数组
write(*,*) num
return
end subroutine
变量生存周期
子程序中定义的变量,在子程序结束后,内存销毁,变量消失,使用SAVE
语句可以增加变量的生存周期,在程序执行期间一致保存变量数据,save
语句作用类似于C类语言中的static关键词,例如
program main
implicit none
call sub()
call sub()
call sub()
stop
end program
subroutine sub()
implicit none
integer,save:: a=1
write(*,"(I4)") a
a=a+1
return
end subroutine
注意:实际测试中,对gfortran编译器,不添加save关键词,子程序中的变量也会被保存
传递函数
传递函数,实现的功能类似于C类语言中的函数指针,可以将函数名称作为变量传递给函数作为参数,例如
program main
implicit none
real,external :: func
real,intrinsic :: sin
call ExecFunc(func) !调用func函数
call ExecFunc(sin) !调用库函数sin
stop
end program
subroutine ExecFunc(f)
implicit none
real,external :: f
write(*,*) f(1.0)
return
end subroutine
real function func(num)
implicit none
real :: num
func = num*2
return
end function
特殊的参数传递方法
函数传递参数时,不希望参数被改变(C类语言中const
关键词可以限定参数的只读)。Fortran 95中,INTENT
命令可以用于设定参数属性。
program main
implicit none
integer :: a=4
integer b
call div(a,b)
write(*,*) a,b
stop
end program
subroutine div(a,b)
implicit none
integer,intent(in) :: a !限定a是只读
integer,intent(out) :: b !指定b应该设置数值
b=a/2
return
end subroutine
以上第12,13行中,添加intent
关键词限定变量属性,不添加intent
也可以正确执行,加上这两行是用来避免编写程序中可能修改不能修改的变量。
函数接口(INTERFACE)
函数接口指:函数调用参数类型,返回值类型。以下情况需要特殊说明他们的“函数接口”。
- 函数返回值为数组时
- 指定参数位置来传递参数时
- 所调用的函数参数数目不固定时
- 输入指标参数时
- 函数返回值为指针时
以下为函数接口定义实例,通过使用interface
语句定义函数接口
program main
implicit none
interface !定义函数接口
function random10(lbound,ubound)
implicit none
real :: lbound,ubound !函数传递参数类型
real :: random10(10) !申明函数返回值是个数组
end function
end interface
real :: a(10)
call random_seed()
a=random10(1.,10.)
write(*,"(10F6.2)") a
stop
end program
function random10(lbound,ubound)
real :: lbound,ubound
real :: len
real :: random10(10) !函数返回值类型
real :: t
integer :: i
len=ubound - lbound !计算范围
do i=1,10,1
call random_number(t) !生成0~1的随机数
random10(i)=lbound+len*t
end do
return
end function
不定个数参数传递方法
Fortran 90中,可以使用OPTIONAL
关键词表示某些参数是”可以省略“。例如:
program main
implicit none
interface !定义函数接口
subroutine sub(a,b)
implicit none
integer :: a
integer,optional :: b
end subroutine
end interface
call sub(1)
call sub(1,2)
stop
end program
subroutine sub(a,b)
implicit none
integer :: a
integer,optional :: b !定义变量b是可省略的
if(present(b)) then !判断变量b是否存在
write(*,"('a=',I3,'b=',I3)") a,b
else
write(*,"('a=',I3,'b=unknown')") a
end if
return
end subroutine
!输出
a= 1 b=unknown
a= 1 b= 2
ps:C类语言中实现这种功能的方式更为简单
改变参数传递位置的方法
Fortran 90中,函数中参数传递的位置是按照顺序对应,可以采用关键词进行赋值
subroutine sub(a,b,c)
...
end subroutine
call sub(1,2,3) !直接输入参数
call sub(b=2,c=3,a=1) !通过关键字赋值,与上一条等效
特殊函数类型
递归函数
递归,允许函数自己调用自己,例如,递归计算阶乘
program main
implicit none
integer :: n
integer,external :: fact
write(*,*) "N="
read(*,*) n
write(*,"(I2,'!=',I8)") n,fact(n)
stop
end program
recursive integer function fact(n) result(ans)
implicit none
integer,intent(in) :: n
if(n<0) then !
ans=-1
else if(n<=1) then
ans=1
return
end if
ans=n*fact(n-1) !递归计算
return
end function
注意:递归在某种程度上会简化代码,但是递归的执行效率相对较低,对于递归的层数也存在内存上的限制,递归并不会比循环执行效率更高。
内部函数
contains
关键词后面书写局部函数,局部函数只能在包含他的函数中调用,其他函数不能调用该局部函数,例如
program main
implicit none
integer :: n
write(*,*) "N="
read(*,*) n
write(*,"(I2,'!=',I10)") n,fact(n)
stop
contains !函数内部定义子函数
recursive integer function fact(n) result(ans)
implicit none
integer,intent(in) :: n
if(n<0) then !
ans=-1
else if(n<=1) then
ans=1
return
end if
ans=n*fact(n-1) !递归计算
return
end function
end program
PURE函数
在FUNCTION
/SUBROUTINE
前面加PURE
关键词就可以使用PURE
函数。一般情况下不需要使用PURE
函数,它是用来配合并行运算使用。使用PURE函数时有很多限制:
- (1)
pure
函数的参数必须都是只读intent(in)
- (2)
pure
子程序的每个参数都要赋值属性 - (3)
pure
函数中不能使用save
- (4)
pure
函数中所包含的内部函数也都必须是pure
类型函数 - (5)
pure
函数中不能使用stop
,print
及跟输出入相关的命令如read,write,open,close,backspce,endfile,rewind,inquire
等等。 - (6)
pure
函数中只能够读取,不能改变全局变量的值。
这些限制都是为了配合并行运算。并行运算可以让程序中,不同部分的程序代码在同时间执行,加快执行速度。如果不对并行运算做限制,会出现一些问题。例如假设现在同时正在执行函数A和函数B,而这两个函数都可以在屏幕上输出一些信息,假设函数A正好要输出a=1,函数B正好要输出b=2,不过由于它们是同时在执行,但是屏幕只有一个,所以有可能会显示a=b=21,ab==12,a=1b=2, b=a1=2
等等的结果。因为a=1跟b=-=2这两段文本都抢着要输出到屏幕上,结果会导致两段文本混在一起显示出来。上面的6项限制,都是为了避免在并行运算时,出现奇怪的执行结果而制定的。来看一一个实例:
!pure function
program main
implicit none
integer,external :: func
write(*,*) func(2,3)
stop
end program
pure integer function func(a,b)
implicit none
integer,intent(in) :: a,b
func=a+b
return
end function
ELEMENTAL函数
在FUNCTION
/SUBROUTINE
前面加elemental
关键词,与pure
关键词类似,同样适合做并行计算,elemental
可以用来对数组进行处理。
elemental
函数主要就是用来配合Forran 90可以对整个数组操作的语法来设置数组内容。下面是一个实例:
program main
implicit none
interface
elemental real function func(num)
implicit none
real,intent(in) :: num
end function
end interface
integer :: i
real :: a(10)=(/(i,i=1,10)/)
write(*,"(10F6.2)") a
a=func(a) !对整个数组进行操作
write(*,"(10F6.2)") a
stop
end program
elemental real function func(num)
implicit none
real,intent(in) :: num
real,parameter :: pi=3.1415926
func=sin(num*pi)+cos(num*pi)
return
end function
封装模块(Module)
MODULE
关键词可以用来封装程序模块,通常把具备相关功能的函数以及变量封装在一起,可以用来简化代码,例如可以把全局变量都封装在一个module
中,只需要在函数中use
这个module
就可以使用它们。基本语法
module module_name !定义module
...
end module
use module_name !使用module
module封装变量
module global !封装变量至global
implicit none
integer :: a=10,b=20
common a,b
end module
program main
use global
implicit none
a=1
b=2
call sub()
stop
end program
subroutine sub()
use global
implicit none
write(*,*) a,b
return
end subroutine
module
封装常量
module constant
implicit none
real,parameter :: PI = 3.1415926
real,parameter :: G = 9.81
end module
module封装函数
module
中还可以封装函数,其结构如下
module module_name
...(函数声明)
contains
...(函数)
end module
use module_name
例如
使用多个文件(include)
通常会把一些具有相关功能的函数,独立编写在不同的文件中,编译器可以分别编译这些程序文件,最后再把它们链接到同一个执行文件中。把程序代码分散在不同文件中有几个好处:
- (1)独立文件中的函数,可以再拿给其他程序使用
- (2)可以加快编详速度,修改其中一个文件时,编译器只需要重新编译这个文件就可以
可以使用include
关键词,插入另一个文件中的内容,例如
注意:可能会产生重定义(multiple definition)的问题
函数库
将函数编译成为静态库*.lib
供其他程序使用。例如
指针(Pointer)
指针的概念与C类语言中的指针概念类似,在此不多作介绍。
定义指针
program main
implicit none
integer,target :: a=1 !添加target关键词
integer,pointer :: p !声明一个指向整数的指针
p=>a !将a的地址赋给p
write(*,"(I5)") p
a=2
write(*,"(I5)") p
p=3
write(*,"(I5)") p
stop
end program
!输出
1
2
3
分配内存
内存分配与释放
定义指针后,为指针分配内存。
program main
implicit none
integer,pointer :: p !定义指针
allocate(p) !分配内存
p=100 !设定数值
write(*,*) p
stop
end program
同时,指针指向的对象是可以更改的,但是需要考虑到内存泄漏的问题,例如,需要在改变指向指针之前释放内存。
program main
implicit none
integer,target :: a=10
integer,pointer :: p !定义指针
allocate(p) !分配内存
p=100 !设定数值
write(*,*) p
deallocate(p) !释放内存
p=>a !改变指针指向a地址
write(*,*) p
stop
end program
检查指针指向
指针在定义后一点要为其定义指向,否则可能出现未知错误,Fortran 95中associated
函数检查指针是否设置指向,返回一个bool类型值。
associated(pointer,[target])
此外,Fortran 95中提供null()
函数,返回一个不可用的内存地址
integer,pointer :: p=>null()
nullify()
函数可以将指针设置成不可用地址
nullify(pointer1,pointer2,...)
例如:
program main
implicit none
integer,pointer :: a=>null()
integer,target :: b=1,c=2
write(*,*) associated(a) !判断a是否有指向 False
a=>c !a指向c
write(*,*) associated(a) !判断a是否有指向 True
write(*,*) associated(a,c) !判断a是否指向c True
write(*,*) associated(a,b) !判断a是否指向b False
nullify(a) !取消a的指向
write(*,*) associated(a) !判断a是否有指向 False
stop
end program
!输出
F
T
T
F
F
注:指针是指向数据的地址,无论是对于integer,real,complex,character或是自定义类型
,每一种指针变量都占用相同大小的内存空间。
指针数组
指针可以声明成数组,主要有以下两种方式实现
- 1、把指针指向其他数组
- 2、分配内存空间
一维数组
!指针指向其他数组
program main
implicit none
integer,pointer :: a(:) !声明指向以为数组的指针
integer :: i
integer,target :: b(5)=(/(i,i=1,5)/)
a=>b
write(*,*) a !a(1~5)=b(1~5)
a=>b(1:3)
write(*,*) a !a(1~3)=b(1~3)
a=>b(1:5:2)
write(*,*) a !a(1)=b(1);a(2)=b(3);a(3)=b(5);
a=>b(5:1:-1)
write(*,*) a !a(1~5)=b(5~1)
stop
end program
!输出
1 2 3 4 5
1 2 3
1 3 5
5 4 3 2 1
利用allocate
分配内存
program main
implicit none
integer,pointer :: a(:) !定义一维数组指针
allocate(a(5)) !分配内存
a=(/1,2,3,4,5/)
write(*,*) a
deallocate(a) !释放内存
stop
end program
多维数组
program main
implicit none
integer,pointer :: a(:,:) !定义二维数组指针
integer,target :: b(3,3,2)
integer :: i
forall(i=1:3)
b(:,i,1)=i
b(:,i,2)=2*i
end forall
a=>b(:,:,1)
write(*,"(9I2)") a
a=>b(:,:,2)
write(*,"(9I2)") a
stop
end program
!输出
1 1 1 2 2 2 3 3 3
2 2 2 4 4 4 6 6 6
指针与函数
指针变量,同样可以作为参数在函数之间传递,也可以作为函数的返回值。
- (1)要把指针传递给函数时,要声明这个函数的参数使用接口
INTERFACE
- (2)指针参数声明时不需要
INTENT
这个形容词 - (3)函数返回值若为指针时,需要定义函数的
INTERFACE
program main
implicit none
integer,target :: a(8)=(/10,15,8,25,9,20,17,-19/)
integer,pointer :: p(:)
interface
function getmin(p)
integer,pointer ::p(:)
integer,pointer ::getmin
end function
end interface
p=>a
write(*,*) getmin(p)
stop
end program
function getmin(p)
implicit none
integer,pointer :: p(:)
integer,pointer :: getmin
integer :: i,s,min
s=size(p,1)
min=2**30
do i=1,s
if(min>p(i)) then
min=p(i)
getmin=>p(i)
end if
end do
return
end function
以上代码中编写函数接口是一件相对比较麻烦的事,可以将函数主体封装进入module
中,use
该模块时可以不需要在编写interface
部分,例如。
module func
contains
function getmin(p)
implicit none
integer,pointer :: p(:)
integer,pointer :: getmin
integer :: i,s,min
s=size(p,1)
min=2**30
do i=1,s
if(min>p(i)) then
min=p(i)
getmin=>p(i)
end if
end do
return
end function
end module
program main
use func
implicit none
integer,target :: a(8)=(/10,15,8,25,9,20,17,-19/)
integer,pointer :: p(:)
p=>a
write(*,*) getmin(p)
stop
end program
指针的应用
指针的优势
- 实现快速的数据交换处理
- 方便使用高维数组中的部分数据
以下为某一结构体排序实例
module func
type person !定义person结构体
character(len=10) :: name !character成员
real :: height,weight !real成员
end type
contains !contains 后面开始定义函数与子程序
subroutine sort(p) !冒泡排序算法
implicit none
type(person) :: p(:) !参数p为数组,数组数据成员为person
type(person) :: temp !定义缓存变量Pperson
integer i,j,s
s=size(p,1) !p的长度
do i=1,s-1 !冒泡排序主题
do j=i+1,s
if(p(j)%height<p(i)%height) then !height排序
temp=p(i)
p(i)=p(j)
p(j)=temp
end if
end do
end do
return
end subroutine
end module
program main
use func !导入func模块
implicit none
!定义person数组
type(person),target :: p(5)=(/person("陈同学",180.,75.),&
person("黄同学",170.,65.),&
person("刘同学",175.,80.),&
person("蔡同学",182.,78.),&
person("许同学",178.,70.)/)
integer i
call sort(p) !排序
write(*,"(5(A8,F6.1,F5.1/))") (p(i),i=1,5)
stop
end program
!输出
黄同学 170.0 65.0
刘同学 175.0 80.0
许同学 178.0 70.0
陈同学 180.0 75.0
蔡同学 182.0 78.0
指针的高级应用
可以以指针为基础,建立串、链表、树等多种动态的数据结构,C类语言中有详细描述。实现这类数据结构,C类语言会比Fortran更加方便灵活。
Fortran95面向对象
OOP功能还不够完善,感觉有点麻烦,不如C++灵活。
文件操作
有使用需求的时候再来补充