Fortran 快速指南



Fortran - 概述

Fortran,源自公式翻译系统 (Formula Translating System),是一种通用的命令式编程语言。它用于数值和科学计算。

Fortran 最初由 IBM 在 20 世纪 50 年代开发,用于科学和工程应用。长期以来,Fortran 一直统治着这个编程领域,并因其高性能计算能力而变得非常流行,因为:

它支持:

  • 数值分析和科学计算
  • 结构化编程
  • 数组编程
  • 模块化编程
  • 泛型编程
  • 超级计算机上的高性能计算
  • 面向对象编程
  • 并发编程
  • 计算机系统之间具有合理的可移植性

关于 Fortran 的事实

  • Fortran 由 John Backus 领导的团队于 1957 年在 IBM 创建。

  • 最初名称全部大写,但当前标准和实现只需要首字母大写。

  • Fortran 代表 FORmula TRANslator(公式翻译器)。

  • 最初开发用于科学计算,对字符字符串和其他通用编程所需的结构的支持非常有限。

  • 后来的扩展和发展使其成为具有良好可移植性的高级编程语言。

  • Fortran I、II 和 III 原版现已过时。

  • 仍在使用的最古老版本是 Fortran IV 和 Fortran 66。

  • 当今最常用的版本是:Fortran 77、Fortran 90 和 Fortran 95。

  • Fortran 77 添加了字符串作为一种不同的类型。

  • Fortran 90 添加了各种线程和直接数组处理。

Fortran - 环境设置

在 Windows 中设置 Fortran

G95 是 GNU Fortran 多架构编译器,用于在 Windows 中设置 Fortran。Windows 版本使用 Windows 下的 MingW 模拟 Unix 环境。安装程序会处理此问题并自动将 g95 添加到 Windows PATH 变量中。

installer setup

mini installer setup

如何使用 G95

安装过程中,如果您选择“推荐”选项,则会自动将 **g95** 添加到您的 PATH 变量中。这意味着您可以简单地打开一个新的命令提示符窗口并键入“g95”以启动编译器。以下是一些基本命令,可帮助您入门。

序号 命令和说明
1

g95 –c hello.f90

将 hello.f90 编译成名为 hello.o 的目标文件

2

g95 hello.f90

编译 hello.f90 并链接它以生成可执行文件 a.out

3

g95 -c h1.f90 h2.f90 h3.f90

编译多个源文件。如果一切顺利,将创建目标文件 h1.o、h2.o 和 h3.o

4

g95 -o hello h1.f90 h2.f90 h3.f90

编译多个源文件并将它们链接到名为“hello”的可执行文件中

G95 的命令行选项

-c Compile only, do not run the linker.
-o Specify the name of the output file, either an object file or the executable.

可以一次指定多个源文件和目标文件。Fortran 文件由以“.f”、“.F”、“.for”、“.FOR”、“.f90”、“.F90”、“.f95”、“.F95”、“.f03”和“.F03”结尾的名称指示。可以指定多个源文件。也可以指定目标文件,并将它们链接以形成可执行文件。

Fortran - 基本语法

Fortran 程序由程序单元的集合组成,例如主程序、模块和外部子程序或过程。

每个程序包含一个主程序,并且可以包含或不包含其他程序单元。主程序的语法如下:

program program_name
implicit none      

! type declaration statements      
! executable statements  

end program program_name

Fortran 中的一个简单程序

让我们编写一个程序来添加两个数字并打印结果:

program addNumbers

! This simple program adds two numbers
   implicit none

! Type declarations
   real :: a, b, result

! Executable statements
   a = 12.0
   b = 15.0
   result = a + b
   print *, 'The total is ', result

end program addNumbers

编译并执行上述程序后,将产生以下结果:

The total is 27.0000000    

请注意:

  • 所有 Fortran 程序都以关键字 **program** 开头,并以关键字 **end program,** 后跟程序名称结尾。

  • **implicit none** 语句允许编译器检查所有变量类型是否已正确声明。您必须始终在每个程序的开头使用 **implicit none**。

  • Fortran 中的注释以感叹号 (!) 开头,因为感叹号之后的所有字符(字符字符串除外)都被编译器忽略。

  • **print *** 命令在屏幕上显示数据。

  • 代码行的缩进是保持程序可读性的良好习惯。

  • Fortran 允许使用大写和小写字母。Fortran 不区分大小写,但字符串文字除外。

基础知识

Fortran 的 **基本字符集** 包含:

  • 字母 A ... Z 和 a ... z
  • 数字 0 ... 9
  • 下划线 (_) 字符
  • 特殊字符 = : + 空格 - * / ( ) [ ] , . $ ' ! " % & ; < > ?

**标记** 由基本字符集中的字符组成。标记可以是关键字、标识符、常量、字符串文字或符号。

程序语句由标记组成。

标识符

标识符是用于标识变量、过程或任何其他用户定义项的名称。Fortran 中的名称必须遵循以下规则:

  • 长度不能超过 31 个字符。

  • 必须由字母数字字符(所有字母和数字 0 到 9)和下划线 (_) 组成。

  • 名称的第一个字符必须是字母。

  • 名称不区分大小写

关键字

关键字是为语言保留的特殊单词。这些保留字不能用作标识符或名称。

下表列出了 Fortran 关键字:

非 I/O 关键字
allocatable allocate assign assignment block data
call case character common complex
contains continue cycle data deallocate
default do double precision else else if
elsewhere end block data end do end function end if
end interface end module end program end select end subroutine
end type end where entry equivalence exit
external function go to if implicit
in inout integer intent interface
intrinsic kind len logical module
namelist nullify only operator optional
out parameter pause pointer private
program public real recursive result
return save select case stop subroutine
target then type type() use
Where While
I/O 相关关键字
backspace close endfile format inquire
open print read rewind Write

Fortran - 数据类型

Fortran 提供五种内在数据类型,但是,您也可以派生自己的数据类型。五种内在类型是:

  • 整数类型
  • 实数类型
  • 复数类型
  • 逻辑类型
  • 字符类型

整数类型

整数类型只能保存整数值。以下示例提取通常的四字节整数中可以保存的最大值:

program testingInt
implicit none

   integer :: largeval
   print *, huge(largeval)
   
end program testingInt

编译并执行上述程序后,将产生以下结果:

2147483647

请注意,**huge()** 函数给出特定整数数据类型可以保存的最大数字。您还可以使用 **kind** 说明符指定字节数。以下示例演示了这一点:

program testingInt
implicit none

   !two byte integer
   integer(kind = 2) :: shortval
   
   !four byte integer
   integer(kind = 4) :: longval
   
   !eight byte integer
   integer(kind = 8) :: verylongval
   
   !sixteen byte integer
   integer(kind = 16) :: veryverylongval
   
   !default integer 
   integer :: defval
        
   print *, huge(shortval)
   print *, huge(longval)
   print *, huge(verylongval)
   print *, huge(veryverylongval)
   print *, huge(defval)
   
end program testingInt

编译并执行上述程序后,将产生以下结果:

32767
2147483647
9223372036854775807
170141183460469231731687303715884105727
2147483647

实数类型

它存储浮点数,例如 2.0、3.1415、-100.876 等。

传统上,有两种不同的实数类型,默认的 **real** 类型和 **double precision** 类型。

但是,Fortran 90/95 通过 **kind** 说明符提供了对实数和整数数据类型精度的更多控制,我们将在“数字”一章中学习。

以下示例显示了实数数据类型的用法:

program division   
implicit none  

   ! Define real variables   
   real :: p, q, realRes 
   
   ! Define integer variables  
   integer :: i, j, intRes  
   
   ! Assigning  values   
   p = 2.0 
   q = 3.0    
   i = 2 
   j = 3  
   
   ! floating point division
   realRes = p/q  
   intRes = i/j
   
   print *, realRes
   print *, intRes
   
end program division  

编译并执行上述程序后,将产生以下结果:

0.666666687    
0

复数类型

这用于存储复数。复数有两个部分,实部和虚部。两个连续的数值存储单元存储这两个部分。

例如,复数 (3.0, -5.0) 等于 3.0 – 5.0i

我们将在“数字”一章中更详细地讨论复数类型。

逻辑类型

只有两个逻辑值:**.true.** 和 **.false.**

字符类型

字符类型存储字符和字符串。字符串的长度可以通过 len 说明符指定。如果未指定长度,则为 1。

例如:

character (len = 40) :: name  
name = “Zara Ali”

表达式 **name(1:4)** 将给出子字符串“Zara”。

隐式类型

旧版本的 Fortran 允许一种称为隐式类型的功能,即您不必在使用前声明变量。如果未声明变量,则其名称的首字母将确定其类型。

以 i、j、k、l、m 或 n 开头的变量名被认为是整数变量,其他变量是实数变量。但是,您必须声明所有变量,因为这是良好的编程实践。为此,您可以使用以下语句开始程序:

implicit none

此语句关闭隐式类型。

Fortran - 变量

变量只不过是赋予程序可以操作的存储区域的名称。每个变量都应该具有特定的类型,这决定了变量内存的大小和布局;可以存储在该内存中的值的范围;以及可以应用于变量的操作集。

变量的名称可以由字母、数字和下划线字符组成。Fortran 中的名称必须遵循以下规则:

  • 长度不能超过 31 个字符。

  • 必须由字母数字字符(所有字母和数字 0 到 9)和下划线 (_) 组成。

  • 名称的第一个字符必须是字母。

  • 名称不区分大小写。

基于上一章中解释的基本类型,以下是变量类型:

序号 类型和说明
1

整数

它只能保存整数值。

2

实数

它存储浮点数。

3

复数

它用于存储复数。

4

逻辑

它存储逻辑布尔值。

5

字符

它存储字符或字符串。

变量声明

变量在程序(或子程序)开头的类型声明语句中声明。

变量声明的语法如下:

type-specifier :: variable_name

例如

integer :: total  	
real :: average 
complex :: cx  
logical :: done 
character(len = 80) :: message ! a string of 80 characters

稍后您可以为这些变量赋值,例如:

total = 20000  
average = 1666.67   
done = .true.   
message = “A big Hello from Tutorials Point” 
cx = (3.0, 5.0) ! cx = 3.0 + 5.0i

您还可以使用内在函数 **cmplx** 为复数变量赋值:

cx = cmplx (1.0/2.0, -7.0) ! cx = 0.5 – 7.0i 
cx = cmplx (x, y) ! cx = x + yi

示例

下面的例子演示了变量的声明、赋值和屏幕显示。

program variableTesting
implicit none

   ! declaring variables
   integer :: total      
   real :: average 
   complex :: cx  
   logical :: done 
   character(len=80) :: message ! a string of 80 characters
   
   !assigning values
   total = 20000  
   average = 1666.67   
   done = .true.   
   message = "A big Hello from Tutorials Point" 
   cx = (3.0, 5.0) ! cx = 3.0 + 5.0i

   Print *, total
   Print *, average
   Print *, cx
   Print *, done
   Print *, message
   
end program variableTesting

当上述代码编译并执行时,它会产生以下结果:

20000
1666.67004    
(3.00000000, 5.00000000 )
T
A big Hello from Tutorials Point         

Fortran - 常量

常量是指程序在其执行过程中无法更改的固定值。这些固定值也称为字面量

常量可以是任何基本数据类型,例如整数常量、浮点常量、字符常量、复数常量或字符串字面量。只有两个逻辑常量:.true..false.

常量的处理方式与普通变量相同,只是它们的 值在定义后不能修改。

命名常量和字面量

常量有两种类型:

  • 字面常量
  • 命名常量

字面常量有值,但没有名称。

例如,以下是字面常量:

类型 示例
整数常量 0 1 -1 300 123456789
实数常量 0.0 1.0 -1.0 123.456 7.1E+10 -52.715E-30
复数常量 (0.0, 0.0) (-123.456E+30, 987.654E-29)
逻辑常量 .true. .false.
字符常量

"PQR" "a" "123'abc$%#@!"

