添加链接
link之家
链接快照平台
  • 输入网页链接,自动生成快照
  • 标签化管理网页链接

Fortran基础课程学习笔记


author: Mr.Bear

date: 2022/9/19

注:参考代码是用锚点链接的,文章里显示不了,懒得改了,需要的可以拿.html笔记文件

Fortran_studynotes.html
170.3K
·
百度网盘

所有课程内容和代码文件均来自 fcode.cn/ ,可自行下载


课程链接

1. Fortran语言简介

1.1 Fortran是什么?

Fortran是一门 编译型 的编程 语言 ,特长是 科学计算

1.2 Fortran的特点

  • 编译型 语言,执行效率高;
  • 语法严格,是和严谨的科学计算;
  • 相比于C++更易掌握,接近自然的 数学 语言;
  • 矩阵 运算强大,自带 复数 运算;
  • 并行 运算的最佳选择。
  • 可视化开发性能差;
  • 比较底层,各类算法需要自己实现。

1.3 Fortran的主要版本

F66、F77:早期版本,固定格式

F90、F95:动态数组,模块化,结构体

F2003、F2008:面向对象

1.4 Fortran的开发工具

集成开发环境(IDE):简单易学,可视化操作;工程管理;不易于交流,不易于理解概念

命令行:生涩难学,纯字符界面;makefile管理;易于交流,理解概念

编译器选择网站

不同的编译器之间差距可能较大。为提高代码的可移植性,应尽量符合语法规范,避免使用平台扩展语法。

1.5 Fortran教材

Fortran95 程序设计》 《 Morden Fortran Explained 》 …

1.6 Fortran学习建议

  1. 直接学习 Fortran90+
  2. 注意书写格式,使用 大小写混合 自由格式 、书写 Implicit None
  3. 解决实际的 数学 物理 问题;
  4. 保存写过的程序 ,回顾并尝试重写以前写的程序,将 新知识 融入进去;
  5. 经常在网络上 交流 ,善用网络 资源

1.7 Fortran网络资源

可利用的一些资源:

1.8 小结

  • 养成良好的编写习惯,包括语法、结构和书写等,提高代码的可读性和可移植性;
  • 入门可以先使用IDE工具,有一定基础后使用命令行工具;
  • 善于利用各类网络资源,多积累、多总结。

2. 常见编译环境的操作

2.1 编译和链接

编译:针对源代码,输出为目标代码

链接:针对一个或多个目标代码、库文件,输出为可执行程序

.f90→编译→.o→+routime.lib(运行时库) 链接→.exe(可执行程序)→运行

编译和链接是开发者的工作,开发者同时也要充当用户运行程序。

2.2 工程管理

工程,用于管理大量代码的一种工具,规定源代码的编译、链接的参数及代码间的依赖关系。 命令行编译方式一般采用makefile进行项目管理。 工程实际上就是可视化的makefile。

2.3 gfortran

GNU组织发布的开源软件,是GCC的一部分,支持Linux和Windows操作系统,支持Fortran2003语法及部分Fortran2008语法。 一般采用命令行编译,也有IDE支持gfortran的编译环境。

Windows:Code::Blocks、Simply Fortran、Eclipse/Photran

Linux:Code::Blocks、Eclipse/Photran

原生gfortran:

  • Windows
  • Linux:sudo yum install gcc-gfortran/sudo apt-get install gfortran

2.4 IVF+VS(intel Fortran Compiler+VS)

课程中使用的编译器为IVF。目前Intel公司推出了oneAPI,已经将intel Fortran免费,本人使用的为IFC+VS环境,附有安装教程及链接。

intel Fortran安装教程

intel oneAPI

具体安装注意事项参考课程及安装教程。

IFC VS
编译器(ifort) 链接器
调试器(idb/gdb) 编辑器
函数库(MKL/IMSL) 集成开发环境
VC++运行库
使用VS时的注意事项:
  1. VS只是集成开发环境,提供编辑器。编译和调试使用的是IVF(IFC),链接使用的是微软的link;
  2. 关注 解决方案管理器 (solution explorer)和 输出 窗口(output);
  3. 了解并使用“ 工具 ”菜单“ 选项 ”,以及“ 工程属性 ”,学会配置属性(Debug、Release等);
  4. 理解 运行 (run without debug)和 调试 (debug)两种方式;
  5. 查看 帮助

配置属性Debug和Release

Debug Release
生成文件较大 生成文件较小
运行速度较慢 运行速度较快
允许调试 不允许调试
基本没有优化 进行了合理优化

2.5 小结

  • Fortran作为一种编译型语言,主要的工作环节有编译、链接和运行;
  • 工程管理对于包含大量代码的项目来说是必不可少的;
  • gfortran主要用于Linux系统,IFC+VS是Windows系统常用的编译环境;
  • 使用IDE时善用环境配置、属性配置等环境工具。

3. 常见问题的检测和排查

错误信息 十分重要,可能会因编译器不同而不同。

主要有编译、链接和运行时错误。

3.1 编译错误

  • 编译错误主要是编译器检查代码是否符合**语法规范;
  • 编译错误的 种类最多 ,但也 最容易解决
  • 编译错误一般可以获得错误发生的 行数
  • 修改时编译错误只看 第一个 ,后续错误暂时不管;
  • 一般的编译错误都是语法结构错误, Syntax Error 是最基础的编译错误。

3.2 链接错误

  • 链接时拼接各代码的过程,编译器检查代码 完整性
  • 链接错误 种类最少 ,涉及用户代码部分容易解决。涉及编译器运行时库、第三方函数库、混编时不易解决;
  • 链接错误通常 无法获得 错误发生的准确行数,错误一般也不是发生在某一行;
  • 链接错误一般先看第一个,后续可暂时不管;
  • 一般涉及用户代码部分的链接错误发生在调用子例程、函数、模块等过程中。

3.3 运行时错误

  • 运行时错误时运行时库(Runtime Library)动态检查的错误;
  • 运行时错误 种类较少 ,大多数 难以解决
  • 复杂的代码几乎难以避免运行时错误;
  • 极端情况下,运行时库给出的运行时错误可能并不准确;
  • 运行时错误在具有 调试信息 时,可以 初略获得 错误发生的行数;
  • 运行时错误一旦发生,程序就会 中止 ,此时可配合 调试器 (debugger)动态检查原因;
  • 一般容易发生的运行时错误有数组越界、数学计算错误、变量类型不匹配、输入输出等;
  • 比较难缠的错误: Program Exception - access violation SIGSEGV: Segmentation fault 等。

3.4 小结

  • 正确认识错误,重视编译器给出的错误信息;
  • 一般的编译错误都是语法结构错误;
  • 一般涉及用户代码部分的链接错误发生在调用子例程、函数、模块等过程中;
  • 一般容易发生的运行时错误有数组越界、数学计算错误、变量类型不匹配、输入输出等;
  • 学会使用调试运行对代码进行调试。

4. Fortran源代码结构

4.1 固定格式和自由格式

早期Fortran使用固定格式,从F90以后推荐使用自由格式代码。

源代码文件的扩展名一般与支持的语法无关,与代码格式有关。

固定格式可以转换为自由格式:

4.2 程序单元

字符=>Token=> 语句 => 程序单元 =>模块=>程序

程序单元:执行某一 特定任务 的具有一定 独立性 的代码区域,并且程序单元间的变量一般不互通。

例如主程序(main program)、子例程(subroutine)、函数(function)。

  • 主程序只是默认被系统首次调用的,具有程序入口作用的函数;
  • 程序单元能够提高代码的重复利用率。一次书写,多次调用;
  • 一组程序单元及一组相关联的变量,可以组成模块。

因此,程序单元彼此间应尽可能独立,才可以充分考虑重复利用率。

4.3 语句类型

Fortran语句大致有三种类型:

  • 声明语句:在 编译 时,告知编译器关于变量的信息;
  • 执行语句:在 运行 时,执行以便完成特定的任务;
  • 结构语句:仅作为代码结构的划分作用。

Fortran是 强变量 类型的语言:

  • 变量类型必须在编译时确定,并且不允许被改变;
  • 建议显式声明所有变量;
  • 若未显式声明变量,则按照IN规则确定类型(ijklmn开头的变量为整形,其他变量为实型);
  • 建议在每一个 程序单元 中都使用Implicit None。

变量声明的基本格式: 类型(属性),形容词,形容词...::变量名1(数组外形)=值,变量名2(数组外形)=值,... 形容词不建议单独书写,双冒号表示形容词的结束。

如果定义变量时同时赋值,默认具有save属性,必须书写双冒号。

建议在声明时添加形容词,并附上 Kind值

5. 数据类型与浮点数

5.1 数据类型

采用不同法则与二进制对应的数据就是不同的数据类型。

计算机往往只存储数据,不说明数据类型。所以可能对同一个数据产生 不同解释

为避免这种情况,编译器可以帮助 管理 数据类型。

Fortran的标准数据类型:

  • Integer(Kind=):整型
  • Real(Kind=):实型、浮点型
  • Complex(Kind=):复数型(两个实型的组合)
  • Logical(Kind=):逻辑型/布尔型
  • Character(Kind=1,len=):字符型
  • Type():派生类型(不同数据类型的组合)

数据除了有不同的类型外,还具有一定的 属性 ,可以通过定义时的形容词来赋予。

如Parameter可以将数据定义为常量。

5.2 Kind

5.2.1 Kind值简介

Kind的正式名称为 种别 ,是区分同一种数据类型,但具有不同 精度 长度 、或 编码方式 的代号,在 编译时 决定。

Kind受编译器的影响,具体数值可能会有差异。并且对不同的变量类型,表达意义也不同。 常数也有Kind值!

  • 对Integer,Kind值影响整数能表达的 最大范围
  • 对Real和Complex,Kind值影响实数的 最大范围 最小精度 .一般为;
  • 对Character,Kind值表示 编码 。一般为1,通常为ASCⅡ码;
  • 对Logical,Kind值表示长度,对逻辑型无影响。

5.2.2 Integer的Kind值

一般为1、2、4、8,大多数编译器默认为4,占4个字节。

在部分编译器可能不支持1、8,在某些编译器可能为1、2、3、4。

k=Selected_Int_Kind(i) 函数可以用于选择满足要求的Kind值,其中i表示所需要的最大的十进制 位数 ,k表示能满足该范围的最小Kind值

5.2.3 Real的Kind值

一般为4、8、16等,大多数编译器默认为4,占4个字节。

部分编译器不支持16,某些编译器用1、2、3表示。

k=Selected_Real_Kind(r,p) 函数可以用于选择满足要求的Kind值,其中r表示所需要的最大的十进制 位数 ,p表示最小的有效位数,k表示能满足该要求的最小Kind值。

5.2.4 Complex的Kind值

Complex数据类型与Real一致,但相同Kind值的Complex函数占位为 Real的两倍

所以Complex(Kind=16)实际上是四精度的复数类型,而非双精度。

Complex的Kind选择函数与Real相同。

5.2.5 Character的Kind值

Character的Kind值通常只有一种,即Kind=1,表示为ASCⅡ编码,通常忽略;

也可以使用 k=Selected_Char_Kind('ASCⅡ') 函数来选择Kind值。

5.2.6 Logical的Kind值

Logical的Kind值通常与Integer一致,一般忽略。

在大多数编译器上,Kind=4,占4个字节. 任何Kind值的Logical都只能表示真、假两个状态。

Logical没有可以用于选择Kind值的函数。

通常只有在 混编 时,才需要更改Kind值。

5.3 整型和字符型

5.3.1 整型

语法中表示 次数(循环变量 )、 个数(数组下标) Kind值 以及 内存地址 等都是整数。

需要注意整型变量的 最大范围 ,尤其在阶乘、指数等可能产生较大数据的计算中,必须先做出评估。

整型和整型的计算结果仍然是整型!

整数和浮点数的计算结果一般是浮点数,但尽量在浮点数的计算中将常数写为浮点数,最好标注 数据精度

5.3.2 字符型

字符串定义时 必须有长度 ,且通常是 固定 的。

Fortran的字符串没有 结束符\0 ,所以在字符串操作中必须注意。

有必要时需用 trim 去除尾部空格。

可通过内部文件在字符串和整型、实型间进行转化:

  • Read(字符串,*) 整型或实型 :字符串->数字
  • Write(字符串,*) 整型或实型 :数字->字符串

字符串允许使用 子字符串 表示字符串的一部分,是一种特殊的数组。

语法中有很多内容本身也是字符串,如 文件名 Open语句的子句 格式Format 等。

利用字符串可以实现 批量文件处理 以及 动态Format 等功能,后续会讲到。

5.4 浮点数

浮点数是用 有限的数据 来表示 无限的可能

