Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

Tags:

fortran

I have come across a nested do construct in an old code that I am using, and hoping to understand and modernize. It uses the same labelled action statement for termination of the do loops, as well as go to statement. Here is a simplified version, that illustrates the logic of the original code with some otherwise trivial operations:

      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

After looking at other questions on this site (and external resources), this is my best effort at rewriting the logic of the original code, without obsolescent features (and 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

I tested the codes with the following program, including alternatives for triggering/not triggering the go to statement:

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

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

and it gives the same result for the original and my modern rewrite:

original:
 6
 48

modern:
 6
 48

The action statement complicated (for me) rewriting the do loops, one can not simply replace it with two end do statements, and this is further complicated by the go to. My rewrite required duplicating the action statement (at the end of the inner loop, and within the body of the if statement). So my question is this:

  • What is the canonical/recommended way for handling the shared labelled action statement?
  • Is my modern subroutine a correct rewrite of the original one?
  • Is it possible to rewrite the original code without duplicating the action statement (k=k+1)?
like image 243
jbdv Avatar asked Aug 12 '21 08:08

jbdv


1 Answers

Your original subroutine is surely the type of code that the Fortran standard had in mind when deleting non-block DO constructs:

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.

If we have a non-block DO with shared termination looking like

do 1
  do 1
1 <action>

then we can write the equivalent

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

(where the labels here can be removed)

The action statement needs writing only once, and that's inside the innermost loop. Because it's a shared termination executing it once signals the end of the iteration of every DO construct sharing it.

If we branch to the action statement (with go to) from the innermost1 construct, like

do 1
  do 1
    go to 1
1 <action>

we have the equivalent

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

Our usual strategies for replacing go to branching then are available.


Let's apply this to the original loop (ignoring any logic changes for the same effect and using redundant statement labels)

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

We can write this as

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

Coming to the go to, we have (at least these) two simple approaches.

Negating the IF:

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

Using blocks:

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

As you can see, the "duplication of the action statement" comes from the use of the cycle statement. In the rewritten loops you've had to duplicate the action statement because you don't reach the end of the loop where the action statement lives. The original loop doesn't have a cycle and cycling changes the behaviour of the loop (the shared termination isn't executed when cycling but is when gone-to).


1 The situation is decidedly more complicated if the branch is not inside the innermost construct. For clarity of this answer I won't address that case here.

like image 103
francescalus Avatar answered Nov 02 '22 13:11

francescalus