" a quote "" "

'PQR' 'a' '123"abc$%#@!'

' an apostrophe '' '

命名常量既有值也有名称。

命名常量应该在程序或过程的开头声明,就像变量类型声明一样,指明其名称和类型。命名常量用参数属性声明。例如:

real, parameter :: pi = 3.1415927

示例

下面的程序计算重力作用下垂直运动的位移。

program gravitationalDisp

! this program calculates vertical motion under gravity 
implicit none  

   ! gravitational acceleration
   real, parameter :: g = 9.81   
   
   ! variable declaration
   real :: s ! displacement   
   real :: t ! time  
   real :: u ! initial speed  
   
   ! assigning values 
   t = 5.0   
   u = 50  
   
   ! displacement   
   s = u * t - g * (t**2) / 2  
   
   ! output 
   print *, "Time = ", t
   print *, 'Displacement = ',s  
   
end program gravitationalDisp

当上述代码编译并执行时,它会产生以下结果:

Time = 5.00000000    
Displacement = 127.374992    

Fortran - 运算符

运算符是一个符号,它告诉编译器执行特定的数学或逻辑运算。Fortran 提供以下类型的运算符:

  • 算术运算符
  • 关系运算符
  • 逻辑运算符

让我们逐一看看这些类型的运算符。

算术运算符

下表显示了 Fortran 支持的所有算术运算符。假设变量A为 5,变量B为 3,则:

示例

运算符 描述 示例
+ 加法运算符,将两个操作数相加。 A + B 将得到 8
- 减法运算符,从第一个操作数中减去第二个操作数。 A - B 将得到 2
* 乘法运算符,将两个操作数相乘。 A * B 将得到 15
/ 除法运算符,将分子除以分母。 A / B 将得到 1
** 幂运算符,将一个操作数提高到另一个操作数的幂。 A ** B 将得到 125

关系运算符

下表显示了 Fortran 支持的所有关系运算符。假设变量A为 10,变量B为 20,则:

示例

运算符 等效 描述 示例
== .eq. 检查两个操作数的值是否相等,如果相等,则条件变为真。 (A == B) 为假。
/= .ne. 检查两个操作数的值是否相等,如果不相等,则条件变为真。 (A != B) 为真。
> .gt. 检查左边操作数的值是否大于右边操作数的值,如果是,则条件变为真。 (A > B) 为假。
< .lt. 检查左边操作数的值是否小于右边操作数的值,如果是,则条件变为真。 (A < B) 为真。
>= .ge. 检查左边操作数的值是否大于或等于右边操作数的值,如果是,则条件变为真。 (A >= B) 为假。
<= .le. 检查左边操作数的值是否小于或等于右边操作数的值,如果是,则条件变为真。 (A <= B) 为真。

逻辑运算符

Fortran 中的逻辑运算符仅适用于逻辑值 .true. 和 .false.

下表显示了 Fortran 支持的所有逻辑运算符。假设变量 A 为 .true.,变量 B 为 .false.,则:

示例

运算符 描述 示例
.and. 称为逻辑与运算符。如果两个操作数都不为零,则条件变为真。 (A .and. B) 为假。
.or. 称为逻辑或运算符。如果两个操作数中的任何一个不为零,则条件变为真。 (A .or. B) 为真。
.not. 称为逻辑非运算符。用于反转其操作数的逻辑状态。如果条件为真,则逻辑非运算符将使其为假。 !(A .and. B) 为真。
.eqv. 称为逻辑等价运算符。用于检查两个逻辑值的等价性。 (A .eqv. B) 为假。
.neqv. 称为逻辑非等价运算符。用于检查两个逻辑值的非等价性。 (A .neqv. B) 为真。

Fortran 中的运算符优先级

运算符优先级决定了表达式中项的组合方式。这会影响表达式的计算方式。某些运算符的优先级高于其他运算符;例如,乘法运算符的优先级高于加法运算符。

例如,x = 7 + 3 * 2;这里,x 被赋值为 13,而不是 20,因为运算符 * 的优先级高于 +,所以它首先与 3*2 相乘,然后加到 7 中。

这里,优先级最高的运算符出现在表的最上面,优先级最低的出现在表的最下面。在一个表达式中,优先级较高的运算符将首先被计算。

示例

类别 运算符 结合性
逻辑非和负号 .not. (-) 从左到右
幂运算 ** 从左到右
乘法 * / 从左到右
加法 + - 从左到右
关系 < <= > >= 从左到右
相等 == /= 从左到右
逻辑与 .and. 从左到右
逻辑或 .or. 从左到右
赋值 = 从右到左

Fortran - 决策

决策结构要求程序员指定一个或多个条件供程序评估或测试,以及如果确定条件为真则要执行的语句(或语句组),以及可选地,如果条件确定为假则要执行的其他语句。

以下是大多数编程语言中常见的决策结构的一般形式:

Decision Making

Fortran 提供以下类型的决策结构。

序号 语句和描述
1 If… then 结构

一个if… then… end if 语句由一个逻辑表达式和一个或多个语句组成。

2 If… then...else 结构

一个if… then 语句可以后跟一个可选的else 语句,当逻辑表达式为假时执行。

3 if...else if...else 语句

一个if 语句结构可以有一个或多个可选的else-if 结构。当if 条件失败时,将执行紧跟其后的else-if。当else-if 也失败时,将执行其后继的else-if 语句(如果有),依此类推。

4 嵌套 if 结构

你可以在另一个ifelse if 语句中使用一个ifelse if 语句。

5 select case 结构

一个select case 语句允许测试变量与值的列表是否相等。

6 嵌套 select case 结构

你可以在另一个select case 语句中使用一个select case 语句。

Fortran - 循环

可能存在这样一种情况,你需要多次执行一段代码。一般来说,语句是顺序执行的:函数中的第一个语句首先执行,然后是第二个语句,依此类推。

编程语言提供了各种控制结构,允许更复杂的执行路径。

循环语句允许我们多次执行语句或语句组,以下是大多数编程语言中循环语句的一般形式:

If Conditional

Fortran 提供以下类型的循环结构来处理循环需求。点击以下链接查看其详细信息。

序号 循环类型和描述
1 do 循环

此结构允许迭代地执行语句或一系列语句,同时给定的条件为真。

2 do while 循环

在给定条件为真的情况下重复执行语句或语句组。它在执行循环体之前测试条件。

3 嵌套循环

你可以在任何其他循环结构中使用一个或多个循环结构。

循环控制语句

循环控制语句会改变执行的正常顺序。当执行离开作用域时,在该作用域中创建的所有自动对象都会被销毁。

Fortran 支持以下控制语句。点击以下链接查看其详细信息。

序号 控制语句和描述
1 exit

如果执行 exit 语句,则循环将退出,程序的执行将从 end do 语句后的第一个可执行语句继续。

2 cycle

如果执行 cycle 语句,程序将从下一次迭代的开始处继续。

3 stop

如果你希望程序的执行停止,可以插入 stop 语句。

Fortran - 数字

Fortran 中的数字由三种内在数据类型表示:

  • 整数类型
  • 实数类型
  • 复数类型

整数类型

整数类型只能保存整数值。下面的例子提取了通常的四字节整数中可以保存的最大值:

program testingInt
implicit none

   integer :: largeval
   print *, huge(largeval)
   
end program testingInt

编译并执行上述程序后,将产生以下结果:

2147483647

请注意,huge() 函数给出特定整数数据类型可以容纳的最大数字。你也可以使用kind 说明符指定字节数。下面的例子演示了这一点:

program testingInt
implicit none

   !two byte integer
   integer(kind = 2) :: shortval
   
   !four byte integer
   integer(kind = 4) :: longval
   
   !eight byte integer
   integer(kind = 8) :: verylongval
   
   !sixteen byte integer
   integer(kind = 16) :: veryverylongval
   
   !default integer 
   integer :: defval
        
   print *, huge(shortval)
   print *, huge(longval)
   print *, huge(verylongval)
   print *, huge(veryverylongval)
   print *, huge(defval)
   
end program testingInt

编译并执行上述程序后,将产生以下结果:

32767
2147483647
9223372036854775807
170141183460469231731687303715884105727
2147483647

实数类型

它存储浮点数,例如 2.0、3.1415、-100.876 等。

传统上,有两种不同的实数类型:默认实数类型和双精度类型。

但是,Fortran 90/95 通过kind 说明符提供了对实数和整数数据类型精度的更多控制,我们稍后将学习。

以下示例显示了实数数据类型的用法:

program division   
implicit none

   ! Define real variables   
   real :: p, q, realRes 
   
   ! Define integer variables  
   integer :: i, j, intRes  
   
   ! Assigning  values   
   p = 2.0 
   q = 3.0    
   i = 2 
   j = 3  
   
   ! floating point division
   realRes = p/q  
   intRes = i/j
   
   print *, realRes
   print *, intRes
   
end program division  

编译并执行上述程序后,将产生以下结果:

0.666666687    
0

复数类型

这用于存储复数。复数有两个部分:实部和虚部。两个连续的数值存储单元存储这两个部分。

例如,复数 (3.0, -5.0) 等于 3.0 – 5.0i

通用函数cmplx() 创建一个复数。它产生的结果的实部和虚部都是单精度,与输入参数的类型无关。

program createComplex
implicit none

   integer :: i = 10
   real :: x = 5.17
   print *, cmplx(i, x)
   
end program createComplex

编译并执行上述程序后,将产生以下结果:

(10.0000000, 5.17000008)

下面的程序演示了复数运算:

program ComplexArithmatic
implicit none

   complex, parameter :: i = (0, 1)   ! sqrt(-1)   
   complex :: x, y, z 
   
   x = (7, 8); 
   y = (5, -7)   
   write(*,*) i * x * y
   
   z = x + y
   print *, "z = x + y = ", z
   
   z = x - y
   print *, "z = x - y = ", z 
   
   z = x * y
   print *, "z = x * y = ", z 
   
   z = x / y
   print *, "z = x / y = ", z 
   
end program ComplexArithmatic

编译并执行上述程序后,将产生以下结果:

(9.00000000, 91.0000000)
z = x + y = (12.0000000, 1.00000000)
z = x - y = (2.00000000, 15.0000000)
z = x * y = (91.0000000, -9.00000000)
z = x / y = (-0.283783793, 1.20270276)

数字的范围、精度和大小

整数的范围、浮点数的精度和大小取决于分配给特定数据类型的位数。

下表显示了整数的位数和范围:

位数 最大值 原因
64 9,223,372,036,854,774,807 (2**63)–1
32 2,147,483,647 (2**31)–1

下表显示了实数的位数、最小值、最大值和精度。

位数 最大值 最小值 精度
64 0.8E+308 0.5E–308 15–18
32 1.7E+38 0.3E–38 6-9

下面的例子演示了这一点:

program rangePrecision
implicit none

   real:: x, y, z
   x = 1.5e+40
   y = 3.73e+40
   z = x * y 
   print *, z
   
end program rangePrecision

编译并执行上述程序后,将产生以下结果:

x = 1.5e+40
          1
Error : Real constant overflows its kind at (1)
main.f95:5.12:

y = 3.73e+40
           1
Error : Real constant overflows its kind at (1)

现在让我们使用一个较小的数字:

program rangePrecision
implicit none

   real:: x, y, z
   x = 1.5e+20
   y = 3.73e+20
   z = x * y 
   print *, z
   
   z = x/y
   print *, z
   
end program rangePrecision

编译并执行上述程序后,将产生以下结果:

Infinity
0.402144760   

现在让我们看看下溢:

program rangePrecision
implicit none

   real:: x, y, z
   x = 1.5e-30
   y = 3.73e-60
   z = x * y 
   print *, z
   
   z = x/y
   print *, z

end program rangePrecision

编译并执行上述程序后,将产生以下结果:

y = 3.73e-60
           1
Warning : Real constant underflows its kind at (1)

Executing the program....
$demo 

0.00000000E+00
Infinity

