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

jbd*_*bdv 4 fortran

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
Run Code Online (Sandbox Code Playgroud)

在查看此站点上的其他问题 (和外部资源)后,这是我重写原始代码逻辑的最大努力,没有过时的功能(和):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
Run Code Online (Sandbox Code Playgroud)

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

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

  write(*, '(/,a)') 'modern:'
  call modern(2)
  call modern(5)
end
Run Code Online (Sandbox Code Playgroud)

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

original:
 6
 48

modern:
 6
 48
Run Code Online (Sandbox Code Playgroud)

操作语句复杂(对我而言)重写do循环,不能简单地用两个end do语句替换它,而go to. 我的重写需要复制 action 语句(在inner循环的末尾,在if 语句的主体内)。所以我的问题是:

  • 处理共享标记操作语句的规范/推荐方法是什么?
  • 我的modern子程序是正确重写的original吗?
  • 是否可以在original不复制动作语句 ( k=k+1) 的情况下重写代码?

fra*_*lus 5

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

DO 循环的非阻塞形式令人困惑且难以维护。与终止和分支目标一样,共享终止和双重使用标记的动作语句特别容易出错。

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

do 1
  do 1
1 <action>
Run Code Online (Sandbox Code Playgroud)

然后我们可以写出等价的

do 2
  do 1
    <action>
  1 end do
2 end do
Run Code Online (Sandbox Code Playgroud)

(这里的标签可以去掉)

action 语句只需要写一次,就在最里面的循环中。因为它是一个共享终止,一旦执行它就表示每个共享它的 DO 构造的迭代结束。

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

do 1
  do 1
    go to 1
1 <action>
Run Code Online (Sandbox Code Playgroud)

我们有等价的

do 3
  do 2
    go to 1
    1 <action>
  2 end do
3 end do
Run Code Online (Sandbox Code Playgroud)

我们通常用于替换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
Run Code Online (Sandbox Code Playgroud)

我们可以把它写成

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
Run Code Online (Sandbox Code Playgroud)

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

否定 IF:

    if (i<=lim) k=k+i*j
    10 k=k+1
Run Code Online (Sandbox Code Playgroud)

使用块:

    nogoto: block
      if (i>lim) exit nogoto
      k=k+i*j
    end block nogoto
    10 k=k+1
Run Code Online (Sandbox Code Playgroud)

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


1如果分支不在最里面的构造中,情况肯定会更加复杂。为清楚起见,我不会在这里讨论这种情况。