Real和Complex都是浮点数类型。 计算机中的浮点数规则很多,其中应用最多的是IEEE标准,其格式为:符号位+有效数+指数。

  • 符号位0表示正数,1表示负数;
  • 有效数表示小于1的小数,参与计算时会 加1
  • 指数位表示整数,参与计算时作为 2的指数 ,使用其 与127的差值 ,便于表示负指数;
  • 最后表示值为: (-1)**(符号位)*(1+有效数)*(2**指数)

除了恰好能被2**(-n)线性组合出来的数,用浮点数表达会存在误差。

一般单精度浮点数可以达到十进制的约 7位 有效数字;双精度浮点数可以达到十进制的 15位 有效数字左右。

由于浮点数存在误差,我们应注意以下操作:

  • 对浮点数进行相等判断: if(a==1.3) ->"if(abs(a-1.3)<1.0e-5)`
  • 用浮点数作为数组角标: b=a(2.0) ->"b=a(2)`
  • 用浮点数作循环变量: Do r=0.0,2.0,0.1 ->"Do i=0,20 r=i/20`

在绝大多数计算中我们需要注意误差的 放大 积累 ,如 累加 指数运算 等。

对此需要设计合理的算法,以减小误差放大和积累。

5.5 小结

  • 注意变量的 Kind 值,及其对于不同类型数据所代表的含义;
  • 建议在 定义变量时指定其Kind值 ,可以使用Parameter和Module用于统一代码中的Kind值;
  • 合理使用 Selected函数 能够有效提高代码的 可移植性
  • 注意Character是一种特殊的数组,后续会详细讲到;
  • 注意浮点数的 误差 问题,避免其误差的放大和积累。

6. 流程控制

参考代码: <a name="Back Code.6"><a href="#Code.6"> "Code.6" </a></a>

6.1 条件判断

6.1.1 If语句

  • 一般的If结构:
If(Expression) Then
End If
  • 简单If结构: If(Expression) ...
  • 多层If结构:
If(ConditionA) Then
Else If(ConditionB) Then
End If

If结构中的逻辑表达式结果必须为 单值 ,不能为数组:如果要进行数组比较可以使用 ANY ALL 等函数。

  • 署名的If:
name:If(ConditionA) Then
Else If(ConditionB) Then [name]
Else [name]
End If name

6.1.2 SELECT CASE语句

  • 基本结构:
Select Case(Expression)
 Case(A)
 Case(B)
 Case Default
End Select

自Fortran2003起,新增了Select Type功能。(参考代码: <a name="Back Demo_Type_main"><a href="#Demo_Type_main"> "Demo_Type_main" </a></a> )

6.2 循环

6.2.1 Do循环

  • 一般的Do语句:
Do i=m,n
End Do
  • 修改增值:
Do i=m,n,r!//修改每次循环时循环变量的增值
End Do
  • Do While语句:
Do While(Expression)
End Do
  • 隐式循环:
a=[(i,i=1,10)]
  • 无穷循环:
Do
End Do
  • 署名的Do循环:
Outer:Do i=1,10
 Inner:Do j=1,10
 End Do Inner
End Do Outer

6.2.2 循环控制

  • Cycle:忽略本次循环的剩余内容,直接 进入下一次循环
  • Exit:忽略循环剩余内容,直接 跳出(指定)循环

使用Cycle和Exit可跳过或退出循环,配合署名可以针对特定循环进行该操作:

Outer:Do i=1,10
 Inner:Do j=1,10
  If(ConditionA) Then
   Cycle Outer
  Else If(ConditionB) Then
   Exit Outer
  End If
 End Do Inner
End Do Outer

(参考代码: <a name="Back Demo_IF_main"><a href="#Demo_IF_main"> "Demo_IF_main" </a></a> )

注意:

  • 循环必须使用 整型变量
  • 循环过程中循环变量的值不允许人为改变,如果想达到类似的效果,可以采用 无限循环配合Exit 的方式完成;
  • 循环的上下限如果通过 表达式指定 ,在进入循环计算期间 不再改变
  • Fortran没有规定循环 正常结束 后循环变量的值。但如果通过 Exit退出 循环,循环变量为退出前的值。 (参考代码: <a name="Back Demo_IF_func"><a href="#Demo_IF_func"> "Demo_IF_func" </a></a> )

6.3 Fortran77的替代

  • Goto:配合行号使用,自己编程时应尽量避免使用
  • Computed Goto:类似If或Select Case的作用
  • Continue:配合/或行号作为Do循环的结尾
  • Arithmetic If:逻辑表达式与0做大小对比

7. 数组和结构体

参考代码: <a name="Back Code.7"><a href="#Code.7"> "Code.7" </a></a> "

7.1 聚合数据

  • 枚举(Parameter):一般定义为 常量 (参考代码: <a name="Back ENUM"><a href="#ENUM"> "ENUM" </a></a> )
  • 数组(Dimension):最基本、最简单的高级数据结构,通常使用循环结构进行访问。字符串是一种特殊的数组(参考代码: <a name="Back ARR"><a href="#ARR"> "ARR" </a></a> )
  • 结构体(Type):可以容纳 不同的数据类型 (参考代码: <a name="Back TYPE"><a href="#TYPE"> "TYPE" </a></a> )
  • 类(Class,Type Derived):结构体的 面向对象 的扩展,数据封装、类的多义性、类的扩展、类的继承等(参考代码: <a name="Back CLASS"><a href="#CLASS"> "CLASS" </a></a> )

7.2 数组

7.2.1 数组的分类

(参考代码: <a name="Back ARR_TYPE"><a href="#ARR_TYPE"> "ARR_TYPE" </a></a> )

  • 固定大小数组
  • 静态数组(声明)
  • 自动数组(传参)
  • 动态分配数组(Allocatable、Deallocatable)

7.2.2 数组的定义

type,Dimension(bound)[,attr]::name
type[,attr]::name(bound)

例如:

Integer,Dimension(100)::IARR
Real::FARR(1:100),CARR(0:99)
Character(Len=256)::CONTENTS(80)!//派生数据数组
Real,Dimension(5)::RARR,RXARR(10)!//混合定义

7.2.3 数组的访问方式

(参考代码: <a name="Back ARR_ACCESS"><a href="#ARR_ACCESS"> "ARR_ACCESS" </a></a> )

Subroutine FUNARR(A,N)
Interger,Parameter::NMAX=100
Real,Dimesion(NMAX)::ARR
Integer,Dimension(5)::VECSUBSCP=(/1,4,6,7,5/)
Interger::NARR,I
I=10!//有效数组位数
  • 整体
ARR=1!//对全部元素赋值
ARR(:NARR)=1!//推荐采用该方式,访问有效数组位数
  • 元素
ARR(1)=-1!//要考虑bound
  • 片段
ARR(2:4)=(/(sin(Pi*(I-1.)/(NARR-1)),I=2,4)/)
Call FUNARR(ARR(2:4),3)!//连续数组片段的传递为地址传递,在子程序内对其改变会改变原数组
  • 不连续段
ARR(VECSUBSCP(:3))=0.5
Call FUNARR(ARR(VECSUBSCP(3:5)),3)!//不连续数组片段的传递会构造新的暂时数组,子程序内对其改变不改变原数组

7.2.4 数组的子程序

子程序对数组作用分为两种:第一是对数组元素进行处理,如 Sqrt sin 等;第二是对数组本身进行处理,如 Dot_product Matmul Sum Product Maxloc Maxval 等。

  • 举例:求解ax=b,a为带宽p+q-1的矩阵(参考代码: <a name="Back BANDMAT"><a href="#BANDMAT"> "BANDMAT" </a></a> )
  • 矩阵的压缩保存
  • 矩阵的LU分解
  • 方程的求解

7.3 结构体

7.3.1 结构体的定义

Type[[,arrt-list]::]name[(type-param-name-list)]
 [type-param-def-stmts]
 [Private statement or Sequence statement]
 [component-definition]...
 [type-bound-procedure-part]
End Type [name]

7.3.2 结构体访问方式

Type%Mem (参考代码: <a name="Back TYPE_ACCESS"><a href="#TYPE_ACCESS"> "TYPE_ACCESS" </a></a> )

7.4 小结

  • 注意数组定义的两种方式,编写代码时根据所需数组的特点合理选用;
  • 注意对数组访问的四种方式,在访问时区分有效数组位数和最大数组容量;
  • 连续数组片段的传递为地址传递,在子程序内对其改变会改变原数组,不连续数组片段的传递会构造新的暂时数组,子程序内对其改变不改变原数组
  • 注意子程序对数组作用的两种方式;
  • 注意结构体的赋值方法,包括片段赋值、构造赋值等方法;
  • 注意结构体的访问方式,尤其是多重结构体的逐级访问。

8. 函数和子例程

参考代码: <a name="Back Code.8"><a href="#Code.8"> "Code.8" </a></a> "

8.1 函数和子例程简介

计算机编程的函数是对数学函数概念的扩展;此外它也作为引导程序执行流程的重要方式。 使用函数的优点:

  • 将长过程 拆分 为若干小过程,有利于代码的 逻辑结构
  • 封装过程 使得函数的使用者不必了解内部实现细节;
  • 一次书写多次调用,便于 修改和维护
  • 函数内部与调用者是 隔离 的,不能直接使用调用者的变量,避免紊乱;
  • 函数局部变量在函数返回后丢失,下次进入时它们的值不确定,节约内存。

缺点:牺牲很小一部分效率(inline展开) 典型函数的书写和调用:

书写:
[形容词][返回类型] Function 名称([虚参1,虚参2...])
 [虚参的声明]
 [局部变量的定义]
 函数内部实现
  名称=返回值
 [return]!//尾部可忽略
End [Function [名称]]
返回变量=名称([实参1,实参2...])

典型子例程的书写和调用:

!//书写:
[形容词] Subroutine 名称([虚参1,虚参2...])
 [虚参的声明]
 [局部变量的定义]
 子例程内部实现
 [return]!//尾部可忽略
End [Subroutine [名称]]
!//调用:
Call 名称([实参1,实参2...])

函数与子例程的区别:

函数 子例程
有返回值 无返回值
var=函数名() Call 子例程名()
虚参可输出数据 虚参可输出数据
允许多个输出 允许多个输出
可包含文件操作 可包含文件操作
由于Fortran默认传址而非传值,所以可通过虚参输出数据达到返回值的作用。
因此函数和子例程其实没有什么区别。

8.2 虚参和实参

(参考代码: <a name="Back Func"><a href="#Func"> "Func" </a></a> )

虚参和实参时调用者与子程序互换数据最直接的方式:

  • 调用者 指定 实参 (Actual Arguments也叫实元)
  • 子程序 指定 虚参 (Dummy Arguments也叫形参,哑元)

虚参和实参一般按顺序结合,实参会传递到子程序中称为虚参。 传递的方式有两种:

  • 传址(By reference):高效,可回传,节约内存
  • 传值(By Value):不可回传,速度慢,浪费内存,安全

建议:

  • 严格匹配 实参和虚参,包括 数据类型 Kind值 数组维度 等;
  • 未特别指定可选参数时,严格匹配 参数个数
  • 大多数情况下,实参和虚参应该使用不同的名字。 实参命名 应考虑在 调用者中的标识 虚参命名 应考虑在 子程序中的作用
  • 要充分考虑子程序可能的变化,将能够交予 外部配置 的信息都定义为虚参,以提高子程序的 可重复使用性

8.3 传递数组和结构体

8.3.1 传递数组

数组除了元素之外,还有 维度 、每个维度的 上下限 大小 等信息。

Call sub(a) 子程序调用过程中,虽然在源码上看只有一个参数,但实际编译后,传递的信息除了 第一个元素的地址 (Address),还可能包括上述的部分信息(以Descriptor的形式)。

传递数组的三种方式:

  • 自动数组(Auto Arrays)
Subroutine sub(a,m,n)
 Integer m,n
 Real a(m,n)
End Subroutine sub

只传递地址,维度由定义确定,各维度上下限由其他虚参确定, 参数多

  • 假定大小(Assumed-size)
Subroutine sub(a)
 Real a(*)
 a(1:6)=1
End Subroutine sub

只传递地址,虚参只能是1维,下线为1,不传递上限, 容易越界

  • 假定形状(Assumed-shape)(参考代码: <a name="Back WriteMatrix"><a href="#WriteMatrix"> "WriteMatrix" </a></a> )
Subroutine sub(a)
 Real a(:,:)
End Subroutine sub

传递地址和各维度大小,下限可自动,上限自动计算, 需要Interface

8.3.2 传递结构体

(参考代码: <a name="Back passtype_degree"><a href="#passtype_degree"> "passtype_degree" </a></a> )