Kind 说明符

在科学编程中,人们通常需要知道正在进行工作的硬件平台的数据的范围和精度。

内在函数kind() 允许你在运行程序之前查询硬件数据表示的详细信息。

program kindCheck
implicit none
   
   integer :: i 
   real :: r 
   complex :: cp 
   print *,' Integer ', kind(i) 
   print *,' Real ', kind(r) 
   print *,' Complex ', kind(cp) 
   
end program kindCheck

编译并执行上述程序后,将产生以下结果:

Integer 4
Real 4
Complex 4

你还可以检查所有数据类型的 kind:

program checkKind
implicit none

   integer :: i 
   real :: r 
   character :: c 
   logical :: lg 
   complex :: cp 
   
   print *,' Integer ', kind(i) 
   print *,' Real ', kind(r) 
   print *,' Complex ', kind(cp)
   print *,' Character ', kind(c) 
   print *,' Logical ', kind(lg)
   
end program checkKind

编译并执行上述程序后,将产生以下结果:

Integer 4
Real 4
Complex 4
Character 1
Logical 4

Fortran - 字符

Fortran 语言可以将字符视为单个字符或连续字符串。

字符可以是来自基本字符集的任何符号,即来自字母、十进制数字、下划线和 21 个特殊字符。

字符常量是一个固定值的字符字符串。

内在数据类型character存储字符和字符串。字符串的长度可以通过len说明符指定。如果未指定长度,则为1。您可以通过位置引用字符串中的单个字符;最左边的字符位于位置1。

字符声明

声明字符类型数据与其他变量相同:

type-specifier :: variable_name

例如:

character :: reply, sex

您可以赋值,例如:

reply = ‘N’ 
sex = ‘F’

以下示例演示了字符数据类型的声明和使用:

program hello
implicit none

   character(len = 15) :: surname, firstname 
   character(len = 6) :: title 
   character(len = 25)::greetings
   
   title = 'Mr. ' 
   firstname = 'Rowan ' 
   surname = 'Atkinson'
   greetings = 'A big hello from Mr. Bean'
   
   print *, 'Here is ', title, firstname, surname
   print *, greetings
   
end program hello

编译并执行上述程序后,将产生以下结果:

Here is Mr. Rowan Atkinson       
A big hello from Mr. Bean

字符连接

连接运算符 // 连接字符。

以下示例演示了这一点:

program hello
implicit none

   character(len = 15) :: surname, firstname 
   character(len = 6) :: title 
   character(len = 40):: name
   character(len = 25)::greetings
   
   title = 'Mr. ' 
   firstname = 'Rowan ' 
   surname = 'Atkinson'
   
   name = title//firstname//surname
   greetings = 'A big hello from Mr. Bean'
   
   print *, 'Here is ', name
   print *, greetings
   
end program hello

编译并执行上述程序后,将产生以下结果:

Here is Mr.Rowan Atkinson       
A big hello from Mr.Bean

一些字符函数

下表显示了一些常用的字符函数及其描述:

序号 函数及描述
1

len(string)

它返回字符字符串的长度。

2

index(string,substring)

它查找子字符串在另一个字符串中的位置,如果未找到则返回 0。

3

achar(int)

它将整数转换为字符。

4

iachar(c)

它将字符转换为整数。

5

trim(string)

它返回删除了尾随空格的字符串。

6

scan(string, chars)

它从左到右搜索“string”(除非 back=.true.)以查找“chars”中包含的任何字符的第一次出现。它返回一个整数,给出该字符的位置,如果未找到“chars”中的任何字符,则返回零。

7

verify(string, chars)

它从左到右扫描“string”(除非 back=.true.)以查找“chars”中不包含的任何字符的第一次出现。它返回一个整数,给出该字符的位置,如果只找到“chars”中的字符,则返回零。

8

adjustl(string)

它左对齐“string”中包含的字符。

9

adjustr(string)

它右对齐“string”中包含的字符。

10

len_trim(string)

它返回一个整数,等于“string”的长度 (len(string)) 减去尾随空格的数量。

11

repeat(string,ncopy)

它返回一个字符串,其长度等于“string”长度的“ncopy”倍,并且包含“ncopy”个连接的“string”副本。

示例 1

此示例显示了index函数的使用:

program testingChars
implicit none

   character (80) :: text 
   integer :: i 
   
   text = 'The intrinsic data type character stores characters and   strings.'
   i=index(text,'character') 
   
   if (i /= 0) then
      print *, ' The word character found at position ',i 
      print *, ' in text: ', text 
   end if
   
end program testingChars

编译并执行上述程序后,将产生以下结果:

The word character found at position 25
in text : The intrinsic data type character stores characters and strings.  

示例 2

此示例演示了trim函数的使用:

program hello
implicit none

   character(len = 15) :: surname, firstname 
   character(len = 6) :: title 
   character(len = 25)::greetings
   
   title = 'Mr.' 
   firstname = 'Rowan' 
   surname = 'Atkinson'
   
   print *, 'Here is', title, firstname, surname
   print *, 'Here is', trim(title),' ',trim(firstname),' ', trim(surname)
   
end program hello

编译并执行上述程序后,将产生以下结果:

 Here isMr.   Rowan          Atkinson       
 Here isMr. Rowan Atkinson

示例 3

此示例演示了achar函数的使用:

program testingChars
implicit none

   character:: ch
   integer:: i
   
   do i = 65, 90
      ch = achar(i)
      print*, i, ' ', ch
   end do
   
end program testingChars

编译并执行上述程序后,将产生以下结果:

65  A
66  B
67  C
68  D
69  E
70  F
71  G
72  H
73  I
74  J
75  K
76  L
77  M
78  N
79  O
80  P
81  Q
82  R
83  S
84  T
85  U
86  V
87  W
88  X
89  Y
90  Z

检查字符的词法顺序

以下函数确定字符的词法顺序:

序号 函数及描述
1

lle(char, char)

比较第一个字符是否在词法上小于或等于第二个字符。

2

lge(char, char)

比较第一个字符是否在词法上大于或等于第二个字符。

3

lgt(char, char)

比较第一个字符是否在词法上大于第二个字符。

4

llt(char, char)

比较第一个字符是否在词法上小于第二个字符。

示例 4

以下函数演示了其用法:

program testingChars
implicit none

   character:: a, b, c
   a = 'A'
   b = 'a'
   c = 'B'
   
   if(lgt(a,b)) then
      print *, 'A is lexically greater than a'
   else
      print *, 'a is lexically greater than A'
   end if
   
   if(lgt(a,c)) then
      print *, 'A is lexically greater than B'
   else
      print *, 'B is lexically greater than A'
   end if  
   
   if(llt(a,b)) then
      print *, 'A is lexically less than a'
   end if
   
   if(llt(a,c)) then
      print *, 'A is lexically less than B'
   end if
   
end program testingChars

编译并执行上述程序后,将产生以下结果:

a is lexically greater than A
B is lexically greater than A
A is lexically less than a
A is lexically less than B

Fortran - 字符串

Fortran 语言可以将字符视为单个字符或连续字符串。

字符字符串的长度可能只有一个字符,甚至可能是零长度。在 Fortran 中,字符常量用一对双引号或单引号括起来。

内在数据类型character存储字符和字符串。字符串的长度可以通过len 说明符指定。如果未指定长度,则为1。您可以通过位置引用字符串中的单个字符;最左边的字符位于位置1。

字符串声明

声明字符串与其他变量相同:

type-specifier :: variable_name

例如:

Character(len = 20) :: firstname, surname

您可以赋值,例如:

character (len = 40) :: name  
name = “Zara Ali”

以下示例演示了字符数据类型的声明和使用:

program hello
implicit none

   character(len = 15) :: surname, firstname 
   character(len = 6) :: title 
   character(len = 25)::greetings
   
   title = 'Mr.' 
   firstname = 'Rowan' 
   surname = 'Atkinson'
   greetings = 'A big hello from Mr. Beans'
   
   print *, 'Here is', title, firstname, surname
   print *, greetings
   
end program hello

编译并执行上述程序后,将产生以下结果:

Here isMr.   Rowan          Atkinson       
A big hello from Mr. Bean

字符串连接

连接运算符 // 连接字符串。

以下示例演示了这一点:

program hello
implicit none

   character(len = 15) :: surname, firstname 
   character(len = 6) :: title 
   character(len = 40):: name
   character(len = 25)::greetings
   
   title = 'Mr.' 
   firstname = 'Rowan' 
   surname = 'Atkinson'
   
   name = title//firstname//surname
   greetings = 'A big hello from Mr. Beans'
   
   print *, 'Here is', name
   print *, greetings
   
end program hello

编译并执行上述程序后,将产生以下结果:

Here is Mr. Rowan Atkinson       
A big hello from Mr. Bean

提取子字符串

在 Fortran 中,您可以通过对字符串进行索引来从字符串中提取子字符串,在一对括号中给出子字符串的起始和结束索引。这称为范围说明符。

以下示例演示如何从字符串“hello world”中提取子字符串“world”:

program subString

   character(len = 11)::hello
   hello = "Hello World"
   print*, hello(7:11)
   
end program subString 

编译并执行上述程序后,将产生以下结果:

World

示例

以下示例使用date_and_time函数来给出日期和时间字符串。我们使用范围说明符分别提取年份、日期、月份、小时、分钟和秒信息。

program  datetime
implicit none

   character(len = 8) :: dateinfo ! ccyymmdd
   character(len = 4) :: year, month*2, day*2

   character(len = 10) :: timeinfo ! hhmmss.sss
   character(len = 2)  :: hour, minute, second*6

   call  date_and_time(dateinfo, timeinfo)

   !  let’s break dateinfo into year, month and day.
   !  dateinfo has a form of ccyymmdd, where cc = century, yy = year
   !  mm = month and dd = day

   year  = dateinfo(1:4)
   month = dateinfo(5:6)
   day   = dateinfo(7:8)

   print*, 'Date String:', dateinfo
   print*, 'Year:', year
   print *,'Month:', month
   print *,'Day:', day

   !  let’s break timeinfo into hour, minute and second.
   !  timeinfo has a form of hhmmss.sss, where h = hour, m = minute
   !  and s = second

   hour   = timeinfo(1:2)
   minute = timeinfo(3:4)
   second = timeinfo(5:10)

   print*, 'Time String:', timeinfo
   print*, 'Hour:', hour
   print*, 'Minute:', minute
   print*, 'Second:', second   
   
end program  datetime

编译并执行上述程序时,它会提供详细的日期和时间信息:

Date String: 20140803
Year: 2014
Month: 08
Day: 03
Time String: 075835.466
Hour: 07
Minute: 58
Second: 35.466

修剪字符串

trim函数接受一个字符串,并在删除所有尾随空格后返回输入字符串。

示例

program trimString
implicit none

   character (len = *), parameter :: fname="Susanne", sname="Rizwan"
   character (len = 20) :: fullname 
   
   fullname = fname//" "//sname !concatenating the strings
   
   print*,fullname,", the beautiful dancer from the east!"
   print*,trim(fullname),", the beautiful dancer from the east!"
   
end program trimString

编译并执行上述程序后,将产生以下结果:

Susanne Rizwan      , the beautiful dancer from the east!
 Susanne Rizwan, the beautiful dancer from the east!

字符串的左对齐和右对齐

函数adjustl接受一个字符串,并通过删除前导空格并将它们附加为尾随空格来返回它。

函数adjustr接受一个字符串,并通过删除尾随空格并将它们附加为前导空格来返回它。

示例

program hello
implicit none

   character(len = 15) :: surname, firstname 
   character(len = 6) :: title 
   character(len = 40):: name
   character(len = 25):: greetings
   
   title = 'Mr. ' 
   firstname = 'Rowan' 
   surname = 'Atkinson'
   greetings = 'A big hello from Mr. Beans'
   
   name = adjustl(title)//adjustl(firstname)//adjustl(surname)
   print *, 'Here is', name
   print *, greetings
   
   name = adjustr(title)//adjustr(firstname)//adjustr(surname)
   print *, 'Here is', name
   print *, greetings
   
   name = trim(title)//trim(firstname)//trim(surname)
   print *, 'Here is', name
   print *, greetings
   
