Skip to main content

条件与循环语句

无论是什么编程语言基本上都有条件语句,因为在程序执行的过程中难免需要根据某个条件执行不同操作。举个例子,一个便利店收银台结算程序需要对不同种类的商品采用不同的税率计算。假设日常食品的税率是 8%,生活用品及其他的税率是 10%,那么我们在计算多个商品时,首先要有一个条件语句来判断商品是日常食品还是生活用品。除此之外,当结算多个商品时我们需要循环做上述的条件语句,那么循环语句也是必不可少的。

循环语句

Fortran 语言中的循环语句与其他高级编程语言中的 For 循环、while 循环有点类似,但也略有不同。

do 循环

Fortran 的最简单的循环语句称为 do 语句,其形式如下所示:

!!! 不设置步长,默认为 1
do 整数型变量 = 起始值, 结束值
......
......
enddo

!!! 示例 1
integer a(10)
do n = 1, 10 ! 计算 1,2,3,4,5,6,7,8,9,10
a(n) = n
enddo

!!! 设置步长,可以设置正整数和负整数,不能为 0
do 整数型变量 = 初始值, 结束值, 步长
......
......
enddo

!!! 示例 2
integer a(5)
do n = 1, 10, 2 ! 计算 1,3,5,7,9
a(nint(n/2)) = n
enddo

!!! 示例 3
integer a(5)
do n = 10, 1, -2 ! 计算 10,8,6,4,2
a(n/2) = n
enddo
小提示

在使用 do 循环语句的时候,我们需要注意在结束循环语句后循环条件变量 n 仍然存在。如果后续计算中还使用到了相同变量,务必要进行重新初始化赋值,否则可能造成数据上的污染。我们可以拿示例 3 举个例子,当循环结束后,n 的值其实为 0 (不是 2)。虽然此时的 n 值并不满足循环继续的条件,但是已经完成了对步长的迭代计算,所以如果后续还是使用相同的变量 n,我们期待的 n 初始值与循环结束后的 n 值就可能不一样。建议在将某个变量设置为循环条件变量之后,循环外尽量不要使用同一个变量,从而避免这一可能存在的干扰。

do 循环语句实际上也可以存在嵌套,即一个 do 循环语句包含了其他的 do 循环语句,如下示例 4 所示。其中需要注意的是,在多重 do 循环语句中,循环语句的条件变量不要使用相同的,否则很可能出现了我们意料之外的问题。建议同一个 do 循环嵌套语句中使用不同的条件变量。如果不在同一个 do 循环嵌套语句中,使用相同的条件变量产生问题的可能性比较小。

!!! 示例 4
integer a(10)
integer b(10, 5)
do n = 1, 10
a(n) = n
do m = 1, 10, 2
b(n, nint(m/2)) = a(n) + m
enddo
enddo
小提示

其实在 do 循环中所指定的条件变量的初始值、结束值及步长也可以用已有值的变量来代替,如下所示。

integer m, sum
m = 10
sum = 0
do n = 1, m
sum = sum + n
enddo

while 循环

通过上面的内容,不难发现上述的 do 循环确实看起来有点像 C、C++ 中的 For 循环,那么 Fortran 语言中是否也存在 while 循环呢?答案是当然了,其形式如下所示。

do while (条件)
......
......
enddo

!!! 示例 5
!!! 计算并输出 10,9,8,7,6,5,4,3,2,1 的平方
integer n
n = 10
do while (n > 0)
print *, n**2
n = n - 1
enddo

我们可以从示例 5 中发现,while 循环其实就是变化后的 do 循环。do 循环中判断条件紧跟着 do 字符串,当符合条件时进入循环体内部继续执行,否则跳出循环。while 循环中将 do 循环的初始值放置在了循环语句的前面进行声明,while 循环的条件中只有对于结束值的声明,而原来 do 循环的步长自增或自减操作则放置在了循环体中。这里有一点不同的是,如果自增或自减语句被放置在循环体的第一行,那么最后的执行结果就与 do 循环的不一致了。这是因为 do 循环实际上是在循环体执行完成后执行的步长自增或自减操作,而 while 循环中则可以自行设置自增或自减操作在循环体中的位置。

