嵌套的DO循环和GO TO语句共享的行动语句的现代Fortran等效形式

5

我发现在我正在使用的旧代码中有一个嵌套的 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

在查看了其他问题外部资源后,我尽力重写了原始代码逻辑,去除了过时的特性(以及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

我使用以下程序对代码进行了测试,包括触发/不触发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 语句的主体内部)。 因此,我的问题是:

  • 处理共享标记行为说明的规范/推荐方法是什么?
  • 我的 modern 子程序是原始程序的正确重写吗?
  • 是否可能重写原始代码而不复制该动作声明(k=k+1)?
1个回答

6

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

非块形式的DO循环很容易让人感到困惑,并且很难维护。共享终止和标记动作语句的双重用途(作为Do终止和分支目标)尤其容易出错。

如果我们有一个非块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,而循环会改变循环的行为(当循环时未执行共享终止,但当跳转时则执行)。
情况显然更加复杂,如果分支不在最内层结构之内。为了本答案的清晰明了,我不会在此讨论该情况。

非常感谢您详细而逐步的解释(一如既往!),十分感激。关于明显简化的目标,那只是我在示例中选择 k=k-1 作为操作的不良选择(再加上动作语句中的 k=k+1)。也许我应该编辑问题,将 k=k-1 改为 k=k*2... - jbdv
这是一个很好的观点:我成功地忽略了无操作实际上正在吞咽终止动作语句的事实。修改问题,使语句执行一些有用的操作会有所帮助。 - francescalus
我编辑了问题中的代码示例,使得内部循环中有条件执行的操作和动作语句更加清晰明了。 - jbdv
很好的解释。我认为你的代码在“do 3”示例中是错误的,因为没有带有“3”标签的语句。可能需要一个“3 end do”。 - steve
谢谢,@steve,非常正确。 - francescalus

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接