end program hello

编译并执行上述程序后,将产生以下结果:

Here is Mr. Rowan  Atkinson           
A big hello from Mr. Bean
Here is Mr. Rowan Atkinson    
A big hello from Mr. Bean
Here is Mr.RowanAtkinson                        
A big hello from Mr. Bean

在字符串中搜索子字符串

index 函数接受两个字符串,并检查第二个字符串是否是第一个字符串的子字符串。如果第二个参数是第一个参数的子字符串,则它返回一个整数,该整数是第二个字符串在第一个字符串中的起始索引,否则返回零。

示例

program hello
implicit none

   character(len=30) :: myString
   character(len=10) :: testString
   
   myString = 'This is a test'
   testString = 'test'
   
   if(index(myString, testString) == 0)then
      print *, 'test is not found'
   else
      print *, 'test is found at index: ', index(myString, testString)
   end if
   
end program hello

编译并执行上述程序后,将产生以下结果:

test is found at index: 11

Fortran - 数组

数组可以存储相同类型元素的固定大小的顺序集合。数组用于存储数据集合,但通常将数组视为相同类型变量的集合更有用。

所有数组都由连续的内存位置组成。最低地址对应于第一个元素,最高地址对应于最后一个元素。

Numbers(1) Numbers(2) Numbers(3) Numbers(4)

数组可以是一维的(如向量),二维的(如矩阵),Fortran 允许您创建最多 7 维的数组。

声明数组

使用dimension属性声明数组。

例如,要声明一个名为 number 的一维数组,其中包含 5 个实数元素,您可以这样写:

real, dimension(5) :: numbers

通过指定其下标来引用数组的各个元素。数组的第一个元素的下标为 1。数组 numbers 包含五个实数变量——numbers(1)、numbers(2)、numbers(3)、numbers(4) 和 numbers(5)。

要创建一个名为 matrix 的 5 x 5 二维整数数组,您可以这样写:

integer, dimension (5,5) :: matrix  

您还可以声明具有某些显式下界的数组,例如:

real, dimension(2:6) :: numbers
integer, dimension (-3:2,0:4) :: matrix  

赋值

您可以为各个成员赋值,例如:

numbers(1) = 2.0

或者,您可以使用循环:

do i  =1,5
   numbers(i) = i * 2.0
end do

可以使用简写符号(称为数组构造器)直接为一维数组元素赋值,例如:

numbers = (/1.5, 3.2,4.5,0.9,7.2 /)

请注意,'(' 和 '/'之间不允许有空格。

示例

以下示例演示了上述概念。

program arrayProg

   real :: numbers(5) !one dimensional integer array
   integer :: matrix(3,3), i , j !two dimensional real array
   
   !assigning some values to the array numbers
   do i=1,5
      numbers(i) = i * 2.0
   end do
   
   !display the values
   do i = 1, 5
      Print *, numbers(i)
   end do
   
   !assigning some values to the array matrix
   do i=1,3
      do j = 1, 3
         matrix(i, j) = i+j
      end do
   end do
   
   !display the values
   do i=1,3
      do j = 1, 3
         Print *, matrix(i,j)
      end do
   end do
   
   !short hand assignment
   numbers = (/1.5, 3.2,4.5,0.9,7.2 /)
   
   !display the values
   do i = 1, 5
      Print *, numbers(i)
   end do
   
end program arrayProg

当上述代码编译并执行时,它会产生以下结果:

 2.00000000    
 4.00000000    
 6.00000000    
 8.00000000    
 10.0000000    
         2
         3
         4
         3
         4
         5
         4
         5
         6
 1.50000000    
 3.20000005    
 4.50000000    
0.899999976    
 7.19999981    

一些与数组相关的术语

下表给出了一些与数组相关的术语:

术语 含义
它是数组的维度数。例如,对于名为 matrix 的数组,秩为 2,对于名为 numbers 的数组,秩为 1。
范围 它是沿一个维度上的元素数。例如,数组 numbers 的范围为 5,名为 matrix 的数组在两个维度上的范围均为 3。
形状 数组的形状是一个一维整数数组,包含每个维度中的元素数(范围)。例如,对于数组 matrix,形状为 (3, 3),对于数组 numbers,形状为 (5)。
大小 它是数组包含的元素数。对于数组 matrix,它为 9,对于数组 numbers,它为 5。

将数组传递给过程

您可以将数组作为参数传递给过程。以下示例演示了该概念:

program arrayToProcedure      
implicit none      

   integer, dimension (5) :: myArray  
   integer :: i
   
   call fillArray (myArray)      
   call printArray(myArray)
   
end program arrayToProcedure


subroutine fillArray (a)      
implicit none      

   integer, dimension (5), intent (out) :: a
   
   ! local variables     
   integer :: i     
   do i = 1, 5         
      a(i) = i      
   end do  
   
end subroutine fillArray 


subroutine printArray(a)

   integer, dimension (5) :: a  
   integer::i
   
   do i = 1, 5
      Print *, a(i)
   end do
   
end subroutine printArray

当上述代码编译并执行时,它会产生以下结果:

1
2
3
4
5

在上面的示例中,子程序 fillArray 和 printArray 只能用于维度为 5 的数组。但是,要编写可用于任何大小数组的子程序,您可以使用以下技术重写它:

program arrayToProcedure      
implicit  none    

   integer, dimension (10) :: myArray  
   integer :: i
   
   interface 
      subroutine fillArray (a)
         integer, dimension(:), intent (out) :: a 
         integer :: i         
      end subroutine fillArray      

      subroutine printArray (a)
         integer, dimension(:) :: a 
         integer :: i         
      end subroutine printArray   
   end interface 
   
   call fillArray (myArray)      
   call printArray(myArray)
   
end program arrayToProcedure


subroutine fillArray (a)      
implicit none      
   integer,dimension (:), intent (out) :: a      
   
   ! local variables     
   integer :: i, arraySize  
   arraySize = size(a)
   
   do i = 1, arraySize         
      a(i) = i      
   end do  
   
end subroutine fillArray 


subroutine printArray(a)
implicit none

   integer,dimension (:) :: a  
   integer::i, arraySize
   arraySize = size(a)
   
   do i = 1, arraySize
     Print *, a(i)
   end do
   
end subroutine printArray

请注意,程序使用size函数来获取数组的大小。

当上述代码编译并执行时,它会产生以下结果:

1
2
3
4
5
6
7
8
9
10

数组节

到目前为止,我们已经引用了整个数组,Fortran 提供了一种简单的方法来使用单个语句引用多个元素或数组的一部分。

要访问数组节,您需要为所有维度提供节的上下界以及步幅(增量)。此表示法称为下标三元组:

array ([lower]:[upper][:stride], ...)

当未提及上下界时,它默认为您声明的范围,步幅值默认为 1。

以下示例演示了该概念:

program arraySubsection

   real, dimension(10) :: a, b
   integer:: i, asize, bsize
   
   a(1:7) = 5.0 ! a(1) to a(7) assigned 5.0
   a(8:) = 0.0  ! rest are 0.0 
   b(2:10:2) = 3.9
   b(1:9:2) = 2.5
   
   !display
   asize = size(a)
   bsize = size(b)
   
   do i = 1, asize
      Print *, a(i)
   end do
   
   do i = 1, bsize
      Print *, b(i)
   end do
   
end program arraySubsection

当上述代码编译并执行时,它会产生以下结果:

5.00000000    
5.00000000    
5.00000000    
5.00000000    
5.00000000    
5.00000000    
5.00000000    
0.00000000E+00
0.00000000E+00
0.00000000E+00
2.50000000    
3.90000010    
2.50000000    
3.90000010    
2.50000000    
3.90000010    
2.50000000    
3.90000010    
2.50000000    
3.90000010 

数组内在函数

Fortran 90/95 提供了几个内在过程。它们可以分为 7 类。

Fortran - 动态数组

动态数组是一个数组,其大小在编译时未知,但在运行时将已知。

动态数组用属性allocatable声明。

例如:

real, dimension (:,:), allocatable :: darray    

必须提到数组的秩,即维度,但是要为这样的数组分配内存,您使用allocate函数。

allocate ( darray(s1,s2) )      

在程序中使用数组后,应使用deallocate函数释放创建的内存。

deallocate (darray)  

示例

以下示例演示了上述概念。

program dynamic_array 
implicit none 

   !rank is 2, but size not known   
   real, dimension (:,:), allocatable :: darray    
   integer :: s1, s2     
   integer :: i, j     
   
   print*, "Enter the size of the array:"     
   read*, s1, s2      
   
   ! allocate memory      
   allocate ( darray(s1,s2) )      
   
   do i = 1, s1           
      do j = 1, s2                
         darray(i,j) = i*j               
         print*, "darray(",i,",",j,") = ", darray(i,j)           
      end do      
   end do      
   
   deallocate (darray)  
end program dynamic_array

当上述代码编译并执行时,它会产生以下结果:

Enter the size of the array: 3,4
darray( 1 , 1 ) = 1.00000000    
darray( 1 , 2 ) = 2.00000000    
darray( 1 , 3 ) = 3.00000000    
darray( 1 , 4 ) = 4.00000000    
darray( 2 , 1 ) = 2.00000000    
darray( 2 , 2 ) = 4.00000000    
darray( 2 , 3 ) = 6.00000000    
darray( 2 , 4 ) = 8.00000000    
darray( 3 , 1 ) = 3.00000000    
darray( 3 , 2 ) = 6.00000000    
darray( 3 , 3 ) = 9.00000000    
darray( 3 , 4 ) = 12.0000000   

数据语句的使用

data语句可用于初始化多个数组或用于数组节初始化。

data 语句的语法如下:

data variable / list / ...

示例

以下示例演示了该概念:

program dataStatement
implicit none

   integer :: a(5), b(3,3), c(10),i, j
   data a /7,8,9,10,11/ 
   
   data b(1,:) /1,1,1/ 
   data b(2,:)/2,2,2/ 
   data b(3,:)/3,3,3/ 
   data (c(i),i = 1,10,2) /4,5,6,7,8/ 
   data (c(i),i = 2,10,2)/5*2/
   
   Print *, 'The A array:'
   do j = 1, 5                
      print*, a(j)           
   end do 
   
   Print *, 'The B array:'
   do i = lbound(b,1), ubound(b,1)
      write(*,*) (b(i,j), j = lbound(b,2), ubound(b,2))
   end do

   Print *, 'The C array:' 
   do j = 1, 10                
      print*, c(j)           
   end do      
   
end program dataStatement

当上述代码编译并执行时,它会产生以下结果:

 The A array:
           7
           8
           9
          10
          11
 The B array:
           1           1           1
           2           2           2
           3           3           3
 The C array:
           4
           2
           5
           2
           6
           2
           7
           2
           8
           2

Where语句的使用

where语句允许您根据某些逻辑条件的结果在表达式中使用数组的某些元素。如果给定条件为真,则允许在元素上执行表达式。

示例

以下示例演示了该概念:

program whereStatement
implicit none

   integer :: a(3,5), i , j
   
   do i = 1,3
      do j = 1, 5                
         a(i,j) = j-i          
      end do 
   end do
   
   Print *, 'The A array:'
   
   do i = lbound(a,1), ubound(a,1)
      write(*,*) (a(i,j), j = lbound(a,2), ubound(a,2))
   end do
   
   where( a<0 ) 
      a = 1 
   elsewhere
      a = 5
   end where
  
   Print *, 'The A array:'
   do i = lbound(a,1), ubound(a,1)
      write(*,*) (a(i,j), j = lbound(a,2), ubound(a,2))
   end do   
   
end program whereStatement