条件语句

Fortran 语言中的条件语句可以分为有条件无条件两种。我们所熟知的 if 语句就是有条件的,而无条件的则是类似于汇编语言的 goto 语句、exit 语句、cycle 语句,甚至还有无条件 do 循环语句。

if 语句

if 语句最简单的方式就是,将条件判断语句与执行语句放在同一行,如下所示。这也是因为执行语句比较短,放置在同一行反而代码更加美观。当然一般来说执行语句可能不是一行,甚至是多分支、多重的,因此也有单分支 if 语句、双分支 if 语句、多重 if 语句,如下所示。

!!! 最简单的 if 语句
if (条件) 执行语句

!!! 示例 6
integer n
integer i
n = 10
i = -4
if (i < 0) n = 5
print *, n


!!! 单分支 if 语句
if (条件) then
......
......
endif

!!! 示例 7
if (i < 0) then
n = 5
endif
print *, n


!!! 双分支 if 语句 (一般)
if (条件) then
......
else
......
endif

!!! 示例 8
if (i > 0) then
n = 8
else
n = 5
endif
print *, n


!!! 多重 if 语句
if (条件 1) then
.....
else if (条件 2) then
......
else
.....
endif
endif

!!! 示例 9
if (i > 0) then
n = 8
else if (i > -2) then
n = 6
else
n = 5
endif
endif
print *, n

在使用条件语句对条件变量进行比较判断时,除了我们常用的 >< 之外,也有其他比较符号可以使用,如下表所示。

比较条件符号含义示例
==左边等于右边n == 10
/=左边不等于右边n /= 10
>左边大于右边n > 10
>=左边大于或等于右边n >=10
<左边小于右边n < 10
<=左边小于或等于右边n <= 10
小提示

这里需要注意一点,如果我们想要比较左右两边是否相等,不是使用 =,而是使用 ==。其实这一点也比较容易理解,= 的含义是将等号右边的值或者计算结果赋值给左边的变量,为了加以区分,比较左右两边时则使用 ==。另外,根据使用其他高级编程语言的经验来看,== 是恒等于的意思,即左右两边不仅值相等、类型也相等,这不正是最稳妥的比较是否相等的方式吗?

当我们使用条件语句的时候还有一类使用场景,就是判断一个变量值是否在一个区间,即存在一个下限值和一个上限值。如果使用我们上述的 if 语句恐怕只有多重 if 语句才能满足我们的需求了,但是这代码写起来可能有点显得多余,毕竟我们在逻辑上是想要同时判断变量值与上下限值的大小。所以 Fortran 语言的 if 语句中也存在逻辑运算符 .and..or..not.,具体如下表所示。

逻辑运算符含义
条件 1 .and. 条件 2同时满足条件 1 和条件 2
条件 1 .or. 条件 2满足条件 1 和条件 2 之一即可
.not. 条件不满足条件,即满足条件的相反条件
!!! 示例 10
if (i < 0 .and. i > -2) then
n = 5
else
n = 10
endif
小提示

我们在其他高级编程语言中可能已经习惯了类似于 -2 < n < 0 这样的写法,有的时候在 Fortran 语言中也会不自觉地写成这样。但是在 Fortran 语言中这种写法是不被支持的,我们需要使用逻辑运算符 .and. 来把上下限值判断连接起来,而不能写在一起。

无条件语句

无条件语句一般来说与条件语句的作用是不相同的,主要是为了程序可以跳转、中途退出,但也有类似于 do 循环这样的,只不过还是需要 if 语句来帮助完成跳出循环的判断。无条件语句有 goto 语句、exit 语句、 cycle 语句三种。

goto 语句

如下所示,这里的 goto 语句的用法与汇编语言的 goto 语句有点类似,指定一个行号即可无条件跳转到该行执行。如果是像下面这样调用 goto 语句的行在跳转到的行之前,那么它们之间的行都会被完全忽略。如示例 11 所示,a = 15 的赋值语句会被忽略所以 a 的值是在 goto 语句执行之前赋的值 10;同理,b = 11 的赋值语句在 goto 语句和跳转行之间会被忽略,跳转行的 b = 20 生效。如示例 12 所示,当 goto 语句跳转到它之前行执行时,就会造成无限循环,每次执行到 goto 语句就会又跳转到前面,这样 goto 语句后面的命令都不会被执行。