传递结构体时需注意,即使两个结构体完全一样,但其 分别独立定义 ,编译器也认为不是同一类。

因此虚参实参如果传递结构体,必须是由 同一个定义Type 得到的结构体实例. 一般采用模块定义Type,在调用者和子程序中分别调用同一个模块来定义实参和虚参。

(参考代码: <a name="Back passtype"><a href="#passtype"> "passtype" </a></a> )

8.4 特殊用法

8.4.1 变量的Save属性

子程序局部变量具有 临时性

子程序返回后,局部变量被收回,下次进入子程序时其值不确定。

Save属性可以指定变量在子程序返回后留存,待下一次进入同一子程序保留上一次的值。

Integer,Save::var
Integer::var=0!//虽然没有书写Save,但定义时初始化值,默认具有Save属性

8.4.2 虚参的Intent属性(需要Interface)

明确指定虚参的使用目的:输入参数(In)、输出参数(Out)、中性参数(Inout)

!//输入参数,在子程序内部不允许被改变
Integer,Intent(In)::input_arg
!//输出参数,子程序返回前必须被改变(对应实参不能是常数或表达式)
Integer,Intent(Out)::output_arg
!//中性参数
Integer,Intent(Inout)::nerter_arg
Integer::nerter_arg!//未指定Intent则为中性

注意:Intent的检查是在 编译时进行 ,而非运行时检查。 建议对每一个虚参都指定Intent属性!

8.4.3 虚参的Value属性(需要Interface)

指定该参数为 传值 参数,而 非传址 参数。

!//传值参数,只能作为输入参数,改变后不影响实参
Integer,Value::by_value_arg

注意:除 混编 外,一般 不使用 Value属性。

8.4.4 可选参数Optional属性(需要Interface)

被指定的参数在某些情况下可以不赋予,运行时动态决定参数的个数。

例如Open语句的大多数子句不需要书写,可以被认为是可选参数的子程序。

Integer,Optional::a

(参考代码: <a name="Back optional"><a href="#optional"> "optional" </a></a> )

8.4.5 更改参数顺序

子程序的实参、虚参可以 不按照顺序 对应。

类似的,Open语句的子句也可以不按照顺序书写,这在拥有较多的可选参数时是必要的。

就算不需要改变参数顺序,采用这种方式书写代码也可以 提高可读性

Call writeresult(Data=var,file="res.txt",size=1000)
Call writeresult(var,1000,"res.txt")

8.4.6 函数名作为参数

实参和虚参都是函数名,让子程序本身 以另外的函数作为参数

例如:书写一个数值积分的子程序,该子程序负责对另一函数进行数值积分,则另一函数由外部提供,是一个函数名的虚参。

此时虚参可以用External定义,也可以用Interface定义,推荐后者。

(参考代码: <a name="Back func_name_arg"><a href="#func_name_arg"> "func_name_arg" </a></a> )

8.4.7 Result后缀

旨在通过对 返回值重命名 以便于理解或书写。

主要用于区分返回值的内外名称,外部名称为函数名,内部名称为Result重命名。

8.4.8 递归子程序

自我调用 的子程序,在特殊的循环或迭代中非常有效。 但是该种用法易出错,对 堆栈的使用开销较大

可递归的子程序前应添加Recursive。

Recursive Function Func(n) Result(r)