当上述代码编译并执行时,它会产生以下结果:

 The A array:
           0           1           2           3           4
          -1           0           1           2           3
          -2          -1           0           1           2
 The A array:
           5           5           5           5           5
           1           5           5           5           5
           1           1           5           5           5

Fortran - 派生数据类型

Fortran 允许您定义派生数据类型。派生数据类型也称为结构,它可以由不同类型的数据对象组成。

派生数据类型用于表示记录。例如,如果您想跟踪图书馆中书籍的信息,您可能想跟踪每本书的以下属性:

  • 标题
  • 作者
  • 主题
  • 图书 ID

定义派生数据类型

要定义派生类型,使用 type 和end type语句。。type 语句定义一个新的数据类型,为您的程序提供多个成员。type 语句的格式如下:

type type_name      
   declarations
end type 

以下是您声明 Book 结构的方式:

type Books
   character(len = 50) :: title
   character(len = 50) :: author
   character(len = 150) :: subject
   integer :: book_id
end type Books

访问结构成员

派生数据类型的一个对象被称为结构体。

可以在类型声明语句中创建一个 Books 类型的结构体,例如:

type(Books) :: book1 

可以使用组件选择符字符 (%) 来访问结构体的组件。

book1%title = "C Programming"
book1%author = "Nuha Ali"
book1%subject = "C Programming Tutorial"
book1%book_id = 6495407

请注意,% 符号前后没有空格。

示例

下面的程序演示了上述概念:

program deriveDataType

   !type declaration
   type Books
      character(len = 50) :: title
      character(len = 50) :: author
      character(len = 150) :: subject
      integer :: book_id
   end type Books
   
   !declaring type variables
   type(Books) :: book1 
   type(Books) :: book2 
   
   !accessing the components of the structure
   
   book1%title = "C Programming"
   book1%author = "Nuha Ali"
   book1%subject = "C Programming Tutorial"
   book1%book_id = 6495407 
   
   book2%title = "Telecom Billing"
   book2%author = "Zara Ali"
   book2%subject = "Telecom Billing Tutorial"
   book2%book_id = 6495700
  
   !display book info
   
   Print *, book1%title 
   Print *, book1%author 
   Print *, book1%subject 
   Print *, book1%book_id  
   
   Print *, book2%title 
   Print *, book2%author 
   Print *, book2%subject 
   Print *, book2%book_id  

end program deriveDataType

当上述代码编译并执行时,它会产生以下结果:

 C Programming                                     
 Nuha Ali                                          
 C Programming Tutorial            
   6495407
 Telecom Billing                                   
 Zara Ali                                          
 Telecom Billing Tutorial            
   6495700

结构体数组

您还可以创建派生类型的数组:

type(Books), dimension(2) :: list

可以按如下方式访问数组的各个元素:

list(1)%title = "C Programming"
list(1)%author = "Nuha Ali"
list(1)%subject = "C Programming Tutorial"
list(1)%book_id = 6495407

下面的程序演示了这个概念:

program deriveDataType

   !type declaration
   type Books
      character(len = 50) :: title
      character(len = 50) :: author
      character(len = 150) :: subject
      integer :: book_id
   end type Books
   
   !declaring array of books
   type(Books), dimension(2) :: list 
    
   !accessing the components of the structure
   
   list(1)%title = "C Programming"
   list(1)%author = "Nuha Ali"
   list(1)%subject = "C Programming Tutorial"
   list(1)%book_id = 6495407 
   
   list(2)%title = "Telecom Billing"
   list(2)%author = "Zara Ali"
   list(2)%subject = "Telecom Billing Tutorial"
   list(2)%book_id = 6495700
  
   !display book info
   
   Print *, list(1)%title 
   Print *, list(1)%author 
   Print *, list(1)%subject 
   Print *, list(1)%book_id  
   
   Print *, list(1)%title 
   Print *, list(2)%author 
   Print *, list(2)%subject 
   Print *, list(2)%book_id  

end program deriveDataType

当上述代码编译并执行时,它会产生以下结果:

C Programming                                     
Nuha Ali                                          
C Programming Tutorial               
   6495407
C Programming                                     
Zara Ali                                          
Telecom Billing Tutorial                                      
   6495700

Fortran - 指针

在大多数编程语言中,指针变量存储对象的内存地址。但是,在 Fortran 中,指针是一个数据对象,其功能不仅仅是存储内存地址。它包含有关特定对象的更多信息,例如类型、秩、范围和内存地址。

指针通过分配或指针赋值与目标关联。

声明指针变量

指针变量是用指针属性声明的。

以下示例显示了指针变量的声明:

integer, pointer :: p1 ! pointer to integer  
real, pointer, dimension (:) :: pra ! pointer to 1-dim real array  
real, pointer, dimension (:,:) :: pra2 ! pointer to 2-dim real array

指针可以指向:

  • 动态分配的内存区域。

  • 与指针类型相同的具有 **target** 属性的数据对象。

为指针分配空间

**allocate** 语句允许您为指针对象分配空间。例如:

program pointerExample
implicit none

   integer, pointer :: p1
   allocate(p1)
   
   p1 = 1
   Print *, p1
   
   p1 = p1 + 4
   Print *, p1
   
end program pointerExample

当上述代码编译并执行时,它会产生以下结果:

1
5

当不再需要分配的存储空间时,应使用 **deallocate** 语句将其清空,避免积累未使用的和不可用的内存空间。

目标和关联

目标是另一个普通变量,为此变量预留了空间。目标变量必须用 **target** 属性声明。

您可以使用关联运算符 (=>) 将指针变量与目标变量关联。

让我们重写前面的示例,以演示此概念:

program pointerExample
implicit none

   integer, pointer :: p1
   integer, target :: t1 
   
   p1=>t1
   p1 = 1
   
   Print *, p1
   Print *, t1
   
   p1 = p1 + 4
   
   Print *, p1
   Print *, t1
   
   t1 = 8
   
   Print *, p1
   Print *, t1
   
end program pointerExample

当上述代码编译并执行时,它会产生以下结果:

1
1
5
5
8
8

指针可以:

  • 未定义
  • 已关联
  • 已解除关联

在上面的程序中,我们使用 => 运算符将指针 p1 与目标 t1 关联。associated 函数测试指针的关联状态。

**nullify** 语句将指针与目标解除关联。

nullify 不会清空目标,因为可能有多个指针指向同一个目标。但是,清空指针也意味着取消关联。

示例 1

以下示例演示了这些概念:

program pointerExample
implicit none

   integer, pointer :: p1
   integer, target :: t1 
   integer, target :: t2
   
   p1=>t1
   p1 = 1
   
   Print *, p1
   Print *, t1
   
   p1 = p1 + 4
   Print *, p1
   Print *, t1
   
   t1 = 8
   Print *, p1
   Print *, t1
   
   nullify(p1)
   Print *, t1
   
   p1=>t2
   Print *, associated(p1)
   Print*, associated(p1, t1)
   Print*, associated(p1, t2)
   
   !what is the value of p1 at present
   Print *, p1
   Print *, t2
   
   p1 = 10
   Print *, p1
   Print *, t2
   
end program pointerExample

当上述代码编译并执行时,它会产生以下结果:

1
1
5
5
8
8
8
T
F
T
952754640
952754640
10
10

请注意,每次运行代码时,内存地址都会不同。

示例 2

program pointerExample
implicit none

   integer, pointer :: a, b
   integer, target :: t
   integer :: n
   
   t = 1
   a => t
   t = 2
   b => t
   n = a + b
   
   Print *, a, b, t, n 
   
end program pointerExample

当上述代码编译并执行时,它会产生以下结果:

2  2  2  4

Fortran - 基本输入输出

到目前为止,我们已经看到可以使用 **read *** 语句从键盘读取数据,并分别使用 **print *** 语句将输出显示到屏幕上。这种形式的输入输出是 **自由格式** I/O,称为 **列表定向** 输入输出。

自由格式简单 I/O 的形式为:

read(*,*) item1, item2, item3...
print *, item1, item2, item3
write(*,*) item1, item2, item3...

但是,格式化 I/O 为您提供了更多关于数据传输的灵活性。

格式化输入输出

格式化输入输出的语法如下:

read fmt, variable_list 
print fmt, variable_list 
write fmt, variable_list 

其中:

  • fmt 是格式说明

  • 变量列表是从键盘读取或写入屏幕的变量列表

格式说明定义了格式化数据显示的方式。它由一个字符串组成,该字符串包含括号中的一系列 **编辑描述符**。

**编辑描述符** 指定精确的格式,例如宽度、小数点后的位数等,字符和数字以这种格式显示。

例如

Print "(f6.3)", pi

下表描述了描述符:

描述符 描述 示例
I

用于整数输出。其形式为“rIw.m”,其中 r、w 和 m 的含义在下表中给出。整数在其字段中右对齐。如果字段宽度不足以容纳整数,则字段将填充星号。

print "(3i5)", i, j, k
F

用于实数输出。其形式为“rFw.d”,其中 r、w 和 d 的含义在下表中给出。实数值在其字段中右对齐。如果字段宽度不足以容纳实数,则字段将填充星号。

print "(f12.3)",pi
E

用于以指数表示法进行实数输出。“E”描述符语句的形式为“rEw.d”,其中 r、w 和 d 的含义在下表中给出。实数值在其字段中右对齐。如果字段宽度不足以容纳实数,则字段将填充星号。

请注意,要打印出具有三位小数的实数,至少需要十位字段宽度。一位用于尾数的符号,两位用于零,四位用于尾数,两位用于指数本身。一般来说,w ≥ d + 7。

print "(e10.3)",123456.0 gives ‘0.123e+06’
ES

用于实数输出(科学计数法)。其形式为“rESw.d”,其中 r、w 和 d 的含义在下表中给出。“E”描述符与上面描述的传统且众所周知的“科学计数法”略有不同。科学计数法的尾数范围为 1.0 到 10.0,而 E 描述符的尾数范围为 0.1 到 1.0。实数值在其字段中右对齐。如果字段宽度不足以容纳实数,则字段将填充星号。这里也必须满足表达式 w ≥ d + 7。

print "(es10.3)",123456.0 gives ‘1.235e+05’
A

用于字符输出。其形式为“rAw”,其中 r 和 w 的含义在下表中给出。字符类型在其字段中右对齐。如果字段宽度不足以容纳字符串,则字段将填充字符串的前“w”个字符。

print "(a10)", str
X

用于空格输出。其形式为“nX”,其中“n”是所需空格数。

print "(5x, a10)", str
/

斜杠描述符 - 用于插入空行。其形式为“/”,并强制将下一个数据输出放在新行上。

print "(/,5x, a10)", str

以下符号用于格式描述符:

序号 符号 & 描述
1

c

列号

2

d

实数输入或输出小数点右边的位数

3

m

要显示的最小位数

4

n

要跳过的空格数

5

r

重复计数 - 使用描述符或描述符组的次数

6

w

字段宽度 - 用于输入或输出的字符数

示例 1

program printPi

   pi = 3.141592653589793238 
   
   Print "(f6.3)", pi 
   Print "(f10.7)", pi
   Print "(f20.15)", pi 
   Print "(e16.4)", pi/100 
   
end program printPi

当上述代码编译并执行时,它会产生以下结果:

3.142
3.1415927
3.141592741012573
0.3142E-01

示例 2

program printName
implicit none

   character (len = 15) :: first_name
   print *,' Enter your first name.' 
   print *,' Up to 20 characters, please'
   
   read *,first_name 
   print "(1x,a)",first_name
   
end program printName

编译并执行上述代码时,将产生以下结果:(假设用户输入名称 Zara)

Enter your first name.
Up to 20 characters, please
Zara 

示例 3

program formattedPrint
implicit none

   real :: c = 1.2786456e-9, d = 0.1234567e3 
   integer :: n = 300789, k = 45, i = 2
   character (len=15) :: str="Tutorials Point"
   
   print "(i6)", k 
   print "(i6.3)", k 
   print "(3i10)", n, k, i 
   print "(i10,i3,i5)", n, k, i 
   print "(a15)",str 
   print "(f12.3)", d
   print "(e12.4)", c 
   print '(/,3x,"n = ",i6, 3x, "d = ",f7.4)', n, d
   