小提示

我们在使用 goto 语句时对跳转的行一定要非常注意,一不小心很容易造成死循环。建议使用 goto 语句的时候跳转到其后的行,杜绝跳转到前面的行。

为了提高代码可读性,我们不想将 goto 语句跳转的行直接指向一个操作或者命令,这时我们就可以用 continue 来代替跳转行,如示例 13 所示。

goto 行号

行号 操作或命令

!!! 示例 11
a = 10
goto 8
b = 11
a = 15
8 b = 20
print *, a, b ! 此处输出 a 的值为 10,b 的值为 20

!!! 示例 12

8 a = 10
b = 11
a = a + b
goto 8
b = b - a ! 永远不会被执行
print *, a, b ! 永远不会被执行

!!! 示例 13
8 continue
a = 10
b = 11
a = a + b
goto 8

exit 语句

当我们在使用 do 循环语句时,如果想要在某个与循环条件无关的条件满足时提前退出循环,就要使用 if 语句和 exit 语句的组合了。如示例 14 所示,当 sum 的值超过 10 时循环就会被提前终止。由于 1+2+3+4=10,所以 i 加到 5 才会使得 sum 值超过 10,此时的 sum 则为 1+2+3+4+5=15,而 i 也因为提前退出循环而不会执行自增操作,所以此时输出的 i 值为最后的 5。如果将原来的 exit 语句换成 goto 语句,并将 goto 语句指向的跳转行设为循环外的下一行,则最后的效果也是一样的,如示例 15 所示。

!!! 示例 14
sum = 0
do i = 1, 10
sum = sum + i
if (sum > 10) exit
enddo
print *, i, sum ! 输出 i 的值为 5,sum 的值为 15

!!! 示例 15
sum = 0
do i = 1, 10
sum = sum + i
if (sum > 10) goto 8
enddo
8 print *, i, sum ! 输出 i 的值仍为 5,sum 的值仍为 15

cycle 语句

cycle 语句与 exit 语句正好表示了相反的意思,它指示程序在不满足条件时继续执行后续语句,满足条件时跳过后续语句进入下一个循环。如示例 16 所示,这个 cycle 语句的效果与示例 14、15 的有些差别。

!!! 示例 16
sum = 0
do i = 1, 10
if (sum > 10) cycle
sum = sum + i
enddo
print *, i, sum ! 输出 i 的值为 11,sum 的值仍为 15

多重循环下的 exit 与 goto 语句

一般来说在复杂的行列式计算中经常会遇到多重循环,这与以上 goto 和 exit 语句的场景稍显不同。goto 语句的跳转行因为可以指定任何一行,所以不会被多重循环影响,可以直接跳转到多重循环之外或者某一重循环之中的某一行,如示例 17 所示。而 exit 语句如果还是按照上面的用法每次只能退出一重循环,碰到需要同时退出多重循环时就比较麻烦。所以 Fortran 语言为了尽量避免这一问题,就给 exit 语句添加一种类似于 goto 语句指示跳转行的方法 exit out,如示例 18 所示。

!!! 示例 17
sum = 0
do i = 1, 10
do j = 1, 10
sum = sum + i + j
if (sum > 10) go 10
enddo
enddo
10 print *, i, j, sum ! 输出 i 的值为 1,j 的值为 4,sum 的值为 11

!!! 示例 18
sum = 0
out: do i = 1, 10
do j = 1, 10
sum = sum + i + j
if (sum > 10) exit out
enddo
enddo out
print *, i, j, sum ! 输出 i 的值为 1,j 的值为 4,sum 的值为 11
小提示

这里需要注意的是,当使用 exit out 方法时,out 是对应于循环的一个别名,需要同时在 do 前和 enddo 后添加 out 的声明。当然,这里的别名也可以使用其他符合规则的字符串,只要不与同一个代码文件中的其他别名或变量名等相同即可。