现代 Fortran 等效于嵌套 DO 和 GO TO 共享的动作语句

Modern Fortran equivalent of an action statement shared by nested DO and GO TO

我在使用的旧代码中发现了一个嵌套的 do 结构,希望能够理解并实现现代化。它使用相同的标记动作语句来终止 do 循环,以及 go to 语句。这是一个简化版本,它通过一些其他微不足道的操作说明了原始代码的逻辑:

      subroutine original(lim)
      k=0
      do 10 i=1,4
        do 10 j=1,3
          k=k-2
          if (i>lim) go to 10
          k=k+i*j
 10   k=k+1
      write(*,*) k
      return
      end

看了其他questions on this site(和外部资源),这是我的 尽最大努力重写原始代码的逻辑,没有过时的功能(和 go to):

subroutine modern(lim)
  integer, intent(in) :: lim
  integer :: i, j, k
  k=0
  outer: do i=1,4
    inner: do j=1,3
      k=k-2
      if (i>lim) then
        k=k+1
        cycle inner
      end if
      k=k+i*j
      k=k+1
    end do inner
  end do outer
  write(*,*) k
end subroutine modern

我用以下程序测试了代码,包括 triggering/not 触发 go to 语句的替代方案:

  write(*, '(a)') 'original:'
  call original(2)
  call original(5)

  write(*, '(/,a)') 'modern:'
  call modern(2)
  call modern(5)
end

它为原始和我的现代重写提供了相同的结果:

original:
 6
 48

modern:
 6
 48

重写 do 循环的操作语句很复杂(对我来说),不能简单地替换它 有两个 end do 语句,go to 使情况更加复杂。我需要重写 复制动作语句(在 inner 循环的末尾,在 if 的主体内 陈述)。所以我的问题是:

您的 original 子例程肯定是 Fortran 标准在删除非块 DO 结构时考虑的代码类型:

The nonblock forms of the DO loop were confusing and hard to maintain. Shared termination and dual use of labeled action statements as do termination and branch targets were especially error-prone.

如果我们有一个共享终止的非块 DO 看起来像

do 1
  do 1
1 <action>

那么我们可以写出等价的

do 2
  do 1
    <action>
  1 end do
2 end do

(这里的标签可以去掉)

动作语句只需要写一次,而且是在最内层的循环中。因为它是一个共享终止,执行它一次标志着每个 DO 构造共享它的迭代结束。

如果我们从最里面的1构造分支到动作语句(go to),比如

do 1
  do 1
    go to 1
1 <action>

我们有等价物

do 3
  do 2
    go to 1
    1 <action>
  2 end do
3 end do

然后我们通常的替换 go to 分支的策略可用。


让我们将其应用于原始循环(忽略任何逻辑更改以获得相同效果并使用冗余语句标签)

do 10 i=1,4
  do 10 j=1,3
    k=k-2
    if (i>lim) go to 10
    k=k+i*j
10 k=k+1

我们可以这样写

do 30 i=1,4
  do 20 j=1,3
    k=k-2
    if (i>lim) go to 10
    k=k+i*j
    10 k=k+1
  20 end do
30 end do

来到 go to,我们有(至少这些)两个简单的方法。

否定 IF:

    if (i<=lim) k=k+i*j
    10 k=k+1

使用积木:

    nogoto: block
      if (i>lim) exit nogoto
      k=k+i*j
    end block nogoto
    10 k=k+1

如您所见,“动作语句的重复”来自于 cycle 语句的使用。在重写的循环中,您必须复制操作语句,因为您没有到达操作语句所在的循环的末尾。原始循环没有 cycle 并且循环会改变循环的行为(共享终止不会在循环时执行,而是在执行时执行)。


1 如果分支不在最内层结构中,情况肯定会更复杂。为了这个答案的清楚,我不会在这里解决这个问题。