end program formattedPrint

当上述代码编译并执行时,它会产生以下结果:

45
045
300789 45  2
300789 45  2
Tutorials Point
123.457
0.1279E-08

n = 300789 d = *******

格式语句

格式语句允许您在一个语句中混合和匹配字符、整数和实数输出。以下示例演示了这一点:

program productDetails 
implicit none 

   character (len = 15) :: name
   integer :: id 
   real :: weight
   name = 'Ardupilot'
   id = 1
   weight = 0.08
   
   print *,' The product details are' 
   
   print 100
   100 format (7x,'Name:', 7x, 'Id:', 1x, 'Weight:')
   
   print 200, name, id, weight 
   200 format(1x, a, 2x, i3, 2x, f5.2) 
   
end program productDetails

当上述代码编译并执行时,它会产生以下结果:

The product details are
Name:       Id:    Weight:
Ardupilot   1       0.08

Fortran - 文件输入输出

Fortran 允许您从文件读取数据并将数据写入文件。

在上一章中,您已经了解了如何从终端读取数据以及如何将数据写入终端。在本章中,您将学习 Fortran 提供的文件输入和输出功能。

您可以读取和写入一个或多个文件。OPEN、WRITE、READ 和 CLOSE 语句允许您实现此目的。

打开和关闭文件

在使用文件之前,必须先打开文件。**open** 命令用于打开用于读取或写入的文件。该命令的最简单形式为:

open (unit = number, file = "name").

但是,open 语句可能具有通用形式:

open (list-of-specifiers)

下表描述了最常用的说明符:

序号 说明符 & 描述
1

[UNIT=] u

单元号 u 可以是 9-99 范围内的任何数字,它指示文件,您可以选择任何数字,但程序中每个打开的文件都必须具有唯一的数字

2

IOSTAT= ios

它是 I/O 状态标识符,应该是整数变量。如果 open 语句成功,则返回的 ios 值为零,否则为非零值。

3

ERR = err

这是一个标签,在发生任何错误时,控制权将跳转到该标签。

4

FILE = fname

文件名,一个字符字符串。

5

STATUS = sta

它显示文件的先前状态。一个字符字符串,可以具有三个值之一:NEW、OLD 或 SCRATCH。临时文件在关闭或程序结束时创建和删除。

6

ACCESS = acc

它是文件访问模式。可以具有两个值之一:SEQUENTIAL 或 DIRECT。默认为 SEQUENTIAL。

7

FORM = frm

它给出文件的格式状态。可以具有两个值之一:FORMATTED 或 UNFORMATTED。默认为 UNFORMATTED

8

RECL = rl

它指定直接访问文件中每个记录的长度。

打开文件后,可以通过 read 和 write 语句访问它。完成后,应使用 **close** 语句将其关闭。

close 语句的语法如下:

close ([UNIT = ]u[,IOSTAT = ios,ERR = err,STATUS = sta])

请注意,括号中的参数是可选的。

示例

此示例演示了打开一个新文件以将一些数据写入文件。

program outputdata   
implicit none

   real, dimension(100) :: x, y  
   real, dimension(100) :: p, q
   integer :: i  
   
   ! data  
   do i=1,100  
      x(i) = i * 0.1 
      y(i) = sin(x(i)) * (1-cos(x(i)/3.0))  
   end do  
   
   ! output data into a file 
   open(1, file = 'data1.dat', status = 'new')  
   do i=1,100  
      write(1,*) x(i), y(i)   
   end do  
   
   close(1) 
   
end program outputdata

编译并执行上述代码时,它将创建文件 data1.dat 并将 x 和 y 数组值写入其中。然后关闭文件。

从文件读取和写入文件

read 和 write 语句分别用于从文件读取和写入文件。

它们的语法如下:

read ([UNIT = ]u, [FMT = ]fmt, IOSTAT = ios, ERR = err, END = s)
write([UNIT = ]u, [FMT = ]fmt, IOSTAT = ios, ERR = err, END = s)

上表中已经讨论了大多数说明符。

END = s 说明符是一个语句标签,当程序到达文件结尾时,程序将跳转到该标签。

示例

此示例演示了从文件读取和写入文件。

本程序读取我们在上一例中创建的文件 data1.dat,并在屏幕上显示其内容。

program outputdata   
implicit none   

   real, dimension(100) :: x, y  
   real, dimension(100) :: p, q
   integer :: i  
   
   ! data  
   do i = 1,100  
      x(i) = i * 0.1 
      y(i) = sin(x(i)) * (1-cos(x(i)/3.0))  
   end do  
   
   ! output data into a file 
   open(1, file = 'data1.dat', status='new')  
   do i = 1,100  
      write(1,*) x(i), y(i)   
   end do  
   close(1) 

   ! opening the file for reading
   open (2, file = 'data1.dat', status = 'old')

   do i = 1,100  
      read(2,*) p(i), q(i)
   end do 
   
   close(2)
   
   do i = 1,100  
      write(*,*) p(i), q(i)
   end do 
   
end program outputdata

当上述代码编译并执行时,它会产生以下结果:

0.100000001  5.54589933E-05
0.200000003  4.41325130E-04
0.300000012  1.47636665E-03
0.400000006  3.45637114E-03
0.500000000  6.64328877E-03
0.600000024  1.12552457E-02
0.699999988  1.74576249E-02
0.800000012  2.53552198E-02
0.900000036  3.49861123E-02
1.00000000   4.63171229E-02
1.10000002   5.92407547E-02
1.20000005   7.35742599E-02
1.30000007   8.90605897E-02
1.39999998   0.105371222    
1.50000000   0.122110792    
1.60000002   0.138823599    
1.70000005   0.155002072    
1.80000007   0.170096487    
1.89999998   0.183526158    
2.00000000   0.194692180    
2.10000014   0.202990443    
2.20000005   0.207826138    
2.29999995   0.208628103    
2.40000010   0.204863414    
2.50000000   0.196052119    
2.60000014   0.181780845    
2.70000005   0.161716297    
2.79999995   0.135617107    
2.90000010   0.103344671    
3.00000000   6.48725405E-02
3.10000014   2.02930309E-02
3.20000005  -3.01767997E-02
3.29999995  -8.61928314E-02
3.40000010  -0.147283033    
3.50000000  -0.212848678    
3.60000014  -0.282169819    
3.70000005  -0.354410470    
3.79999995  -0.428629100    
3.90000010  -0.503789663    
4.00000000  -0.578774154    
4.09999990  -0.652400017    
4.20000029  -0.723436713    
4.30000019  -0.790623367    
4.40000010  -0.852691114    
4.50000000  -0.908382416    
4.59999990  -0.956472993    
4.70000029  -0.995793998    
4.80000019  -1.02525222    
4.90000010  -1.04385209    
5.00000000  -1.05071592    
5.09999990  -1.04510069    
5.20000029  -1.02641726    
5.30000019  -0.994243503    
5.40000010  -0.948338211    
5.50000000  -0.888650239    
5.59999990  -0.815326691    
5.70000029  -0.728716135    
5.80000019  -0.629372001    
5.90000010  -0.518047631    
6.00000000  -0.395693362    
6.09999990  -0.263447165    
6.20000029  -0.122622721    
6.30000019   2.53026206E-02
6.40000010   0.178709000    
6.50000000   0.335851669    
6.59999990   0.494883657    
6.70000029   0.653881252    
6.80000019   0.810866773    
6.90000010   0.963840425    
7.00000000   1.11080539    
7.09999990   1.24979746    
7.20000029   1.37891412    
7.30000019   1.49633956    
7.40000010   1.60037732    
7.50000000   1.68947268    
7.59999990   1.76223695    
7.70000029   1.81747139    
7.80000019   1.85418403    
7.90000010   1.87160957    
8.00000000   1.86922085    
8.10000038   1.84674001    
8.19999981   1.80414569    
8.30000019   1.74167395    
8.40000057   1.65982044    
8.50000000   1.55933595    
8.60000038   1.44121361    
8.69999981   1.30668485    
8.80000019   1.15719533    
8.90000057   0.994394958    
9.00000000   0.820112705    
9.10000038   0.636327863    
9.19999981   0.445154816    
9.30000019   0.248800844    
9.40000057   4.95488606E-02
9.50000000  -0.150278628    
9.60000038  -0.348357052    
9.69999981  -0.542378068    
9.80000019  -0.730095863    
9.90000057  -0.909344316    
10.0000000  -1.07807255    

Fortran - 过程

过程(procedure)是一组执行特定任务的语句,可以从程序中调用。信息(或数据)作为参数传递给调用程序。

过程有两种类型:

  • 函数 (Functions)
  • 子程序 (Subroutines)

函数 (Function)

函数是一个返回单个值的程序。函数不应修改其参数。

返回的值称为函数值 (function value),用函数名表示。

语法 (Syntax)

函数的语法如下:

function name(arg1, arg2, ....)  
   [declarations, including those for the arguments]   
   [executable statements] 
end function [name]

以下示例演示了一个名为 area_of_circle 的函数。它计算半径为 r 的圆的面积。

program calling_func

   real :: a
   a = area_of_circle(2.0) 
   
   Print *, "The area of a circle with radius 2.0 is"
   Print *, a
   
end program calling_func


! this function computes the area of a circle with radius r  
function area_of_circle (r)  

! function result     
implicit none      

   ! dummy arguments        
   real :: area_of_circle   
   
   ! local variables 
   real :: r     
   real :: pi
   
   pi = 4 * atan (1.0)     
   area_of_circle = pi * r**2  
   
end function area_of_circle

编译并执行上述程序后,将产生以下结果:

The area of a circle with radius 2.0 is
   12.5663710   

请注意:

  • 您必须在主程序和过程中都指定implicit none

  • 被调用函数中的参数 r 称为哑元参数 (dummy argument)。

结果选项 (The result Option)

如果您希望将返回值存储在函数名以外的其他名称中,可以使用result选项。

您可以指定返回变量名如下:

function name(arg1, arg2, ....) result (return_var_name)  
   [declarations, including those for the arguments]   
   [executable statements] 
end function [name]

子程序 (Subroutine)

子程序不返回值,但可以修改其参数。

语法 (Syntax)

subroutine name(arg1, arg2, ....)    
   [declarations, including those for the arguments]    
   [executable statements]  
end subroutine [name]

调用子程序 (Calling a Subroutine)

您需要使用call语句调用子程序。

以下示例演示了子程序 swap 的定义和使用,它更改其参数的值。

program calling_func
implicit none

   real :: a, b
   a = 2.0
   b = 3.0
   
   Print *, "Before calling swap"
   Print *, "a = ", a
   Print *, "b = ", b
   
   call swap(a, b)
   
   Print *, "After calling swap"
   Print *, "a = ", a
   Print *, "b = ", b
   
end program calling_func


subroutine swap(x, y) 
implicit none

   real :: x, y, temp   
   
   temp = x  
   x = y 
   y = temp  
   
end subroutine swap

编译并执行上述程序后,将产生以下结果:

Before calling swap
a = 2.00000000    
b = 3.00000000    
After calling swap
a = 3.00000000    
b = 2.00000000   

指定参数的意图 (Specifying the Intent of the Arguments)

intent 属性允许您指定在过程中使用参数的意图。下表提供了 intent 属性的值:

值 (Value) 用作 (Used as) 解释 (Explanation)
in intent(in) 用作输入值,函数中不会更改
out intent(out) 用作输出值,会被覆盖
inout intent(inout) 参数既被使用又被覆盖

以下示例演示了该概念:

program calling_func
implicit none

   real :: x, y, z, disc
   
   x = 1.0
   y = 5.0
   z = 2.0
   
   call intent_example(x, y, z, disc)
   
   Print *, "The value of the discriminant is"
   Print *, disc
   
end program calling_func


