简单规则
建议使用自有格式编码(free-format). 注释使用“!”开始。
但是老的程序,可能由固定格式与自由模式转换。
程序单元尽可能彼此独立,重复利用率
程序单元间的变量,一般是不相通的。
语句大致分三类:
1 声明语句,只在编译时运行。
2 执行语句
3 结构语句
声明都先写:Implicit None
1 一个项目开始 program name, 结束:end progarm.
2 大小写不区分,如 A , a 是一样的
3 ! 注释符号,;&代码换行, /:跳到下一行
4 变量不能以数字开头
5 最好都写: implicit none, 即不使用默认的数据类型
6 各个数据类型表示:
implicit none
integer::number=5, data, i=0, j !整型,kind可以改变其最大值与最新值,1,2,4,8
real::value,x,y,pi=3.1415 !浮点.kind=4(单精度,38位数), 8(双精度), 16(四精度)
character (4) :: file='test.data' !字符 Kind=1, ASCII编码
logical::end. file = .false. !逻辑型
complex(kind=??):: !复数型(两个浮点型的组合)
type(??):: !派生型(上述类型的组合)
属性: 形容词来赋予,
如: Real, Parameter::PI=3.1415 !常数
Kind 种别,来区分同一种数据类型,但不同长度或精度或编码方式,在编译时决定。 常数也有kind值; Selected_Int_Kind(), or Selected_Real_Kind(r,p)(10^r为最大值,p为有效数), 可以给出不同编辑器的使用kind值
转换
read(字符串,) 整型或实型变量。 字符串 => 数字
write(字符串,) 整型或实型变量。 数字 => 字符串
7 Arrays (数组)
integer::x(5,50) 5行50列的整数数组
character(5)::x(25) 25个元素的符号向量,每个元素5个字符长度
real::c(3,4,5) 60个元素(3x4x5)的3-D数组
默认生成的数组元素都为1
8 设置常量
integer, parameter::n=5, m=20
real, parameter::pi=3.1415
real::x(n,m). ! 提前设置x矩阵的为n行,m列
9 字符串,有利于实现批量处理
10 浮点数需注意:
(1)对浮点数进行相等判断,应写: if( abs(a-1.3)<1.0e-5 )
(2)不要以浮点数作为数组的角标, 应:b=a(2)
(3)不要用其作为循环变量,应 Do i = 0, 20
r = i/10.0
运算
+, - , * , / : 分别表示加减乘除; 但是整数和实数不能相互转化
** : 表示次方,如2**3, 表示2的3次方
注意区分: 整数与实数不能直接进行运算
整数除以整数,结果是整数,eg: 1/2=0
整数除以浮点数,结果是浮点数,eg: 1/2.0=0.5
矩阵
1 矩阵*常数 = 矩阵中每个元素分别乘以常数
2 matmul(z,y) ! 矩阵z, y相乘
3 v=z(1:100:5) !间隔为5,
4 x = transpose(y) !x=y'
5 dot_product(v,w) !v'w
6 a=maxval(x) ! x中的最大值
7 k=sum(y, mask=y>2.5) !大于2.5的元素求和
8 k=sum(y, dim=1) !按行求和
结构语句
if
if (condition1) then
.... !执行命令
elseif (condition2)
... !执行命令
elseif (condition3)
... !执行命令
else
...
endif
condition 中可以使用 < , <=, ==(.eq. 相等), >=, > , /=(.ne. 不等)
.and. , .not. , .or.
比较数组是,使用any 或者all
select case
select case (keywrod)
case (A)
....
case (B)
.....
case default
....
end select
do loop
do i=1,n,2 !从1到n继续间隔为2的循环运行
... !执行命令
enddo
do while ()
....
end do
隐式循环
a = [(i,i=1,10)]
相当于:
do i = 1,10
a(i) = i
end do
署名的do循环
outer: do i=1, 10
inner: do j=1,20
....
...
end do inner
end do outer
循环控制:Cycle, exit
do
...
if (expression) then
cycle
else
exit
end if
end do
cycle:表示忽略本轮循环剩余内容,直接进入下一轮循环
exit:(用于循环时)忽略循环剩余内容,跳出(指定)循环。
用其循环和退出特定署名的循环
outer: do i=1,10
inner: do j=1,20
if (condition A) then
cycle outer
else if (condition B) then
exit outer
end if
end do inner
end do outer
聚合数据
常见:
枚举(parameter),
数组(dimension),只存在一类
结构体(type), 可以多类
类(class, type derived),面向对象的扩展,数据的组装
数组
高频使用,是Fortran数值计算的最重要元素
分类:1 固定大小数组:(1)静态数组(声明)(2)自动数组(传参)
2 动态分布数组:allocate分布...deallocate结束
定义: type, dimension(bound) [,attr]::name #较多固定数组大小
type [,attr]::name(bound) # 少量数组相同大小
访问方式: 整体(数组),元素,片段,不连续段
使用的函数: SQRT(num) 求方根,单独求取
整体求
结构体
定义: type[[],attr-list::] name
...
end type name
访问方式: type%mem
在数值计算以外的扩展,
类
在结构体基础上,向面向对象拓展
常用函数
狭义(数学上)的函数:
Fortain 也有一些提前自己定义的函数
int() - 直接截取为整数
nint - 四舍五入为整数
max, min, sqrt, sin, cos, tan, exp, log, log10, abs, mode,
len: 符号的长度
广义:引导程序执行流程的重要方式(指function,subroutine,module)
使用函数优点:
- 将长过程拆分成多个小的过程,分别实现,有利于代码的逻辑结构
- 封装,类似包
- 一次书写,多次调用
- 函数内部与调用者是隔离的,不能直接使用调用者的变量
- 函数的局部变量在函数返回后丢失,下次进入时他们的值不确定
缺点:牺牲一小部分的效率
function ,subroutine和module
function
function trace_f(x,n) result(t)
integer::n,i
real::x(n,n), t
.....
end
调用: result = trace_f(x,n)
subroutine
subroutine trace_sub(t,x,n)
integer::n,i
real::x(n,n), t
.....
end
调用: call trace_sub(t,x,n)
function 的变量只能读入,result输出; subrountine的变量可以读入和输出
function和 subrountine都对内部变量都不会每次都初始化,如调运运行一次后,下一次变量值就会改变。需要使用使用save来固定初始值,
如: integer, save::count=0 !这样每次count都为0
虚参:子程序指定 subroutine( x, y)
实参: 调用者指定 call subrountine(5,6)
一般按顺序结合,
传递参数的方式两种: 传地址(高效,可回传,节省内存), 和传值(先复制一份再修改,不可回传,速度慢,浪费内存,但是安全)
Fortran默认使用的是传地址。
实参和虚参的数据类型,Kind值,数组的维度,尽量保持一致。
把能够交给外部配置的信息,都定义为虚参,提高子程序重复利用。
传递数组
数组,包含元素,维度,每个维度的上下限,大小信息。
1 自动数组
subroutine sub(a,m,n)
Integer m, n
Real a(m, n)
a = 2
End Subroutine sub ! 只传地址,维度设置成了参数,可以自己定义,这样参数较多
2 假定大小
Subroutine sub (a)
Real a(*)
a(1:6) = 1
End subroutine sub !只传递地址,虚参只能是1维,下限为1,不传递上限,容易越界
3 假设性状
subroutine sub (a)
real a(:,:)
a =1
end subroutine sub ! 传递地址,各维度大小,下限可自动,需要interface
第3类,是被推荐使用。
特殊用法
1 变量的save属性:局部变量具有临时性,使用save,可以让其保持上一次的值,
eg: Integer, save :: var, 或者Interg ::var=0
2 虚参的Intent属性(需要Interface):明确指定虚参的目的:输入参数,输出参数,中性参数,
!输入参数,在子程序内部不许改变
Integer, Intent(IN)::input_arg
!输出参数,子程序返回前必须改变,所以不能是常数,也不能是表达式
Integer, Intent(OUT) :: output_arg
!中性参数
Integer, Intent(INOUT):: neuter_arge
Integer::neuter_arg !未定Intent为中性
请注意:Intent的检查是在编译时进行,而非运行时检查,这样可以在变编程时,发现问题。
建议:对每一个虚参都指定Intent属性
3 可选参数,运行时,动态决定参数的个数: Open语句,
Integer, optional::c
4更改参数的顺序:即实参和虚参顺序不一定对应。
eg: call writeresult(Data=var, file="res.txt", size=1000) !指定虚参名称=
5 函数名作为参数:实参与虚参都是函数名,让子程序本身以另外的子程序做参数。 可以用interface定义。
6 result 后缀:旨在通过对返回值重命名以便于理解或书写,
eg: Type (ST_d) Function Rdegree_to_typedegree (rr) result (stt)
7 递归子程序:自己调用自己的子程序,在特殊循环或迭代非常有效, 需要在最前面加Recursive
能不用链表,尽量别用
一定控制终止条件,
尽量少量使用局部变量
8 一进多出递归,往往与链表结合使用, 例如著名的二叉树, 如操作系统的树形文件夹结构。
module
module trace_m
integer::n,i
real::x(n,n), t
.....
end module
Program main
use trace_m !可以直接使用内部定义的任何功能函数
....
end
当module有subroutine时, 需要将其与主程序连接
注意: module可能更方便封装函数和使用
读入和写出
read()读入
write() 输出
通用:print, 'value of a , b, and c are ', a, b, c
也可对输出的进行格式设定:
print '(5x, "value of i, x = "), i4, 2x,f6.2, f6.2)', i, x
i4: 4个整数的长度
a5: 5个字符的长度
f6.2: 总长度为6
在输出中,小数点后2位数,
5x: 在输出中, 5个空格
在读入中, 跳过5个字符
读入一个文件:
open(2, file='/home/data.csv') !需要一个整数来关联读入的文件
read,a,b
隐式循环
read, ((y(i,j),j=1,m), j=1,n) #按行读取
read, ((y(i,j),j=1,n), i=1,m) #按列读取
这对数组非常有用:
real::x(n,n),d(n)
integer::cont(100)
!
d=(/(x(i,i),i=1,n)/) ! d为x的对角线数据
cont=(/(i,i=1,100)/) !cont为1 2 3 4 ... 100
注意: 无论是否已读取所有字符(一行),一条read语句通常只读取一行! 因此,下一条读取语句无法读取上一条已跳过的字符
每次I/O格式完成计算后,都需要使字符格式转换二进制,二进制节省空间。
但是不同的电脑和编译器得到的二进制格式不同,所以不同的环境生成的二进制可能不同。
读数据可能需要格式:
character (20)::ff
write(ff, ' (i2) ' ) i
ff = ' ( ' // ff // ' f10.2) ' ! 如果i = 5, ff='( 5f10.2)'
编译器
具有:f90 , ifort 等
eg: ifort -parallel -o mtc mtc1.f mtc2.f second.f ! 编译后三个代码,生成mtc软件
矩阵
real::x(2), y(1000), g(3,3)
x=(/13.5, 2.7/) !x为(13.5与2.7)
y=(/ (i/10.0, i=1,1000) /) ! y为[0.1 0.2 ... 100]
real::dimension(n,m)::x,y,z !x,y,z都为[n,m]大小的矩阵
real,allocatable::a(:,:) !生成2维可分配数组
allocated (a(n,m)) !a为[n,m]的可分配数值
指针数组:
real,poniter::a(:,:),b(:,:) !a,b为2维数组
数据的元素也能加入指向,并且进行初始化:
type spare_matrix
integer::n=0
integer,pointer::row(:)=>null(), col(:)=>null()
real,pointer::val(:)=>null()
end type
新程序的组织-internal functions/subroutines and modules
前面描述的Functions and subroutines是单独的程序单元。 它们可以与主程序分开编译并放入库中。 但是,由于不进行参数检查,因此容易出错,例如,在编译过程中可能会以错误的数量或类型的参数调用子例程而没有任何警告。
Fortran 90为程序放置提供了两种新的可能性:内部子程序和内部模块。
eg:
常规的程序如下:
program test
call sub1(x,y)
z=fun(h,k)
end program
subroutine sub1(a,b)
....
end subroutine
function fun(i,j)
....
end function
但是改成为: 内部子程序使用方法:
program test2
call sub(x,y)
z=fun(h,k)
contains !表明插入
subroutine sub(a,b)
....
end subroutine
function fun(i,j)
.....
end function
end program
其中主程序可以使用所以的变量(同样子程序内可以使用主程序定义的变量),并不是library的一部分。
使用内部modules
module mm2
contains
subroutine sub(a,b)
.....
end subroutine
function fun(i,j)
....
end function
end module
program test3 !主程序
use mm2
call sub(x,y)
z=fun(h,k0
end program