summaryrefslogtreecommitdiffhomepage
path: root/zh-cn/fortran-cn.html.markdown
blob: d1b31d2c088540b2a66855ed9e516f3ff85bbd90 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
---
language: Fortran
filename: learnfortran-cn.f90
contributors:
    - ["Robert Steed", "https://github.com/robochat"]
translators:
    - ["Corvusnest", "https://github.com/Corvusnest"]
lang: zh-cn
---

Fortran 是最古老的计算机语言之一。它由 IBM 开发于 1950 年用于数值运算(Fortran 为 "Formula
Translation" 的缩写)。虽然该语言已年代久远,但目前仍用于高性能计算,如天气预报。
该语言仍在持续发展,并且基本保持向下兼容。知名的版本为 Fortran 77, Fortran 90,
Fortran 95, Fortran 2008, Fortran 2015 和 Fortran 2023。

这篇概要将讨论 Fortran 2008 的一些特征。因为它是目前所广泛采用的标准版本,并且与最新版本的内容
也基本相同(而 Fortran 77 则是一个非常不同的版本)。

```fortran
! 这是一个注释

program example         ! 声明一个名为 example 的程序

    ! 代码只能存在于程序、函数、子程序或模块中
    ! 使用缩进不是必需的,但推荐使用

    ! 声明变量
    ! =========

    ! 所有的声明必须在语句和表达式之前

    implicit none       ! 防止动态声明变量(推荐!)
    ! implicit none 推荐在每个函数/程序/模块中重新声明...

    ! 注意 - Fortran 中对大小写不敏感
    real z
    REAL Z2

    real :: v, x        ! 警告:默认的初始值取决于编译器!
    real :: a = 3, b = 2E12, c = 0.01
    integer :: i, j, k = 1, m
    real, parameter :: PI = 3.1415926535897931    ! 声明一个常数
    logical :: y = .TRUE., n = .FALSE.            ! 布尔类型
    complex :: w = (0, 1)                         ! 单位虚数
    character(len=3) :: month                     ! 字符串,长度为 3 个字符

    real :: array(6)                              ! 声明一个包含 6 个实数的数组
    real, dimension(4) :: arrayb                  ! 另一种声明数组的方式
    integer :: arrayc(-10:10)                     ! 具有自定义索引的数组
    real :: array2d(3, 2)                         ! 多维数组

    ! 这些分隔符 '::' 并不总是必需的,但推荐使用

    ! 还有许多其他的变量属性:
    real, pointer :: p                            ! 声明一个指针

    integer, parameter :: LP = selected_real_kind(20)
    real(kind=LP) :: d                            ! 长精度变量

    ! 警告:在声明过程中初始化变量会在函数中造成问题,
    ! 因为这会自动暗示 'save' 属性,
    ! 在函数调用之间保存变量的值一般情况下,
    ! 除了常量外,声明和初始化的代码应该分开!

    ! 字符串
    ! =======

    character :: a_char = 'i'
    character(len=6) :: a_str = "qwerty"
    character(len=30) :: str_b
    character(len=*), parameter :: a_long_str = "This is a long string."
    ! 使用 (len=*) 可以自动计算长度,但只适用于常量

    str_b = a_str//" keyboard"      ! 使用 // 运算符连接字符串

    ! 赋值和算术
    ! =============

    Z = 1                           ! 对上面声明的变量 z 进行赋值(对大小写不敏感)
    j = 10 + 2 - 3
    a = 11.54/(2.3*3.1)
    b = 2**3                        ! 幂运算

    ! 流程控制语句和操作符
    ! ===================

    ! 单行 if 语句
    if (z == a) b = 4               ! 条件始终需要在括号中

    if (z /= a) then                ! z 不等于 a
        ! 其他的比较操作符包括 < > <= >= == /=
        b = 4
    else if (z .GT. a) then         ! z 大于 a
        ! 文本等价于符号操作符中的 .LT. .GT. .LE. .GE. .EQ. .NE.
        b = 6
    else if (z < a) then            ! 'then' 必须在本行上
        b = 5                       ! 执行块必须在新的一行上
    else
        b = 10
    end if                          ! end 语句后需要 'if'(或可以使用 'endif')

    if (.NOT. (x < c .AND. v >= a .OR. z == z)) then    ! 布尔运算符
        inner: if (.TRUE.) then     ! 可以对 if 结构命名
            b = 1
        end if inner                ! then 必须命名对应的 endif 语句
    end if

    i = 20
    select case (i)
    case (0, 1)                     ! 当 i == 0 或 i == 1 时
        j = 0
    case (2:10)                     ! 当 i 在 2 到 10 之间(包括边界)时
        j = 1
    case (11:)                      ! 当 i >= 11 时
        j = 2
    case default
        j = 3
    end select

    month = 'jan'
    ! 条件可以是整数、逻辑、或字符类型
    ! Select 结构也可以命名
    monthly:select case(month)
    case ("jan")
        j = 0
    case default
        j = -1
    end select monthly

    do i = 2, 10, 2             ! 循环从 2 到 10(包括)以 2 为步长
        innerloop: do j = 1, 3  ! 循环也可以命名
            exit                ! 退出循环
        end do innerloop
        cycle                   ! 跳到下一个循环迭代
    end do

    ! 虽然存在 Goto 语句,但它被强烈不推荐
    goto 10
    stop 1                      ! 立即停止代码(并返回指定的条件代码)
10  j = 201                     ! 这一行被标记为 10 行

    ! 数组
    ! =====
    array = (/1, 2, 3, 4, 5, 6/)
    array = [1, 2, 3, 4, 5, 6]  ! 使用 Fortran 2003 的表示法
    arrayb = [10.2, 3e3, 0.41, 4e-5]
    array2d = reshape([1.0, 2.0, 3.0, 4.0, 5.0, 6.0], [3, 2])

    ! Fortran 数组索引从 1 开始
    ! (默认情况下,但对于特定数组可以定义不同的索引)
    v = array(1)                ! 取数组的第一个元素
    v = array2d(2, 2)

    print *, array(3:5)         ! 打印从第三个到第五个元素(包括)
    print *, array2d(1, :)      ! 打印二维数组的第一列

    array = array*3 + 2         ! 可以对数组应用数学表达式
    array = array*array         ! 数组操作是逐元素进行的
    ! array = array*array2d     ! 这两个数组是不兼容的

    ! 有许多内置函数可用于数组
    c = dot_product(array, array)   ! 这是点积
    ! 使用 matmul() 进行矩阵运算
    c = sum(array)
    c = maxval(array)
    print *, minloc(array)
    c = size(array)
    print *, shape(array)
    m = count(array > 0)

    ! 循环数组(通常使用 product() 函数)
    v = 1
    do i = 1, size(array)
        v = v*array(i)
    end do

    ! 条件性地执行逐元素赋值
    array = [1, 2, 3, 4, 5, 6]
    where (array > 3)
        array = array + 1
    elsewhere(array == 2)
        array = 1
    elsewhere
        array = 0
    end where

    ! 隐含 do 循环是创建数组的紧凑方式
    array = [(i, i=1, 6)]       ! 创建一个数组 [1,2,3,4,5,6]
    array = [(i, i=1, 12, 2)]   ! 创建一个数组 [1,3,5,7,9,11]
    array = [(i**2, i=1, 6)]    ! 创建一个数组 [1,4,9,16,25,36]
    array = [(4, 5, i=1, 3)]    ! 创建一个数组 [4,5,4,5,4,5]

    ! 输入/输出
    ! =========

    print *, b                  ! 将变量 'b' 打印到命令行

    ! 可以对打印的输出进行格式化
    print "(I6)", 320           ! 打印 '   320'
    print "(I6.4)", 3           ! 打印 '  0003'
    print "(F6.3)", 4.32        ! 打印 ' 4.320'

    ! 字母表示预期的类型,后面的数字表示用于打印值的字符数
    ! 字母可以是 I(整数),F(实数),E(工程表示法),
    ! L(逻辑),A(字符串)...
    print "(I3)", 3200          ! 打印 '***',因为该数字不适合

    ! 可以有多个格式规范
    print "(I5,F6.2,E6.2)", 120, 43.41, 43.41
    print "(3I5)", 10, 20, 30                       ! 整数的三次重复(字段宽度为 5)
    print "(2(I5,F6.2))", 120, 43.42, 340, 65.3     ! 格式重复组合

    ! 我们还可以从终端读取输入
    read (*, *) v
    read (*, "(2F6.2)") v, x                        ! 读取两个数字

    ! 写入文件
    open (unit=12, file="records.txt", status="replace")
    ! 文件通过 'unit number' 引用,这个数字可以在 9:99 范围内选择
    ! Status 可以是 {'old','replace','new'} 中的一个
    write (12, "(F10.2,F10.2,F10.2)") c, b, a
    close (12)

    ! 读取文件
    open (newunit=m, file="records.txt", status="old")
    ! 文件通过 'new unit number' 引用,编译器为您选择一个整数
    read (unit=m, fmt="(3F10.2)") a, b, c
    close (m)

    ! 还有更多功能可用,超出了本文所讨论的范围,
    ! 还有由于与旧版本的 Fortran 的向后兼容性而存在的替代方案

    ! 内置函数
    ! ===========

    ! Fortran 大约有 200 个语言内部的函数/子程序
    ! 例如 -
    call cpu_time(v)        ! 将 'v' 设置为以秒为单位的时间
    k = ior(i, j)           ! 两个整数的位 OR 运算
    v = log10(x)            ! 以 10 为底的对数
    i = floor(b)            ! 返回小于或等于 x 的最接近的整数
    v = aimag(w)            ! 复数的虚部

    ! 函数和子程序
    ! ==============

    ! 子程序运行一些代码,并可以对输入值产生副作用或修改输入值

    call routine(a, c, v)   ! 子程序调用

    ! 函数采用一系列输入参数,并返回一个单个值
    ! 不过,输入参数可能仍会被修改,并且会执行副作用

    m = func(3, 2, k)       ! 函数调用

    ! 函数调用还可以在表达式中使用
    print *, func2(3, 2, k)

    ! 一个纯函数是一个不修改其输入参数,
    ! 也不会引起任何副作用的函数
    m = func3(3, 2, k)

contains                    ! 包含程序内部定义的子程序的区域

    ! Fortran 有几种稍微不同的方式来定义函数

    integer function func(a, b, c)      ! 函数返回一个整数值
        ! implicit none                 ! 子变量域可以不再声明 implicit none
        integer, intent(in) :: a, b, c  ! 在函数内部定义输入参数的类型

        if (a >= 2) then
            func = a + b + c            ! 返回变量默认为函数名
            return                      ! 随时可以从函数返回当前值
        end if
        func = a + c

        ! 在函数的末尾不需要 return 语句
    end function func

    function func2(a, b, c) result(f)   ! 返回变量声明为 'f'
        integer, intent(in) :: a, b     ! 可以声明和强制约定变量
        ! 不会被函数修改
        integer, intent(inout) :: c
        integer :: f                    ! 函数返回类型在函数内部声明
        integer :: cnt = 0              ! 注意:初始化暗示变量在函数调用之间保存
        !

        f = a + b - c
        c = 4                           ! 修改输入变量的值
        cnt = cnt + 1                   ! 计算函数调用的次数

    end function func2

    pure function func3(a, b, c)        ! 纯函数不能有副作用
        integer, intent(in) :: a, b, c
        integer :: func3

        func3 = a*b*c

    end function func3

    subroutine routine(d, e, f)
        real, intent(inout) :: f
        real, intent(in) :: d, e

        f = 2*d + 3*e + f

    end subroutine routine

end program example                     ! 程序定义结束--------------------------

! 函数和子程序在程序列表之外声明,在程序之间以及模块中声明时,需要使用 interface 声明(即使它们在同一源文件中)(见下面)将它们定义在模块或程序的 'contains' 部分更容易

elemental real function func4(a) result(res)
! elemental 函数是一个纯函数,它采用标量输入变量,
! 但也可以在数组上独立应用,并返回一个新的数组
    real, intent(in) :: a

    res = a**2 + 1.0

end function func4

! 模块
! =======

! 模块是在可重用性中将相关的声明、函数和子程序结合在一起的有用方式

module fruit

    real :: apple
    real :: pear
    real :: orange

end module fruit

module fruity
    ! 声明的顺序必须是:模块、接口、变量
    !(也可以在程序中声明模块和接口)

    use fruit, only: apple, pear    ! 使用 fruit 模块中的 apple 和 pear
    implicit none                   ! 导入模块之后

    private                         ! 将一些内容私有化(默认为公共)
    ! 显式将一些变量/函数声明为公共
    public :: apple, mycar, create_mycar
    ! 将一些变量/函数声明为模块私有(本例中是多余的)
    private :: func4

    ! 接口
    ! ========
    ! 在模块内部(最好放在 'contains' 部分)显式声明外部函数/过程
    interface
        elemental real function func4(a) result(res)
            real, intent(in) :: a
        end function func4
    end interface

    ! 可以使用命名接口定义重载函数
    interface myabs
        ! 可以使用 'module procedure' 关键字包括模块内已经定义的函数
        module procedure real_abs, complex_abs
    end interface

    ! 派生数据类型
    ! ==================
    ! 可以创建自定义的结构化数据集合
    type car
        character(len=100) :: model
        real :: weight              !(千克)
        real :: dimensions(3)       ! 即,长度-宽度-高度(米)
        character :: colour
    contains
        procedure :: info           ! 将过程绑定到类型
    end type car

    type(car) :: mycar              ! 声明自定义类型的变量
    ! 请查看 create_mycar() 程序的用法

    ! 注意:模块中没有可以执行的语句

contains

    subroutine create_mycar(mycar)
        ! 演示派生数据类型的用法
        type(car), intent(out) :: mycar

        ! 使用 '%' 运算符访问类型元素
        mycar%model = "Ford Prefect"
        mycar%colour = 'r'
        mycar%weight = 1400
        mycar%dimensions(1) = 5.0   ! 默认索引从 1 开始!
        mycar%dimensions(2) = 3.0
        mycar%dimensions(3) = 1.5

    end subroutine create_mycar

    subroutine info(self)
        class(car), intent(in) :: self
        ! 使用 'class' 关键字将过程绑定到类型

        print *, "Model     : ", self%model
        print *, "Colour    : ", self%colour
        print *, "Weight    : ", self%weight
        print *, "Dimensions: ", self%dimensions

    end subroutine info

    real pure function real_abs(x)
        real, intent(in) :: x

        if (x < 0) then
            real_abs = -x
        else
            real_abs = x
        end if

    end function real_abs

    real pure function complex_abs(z)
        complex, intent(in) :: z
        ! 长行可以使用继续字符 '&' 进行延续

        complex_abs = sqrt(real(z)**2 + &
                           aimag(z)**2)

    end function complex_abs

end module fruity
```

### 更多资源

了解更多的 Fortran 信息:

+ [wikipedia](https://en.wikipedia.org/wiki/Fortran)
+ [Fortran-lang Organization](https://fortran-lang.org/)
+ [Fortran_95_language_features](https://en.wikipedia.org/wiki/Fortran_95_language_features)
+ [fortranwiki.org](http://fortranwiki.org)
+ [www.fortran90.org/](http://www.fortran90.org)
+ [list of Fortran 95 tutorials](http://www.dmoz.org/Computers/Programming/Languages/Fortran/FAQs%2C_Help%2C_and_Tutorials/Fortran_90_and_95/)
+ [Fortran wikibook](https://en.wikibooks.org/wiki/Fortran)
+ [Fortran resources](http://www.fortranplus.co.uk/resources/fortran_resources.pdf)
+ [Mistakes in Fortran 90 Programs That Might Surprise You](http://www.cs.rpi.edu/~szymansk/OOF90/bugs.html)