Intel® Fortran Compiler
Build applications that can scale for the future with optimized code designed for Intel® Xeon® and compatible processors.

Q about recursive subroutines

WSinc
New Contributor I
866 Views

I assume that if I have any LOCAL variables within the routine, there will ab a separate copy of all of those

for each recursive call.

What if I have variables that I want to share between ALL recursive calls?

Would I use the SAVE attribute?

Maybe there is an article elaborating this topic. So far, I have not found it.

example:

recursive subroutine sub1

integer,SAVE::kcall

data kcall/0/

if(kcall < 20)call sub1

kcall=kcall+1

end subroutine

In the above example, I want to limit the recursions to 20 deep or less

Obviously, kcall has to be initialized some way.

And there has to be only one copy of it.

Maybe there is a BETTER way to do that?

0 Kudos
8 Replies
Steven_L_Intel1
Employee
866 Views

The problem with using SAVE for this purpose (DATA implies SAVE, by the way), is that the initialization to 0 happens only on the first call. If you make another set of nested calls, the value of kcall will be retained and it will stop immediately. A better way is to have kcall as a dummy argument, passed as 0 on the initial call. The routine then tests kcall and if it wants to continue, calls itself again passing kcall+1.

0 Kudos
WSinc
New Contributor I
866 Views

Well, i will use your method, but the one I gave seems to run also.

See the test case.

0 Kudos
Steven_L_Intel1
Employee
866 Views

But what happens if you add a second call to sub1 from the main program? This is what I was getting at.

0 Kudos
jimdempseyatthecove
Honored Contributor III
866 Views

Or

Assume you use OpenMP, you would expect (require) each thread to achieve the same recursion level. Also, kcall=kcall+1 would not be thread safe in the event you actually want all threads to share an application-wide recursion level.

Using the passed in dummy argument is the way to go

** provided you do not

   kcall=kcall+1

instead use:

  call sub1(kcall+1, ...)

This will create a temporary on the stack for the call.

Jim Dempsey

0 Kudos
WSinc
New Contributor I
866 Views

I still am wondering if there is a way to share the same set of variables between ALL calls of the routine.

I have an application that requires that -

I could put those in a MODULE, of course, but maybe that isn't the best way to do it.

0 Kudos
Steven_L_Intel1
Employee
866 Views

It depends on what you want. If you want the variables used only by that routine, you can make them local and SAVE (and VOLATILE, I would recommend.) If the variables are used by a collection of routines, put the routines in a module and declare the variables in the module scope, marking them PRIVATE. If they are to be shared by the whole program, like COMMON, put them in a module.

0 Kudos
WSinc
New Contributor I
866 Views

OK, I will experiment.

Thanks for your input ! !

0 Kudos
FortranFan
Honored Contributor III
866 Views

Steve Lionel (Intel) wrote:

It depends on what you want. If you want the variables used only by that routine, you can make them local and SAVE (and VOLATILE, I would recommend.) If the variables are used by a collection of routines, put the routines in a module and declare the variables in the module scope, marking them PRIVATE. If they are to be shared by the whole program, like COMMON, put them in a module.

Depending on the needs, derived types with type-bound procedures may be a worthwhile option for you to consider.  It has the advantage of not requiring global and static (or volatile) variables which can help with parallel processing at the higher levels.

MODULE m

   !..
   IMPLICIT NONE

   !..
   PRIVATE

   TYPE, PUBLIC :: t
      CHARACTER(LEN=:), ALLOCATABLE :: MyName
      INTEGER :: n
   CONTAINS
      PROCEDURE, PASS(This), PUBLIC :: foo
   END TYPE t

CONTAINS

   RECURSIVE SUBROUTINE foo(This, x)

      !.. Argument list
      CLASS(t), INTENT(IN)   :: This
      INTEGER, INTENT(INOUT) :: x

      !..
      IF (x < This%n) THEN
         x = x + 1
         PRINT *, This%MyName, ': x = ', x
         CALL This%foo(x)
      END IF

      !..
      RETURN

   END SUBROUTINE foo

END MODULE m

PROGRAM p

   USE m, ONLY : t

   !.. Local variables
   TYPE(t) :: A
   INTEGER :: x

   PRINT *, " Test #71: Recursion "

   A%MyName = "Thing One"
   A%n = 5
   x = 0
   CALL A%foo(x)

   STOP

END PROGRAM p

When above code is executed, 

  Test #71: Recursion
 Thing One: x =  1
 Thing One: x =  2
 Thing One: x =  3
 Thing One: x =  4
 Thing One: x =  5
Press any key to continue . . .

 

0 Kudos
Reply