subroutine intent_example (a, b, c, d)     
implicit none     

   ! dummy arguments      
   real, intent (in) :: a     
   real, intent (in) :: b      
   real, intent (in) :: c    
   real, intent (out) :: d   
   
   d = b * b - 4.0 * a * c 
   
end subroutine intent_example

编译并执行上述程序后,将产生以下结果:

The value of the discriminant is
   17.0000000    

递归过程 (Recursive Procedures)

当编程语言允许您在同一函数内调用函数时,就会发生递归。这称为函数的递归调用。

当一个过程直接或间接地调用自身时,称为递归过程。您应该通过在其声明前加上recursive关键字来声明此类过程。

当函数递归使用时,必须使用result选项。

以下是一个示例,它使用递归过程计算给定数字的阶乘:

program calling_func
implicit none

   integer :: i, f
   i = 15
   
   Print *, "The value of factorial 15 is"
   f = myfactorial(15)
   Print *, f
   
end program calling_func

! computes the factorial of n (n!)      
recursive function myfactorial (n) result (fac)  
! function result     
implicit none     

   ! dummy arguments     
   integer :: fac     
   integer, intent (in) :: n     
   
   select case (n)         
      case (0:1)         
         fac = 1         
      case default    
         fac = n * myfactorial (n-1)  
   end select 
   
end function myfactorial

内部过程 (Internal Procedures)

当过程包含在程序中时,称为程序的内部过程。包含内部过程的语法如下:

program program_name     
   implicit none         
   ! type declaration statements         
   ! executable statements    
   . . .     
   contains         
   ! internal procedures      
   . . .  
end program program_name

以下示例演示了该概念:

program mainprog  
implicit none 

   real :: a, b 
   a = 2.0
   b = 3.0
   
   Print *, "Before calling swap"
   Print *, "a = ", a
   Print *, "b = ", b
   
   call swap(a, b)
   
   Print *, "After calling swap"
   Print *, "a = ", a
   Print *, "b = ", b
 
contains   
   subroutine swap(x, y)     
      real :: x, y, temp      
      temp = x 
      x = y  
      y = temp   
   end subroutine swap 
   
end program mainprog   

编译并执行上述程序后,将产生以下结果:

Before calling swap
a = 2.00000000    
b = 3.00000000    
After calling swap
a = 3.00000000    
b = 2.00000000   

Fortran - 模块

模块就像一个包,您可以将函数和子程序保存在其中,如果您正在编写一个非常大的程序,或者您的函数或子程序可以在多个程序中使用。

模块提供了一种在多个文件之间分割程序的方法。

模块用于:

  • 打包子程序、数据和接口块。

  • 定义可被多个例程使用的全局数据。

  • 声明可以在您选择的任何例程中使用的变量。

  • 导入整个模块以在另一个程序或子程序中使用。

模块的语法 (Syntax of a Module)

模块由两部分组成:

  • 声明语句的说明部分
  • 包含子程序和函数定义的部分

模块的一般形式如下:

module name     
   [statement declarations]  
   [contains [subroutine and function definitions] ] 
end module [name]

在程序中使用模块 (Using a Module into your Program)

您可以通过 use 语句将模块包含到程序或子程序中:

use name  

请注意

  • 您可以根据需要添加多个模块,每个模块都将存储在单独的文件中并单独编译。

  • 一个模块可以在不同的程序中使用。

  • 一个模块可以在同一个程序中多次使用。

  • 在模块说明部分声明的变量对于该模块是全局的。

  • 在模块中声明的变量成为使用该模块的任何程序或例程中的全局变量。

  • use 语句可以出现在主程序或任何其他使用在特定模块中声明的例程或变量的子程序或模块中。

示例

以下示例演示了该概念:

module constants  
implicit none 

   real, parameter :: pi = 3.1415926536  
   real, parameter :: e = 2.7182818285 
   
contains      
   subroutine show_consts()          
      print*, "Pi = ", pi          
      print*,  "e = ", e     
   end subroutine show_consts 
   
end module constants 


program module_example     
use constants      
implicit none     

   real :: x, ePowerx, area, radius 
   x = 2.0
   radius = 7.0
   ePowerx = e ** x
   area = pi * radius**2     
   
   call show_consts() 
   
   print*, "e raised to the power of 2.0 = ", ePowerx
   print*, "Area of a circle with radius 7.0 = ", area  
   
end program module_example

编译并执行上述程序后,将产生以下结果:

Pi = 3.14159274    
e =  2.71828175    
e raised to the power of 2.0 = 7.38905573    
Area of a circle with radius 7.0 = 153.938049   

模块中变量和子程序的可访问性 (Accessibility of Variables and Subroutines in a Module)

默认情况下,use语句使模块中的所有变量和子程序都可用于使用模块代码的程序。

但是,您可以使用privatepublic属性来控制模块代码的可访问性。当您将某些变量或子程序声明为 private 时,它在模块外部不可用。

示例

以下示例说明了这个概念:

在前面的示例中,我们有两个模块变量epi。让我们将它们设为 private 并观察输出:

module constants  
implicit none 

   real, parameter,private :: pi = 3.1415926536  
   real, parameter, private :: e = 2.7182818285 
   
contains      
   subroutine show_consts()          
      print*, "Pi = ", pi          
      print*, "e = ", e     
   end subroutine show_consts 
   
end module constants 


program module_example     
use constants      
implicit none     

   real :: x, ePowerx, area, radius 
   x = 2.0
   radius = 7.0
   ePowerx = e ** x
   area = pi * radius**2     
   
   call show_consts() 
   
   print*, "e raised to the power of 2.0 = ", ePowerx
   print*, "Area of a circle with radius 7.0 = ", area  
   
end program module_example

编译并执行上述程序时,会显示以下错误消息:

   ePowerx = e ** x
   1
Error: Symbol 'e' at (1) has no IMPLICIT type
main.f95:19.13:

   area = pi * radius**2     
   1
Error: Symbol 'pi' at (1) has no IMPLICIT type

由于epi都被声明为 private,程序 module_example 无法再访问这些变量。

但是,其他模块子程序可以访问它们:

module constants  
implicit none 

   real, parameter,private :: pi = 3.1415926536  
   real, parameter, private :: e = 2.7182818285 
   
contains      
   subroutine show_consts()          
      print*, "Pi = ", pi          
      print*, "e = ", e     
   end subroutine show_consts 
   
   function ePowerx(x)result(ePx) 
   implicit none
      real::x
      real::ePx
      ePx = e ** x
   end function ePowerx
    
   function areaCircle(r)result(a)  
   implicit none
      real::r
      real::a
      a = pi * r**2  
   end function areaCircle
    
end module constants 


program module_example     
use constants      
implicit none     

   call show_consts() 
   
   Print*, "e raised to the power of 2.0 = ", ePowerx(2.0)
   print*, "Area of a circle with radius 7.0 = ", areaCircle(7.0)  
   
end program module_example

编译并执行上述程序后,将产生以下结果:

Pi = 3.14159274    
e = 2.71828175    
e raised to the power of 2.0 = 7.38905573    
Area of a circle with radius 7.0 = 153.938049   

Fortran - 内在函数

内在函数是一些常用且重要的函数,作为 Fortran 语言的一部分提供。我们已经在数组、字符和字符串章节中讨论了其中一些函数。

内在函数可以分为:

  • 数值函数 (Numeric Functions)
  • 数学函数 (Mathematical Functions)
  • 数值查询函数 (Numeric Inquiry Functions)
  • 浮点运算函数 (Floating-Point Manipulation Functions)
  • 位操作函数 (Bit Manipulation Functions)
  • 字符函数 (Character Functions)
  • 类型函数 (Kind Functions)
  • 逻辑函数 (Logical Functions)
  • 数组函数 (Array Functions)。

我们在数组章节中讨论了数组函数。在下一节中,我们将简要介绍其他类别中的所有这些函数。

在函数名列中:

  • A 代表任何类型的数值变量
  • R 代表实数或整数变量
  • X 和 Y 代表实数变量
  • Z 代表复数变量
  • W 代表实数或复数变量

数值函数 (Numeric Functions)

序号 函数及描述
1

ABS (A)

返回 A 的绝对值

2

AIMAG (Z)

返回复数 Z 的虚部

3

AINT (A [, KIND])

将 A 的小数部分向零截断,返回一个实数整数。

4

ANINT (A [, KIND])

返回一个实数值,最接近的整数。

5

CEILING (A [, KIND])

返回大于或等于数字 A 的最小整数。

6

CMPLX (X [, Y, KIND])

将实数变量 X 和 Y 转换为复数 X+iY;如果 Y 缺失,则使用 0。

7

CONJG (Z)

返回任何复数 Z 的共轭复数。

8

DBLE (A)

将 A 转换为双精度实数。

9

DIM (X, Y)

返回 X 和 Y 的正差。

10

DPROD (X, Y)

返回 X 和 Y 的双精度实数乘积。

11

FLOOR (A [, KIND])

返回小于或等于数字 A 的最大整数。

12

INT (A [, KIND])

将数字(实数或整数)转换为整数,将实数部分向零截断。

13

MAX (A1, A2 [, A3,...])

返回参数中的最大值,所有参数类型相同。

14

MIN (A1, A2 [, A3,...])

返回参数中的最小值,所有参数类型相同。

15

MOD (A, P)

返回 A 除以 P 的余数,两个参数类型相同 (A-INT(A/P)*P)

16

MODULO (A, P)

返回 A 模 P:(A-FLOOR(A/P)*P)

17

NINT (A [, KIND])

返回数字 A 的最接近整数

18

REAL (A [, KIND])

转换为实数类型

19

SIGN (A, B)

返回 A 的绝对值乘以 P 的符号。基本上它将 B 的符号转移到 A。

示例

program numericFunctions
implicit none  

   ! define constants  
   ! define variables
   real :: a, b 
   complex :: z
   
   ! values for a, b 
   a = 15.2345
   b = -20.7689
    
   write(*,*) 'abs(a): ',abs(a),' abs(b): ',abs(b)   
   write(*,*) 'aint(a): ',aint(a),' aint(b): ',aint(b) 
   write(*,*) 'ceiling(a): ',ceiling(a),' ceiling(b): ',ceiling(b)   
   write(*,*) 'floor(a): ',floor(a),' floor(b): ',floor(b)  
    
   z = cmplx(a, b)
   write(*,*) 'z: ',z   
   
end program numericFunctions

编译并执行上述程序后,将产生以下结果:

abs(a): 15.2344999   abs(b): 20.7688999    
aint(a): 15.0000000  aint(b): -20.0000000    
ceiling(a): 16  ceiling(b): -20
floor(a): 15  floor(b): -21
z: (15.2344999, -20.7688999)

数学函数 (Mathematical Functions)

序号 函数及描述
1

ACOS (X)

返回范围 (0, π) 内的反余弦值,以弧度表示。

2

ASIN (X)

返回范围 (-π/2, π/2) 内的反正弦值,以弧度表示。

3

ATAN (X)

返回范围 (-π/2, π/2) 内的反正切值,以弧度表示。

4

ATAN2 (Y, X)

返回范围 (-π, π) 内的反正切值,以弧度表示。

5

COS (X)

返回以弧度表示的自变量的余弦值。

6

COSH (X)

返回以弧度表示的自变量的双曲余弦值。

7

EXP (X)

返回 X 的指数值。

8

LOG (X)

返回 X 的自然对数值。

9

LOG10 (X)

返回 X 的常用对数值(以 10 为底)。

10

SIN (X)

返回以弧度表示的自变量的正弦值。

11

SINH (X)

返回以弧度表示的自变量的双曲正弦值。

12

SQRT (X)

返回 X 的平方根。

13

TAN (X)

返回以弧度表示的自变量的正切值。

14

TANH (X)

返回以弧度表示的自变量的双曲正切值。

示例

下面的程序计算弹丸在时间 t 后的水平位置 x 和垂直位置 y:

其中,x = u t cos a 和 y = u t sin a - g t2 / 2

