Fortran 学习笔记

Fortran 学习笔记
陈天文
第一版于2021.07.18 on Typora
CodeBlocks + GNU Fortran编译器
参考教程: 《Fortran 95程序设计》彭国伦
目录

文章目录


基本语法

格式

固定格式(Fixed Format):Fortran77 程序需要满足一种特定的格式

自由格式(Free Format):Fortran90之后程序书写形式更为自由

数据类型

数据类型关键词C中对应
整型integerint/long …
浮点型realfloat/double …
复数complex<complex.h>
字符/字符串characterchar …
逻辑判断logicalbool …

表达式与逻辑运算

!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(a2)<2e5,这种方式来作判定

代码结构

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=bc 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=bc 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++灵活。

文件操作

有使用需求的时候再来补充

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值