(参考代码: <a name="Back recursive_do"><a href="#recursive_do"> "recursive_do" </a></a> "、 <a name="Back recursive"><a href="#recursive"> "recursive" </a></a> )

代码中介绍的只是 一进一出 的最简单的递归形式,而更复杂的 一进多出 递归,往往与链表结合使用。

例如著名的二叉树(数据结构),要遍历这个结构就可以用到递归函数。

操作系统的树形文件夹结构为我们带来了方便。其实操作系统自身也使用一进多出的递归函数来管理文件系统。

递归函数的使用原则:

  • 不涉及链表 的数据结构中,能不使用则尽量不用;
  • 一定要控制好递归 终止条件 ,否则容易无穷递归;
  • 递归函数中尽量 少使用局部变量
  • 多级,一进多出 的递归函数尤其需要特殊的优化。

8.4.9 不建议使用的特殊用法

  • 多入口函数(Entry)
  • 返回语句标号的函数(Alternate returns)
  • 语句函数(Statement function)

8.5 小结

  • 在编程过程中善于利用子程序的独立性和可重复使用性,将长过程拆解为小过程,封装为子程序便于使用;
  • 由于Fortran 默认传址 的特性,函数和子例程没有本质上的区别;
  • 子程序具有 隔离性 ,局部变量具有 临时性
  • 注意虚参和实参的匹配,包括 数值类型 Kind 值、 数组维度 等;
  • 注意虚参和实参的命名方法,虚参主要代表在子程序内部的作用;
  • 充分考虑子程序可能发生的变化,将 外部配置 的信息设置为虚参;
  • 注意 数组 结构体 类型的虚实参传递,包括数组维度、大小、上下限以及结构体虚实参的定义等。推荐的数组传递方式是 假定形状
  • 注意Intent、Value、Optional参数的特殊属性,需要Interface;
  • 注意初始化变量的Save属性, 不会 在运行过程中再次初始化,在子程序结束后会 保留 当前值;
  • 注意 带有虚参名的变量传递 ,不仅可以用于更改参数顺序,还能提高代码可读性;
  • 函数名作为参数时推荐使用 Interface定义
  • 函数的Result后缀主要用于区分返回值的内外名,便于理解和书写;
  • 注意递归子程序的使用原则;
  • 推荐所有虚参声明时都附上Intent属性,对于参数较多的子程序推荐使用带有虚参名的变量传递;
  • 在编写子程序时养成良好习惯,包括虚实参的命名、属性声明等,以及各类特殊用法的合理使用。

9. 模块(Module)

参考代码: <a name="Back Code.9"><a href="#Code.9"> "Code.9" </a></a> "

9.1 接口(Interface)

9.1.1 Interface简介

子程序将整个程序 划分 隔离 ,它们之间通过虚实参结合、Common、Module等 交换数据 ,通过 相互调用 实现程序流程控制。

接口(Interface)是 控制 子程序相互调用或传递参数的"指导"。

其他语言或Fortran77以前没有显示的Interface语法,函数间调用不安全,只检查 个数 类型

Fortran90之后出现了显示的Interface语法,指定了函数的 调用方式 各参数的传递方式 详细信息 上下限 变量的限定 是否可忽略 等。

Interface接口可以在 编译阶段 就查找出许多错误,避免程序运行时出现更多错误。

9.1.2 Interface使用

(参考代码: <a name="Back interface"><a href="#interface"> "interface" </a></a> ) 使用以下用法时,必须使用Interface:

  • 函数返回值为 数组 指针
  • 参数为 假定形状 数组;
  • 参数具有 Intent Value 等属性;
  • 参数具有 可选参数 、改变参数顺序。

函数名 作为虚参和实参时,虽然不强制要求,但也推荐使用Interface。

建议在任何子程序调用时,都使用接口!

Interface用于将被调用者的信息告知调用者。 书写时只需包含在

Interface
End Interface

其中的内容为:将被调用者的 执行语句 局部变量定义 去掉,一般只包含 子程序的头尾 虚参的声明

9.2 Module

Module用于将一部分(有关联的)数据和函数 聚合 起来。 使用Module主要有以下作用:

  • 避免手动书写Interface;
  • 数据 共享
  • 数据与过程的 封装 保护 继承

Module是Fortran90区别于Fortran77的重要特征,也是重大进步。

在Fortran2003以后的语法中, Module与Type结合 使得Fortran 面向对象 编程成为可能。

9.2.1 Module简介

函数是具有输入输出的、在内部实现一个特定功能的过程,而Module将一系列相关的函数及数据 再封装 以实现一系列 功能的组合

对于函数,虚实参结合是共享数据的一种方式,而Common和Module也可以用于共享数据。 需要注意的是Fortran中 没有全局变量 ,Common叫做(未命名的)公共区,而Module叫做模块,在使用时必须写明 Common /变量列表/ Use 模块名

  • 其中Common按字节数结合,而非变量名结合,建议弃用。
  • Module中的变量在Module内部可以自由使用,任何 Use 该Module的程序单元也可自由使用,其值保持一致。(参考代码: <a name="Back module"><a href="#module"> "module" </a></a> )

上述三种共享数据方式在使用时仅需选用一种,尤其是Common和Module方法不可同时使用。

Module比Common更简单更安全。

9.2.2 Module的访问

访问Module:

Module monitorsys
 Use smartHome

通过Only仅访问部分变量:

Module monitorsys
 Use smartHome,Only:tv,pc

仅访问部分变量且重命名:

Module monitorsys
 Use smartHome,Only:screen=>tv,pc

Only可以 避免命名冲突 加快编译速度 ,但 不能 节约内存。

Module向外部提供变量(数据共享)和子程序,并且拥有以下几种权限:

变量 子程序
Public 外部可读取、修改 外部可调用
Protect 外部可读取,不可修改 ——
Private 外部无法访问 外部无法调用
内部均可访问、修改 内部均可调用

分别注意外部程序和Module内部子程序对Module内部变量的访问权限,以及内部子程序间的调用。

注意Module内部隐含了Interface,在源码中无需显示写出,但是具有接口控制功能。

(参考代码:<a name="Back module_view"><a href="#module_view">"module_view"</a></a>)

注意内部子程序直接访问内部变量时没有接口控制,而通过虚实参结合访问内部变量时需要符合Interface要求。

9.2.3 Module的继承

Module还可以被 继承 ,ModA可以通过 Use ModB 获得ModB对外提供的Public变量和子程序。

在较大的程序中,Module的继承会形成类似树型的 模块树

需要注意,一个模块可以Use多个模块,也可以被多个模块Use,但要防止出现 环状依赖

  • Module会降低程序的 编译效率 。 (参考代码: <a name="Back module_protect"><a href="#module_protect"> "module_protect" </a></a> )

9.3 特殊用法

9.3.1 统一接口(函数重载)

通过统一接口可以把 多个子程序 捆绑为一个名字,由编译器根据实参的情况,决定到底调用哪一个函数。

对于功能相同或相似但输入有所不同的子程序,可以采用该方法将这些子程序统一命名,方便程序编写。

Module name
 Interface Is
  Module Procedure::Is_Int,Is_RR,Is_Char
 End Interface Is
Contains
 Integer Function Is_Int(logic,itrue,ifalse)...
 Real(Kind=8) Function Is_RR(logic,rtrue,rfalse)...
 Character(Len=64) Function Is_Char(logic,ctrue,cfalse)...
End Module name

大多数数学函数都利用了统一接口,如 sin()

他们绝大多数还是 逐元函数 ,即实参可以是数组。

9.3.2 自定义操作符(操作符重载)

通过Module Procedure和Interface Operator可以将操作符进行重载,从而实现一些更加“人性化”的程序。(参考代码: <a name="Back module_oper"><a href="#module_oper"> "module_oper" </a></a> )

9.4 小结

  • Interface作为控制子程序间数据交换的关键,建议在所有子程序调用时都使用接口;
  • 注意数据交换(共享)的三种方式,其中推荐使用Module而不使用Common;
  • Module的使用能够完成数据共享,并且对数据和过程进行 封装 保护 继承 ,提高安全性;
  • 通过Module和Type的结合,可以完成 面向对象 的编程;
  • 注意Module提供数据的三种 权限 ,并且子程序 隐式 提供Interface功能,外部调用时需满足接口要求;
  • 注意内部子程序 直接访问 内部变量时没有接口控制,而通过 虚实参结合 访问内部变量时需要符合Interface要求;
  • 编写代码时合理使用Module以提高数据的 安全性 、代码的 可维护性 和程序的 拟人性

10. 文件读写

参考代码: <a name="Back Code.10"><a href="#Code.10"> "Code.10" </a></a> "

10.1 文件和路径

各类操作系统(Windows、Linux、Mac)都习惯采用 树型目录结构 来管理文件系统。

通常每个文件都有一个 路径 和自己的 文件名 ,依靠路径和文件名去查找文件。

10.1.1 文件

文件名一般分为 主文件名 扩展名 。 但是扩展名并不是必须的,只是为了方便区分,用来表示文件的类型,并不影响文件实质。

Windows和部分Linux的界面,采用文件扩展名来决定直接双击通过什么应用程序打开文件。这种文件关联只是为了便于人们的操作。

10.1.2 文本文件

本质上文件都是二进制的。所有数据都是有效的可显示的 字符 的文件叫做 文本文件

  • 文本文件,如.f90 .txt .log等,用于人类阅读;
  • 二进制文件,如.mp3 .avi 。exe等,用于机器阅读。

文本文件通常用文本编辑器(记事本,VI)打开,容易读懂。二进制文件用文本编辑器打开一般会看到乱码,解释方式不正确。

文本文件的行通过换行符(CR+LF(Windows)、CR(Linux)、LF(Mac))分隔,而列通过间隔符(空格、逗号、TAB)分隔。

10.1.3 二进制文件

二进制文件是原始计算机数据,存在解释方式的问题。即对于同样的二进制序列可以有多种不同的解释。

一般二进制文件 只存储数据 不存储解释方式 。因此对于一个陌生的未说明的二进制文件,只能进行 猜测

对于二进制文件,除了使用专属的应用程序打开,还可以用十六进制编辑器打开(UltraEdit、VIM :%!xxd 、WinHex、VS)。

二进制文件没有行列的概念,是无间隔的存储。

10.1.4 文件读取方式

由于文件形式的不同,Fortran语法规定了不同方式来读取:

文本文件(有格式文件) 二进制文件(无格式文件)
顺序读取 顺序读取有格式文件(用的最多) 顺序读取无格式文件(在记录前后各增加4字节,表示记录长度)
直接读取 直接读取有格式文件(要求每行一样长) 直接读取无格式文件(用得较多)
流文件 —— 流文件(非常方便,强大)
  • 内存里是 无格式 的;
  • 有格式、无格式是文件本身的 属性
  • 顺序读取、直接读取、流文件是 访问的方式
  • 文本文件一般不用流文件读写。

文本文件和二进制文件的对比:

文本文件(有格式文件) 二进制文件(无格式文件)
读写慢速 读写快速
适合中小文件(100MB以内) 适合较大文件
可能有读写误差 读写无误差
人类阅读方便 人类阅读困难
容易出错 不容易出错

读取方式的对比:

顺序读写 直接读写
按顺序 可任意跳转
速度慢 速度快
每次不一样长 每次一样长
简单 复杂
灵活 死板

10.2 顺序读写有格式文件

文本文件一般用顺序读写、直接读写。

  • 顺序读写:按照先后顺序读取该文件;
  • 直接读写:指定特定的行直接读写(需每行长度一样)。

读写文件至少需要用到4个语句:

  • Open:打开文件
  • Read:读取文件
  • Write:写入文件
  • Close:关闭文件

10.2.1 Open

Open(子句=值,...) Open有二十多个子句,有不同的作用。其中必要的只有两个:

  • Open(Unit=通道号,File="文件名")
  • Open(通道号,File="文件名")

文件通道号由程序员给定,一般用 大于10 的数字。

10以下的数字由不同编译器预留(一些编译器用5、6表示标准输入输出)。

通道号是操作该文件的"凭证",在Read、Write、Close时指定。也可以用变量、循环打开多个文件。

文件打开后,读取位置默认在文件开始处。

通道号 句柄 的区别是:句柄由过程内部 自动分配 ,外部不能决定是多少;而通道号由 程序给定 ,万一给错了,就会出错。

Fortran2003以后增加了NewUnit这个子句,可以由编译器 自动分配通道号 ,程序员需要保存这个通道号( File_ In )。

Integer::File_ In
Open(NewUnit=File_ In,File="2d_text.txt")

10.2.2 Read

Read(文件通道号,*)变量列表 如果不特殊指定格式,每个Read语句读取整的N行。

星号*表示表控格式(List-Direct),即让 变量列表自动控制格式

一般Read不特别指定读取格式,使用表控格式即可;而Write需要指定写入格式。

所以一般情况下,一个Read语句只读取一行数据。如果要读取多行,需要采用 多个Read语句 循环读取

下面是一些例子:

  • 读取文件到数组:通过 数组片段 ,顺序读写有格式文件针对二维数组也非常方便。(参考代码: <a name="Back Read_2D_text"><a href="#Read_2D_text"> "Read_2D_text" </a></a> )
  • 读取(跳过) 指定位置 :可以通过 空Read 跳过一行(包括空白行),通过读取一个 不使用的变量 (r)跳过若干列。(参考代码: <a name="Back Read_text"><a href="#Read_text"> "Read_text" </a></a> )
  • 读取 未知行列数 文件:对于未知行列数的文件,可以采用函数先获取行列数,配合 可分配数组 进行文件读取。注意参考代码中对文件读取位置的处理。(参考代码: <a name="Back Read_text_alloc"><a href="#Read_text_alloc"> "Read_text_alloc" </a></a> )
  • 读取 行的特定内容
Read(FILE_IN,100) r   100 Format(11x,f7.3)!//只能读取固定位置的数据-  
read(FILE_IN,'(a512)') cStr!//读取第一行   
i=index(cStr,"Δt")+3   j=idex(cStr,"ms")   
Read(cStr(i+1:j-1),*) r   !//通过对字符串处理,读取指定格式位置的数据。

10.3 直接读写有格式文件

直接读取方式读写文本文件,可以 任意指定 读取哪一行。

对于每行不一样长的文件,不适用直接读取。

为了使用直接读取可以将每一行 补齐 成一样的长度。 直接读取Open格式: Open(NewUnit=FID,File="text_ditect.txt",form="formatted",access="Direct",RecL=64)

  • form="formatted" :指定为有格式文件(文本文件)。在顺序读取时是默认值,可以不指定;
  • access="Direct :指定为直接读取方式。在顺序读取时可以指定"SEQUENTIAL",它是默认值,可以不指定;
  • RecL=64 :指定记录长度(Record Length)是64字节,仅在直接读取时指定。

直接方式读取时,必须指定 读取格式 读取记录

Read(FID,'(i7,3(1x,g15.6))',Rec=5) i,a,b,c

  • '(i7,3(1x,g15.6))' :表示读取格式,7长度的整数,3个空格和15长度的浮点数。
  • rec=5 :表示读取第5笔记录。因为 RecL=64 ,而每一行是64字节,所以 rec=5 对应文件第5行(注意:读取时一笔记录的长度和文件一行的长度不同,这里只是设置为相同)。 (参考代码: <a name="Back Read_text_direct"><a href="#Read_text_direct"> "Read_text_direct" </a></a> )

直接读取需要指定读取格式,对文件格式要求较高,并且不能使用表控格式。

可以先用 字符串读取数据 ,再从字符串中按照一定规则,用表控格式读取数据。尤其在 不规则 文件的读写中,经常使用这种方式。

Read(FID,'(a64)',Rec=5) str
Read(str,*) i,a,b,c
Write(*,*) i,a,b,c

建议:

  • 对于 少量的 、起 控制作用 的、需要 经常修改 的数据,采用 顺序读写 文本文件;
  • 对于 较大量的 规则排列 的数据,采用 直接读写 文本文件;
  • 而 对于 大量的 数据,采用 二进制 文件。

10.4 顺序读写无格式文件

10.4.1 无格式文件

无格式文件(二进制文件)用于机器阅读,它的 格式与内存一样

因此二进制文件读写是简单的复制,速度非常快。非常适合 大量数据 中间数据 、不希望用户看懂和随意更改的数据。(SEGY、netcdf、bmp、surfer grid等都是二进制文件)

代码资源内提供了一款十六进制编辑器MiniHex,可以用于打开任何的二进制文件。

10.4.2 无格式文件的顺序读写

顺序写入 无格式文件,以 记录 为基本单元,读写过程分若干笔记录。

每一次记录,在记录前后各多出4个字节,用于记录本次写入的 长度 . 顺序读取 时,通过这4个字节知悉当前记录长度。 (参考代码: <a name="Back write_myfile"><a href="#write_myfile"> "write_myfile" </a></a> )

顺序读取 该文件时,程序会首先读入前4个字节,获得本笔记录的长度。

随后读入后4个字节,以验证是否与前4个字节相同(不同则报错)。

最后将中间字节按顺序读取到变量列表中。

(参考代码: <a name="Back read_myfile"><a href="#read_myfile"> "read_myfile" </a></a> )

由于特殊的前后4字节:

  • Fortran顺序写入的文件,尽量用Fortran顺序读取;
  • 非Fortran写入的文件,尽量不要用Fortran顺序读取;
  • 用其他方式也可以读写顺序写入的文件,但是比较啰嗦繁杂。

所以只用Fortran,可以用顺序读写;如果和其他软件、语言交互,通常不使用顺序读写。

10.5 直接读写无格式文件

  • 直接读写 无格式文件,也以记录为基本单元,将读写过程分为若干笔记录。 但是每一笔记录的 长度是固定的 ,在Open时指定(注意:RecL在某些编译器上代表 4字节 的个数而非字节的个数)。 (参考代码: <a name="Back write_mydirect"><a href="#write_mydirect"> "write_mydirect" </a></a> )
  • 直接读写可以跳转到 任何整记录 读写,也可以 一边读一边写 。 读取时也不一定按照写入时的顺序。 (参考代码: <a name="Back read_mydirect"><a href="#read_mydirect"> "read_mydirect" </a></a> )
  • 直接读写可以跳转读取位置,比较 灵活 ,但要求每个 记录长度 一样,并且Open之后就无法改变。 对于 数据结构比较混乱 (有多种字节)的文件,读取不方便。 很多时候不得不 多次Open Close ,用 不同的RecL 来读写不同长度的数据部分。 过去可以使用Equivalence、Mvbits函数等方法实现一次读取。 现在推荐使用 流文件方式 来读取。 例:直接读写Goldensoft Surfer*.grd文件。 (参考代码: <a name="Back read_fcode_test_direct"><a href="#read_fcode_test_direct"> "read_fcode_test_direct" </a></a> )
  • 直接读写理论上可以读写任何文件,但更适合于 大量的长度相同的 数据,或纯粹的 数组 。 在Fortran2003以前,直接读写是读取二进制文件的唯一选择。在VF系列编译器上,还扩展有Binary方式的读写,类似顺序读写,但没有记录长度字节。但由于是扩展语法,不够规范。

10.6 流文件读写无格式文件

  • 流文件是Fortran2003的新语法,允许 自由控制读写位置 自动判断 本次读写的长度。 (参考代码: <a name="Back read_fcode_test_stream"><a href="#read_fcode_test_stream"> "read_fcode_test_stream" </a></a> )
  • 流文件打开时不指定RecL,打开后读取位置自动在文件开始处。 执行Read语句时不指定Rec,读取长度L根据后面的变量表自动计算。 读取后,读取位置自动向后移动L个字节。
  • 除了可以自动判断位置和长度外,流文件允许随意跳转。 上述例子中,如果只想读取m、n,可以直接跳转到第5字节读取,而不读取DSBB: Read(FID,Pos=5) m,n 也可以查询当前读取的位置: Inquire(FID,Pos=i) 查询后,可以跳过一定字节继续读取: Read(FID,Pos=i+32) a,b
  • 可以通过一个Type来定义文件的 头部信息 ,之后一次读取整个头部。 尤其对于类似SEGY这种有多个头部(道头)的文件,非常方便。 (参考代码: <a name="Back read_fcode_test_stream_type"><a href="#read_fcode_test_stream_type"> "read_fcode_test_stream_type" </a></a> )
  • 流文件读写非常方便,甚至可以方便地读取 顺序写入的二进制文件

10.7 其他问题

10.7.1 字节序

对于超过1个字节的数据类型,存在字节的存储顺序问题,这就是 字节序 。 一般个人PC采用小端(Little_Endian)字节序,有些工作站采用大端(Big_Endian)字节序。 对于大端字节序数据,intel编译器可以采用如下方式读取: Open(FID,File="文件名",convert="Big_Endian")

10.7.2 文件路径

  • Fortran标准语法中,没有规定任何文件夹(目录/路径)的问题;
  • Open语句的File子句,是传递给操作系统(系统调用),因此一般可以使用带(绝对或相对)路径的文件名。
Open(12,File="subfolder\filename.ext")!//Windows
Open(12,File="D:\dir\subdir\filename.ext")!//Windows
Open(12,File="/etc/passwd")!//Linux
Open(12,File="../../otherdir/filename.ext")!//Linux
  • 如果不带任何路径,则被认为是 当前路径 (Working Directory);
  • 如果带相对路径,以当前路径为 基准
  • 通过命令行执行时,当前路径一般在 提示符 上(或pwd命令查看);
  • 直接双击可运行程序,当前路径在.exe所在文件夹;
  • 通过IDE方式运行,当前路径一般在 工程 所在文件夹;
  • 一些IDE允许设置当前路径。

程序中可以通过一些方式, 动态地 更改当前路径,不同的编译器都有类似的扩展。 IVF:ChangeDirQQ;gfortran:ChDir。 有的编译器允许获得当前路径,如IVF/gfortran:GetCWD。 (参考代码: <a name="Back cwd"><a href="#cwd"> "cwd" </a></a> ) 注意例程中可执行文件路径和当前路径(工作路径)的区别。

10.8 小结

  • 文件分为 文本文件 二进制文件 两种;
  • Read读取文件时尽量采用 表控 格式(*);
  • 注意不同情况下文件读取方式的选择,包括文件的 属性 大小 格式 等;
  • 对于 大量的 规律的 纯粹数组,直接读写方式效率最高;
  • 注意直接读写方式的记录长度,在Open后就无法改变;
  • 二进制文件的读写,一定建立在 知晓文件格式 的前提下;
  • 流文件能够方便地读写 任何 二进制文件,善于使用流文件处理无格式文件;
  • 编写程序时文件路径尽量采用 相对路径 ,不使用绝对路径;

11. 标准函数

11.1 标准函数的一般问题

  • Fortran较为底层,仅提供非常 底层 的数学函数。绝大多数高层的数学问题需要独立书写代码实现。 语法函数的种类和用法与其他软件和语言有所差异,使用需要查阅 帮助文档 intel Fortran Compiler帮助文档下载 Fortran常用函数
  • Fortran函数有严格的参数类型和返回值类型。虽然很多语法内的函数提供了不同数据类型的版本,但并非每一种都有。 例如 y=exp(2) 是错误的写法,因为exp()函数未提供Integer版本。正确应写为 y=exp(2.0)
  • Fortran内部的多数数值函数有Generic Name和Specific Name。绝大多数情况下,使用Generic Name足矣。 只有极少数情况下,才必须使用Specific Name。例如 输入和输出类型 不一致,或多个 参数类型 不一致。
  • Fortran内部的大多数函数,还是逐元函数(Elemetal)。 所谓逐元,简单而言就是对于 单变量参数 ,可以是 任意维度的数组
  • 优化:优秀的编译器提供优秀的优化方案。优化的作用是,使得计算方法通过一定的变换实现更快的执行。 例如 a=a*25 b=a*4+a,a=b*4+a 对于计算机来说,执行会更快。优化水平往往能够体现不同编译器的差别。 对于同一个数学问题,自己写代码实现往往不如编译器厂家优化效果好。因此对于绝大多数问题,如果 能够使用语法内函数,就尽量不要自己写代码

11.2 数学数值函数

11.2.1 类型转换函数

  • 转换为 Real 型:
r=Real(x)
r=Real(x,Kind=8)!//双精度Real
r=Float(x)/Dble(x)
  • 转换为 Integer 类型:
i=Int(x)
i=Int(x,Kind=8)!//双精度Integer
i=Nint(x)!//四舍五入并转换
  • 转换为 Complex
c=Cmplx(a,b)!//将a,b转换为复数,分别为实部和虚部
c=Complex(a,b,Kind=8)
  • 内部文件( Char )转换:
Write(字符串,*) 其他数值!//其他类型转字符串
Read(字符串,*) 其他数值!//字符串转其他类型

11.2.2 一般数值函数

  • i=Ceiling(r) :天花板函数,返回 大于等于 变量的最小整数(Kind);
  • i=Floor(r) :地板函数,返回 小于等于 变量的最大整数(Kind);
  • ri=Abs(ri) 绝对值 函数;
  • c=Conjg(c) :取 共轭复数
  • ri=Max(ri1,ri2,ri3,...) 最大值 函数;
  • ri=Min(ri1,ri2,ri3,...) 最小值 函数;
  • ri=Mod(a,b) 取余数 函数;
  • 三角函数(加h为 双曲 三角函数,加D为 角度制 三角函数(不标准,不建议使用)):
  • r=sin(r)/cos(r)/tan(r)
  • r=asin(r)/acos(r)/atan(r)
  • r=log(r)/log10(r)/exp(r) :对数指数函数;
  • 向量矩阵函数:
  • c=dot_product(a,b) :内积函数;
  • b=transpose(a) :转置函数;
  • c=matmul(a,b) :矩阵相乘函数

11.3 字符串和数组函数

灵活地使用字符串函数,可以实现很多类似 批量化处理 的功能。 因为语法中的 文件名 格式符 都是字符串,并且允许使用变量。 因此动态变化的字符串变量,可以实现动态的文件名和格式符。

11.3.1 字符串函数

  • str=trim(str) :去掉字符串尾部的空格并返回;
  • str=adjustl(str) 左对齐 字符串,长度不变;
  • str=adjustr(str) 右对齐 字符串,长度不变;
  • New_line() :返回一个 回车符 (因为手动添加不同操作系统不同)
  • c=char(i)/achar(i) :返回编码(ASCⅡ码)为i的 字符
  • i=ichar(c)/iachar(c) :返回c的 编码 (ASCⅡ码);
  • i=len(c) :返回字符串长度( 定义长度 );
  • i=len_trim(c) :返回字符串长度( trim长度 );
  • i=index(c,cf[,back]) :在字符串c中查找 cf第一次出现的位置 ,可指定 back=.true. 从后面开始查询;
  • i=scan(c,cf[,back]) :在字符串c中查找 cf中的每一个字符 ,返回第一次出现的位置,可指定 back=.true. 从后面开始查询;
  • i=verify(c,cf[,back]) :在字符串c中查找 不属于cf中的字符 ,返回第一次出现的位置,可指定 back=.true. 从后面开始查询; (参考代码: <a name="Back string"><a href="#string"> "string" </a></a> )

11.3.2 数组函数

灵活使用数组函数可以减少很多循环代码:

  • b=reshape(array,shape) :重塑数组外形;
  • b=all(array[,dim]) :判断逻辑数组元素是否 全为真
  • b=any(array[,dim]) :判断逻辑数组元素是否 至少一个为真 if(all(满足)) 等效于 if(.not.any(.not.满足)) ; (参考代码: <a name="Back any_all"><a href="#any_all"> "any_all" </a></a> ) any和all带有dim(维度)参数,可以用于将多维数组按低一级维度分别处理。
integer::a(3,3)=reshape([11,2,3,14,5,6,7,8,9],[3,3])
write(*,*) all(a<10,dim=1)!//将按第1维分别输出结果
  • n=count(array[,dim]) :统计逻辑数组中 真值的个数
  • n=maxval(array[,dim][,mask]) :获得数组中的 最大值 ,可附带条件,也可指定维度;
integer::a(5)=[11,2,3,1,-4]
maxval(a,mask=(a<3))!//求a中小于3的最大值
  • n=minval(array[,dim][,mask]) :获得数组中的 最小值 ,可附带条件,也可指定维度;
  • [m,n,...]=maxloc(array[,dim][,mask]) :获得数组中 最大值的坐标 ,可附带条件,也可指定维度。注意返回值为在数组中的位置,数据结构为数组,大小为array的维数; 如果加入dim参数,返回值数组的数量为,原数组被拆分成该维度数组的数量,返回值数组的元素代表在该维度下数组内最大值的坐标
  • [m,n,...]=maxloc(array[,dim][,mask]) :获得数组中- [m,n,...]=maxloc(array[,dim][,mask]) :获得数组中最大值的下标,可附带条件,也可指定维度。,可附带条件,也可指定维度。
  • array=cshift(array,n[,dim]) :对数组进行 圆周平移 ,可使用dim指定按列(行)平移;
  • array=eoshift(array,n[,dim]) :对数组进行 单向平移 ,可使用dim指定按列(行)平移;

11.4 其他函数

  • i=Command_Argument_Count() :获得 命令行参数 的个数;
  • call Get_Command_Argumen(number,str[,leng,status]) :获得某个命令行参数; (参考代码: <a name="Back args"><a href="#args"> "args" </a></a> )
  • call Get_Environment_Variable(name[,value,lenght,satatus,trim_name]) :获取名为name的环境变量,value为结果,length为长度,status为状态,trim_name为逻辑型输入,代表是否trim名称;
  • 时间相关函数:
  • i=Date_And_Time([date][,time][,zone[,values]) :获得系统日期、时间及时区,精度不高;
  • call CPU_Time(time) :获得当前CPU时钟,可用于检查程序段的执行时间。特点是多线程会多次计算,其他进程占用的CPU不计入;
  • call System_Clock([count][,count_rate][,count_max]) :获取系统时间(基于1970年),也可用于检查程序段执行时间,特点是计算物理时间消耗。

12. 参考代码

12.1 <a name="Code.6">"Code.6"</a>(<a href="#Back Code.6">"Back"</a>)

12.1.1 <a name="Demo_IF_func"> "Demo_IF_func" </a>(<a href="#Back Demo_IF_func"> "Back" </a> )

MODULE func
    IMPLICIT NONE
CONTAINS
    SUBROUTINE readcmd(keyword, pars)
        ! This SUBROUTINE reads a line of inpur from the commandline, and
        ! It returns the first phrase as keyword using variable 'keyword', and 
        ! returns the following numerical parameters using the allocatable real
        ! array 'pars'
        ! The size of array 'pars' is determined by another FUNCTION ct_pars.
        CHARACTER (LEN=:), ALLOCATABLE, INTENT (INOUT) :: keyword
        REAL, ALLOCATABLE, INTENT (INOUT) :: pars(:)
        CHARACTER (LEN=80) :: cmdline, kwt
        INTEGER :: npars
        READ (*, '(a80)') cmdline
        READ (cmdline, *) kwt
        IF (ALLOCATED(keyword)) DEALLOCATE(keyword)
        IF (ALLOCATED(pars)) DEALLOCATE(pars)
        ALLOCATE (CHARACTER(LEN=len_trim(adjustl(kwt))) :: keyword)
        ALLOCATE (pars(ct_pars(cmdline)))
        READ (cmdline, *) keyword, pars
    END SUBROUTINE
    FUNCTION ct_pars(cmdline) RESULT (npars)
        ! This FUNCTION counts the number of space separated parameters in the 
        ! command line string 'cmdline'.
        ! Abbreviations:
        ! * par = Parameter
        ! * cmd = Command
        ! * tmp = Temporary
        ! * n = Number
        CHARACTER (LEN=*), INTENT (IN) :: cmdline
        CHARACTER (LEN=:), ALLOCATABLE :: ctmp
        INTEGER :: npars, i
        npars = 0
        i = 1
        ALLOCATE (CHARACTER(LEN=len_trim(adjustl(cmdline))) :: ctmp)
        ctmp = trim(adjustl(cmdline))
            IF (ctmp(i:i)/=' ') THEN
                i = i + 1
                npars = npars + 1
                    i = i + 1
                    IF (ctmp(i:i)/=' ') EXIT
                END DO
            END IF
            IF (i==len(ctmp)) EXIT
        END DO
    END FUNCTION
END MODULE

12.1.2 <a name="Demo_IF_main"> "Demo_IF_main" </a>(<a href="#Back Demo_IF_main"> "Back" </a> )

PROGRAM main
    USE func
    IMPLICIT NONE
    REAL, PARAMETER :: pi = acos(-1.)
    CHARACTER (LEN=:), ALLOCATABLE :: keywd
    REAL, ALLOCATABLE :: pars(:)
        ! Try input the following strings in the commandline:
        ! cir 20.
        ! sqr 10.
        ! rect 1. 2.
        CALL readcmd(keywd, pars)
        IF (keywd=='end') THEN
        ELSE IF (keywd=='cir' .OR. keywd=='sqr') THEN
            IF (size(pars) /= 1) THEN
                WRITE (*, *) 'Incorrect number of parameters.'
                CYCLE
            END IF
        ELSE IF (keywd=='rect') THEN
            IF (size(pars) /= 2) THEN
                WRITE (*, *) 'Incorrect number of parameters.'
                CYCLE
            END IF
            WRITE (*,*) 'Keyword unknown...'
            CYCLE
        END IF
        IF (keywd=='cir') THEN
            WRITE (*, *) 'Area = ', pi*pars(1)**2.
        ELSE IF (keywd=='sqr') THEN
            WRITE (*, *) 'Area = ', pars(1)**2.
        ELSE IF (keywd=='rect') THEN
            WRITE (*, *) 'Area = ', pars(1)*pars(2)
        END IF
    END DO
END PROGRAM

12.1.3 <a name="Demo_Type_main"> "Demo_Type_main" </a>(<a href="#Back Demo_Type_main"> "Back" </a> )

MODULE types
        TYPE :: var_base
            CHARACTER (LEN=10) :: name
        END TYPE
        TYPE, EXTENDS (var_base) :: real_var
            REAL :: r
        END TYPE
        TYPE, EXTENDS (var_base) :: int_var
            INTEGER :: i
        END TYPE
    CONTAINS
        SUBROUTINE pnt_var(var)
            IMPLICIT NONE
            CLASS (var_base), INTENT (IN) :: var
            SELECT TYPE (var)
            CLASS IS (real_var)
                WRITE (*, *) 'Real Value ' // trim(var%name) // ' = ', var%r
            CLASS IS (int_var)
                WRITE (*, *) 'Integer Value ' // trim(var%name) // ' = ', var%i
            CLASS DEFAULT
                ERROR STOP 'Unknown type.'
            END SELECT
        END SUBROUTINE
    END MODULE
    PROGRAM main
        USE types
        IMPLICIT NONE
        TYPE (real_var) :: vr = real_var('a', 3.14)
        TYPE (int_var) :: vi = int_var('b', 3)
        CALL pnt_var(vr)
        CALL pnt_var(vi)
    END PROGRAM

12.2 <a name="Code.7"> "Code.7" </a>(<a href="#Back Code.7"> "Back" </a> )

12.2.1 <a name="ENUM"> "ENUM" </a>(<a href="#Back ENUM"> "Back" </a> )

PROGRAM WWW_FCODE_CN
    IMPLICIT NONE
!//ENUM WEEKDAY
    INTEGER,PARAMETER:: SUN=0,MON=1,TUE=2,WED=3,THU=4,FRI=5,SAT=6
!//USER INPUTS AN INTEGER,THEN PRINT THE WEEKDAY NAME AND OTHER INFORMATIONS.
    INTEGER:: WKDAY,IERR
        WRITE(*,*)"PLEASE INPUT AN INTEGER NUMBER:"
        READ(*,*)WKDAY
        IERR=0
        SELECT CASE (WKDAY)
        CASE(SUN)
            PRINT *,WKDAY,"IS SUNDAY"
        CASE(MON)
            PRINT *,WKDAY,"IS MONDAY"
        CASE(TUE)
            PRINT *,WKDAY,"IS TUESDAY"
        CASE(WED)
            PRINT *,WKDAY,"IS WEDNESDAY"
        CASE(THU)
            PRINT *,WKDAY,"IS THURSDAY"
        CASE(FRI)
            PRINT *,WKDAY,"IS FRIDAY"
        CASE(SAT)
            PRINT *,WKDAY,"IS SATURDAY "
        CASE DEFAULT
            PRINT *,"ARE YOU MARTIAN ?"
            IERR=1
        END SELECT
        IF(IERR/=0)EXIT
        SELECT CASE (WKDAY)
        CASE(MON:FRI)
            PRINT *,WKDAY,"IS WORKDAY"
        CASE(SUN,SAT)
            PRINT *,WKDAY,"IS WEEKEND"
        CASE DEFAULT
            PRINT *,"ARE YOU MARTIAN ?"
        END SELECT
    ENDDO
    PRINT *,"PROGRAM TERMINATED"
END PROGRAM WWW_FCODE_CN

12.2.2 <a name="ARR"> "ARR" </a>(<a href="#Back ARR"> "Back" </a> )

PROGRAM WWW_FCODE_CN
    IMPLICIT NONE
    INTEGER ::I
    INTEGER,DIMENSION(5):: ARR
    DATA ARR/1, 2, 5, 4, 3/
    WRITE(*,'(A,3X,A)')'INDEX','VALUE'
    DO I=1,5
        WRITE(*,'(2I5)')I,ARR(I)
    ENDDO
    PRINT *
    WRITE(*,'(A,1X,A)')'MAX_LOC','MAX_VAL'
    WRITE(*,'(2I5)')MAXLOC(ARR),MAXVAL(ARR)
    PRINT *,"PROGRAM TERMINATED"
END PROGRAM WWW_FCODE_CN

12.2.3 <a name="TYPE"> "TYPE" </a>(<a href="#Back TYPE"> "Back" </a> )

PROGRAM WWW_FCODE_CN
    IMPLICIT NONE
    TYPE :: STUDENT
        INTEGER :: ID
        CHARACTER(LEN=100)::NAME
        INTEGER:: CHINESESCO,MATHSCO,ENGLISHSCO
    END TYPE
    TYPE (STUDENT):: FLYBIRD
    FLYBIRD%ID = 001
    FLYBIRD%NAME = "FLYBIRD"
    FLYBIRD%CHINESESCO = 60
    FLYBIRD%MATHSCO = 97
    FLYBIRD%ENGLISHSCO = 17
    WRITE(*,*)'STUDENT INFOR:'
    WRITE(*,*)"NAME:         ",FLYBIRD%NAME
    WRITE(*,*)"STUDENT NO.:  ",FLYBIRD%ID
    WRITE(*,*)"CHINESE SCORE:",FLYBIRD%CHINESESCO
    WRITE(*,*)"MATH SCORE:   ",FLYBIRD%MATHSCO
    WRITE(*,*)"ENGLISH SCORE:",FLYBIRD%ENGLISHSCO
    PRINT *,"PROGRAM TERMINATED"
END PROGRAM WWW_FCODE_CN

12.2.4 <a name="CLASS"> "CLASS" </a>(<a href="#Back CLASS"> "Back" </a> )

MODULE STUDENTCLASS
    TYPE STUDENT
        INTEGER :: ID
        CHARACTER(LEN=100)::NAME
        INTEGER:: CHINESESCO,MATHSCO,ENGLISHSCO
    CONTAINS
        PROCEDURE :: PRINT_STUDENT_INFORTMATION => PRINT_STUDENT_INFORTMATION
    END TYPE STUDENT
CONTAINS
    SUBROUTINE PRINT_STUDENT_INFORTMATION(THIS)
        CLASS (STUDENT) :: THIS
        WRITE(*,*)'STUDENT INFOR:'
        WRITE(*,*)"NAME:         ",THIS%NAME
        WRITE(*,*)"STUDENT NO.:  ",THIS%ID
        WRITE(*,*)"CHINESE SCORE:",THIS%CHINESESCO
        WRITE(*,*)"MATH SCORE:   ",THIS%MATHSCO
        WRITE(*,*)"ENGLISH SCORE:",THIS%ENGLISHSCO
    END SUBROUTINE PRINT_STUDENT_INFORTMATION
END MODULE STUDENTCLASS
PROGRAM WWW_FCODE_CN
    USE STUDENTCLASS
    IMPLICIT NONE
    TYPE (STUDENT):: FLYBIRD
    FLYBIRD=STUDENT(2,"FLYBIRD",60,97,17)
    CALL FLYBIRD%PRINT_STUDENT_INFORTMATION()
    PRINT *,"PROGRAM TERMINATED"
END PROGRAM WWW_FCODE_CN

12.2.5 <a name="ARR_TYPE"> "ARR_TYPE" </a>(<a href="#Back ARR_TYPE"> "Back" </a> )

SUBROUTINE SWAP(AUTOARR1,AUTOARR2,N)
    IMPLICIT NONE
    INTEGER:: N
    REAL,DIMENSION(N) :: AUTOARR1,AUTOARR2
    REAL,ALLOCATABLE,DIMENSION(:)::DYNARR
    ALLOCATE(DYNARR(N))
    DYNARR(:N)=AUTOARR1(:N)
    AUTOARR1(:N)=AUTOARR2(:N)
    AUTOARR2(:N)=DYNARR(:N)
    DEALLOCATE(DYNARR)
END SUBROUTINE SWAP
PROGRAM WWW_FCODE_CN
    IMPLICIT NONE
    INTEGER,PARAMETER:: NARR=5
    REAL,PARAMETER:: PI=3.14159265
    REAL,DIMENSION(NARR):: FIXARR1,FIXARR2
    INTEGER::I
!  ASSIGN
    FIXARR1(:NARR)=(/(SIN(PI*(I-1.)/(NARR-1)),I=1,NARR)/)
!  ASSIGN,FUNCTION ARG
    FIXARR2(:NARR)=COS( (/(PI*(I-1.)/(NARR-1),I=1,NARR)/) )
    WRITE(*,*)"BEFORE SWAP..."
    WRITE(*,'(7F12.6)')FIXARR1(:NARR)
    WRITE(*,'(7F12.6)')FIXARR2(:NARR)
    CALL SWAP(FIXARR1,FIXARR2,NARR)
    WRITE(*,*)"AFTER SWAP..."
    WRITE(*,'(7F12.6)')FIXARR1(:NARR)
    WRITE(*,'(7F12.6)')FIXARR2(:NARR)
END PROGRAM WWW_FCODE_CN

12.2.6 <a name="ARR_ACCESS"> "ARR_ACCESS" </a>(<a href="#Back ARR_ACCESS"> "Back" </a> )

SUBROUTINE FUNARR (A,N)
    IMPLICIT NONE
    INTEGER:: N
    REAL,DIMENSION(N):: A
    WRITE(*,*)"FUNARR VECTOR SUBSCRIPT....IN"
    WRITE(*,'(10F12.6)')A(:N)
    WRITE(*,*)"FUNARR VECTOR SUBSCRIPT....OUT"
    A(:N)=A(:N)-100
END SUBROUTINE
PROGRAM WWW_FCODE_CN
    IMPLICIT NONE
    INTEGER,PARAMETER:: NMAX=100
    REAL,PARAMETER:: PI=3.14159265
    REAL,DIMENSION(NMAX):: ARR
    INTEGER,DIMENSION(5):: VECSUBSCP=(/1,4,6,7,5/)
    INTEGER:: NARR,I
    NARR=10
    ARR=1.
    ARR(:NARR)=1.   !ÍÆ¼ö
    WRITE(*,*)"ASSIGN TOTAL"
    WRITE(*,'(10F12.6)')ARR(:NARR)
    WRITE(*,*)
    ARR(1)=-1.
    WRITE(*,*)"ASSIGN ELEMENT"
    WRITE(*,'(10F12.6)')ARR(:NARR)
    WRITE(*,*)
    ARR(2:4)=(/(SIN(PI*(I-1.)/(NARR-1)),I=2,4)/)
    WRITE(*,*)"ASSIGN SLICE"
    WRITE(*,'(10F12.6)')ARR(:NARR)
    WRITE(*,*)
    CALL FUNARR(ARR(2:4),3)
    WRITE(*,*)"FUNARR SLICE"
    WRITE(*,'(10F12.6)')ARR(:NARR)
    WRITE(*,*)
    ARR(VECSUBSCP(:3))=0.5
    WRITE(*,*)"ASSIGN VECTOR SUBSCRIPT"
    WRITE(*,'(10F12.6)')ARR(:NARR)
    WRITE(*,*)
! ACTUAL ARGUMENT IS AN ARRAY SECTION WITH A VECTOR SUBSCRIPT
! DISCONTINUE,COPY,UNKNOWN POSITION,CANNOT BE INTENT(OUT)
    CALL FUNARR(ARR(VECSUBSCP(3:5)),3)
    WRITE(*,*)"FUNARR VECTOR SUBSCRIPT"
    WRITE(*,'(10F12.6)')ARR(:NARR)
    WRITE(*,*)
END PROGRAM WWW_FCODE_CN

12.2.7 <a name="BANDMAT"> "BANDMAT" </a>(<a href="#Back BANDMAT"> "Back" </a> )

!FULL
!    DO I=1,N
!        DO J=1,N
!            KMAX=MIN(I,J)-1
!            MATRIX(I,J)=MATRIX(I,J)-SUM(MATRIX(I,1:KMAX)*MATRIX(1:KMAX,J))
!            IF(I>J) MATRIX(I,J)=MATRIX(I,J)/MATRIX(J,J)
!        ENDDO
!    ENDDO
!BAND
!    DO I=1,N
!        JMIN=MAX(I-(P-1),1)
!        JMAX=MIN(I+(Q-1),N)
!        DO J=JMIN,JMAX
!            KMIN=MAX(JMIN,J-(Q-1))
!            KMAX=MIN(I,J)-1
!            MATRIX(I,J)=MATRIX(I,J)-SUM(MATRIX(I,KMIN:KMAX)*MATRIX(KMIN:KMAX,J))
!            IF(I>J) MATRIX(I,J)=MATRIX(I,J)/MATRIX(J,J)
!        ENDDO
!    ENDDO
!BAND,COMPRESSED
    DO I=1,N
        JMIN=MAX(I-(P-1),1)
        JMAX=MIN(I+(Q-1),N)
        DO J=JMIN,JMAX
            KMIN=MAX(JMIN,J-(Q-1))
            KMAX=MIN(I,J)-1
            DO K=KMIN,KMAX
                A=A+MATRIX(I,K-I+P)*MATRIX(K,J-K+P)
            ENDDO
            MATRIX(I,J-I+P)=MATRIX(I,J-I+P)-A
            IF(I>J) MATRIX(I,J-I+P)=MATRIX(I,J-I+P)/MATRIX(J,P)
        ENDDO
    ENDDO

12.2.8 <a name="TYPE_ACCESS"> "TYPE_ACCESS" </a>(<a href="#Back TYPE_ACCESS"> "Back" </a> )

PROGRAM WWW_FCODE_CN
    IMPLICIT NONE
    TYPE EMPLOYEE_NAME
        CHARACTER(LEN=25):: LAST_NAME
        CHARACTER(LEN=15):: FIRST_NAME
    END TYPE
    TYPE EMPLOYEE_ADDR
        CHARACTER(LEN=20):: STREET_NAME
        INTEGER(KIND=2)  :: STREET_NUMBER
        INTEGER(KIND=2)  :: APT_NUMBER
        CHARACTER(LEN=20):: CITY
        CHARACTER(LEN=2) :: STATE
        INTEGER(KIND=8)  :: ZIP
    END TYPE
    TYPE EMPLOYEE_DATA
        TYPE (EMPLOYEE_NAME):: NAME
        TYPE (EMPLOYEE_ADDR):: ADDR
        INTEGER(KIND=4)     :: TELEPHONE
        INTEGER(KIND=2)     :: DATE_OF_BIRTH
        INTEGER(KIND=2)     :: DATE_OF_HIRE
        INTEGER(KIND=2)     :: SOCIAL_SECURITY(3)
        LOGICAL(KIND=2)     :: MARRIED
        INTEGER(KIND=2)     :: DEPENDENTS
    END TYPE
    TYPE (EMPLOYEE_DATA), DIMENSION (20) :: EMPLOYEES
    DATA EMPLOYEES(1)%NAME /EMPLOYEE_NAME("BIRD","FLY")/
    EMPLOYEES(2)=EMPLOYEE_DATA(  EMPLOYEE_NAME("BIRD","FLY"),                      &
                                 EMPLOYEE_ADDR("ZGC",12,34,"BEIJING","BJ",100000), &
                                 10086,04,15,(/0,0,0/),.TRUE.,0                    &
    PRINT *,EMPLOYEES(1)%NAME%FIRST_NAME,EMPLOYEES(1)%NAME%LAST_NAME
    PRINT *,EMPLOYEES(2)%NAME%FIRST_NAME,EMPLOYEES(2)%NAME%LAST_NAME
END PROGRAM WWW_FCODE_CN

12.3 <a name="Code.8"> "Code.8" </a>(<a href="#Back Code.8"> "Back" </a> )

12.3.1 <a name="Func"> "Func" </a>(<a href="#Back Func"> "Back" </a> )

Program www_fcode_cn
  Implicit None
  integer :: i , now_year = 2015
  Integer :: PrintCopyRight
  i = PrintCopyRight( "fcode.cn" , now_year )
  i = PrintCopyRight( "www.fcode.cn" , now_year )
End Program www_fcode_cn
Integer Function PrintCopyRight( cDomain , now_year )
  Implicit None
  Character(len=*) , Intent( IN ) :: cDomain
  Integer , Intent( IN ) :: now_year
  write( * , * ) 'Powered by http://' , cDomain , now_year
  PrintCopyRight = now_year  
  !return
End Function PrintCopyRight

12.3.2 <a name="WriteMatrix"> "WriteMatrix" </a>(<a href="#Back WriteMatrix"> "Back" </a> )

Program www_fcode_cn
  Implicit None
  integer :: x(3,3) = reshape( &
    [1,2,3, &
     4,5,6, &
     7,8,9],[3,3])
  call WriteMatrix( x(1:2,3:3) )
contains
  Subroutine WriteMatrix( iMat )
    Integer , Intent( IN ) :: iMat(:,:)
    integer :: i
    Do i = 1 , size( iMat , dim = 2 )
      write( * , * ) iMat( : , i )
    End Do
  End Subroutine WriteMatrix
End Program www_fcode_cn

12.3.3 <a name="passtype_degree"> "passtype_degree" </a>(<a href="#Back passtype_degree"> "Back" </a> )

Module typedef
  Implicit None
  Integer , parameter :: DP = Selected_Real_Kind( p = 9 )
  Type ST_Degree
    Integer(Kind=2) :: d , m
    real :: s  
  End Type ST_Degree
contains
  Type( ST_Degree ) Function RDegree_To_TypeDegree( rrDeg ) result( stt )
    Real(Kind=DP) ,Intent( IN ):: rrDeg
    real(Kind=DP) :: rr
    stt%d = int(rrDeg)
    rr = rrDeg - stt%d
    stt%m = ( rr ) * 60.0_DP
    rr = rr - stt%m/60.0_DP
    stt%s = ( rr ) * 60.0_DP**2
  End Function RDegree_To_TypeDegree
End Module typedef
Program www_fcode_cn
  use typedef
  Implicit None
  type( ST_Degree ) :: st
  real(kind=DP) :: r
    read( * , * ) r
    st = RDegree_To_TypeDegree( r )
    write(*,*) st
  end do
End Program www_fcode_cn

12.3.4 <a name="passtype"> "passtype" </a>(<a href="#Back passtype"> "Back" </a> )

Module typedef
  Implicit None
  type ST
    real :: a , b
  end type ST
End Module typedef
Program www_fcode_cn
  use typedef
  Implicit None  
  type ( ST ) :: st1
  call sub( st1 )
End Program www_fcode_cn
Subroutine sub( stda )
  use typedef
  Implicit None
  type ( ST ) :: stda
  stda%a = 1.0
End Subroutine sub

12.3.5 <a name="optional"> "optional" </a>(<a href="#Back optional"> "Back" </a> )

Program www_fcode_cn
  Implicit None
  call sub( 7 , 6 )
  call sub( 5 , 4 , 3 )
contains
  Subroutine sub( a , b , c )
    Integer :: a , b
    Integer , optional :: c
    write( * , * ) a , b
    if ( present( c ) ) write(*,*) 'optional:' , c
  End Subroutine sub
End Program www_fcode_cn

12.3.6 <a name="func_name_arg"> "func_name_arg" </a>(<a href="#Back func_name_arg"> "Back" </a> )

Program www_fcode_cn
  Implicit None
  write( * , * ) integral( x2 , 1.0 , 2.0 , 0.01 )
  write( * , * ) 2.0**3/3 - 1.0**3/3
  write( * , * ) integral( x3 , 1.0 , 2.0 , 0.01 )
  write( * , * ) 2.0**4/4 - 1.0**4/4
contains
  Real Function x2( x )
    real :: x
    x2 = x*x
  End Function x2  
  Real Function x3( x )
    real :: x
    x3 = x*x*x
  End Function x3
  Real Function integral( func , low_bound , up_bound , delta ) result ( y )
    !Real , External :: func
    Interface 
      Real Function func( x )
        real :: x
      End Function func
    End Interface
    Real , intent( IN ) :: low_bound , up_bound , delta
    integer :: i
    real :: x
    y = 0.0
    x = low_bound + delta/2.0
    Do !// Do x = low_bound , up_bound , delta
      y = y + func( x )
      x = x + delta
      if ( x > up_bound ) exit
    End Do
    y = y * delta
  End Function integral
End Program www_fcode_cn

12.3.7 <a name="recursive_do"> "recursive_do" </a>(<a href="#Back recursive_do"> "Back" </a> )

Program www_fcode_cn
  Implicit None
  Integer , parameter :: DP = Selected_Real_Kind( p = 12 )  
  real(Kind=DP) :: r = sqrt( 2.0_DP )
  integer :: i
  Do i = 1 , 60
    r = sqrt( 2.0_DP * r )
    write(*,*) r
  End Do
End Program www_fcode_cn

12.3.8 <a name="recursive"> "recursive" </a>(<a href="#Back recursive"> "Back" </a> )

module def
  Integer , parameter :: DP = Selected_Real_Kind( p = 12 )
end module def
Program www_fcode_cn
  use def
  Implicit None
  Real(Kind=DP) :: Func , r
  r = Func( 5 ) 
  write(*,*) r !// 此处不能写为 write(*,*) Func(5) 
End Program www_fcode_cn
Recursive Function Func( n ) result( r )
  use def
  Implicit None
  Real(Kind=DP) :: r
  Integer :: n
  !write(*,*) 'in:',n
  if ( n > 0 ) then
    r = sqrt( 2.0_DP * Func(n-1) )
    r = sqrt( 2.0_DP )
  end if
  !write(*,*) 'out:',n,r
End Function Func

12.4 <a name="Code.9"> "Code.9" </a>(<a href="#Back Code.9"> "Back" </a> )

12.4.1 <a name="interface"> "interface" </a>(<a href="#Back interface"> "Back" </a> )

Program www_fcode_cn
  Implicit None
  Interface
    Subroutine sub( x )
      Real :: x(:,:)
    End Subroutine sub
  End Interface
  Real :: a( 30 , 30 )
  call sub( a )
  call call_sub()
End Program www_fcode_cn
Subroutine sub( x )
  Implicit None
  Real :: x(:,:)
  write(*,*) size(x,dim=1) , size(x,dim=2)
End Subroutine sub
Subroutine call_sub()
  Implicit None
  Interface
    Subroutine sub( x )
      Real :: x(:,:)
    End Subroutine sub
  End Interface
  Real :: b( 15 , 15 )
  call sub( b )
End Subroutine call_sub

12.4.2 <a name="module"> "module" </a>(<a href="#Back module"> "Back" </a> )

Module fcode_Mod
  Implicit None
  Real , save :: a = 0 , b = 0 , c = 0
contains
  Subroutine fc_Init()
    a = 3 ; b = 4 ; c = 5
  End Subroutine fc_Init
End Module fcode_Mod
Program main
  use fcode_Mod
  Implicit None
  write( * , * ) a , b , c
  call fc_Init()
  write( * , * ) a , b , c
  a = 1 ; b = 2 ; c = 3
  write( * , * ) a , b , c
  call fc_Init()
  write( * , * ) a , b , c
End Program main

12.4.3 <a name="module_view"> "module_view" </a>(<a href="#Back module_view"> "Back" </a> )

Module mod_name
  Implicit None
  real , public     :: pub_var   =1.
  real , private    :: prvat_var =2.
  real , protected  :: prtct_var =3.
contains
  Subroutine sub1( dum_a , dum_b )
    Real , Intent( IN ) :: dum_a 
    Real , Intent( INOUT ) :: dum_b
    real :: loc_1 , loc_2
    write(*,*) dum_a , dum_b , pub_var , prvat_var , prtct_var
    loc_1 = 5.0
    write(*,*) f1( loc_1 , dum_b )
  End Subroutine sub1
  Integer Function f1( dum_1 , dum_2 )
    Real , Intent( IN ) :: dum_1 
    Real , Intent( INOUT ) :: dum_2
    real :: loc_1 , loc_2
    f1    = dum_1 + dum_2 + pub_var + prvat_var + prtct_var
    dum_2 = 40.0
  End Function f1
End Module mod_name
Program main
  use mod_name
  Implicit None
  !//call sub1( prvat_var , pub_var ) !// 无法编译,因为 prvat_var 是私有的
  !//call sub1( pub_var , prtct_var ) !// 无法编译,因为 prtct_var 是被保护的,无法当做Intent(OUT) 的变量
  call sub1( prtct_var , pub_var )
End Program main

12.4.4 <a name="module_protect"> "module_protect" </a>(<a href="#Back module_protect"> "Back" </a> )

Module Grid_Mod
  Implicit None
  Type ST_Info
    integer :: m , n
  End Type ST_Info
  Type( ST_Info ) , protected     :: grd_stInfo
  Real            , allocatable   :: grd_rData( : , : )
  Character(len=512)              :: grd_cFilename
contains
  Logical Function Grid_Alloc( x , y ) result( bAlloc )
    Integer , Intent( IN ) :: x , y
    integer :: i
    bAlloc = .false.
    if ( (x * y) > 1000 ) return
    allocate( grd_rData( x , y ) , STAT = i )
    if ( i/=0 ) return
    grd_stInfo%m = x
    grd_stInfo%n = y
    bAlloc = .true.  
  End Function Grid_Alloc
  Logical Function Grid_DeAlloc()
    Grid_DeAlloc = .false.
    if ( allocated( grd_rData ) ) then
      Deallocate( grd_rData )
      Grid_DeAlloc = .true.
    end if
  End Function Grid_DeAlloc
End Module Grid_Mod
Program main
  use Grid_Mod
  Implicit None
  logical :: b
  integer :: i
  b = Grid_Alloc( 5 , 5 )
  if ( .not.b ) stop "cann't allocate"
  write( * , '(a,g0,"x",g0)' ) 'Grid Size: ' , grd_stInfo  
  !//grd_stInfo%m = 50 !// 出错  
  call FillGrid()
  write( * , * ) 'Grid: '
  Do i = 1 , grd_stInfo%n
    write( * , '(999f6.2)' ) grd_rData( : , i )
  End Do
  b = Grid_Dealloc()
End Program main
Subroutine FillGrid()
  use Grid_Mod
  Implicit None
  integer :: i , j
  Do i = 1 , grd_stInfo%n
    Do j = 1 , grd_stInfo%m
      grd_rData(i,j) = i * j
    End Do
  End Do
End Subroutine FillGrid

12.4.5 <a name="module_oper"> "module_oper" </a>(<a href="#Back module_oper"> "Back" </a> )

12.5 <a name="Code.10"> "Code.10" </a>(<a href="#Back Code.10"> "Back" </a> )

12.5.1 <a name="Read_2D_text"> "Read_2D_text" </a>(<a href="#Back Read_2D_text"> "Back" </a> )

Program www_fcode_cn
  Implicit None
  Real    :: a( 3 , 4 )
  integer :: FILE_IN , i
  Open( NewUnit = FILE_IN , File = "2d_text.txt" )
  Do i = 1 , size(a,dim=2)
    Read( FILE_IN , * ) a( : , i )
    write( * , * ) a( : , i )
  End Do
  Close( FILE_IN )
End Program www_fcode_cn

12.5.2 <a name="Read_text"> "Read_text" </a>(<a href="#Back Read_text"> "Back" </a> )

Program www_fcode_cn
  Implicit None
  Real    :: a(3,7) , r
  integer :: FILE_IN , i
  Open( NewUnit = FILE_IN , File = "text.txt" )
  Read( FILE_IN , * ) !//跳过第一行
  Read( FILE_IN , * ) !//跳过第二行
  Do i = 1 , 7
    Read( FILE_IN , * ) r , a( : , i )
    write( * , * ) a(:,i)
  End Do
  Close( FILE_IN )
End Program www_fcode_cn

12.5.3 <a name="Read_text_alloc"> "Read_text_alloc" </a>(<a href="#Back Read_text_alloc"> "Back" </a> )

Module DFile_Mod
  Implicit None
contains 
  Integer Function GetDataN( cStr )
    Character( Len = * ) , Intent( IN ) :: cStr
    Integer :: i
    Logical :: bIsSeparator , bIsQuote
    GetDataN = 0
    bIsSeparator = .TRUE.
    bIsQuote = .FALSE.
    Do i = 1 , Len_Trim( cStr )
      Select Case( cStr(i:i) )
      Case( '"' , "'" ) !// 如果遇到引号
        If ( .Not.bIsQuote ) GetDataN = GetDataN + 1  !//如果不在引号中,则增加一个数据
        bIsQuote = .Not.bIsQuote !// 引号结束或开始
        bIsSeparator = .FALSE.
      Case( " " , "," , char(9) ) !// 如果遇到分隔符
        If ( .Not.bIsQuote ) then  !// 分隔符如果不在引号中
          bIsSeparator = .TRUE.
        End If
      Case Default  
        If ( bIsSeparator ) then
          GetDataN = GetDataN + 1
        End If
        bIsSeparator = .FALSE.
      End Select
    End Do
  End Function GetDataN
  Integer Function GetFileN( iFileUnit )
  !// 此函数应在打开文件后立即调用。调用后读取位置返回文件起始位置
    Implicit None
    Integer , Intent( IN ) :: iFileUnit
    character( Len = 1 ) :: cDummy
    integer :: ierr
    GetFileN = 0
    Rewind( iFileUnit )
      Read( iFileUnit , * , ioStat = ierr ) cDummy
      If( ierr /= 0 ) Exit
      GetFileN = GetFileN + 1
    End Do
    Rewind( iFileUnit )
  End Function GetFileN 
End Module DFile_Mod
Program www_fcode_cn
  Use DFile_Mod
  Implicit None
  Real , allocatable :: a(:,:)
  Character(len=512) :: cStr
  Real    :: r  
  integer :: FILE_IN , i , nRow , nCol
  Open( NewUnit = FILE_IN , File = "text.txt" )
  nRow = GetFileN( FILE_IN ) - 2 !//获得文件行数 
  Read( FILE_IN , * ) !//跳过第一行
  Read( FILE_IN , * ) !//跳过第二行
  Read( FILE_IN , '(a512)' ) cStr!//读取第三行
  nCol = GetDataN( cStr ) - 1 !//获得第三行有多少列
  write( *, '("Row:",g0," Col: ",g0)' ) nRow , nCol
  Allocate( a( nCol , nRow ) )
  Backspace( FILE_IN ) !//退回到第三行
  Do i = 1 , nRow
    Read( FILE_IN , * ) r , a( : , i )
    write( * , * ) a(:,i)
  End Do
  Deallocate( a )
  Close( FILE_IN )
End Program www_fcode_cn

12.5.4 <a name="Read_text_direct"> "Read_text_direct" </a>(<a href="#Back Read_text_direct"> "Back" </a> )

Program pcm
  Implicit None
  Integer :: FID , i
  Real :: a, b , c
  Open( NewUnit=FID , File="text_direct.txt" , form="formatted" , access="Direct" , RecL=64 )
  Read( FID , '(i7,3(1x,g15.6))' ,Rec=5 ) i , a , b , c
  Write( * , * ) i , a , b , c
  Close( FID )
End Program pcm

12.5.5 <a name="write_myfile"> "write_myfile" </a>(<a href="#Back write_myfile"> "Back" </a> )

Program www_fcode_cn
  Implicit None
  Integer :: FID , a = 1000 , c = 2338021
  real :: b = 3.141592654
  character(len=8) :: s = "fcode.cn"
  Open(NewUnit=FID,File="myfile.bin",form='unformatted')
  Write( FID ) a , s !//4+8=12 , 加上前后4 = 20
  Write( FID ) b , c !//4+4=8  , 加上前后4 = 16
  Close(FID)
End Program www_fcode_cn

12.5.6 <a name="read_myfile"> "read_myfile" </a>(<a href="#Back read_myfile"> "Back" </a> )

Program www_fcode_cn
  Implicit None
  Integer :: FID , a , c
  real :: b
  character(len=8) :: s
  Open(NewUnit=FID,File="myfile.bin",form='unformatted')
  Read( FID ) a , s !//4+8=12 , 加上前后4 = 20
  Read( FID ) b , c !//4+4=8  , 加上前后4 = 16
  write(*,*) a , b , c , s
  Close(FID)
End Program www_fcode_cn

12.5.7 <a name="write_mydirect"> "write_mydirect" </a>(<a href="#Back write_mydirect"> "Back" </a> )

Program www_fcode_cn
  Implicit None
  Integer :: FID , a = 1000 , c = 2338021
  real :: b = 3.141592654
  character(len=8) :: s = "fcode.cn"
  Open(NewUnit=FID,File="mydirect.bin",form='unformatted',access='direct',RecL=12)
  Write( FID ,REC = 2 ) a , s !//4+8=12 
  Write( FID ,REC = 1 ) b , c !//4+4=8,会填充4个字节的 00  
  Close(FID)
End Program www_fcode_cn

12.5.8 <a name="read_mydirect"> "read_mydirect" </a>(<a href="#Back read_mydirect"> "Back" </a> )

Program www_fcode_cn
  Implicit None
  Integer :: FID , a , c
  real :: b
  character(len=8) :: s
  Open(NewUnit=FID,File="mydirect.bin",form='unformatted',access='direct',RecL=12)
  Read( FID ,REC = 1 ) b , c !//4+4=8,会填充4个字节的 00  
  Read( FID ,REC = 2 ) a , s !//4+8=12  
  write(*,*) a , b , c , s
  Close(FID)
End Program www_fcode_cn

12.5.9 <a name="read_fcode_test_direct"> "read_fcode_test_direct" </a>(<a href="#Back read_fcode_test_direct"> "Back" </a> )

Program www_fcode_cn
  Implicit None
  integer :: FID , i , j , k
  integer(kind=2) :: m , n
  character(len=4) :: flag
  real(kind=8) :: minX , maxX , minY , maxY , minZ , maxZ
  real , allocatable :: g(:,:)
  Open(NewUnit=FID,File="fcode_test.grd",form='unformatted',access='direct',RecL=8)
  Read(FID,REC=1) flag , m , n
  write( * , '("m=",g0," n=",g0)' ) m , n
  allocate( g(m,n) )
  read(FID,REC=2) minX
  read(FID,REC=3) maxX
  read(FID,REC=4) minY
  read(FID,REC=5) maxY
  read(FID,REC=6) minZ
  read(FID,REC=7) maxZ
  write( * , '(6f7.3)' ) minX , maxX , minY , maxY , minZ , maxZ
  k = 8
  Do i = 1 , n
    Do j = 1 , m , 2
      Read(FID,REC=k) g( j:j+1 , i )
      k = k + 1
    End Do
    write(*,'(99f7.3)') g( : , i )
  End Do
  Deallocate( g )
  Close(FID)
End Program www_fcode_cn

12.5.10 <a name="read_fcode_test_stream"> "read_fcode_test_stream" </a>(<a href="#Back read_fcode_test_stream"> "Back" </a> )

Program www_fcode_cn
  Implicit None
  integer :: FID , i
  integer(kind=2) :: m , n
  character(len=4) :: flag
  real(kind=8) :: minX , maxX , minY , maxY , minZ , maxZ
  real , allocatable :: g(:,:)
  Open(NewUnit=FID,File="fcode_test.grd",form='unformatted',access='stream')
  Read(FID) flag , m , n , minX , maxX , minY , maxY , minZ , maxZ
  write( * , '("m=",g0," n=",g0)' ) m , n
  write( * , '(6f7.3)' ) minX , maxX , minY , maxY , minZ , maxZ
  allocate( g(m,n) )
  Read(FID) g
  Do i = 1 , n  
    write(*,'(99f7.3)') g( : , i )
  End Do
  Deallocate( g )
  Close(FID)
End Program www_fcode_cn

12.5.11 <a name="read_fcode_test_stream_type"> "read_fcode_test_stream_type" </a>(<a href="#Back read_fcode_test_stream_type"> "Back" </a> )

Program www_fcode_cn
  Implicit None
  integer :: FID , i
  type :: GRD_HEAD
    SEQUENCE !// 重要
    character(len=4) :: flag
    integer(kind=2) :: m , n
    real(kind=8) :: minX , maxX , minY , maxY , minZ , maxZ
  end type
  type ( GRD_HEAD ) :: stHead
  real , allocatable :: g(:,:)
  Open(NewUnit=FID,File="fcode_test.grd",form='unformatted',access='stream')
  Read(FID) stHead
  write( * , '("m=",g0," n=",g0)' ) stHead%m , stHead%n
  allocate( g(stHead%m,stHead%n) )
  Read(FID) g
  Do i = 1 , stHead%n  
    write(*,'(99f7.3)') g( : , i )
  End Do
  Deallocate( g )
  Close(FID)
End Program www_fcode_cn

12.5.12 <a name="cwd"> "cwd" </a>(<a href="#Back cwd"> "Back" </a> )

Program www_fcode_cn
  Implicit None
  character(len=512) :: c
  call GET_COMMAND_ARGUMENT( 0 , c )
  write(*,'(a,/,a)') 'exe path:',trim(c)
  call GETCWD( c )
  write(*,'(a,/,a)') 'working directory:',trim(c)
  open(12,File="where.txt")
  close(12)
  read(*,*)
End Program www_fcode_cn

12.6 <a name="Code.11"> "Code.11" </a>(<a href="#Back Code.11"> "Back" </a> )

12.6.1 <a name="string"> "string" </a>(<a href="#Back string"> "Back" </a> )

Program www_fcode_cn
  Implicit None
  character(len=64) :: str,temp,cfind
  real :: weight , distance
  integer :: i
  str = "Informatin: weight=1.5kg distance=100.0km"
  !//换成下面这行的格式也可以正常读取
  !str = "Informatin: distance=100km weight=  01.5KG"
  cfind= "weight="
  i = index(str,trim(cfind))
  temp = str(i+len_trim(cfind):)
  i = verify(temp,"1234567890. ")
  read( temp(:i-1) , * ) weight
  write(*,*) trim(cfind) , weight
  cfind= "distance="
  i = index(str,trim(cfind))
  temp = str(i+len_trim(cfind):)
  i = verify(temp,"1234567890. ")
  read( temp(:i-1) , * ) distance
  write(*,*) trim(cfind) , distance
End Program www_fcode_cn

12.6.2 <a name="any_all"> "any_all" </a>(<a href="#Back any_all"> "Back" </a> )

Program www_fcode_cn
  Implicit None
  integer :: a(5) = [1,2,3,-5,3]
  write(*,*) 'a>0',a>0
  write(*,*) 'all',all(a>0)
  write(*,*) 'a==2',a==2
  write(*,*) 'any',any(a==2)
End Program www_fcode_cn

12.6.3 <a name="args"> "args" </a>(<a href="#Back args"> "Back" </a> )

Program www_fcode_cn
  Implicit None
  integer :: i , leng