program projectileMotion  
implicit none  

   ! define constants  
   real, parameter :: g = 9.8  
   real, parameter :: pi = 3.1415927  
   
   !define variables
   real :: a, t, u, x, y   
   
   !values for a, t, and u 
   a = 45.0
   t = 20.0
   u = 10.0
   
   ! convert angle to radians  
   a = a * pi / 180.0  
   x = u * cos(a) * t   
   y = u * sin(a) * t - 0.5 * g * t * t  
   
   write(*,*) 'x: ',x,'  y: ',y   
   
end program projectileMotion

编译并执行上述程序后,将产生以下结果:

x: 141.421356  y: -1818.57861  

数值查询函数 (Numeric Inquiry Functions)

这些函数使用某种整数和浮点运算模型。函数返回与变量 X 类型相同的数字属性,X 可以是实数,在某些情况下是整数。

序号 函数及描述
1

DIGITS (X)

返回模型的有效数字位数。

2

EPSILON (X)

返回与 1 相比几乎可以忽略不计的数字。换句话说,它返回最小的值,使得 REAL( 1.0, KIND(X)) + EPSILON(X) 不等于 REAL( 1.0, KIND(X))。

3

HUGE (X)

返回模型的最大数。

4

MAXEXPONENT (X)

返回模型的最大指数。

5

MINEXPONENT (X)

返回模型的最小指数。

6

PRECISION (X)

返回十进制精度。

7

RADIX (X)

返回模型的基数。

8

RANGE (X)

返回十进制指数范围。

9

TINY (X)

返回模型中最小的正数。

浮点运算函数 (Floating-Point Manipulation Functions)

序号 函数及描述
1

EXPONENT (X)

返回模型数的指数部分。

2

FRACTION (X)

返回数字的小数部分。

3

NEAREST (X, S)

返回给定方向上最接近的不同处理器数字。

4

RRSPACING (X)

返回给定数字附近模型数字的相对间距的倒数。

5

SCALE (X, I)

将实数乘以其基数的整数次幂。

6

SET_EXPONENT (X, I)

返回数字的指数部分。

7

SPACING (X)

返回给定数字附近模型数字的绝对间距。

位操作函数 (Bit Manipulation Functions)

序号 函数及描述
1

BIT_SIZE (I)

返回模型的位数。

2

BTEST (I, POS)

位测试

3

IAND (I, J)

逻辑与

4

IBCLR (I, POS)

清除位

5

IBITS (I, POS, LEN)

位提取

6

IBSET (I, POS)

设置位

7

IEOR (I, J)

异或

8

IOR (I, J)

9

ISHFT (I, SHIFT)

逻辑移位

10

ISHFTC (I, SHIFT [, SIZE])

循环移位

11

NOT (I)

逻辑非

字符函数 (Character Functions)

序号 函数及描述
1

ACHAR (I)

返回 ASCII 排序序列中的第 I 个字符。

2

ADJUSTL (STRING)

通过删除所有前导空格并插入尾随空格来左对齐字符串。

3

ADJUSTR (STRING)

通过删除尾随空格并插入前导空格来右对齐字符串。

4

CHAR (I [, KIND])

返回机器特定的排序序列中的第 I 个字符。

5

IACHAR (C)

返回字符在 ASCII 排序序列中的位置。

6

ICHAR (C)

它返回字符在机器(处理器)特定排序顺序中的位置。

7

INDEX (STRING, SUBSTRING [, BACK])

它返回 SUBSTRING 在 STRING 中最左边(如果 BACK 为 .TRUE.,则为最右边)的起始位置。

8

LEN (STRING)

它返回字符串的长度。

9

LEN_TRIM (STRING)

它返回字符串的长度,不包括尾随的空格字符。

10

LGE (STRING_A, STRING_B)

字典序大于或等于

11

LGT (STRING_A, STRING_B)

字典序大于

12

LLE (STRING_A, STRING_B)

字典序小于或等于

13

LLT (STRING_A, STRING_B)

字典序小于

14

REPEAT (STRING, NCOPIES)

重复连接

15

SCAN (STRING, SET [, BACK])

它返回 STRING 中属于 SET 的最左边(如果 BACK 为 .TRUE.,则为最右边)字符的索引,如果都不属于则返回 0。

16

TRIM (STRING)

删除尾随空格字符

17

VERIFY (STRING, SET [, BACK])

验证字符串中的一组字符

类型函数 (Kind Functions)

序号 函数及描述
1

KIND (X)

它返回类型参数值。

2

SELECTED_INT_KIND (R)

它返回指定指数范围的类型参数。

3

SELECTED_REAL_KIND ([P, R])

给定精度和范围的实数类型参数值

逻辑函数

序号 函数及描述
1

LOGICAL (L [, KIND])

在具有不同类型参数的逻辑类型对象之间转换

Fortran - 数值精度

我们已经讨论过,在较旧版本的 Fortran 中,有两种**实数**类型:默认实数类型和**双精度**类型。

然而,Fortran 90/95 通过**kind**说明符提供了对实数和整数数据类型精度的更多控制。

Kind 属性

不同类型的数字在计算机中的存储方式不同。**kind**属性允许您指定数字在内部的存储方式。例如,

real, kind = 2 :: a, b, c
real, kind = 4 :: e, f, g
integer, kind = 2 :: i, j, k
integer, kind = 3 :: l, m, n

在上面的声明中,实数变量 e、f 和 g 比实数变量 a、b 和 c 具有更高的精度。整数变量 l、m 和 n 可以存储更大的值,并且比整数变量 i、j 和 k 具有更多的存储位数。尽管这取决于机器。

示例

program kindSpecifier
implicit none

   real(kind = 4) :: a, b, c
   real(kind = 8) :: e, f, g
   integer(kind = 2) :: i, j, k
   integer(kind = 4) :: l, m, n
   integer :: kind_a, kind_i, kind_e, kind_l
   
   kind_a = kind(a)
   kind_i = kind(i)
   kind_e = kind(e)
   kind_l = kind(l)
   
   print *,'default kind for real is', kind_a
   print *,'default kind for int is', kind_i
   print *,'extended kind for real is', kind_e
   print *,'default kind for int is', kind_l
   
end program kindSpecifier

编译并执行上述程序后,将产生以下结果:

default kind for real is 4
default kind for int is 2
extended kind for real is 8
default kind for int is 4

查询变量的大小

有一些内在函数允许您查询数字的大小。

例如,**bit_size(i)** 内在函数指定用于存储的位数。对于实数,**precision(x)** 内在函数返回十进制精度的位数,而 **range(x)** 内在函数返回指数的十进制范围。

示例

program getSize
implicit none

   real (kind = 4) :: a
   real (kind = 8) :: b
   integer (kind = 2) :: i
   integer (kind = 4) :: j

   print *,'precision of real(4) =', precision(a)
   print *,'precision of real(8) =', precision(b)
   
   print *,'range of real(4) =', range(a)
   print *,'range of real(8) =', range(b)
   

   print *,'maximum exponent of real(4) =' , maxexponent(a)
   print *,'maximum exponent of real(8) =' , maxexponent(b)
  
   print *,'minimum exponent of real(4) =' , minexponent(a)
   print *,'minimum exponent of real(8) =' , minexponent(b)
   
   print *,'bits in integer(2) =' , bit_size(i)
   print *,'bits in integer(4) =' , bit_size(j)
   
end program getSize

编译并执行上述程序后,将产生以下结果:

precision of real(4) = 6
precision of real(8) = 15
range of real(4) = 37
range of real(8) = 307
maximum exponent of real(4) = 128
maximum exponent of real(8) = 1024
minimum exponent of real(4) = -125
minimum exponent of real(8) = -1021
bits in integer(2) = 16
bits in integer(4) = 32

获取 Kind 值

Fortran 提供了另外两个内在函数来获取所需整数和实数精度的 kind 值:

  • selected_int_kind (r)
  • selected_real_kind ([p, r])

selected_real_kind 函数返回一个整数,该整数是给定十进制精度 p 和十进制指数范围 r 所需的 kind 类型参数值。十进制精度是有效数字的位数,十进制指数范围指定可表示的最小和最大数字。因此,范围是从 10-r 到 10+r。

例如,selected_real_kind (p = 10, r = 99) 返回实现 10 位十进制精度和至少 10-99 到 10+99 范围所需的 kind 值。

示例

program getKind
implicit none

   integer:: i
   i = selected_real_kind (p = 10, r = 99) 
   print *,'selected_real_kind (p = 10, r = 99)', i
   
end program getKind

编译并执行上述程序后,将产生以下结果:

selected_real_kind (p = 10, r = 99) 8

Fortran - 程序库

有各种 Fortran 工具和库。有些是免费的,有些是付费服务。

以下是一些免费库:

  • RANDLIB,随机数和统计分布生成器
  • BLAS
  • EISPACK
  • GAMS–NIST 可用数学软件指南
  • NIST 的一些统计和其他例程
  • LAPACK
  • LINPACK
  • MINPACK
  • MUDPACK
  • NCAR 数学库
  • Netlib 数学软件、论文和数据库集合。
  • ODEPACK
  • ODERPACK,一组用于排序和排列的例程。
  • Expokit 用于计算矩阵指数
  • SLATEC
  • SPECFUN
  • STARPAC
  • StatLib 统计库
  • TOMS
  • 字符串排序和合并

以下库不是免费的:

  • NAG Fortran 数值库
  • Visual Numerics IMSL 库
  • 数值方法

Fortran - 编程风格

编程风格是关于在开发程序时遵循某些规则。这些良好的实践赋予程序可读性和清晰性。

一个好的程序应该具有以下特点:

  • 可读性
  • 正确的逻辑结构
  • 自解释的注释

例如,如果您添加如下注释,它不会有太大帮助:

! loop from 1 to 10 
do i = 1,10  

但是,如果您正在计算二项式系数,并且需要此循环用于 nCr,那么这样的注释将很有帮助:

! loop to calculate nCr 
do i = 1,10
  • 缩进的代码块,使不同级别的代码清晰易懂。

  • 自检代码,以确保不会出现数值错误,例如除以零、负实数的平方根或负实数的对数。

  • 包含确保变量不会取非法值或超出范围值的代码,即输入验证。

  • 不要在不必要的地方添加检查,这会降低执行速度。例如:

real :: x 
x = sin(y) + 1.0

if (x >= 0.0) then
   z = sqrt(x)
end if
  • 使用合适的算法编写清晰的代码。
  • 使用续行符“&”分割长表达式。
  • 使用有意义的变量名。

Fortran - 程序调试

调试工具用于查找程序中的错误。

调试程序逐步执行代码,并允许您在程序执行期间检查变量和其他数据对象的值。

它加载源代码,您应该在调试器中运行程序。调试器通过以下方式调试程序:

  • 设置断点
  • 单步执行源代码
  • 设置监视点。

断点指定程序应该停止的位置,具体是在一段关键代码之后。在检查断点处的变量后,程序执行。

调试程序也逐行检查源代码。

监视点是在需要检查某些变量的值的点,尤其是在读写操作之后。

gdb 调试器

gdb 调试器(GNU 调试器)随 Linux 操作系统一起提供。对于 X 窗口系统,gdb 带有图形界面,程序名为 xxgdb。

下表提供了一些 gdb 命令:

命令 用途
break 设置断点
run 开始执行
cont 继续执行
next 仅执行下一行源代码,不进入任何函数调用
step 执行下一行源代码,如果发生函数调用则进入函数。

dbx 调试器

还有一个调试器,dbx 调试器,用于 Linux。

下表提供了一些 dbx 命令:

命令 用途
stop[var] 当变量 var 的值发生变化时设置断点。
stop in [proc] 进入过程 proc 时停止执行
stop at [line] 在指定行设置断点。
run 开始执行。
cont 继续执行。
next 仅执行下一行源代码,不进入任何函数调用。
step 执行下一行源代码,如果发生函数调用则进入函数。
广告