Calling a subroutine multiple times with different function as argument each time

会有一股神秘感。 提交于 2020-01-06 20:08:10

问题


I'm enough of a novice to not know the terminology, so I can't search the Web for the answer to this.

More than once, in programming, I've wanted to do something like this.

A and B are subroutines, c and d are functions. A and B each call a function multiple times inside them.

call A(c(x))
call A(d(x))
call B(c(x))
call B(d(x))

This structure doesn't work. I'm told that Fortran doesn't support aliasing of functions, at least in this context. (Most search results involving "aliasing" refer to aliasing variables rather than functions, which is why I haven't found an answer.)

So what structure can I use to do this without having to write multiple versions of A and B?


回答1:


Not totally sure I understand what you want, but is it something like the following?

Program f

  Implicit None

  Interface
     Integer Function c( x )
       Implicit None
       Integer, Intent( In ) :: x
     End Function c
     Integer Function d( x )
       Implicit None
       Integer, Intent( In ) :: x
     End Function d
  End Interface

  Call a( 3, c )
  Call a( 4, d )

  Call b( 5, c )
  Call b( 6, d )

Contains

  Subroutine a( x, func )

    Integer, Intent( In ) :: x
    Interface
       Integer Function func( x )
         Implicit None
         Integer, Intent( In ) :: x
       End Function func
    End Interface

    Write( *, * ) 'In a. Func = ', func( x )

  End Subroutine a

  Subroutine b( x, func )

    Integer, Intent( In ) :: x
    Interface
       Integer Function func( x )
         Implicit None
         Integer, Intent( In ) :: x
       End Function func
    End Interface

    Write( *, * ) 'In b. Func = ', func( x )

  End Subroutine b

End Program f

Integer Function c( x )
  Implicit None
  Integer, Intent( In ) :: x
  c = 2 * x
End Function c

Integer Function d( x )
  Implicit None
  Integer, Intent( In ) :: x
  d = 10 * x
End Function d
Wot now? gfortran -std=f95 f.f90 
Wot now? ./a.out
 In a. Func =            6
 In a. Func =           40
 In b. Func =           10
 In b. Func =           60
Wot now? 

An alternative is procedure pointers, but you'll need a f2003 compiler for that and those are not so common yet - the above is fine back to f90 and even earlier than that External will do what you want, but has less error checking capabilities




回答2:


call A(c(x)) looks like evaluate c(x) and pass that to subroutine A, as IanH says in his comment.

If you want to pass a function "C" that takes an argument of type such as X to subroutine A, there are several ways to do that.

As already mentioned, procedure pointers are a new way. While extremely few compilers exist that support all of the Fortran 2003 standard, this portion is widely supported.

Here is an example adapted from Function pointer arrays in Fortran

module ProcsMod

  implicit none

contains

function f1 (x)
  real :: f1
  real, intent (in) :: x

  f1 = 2.0 * x

  return
end function f1


function f2 (x)
   real :: f2
   real, intent (in) :: x

   f2 = 3.0 * x**2

   return
end function f2


subroutine fancy (func, x, answer)

   real, intent (in) :: x
   real, intent (out) :: answer

   interface AFunc
      function func (y)
         real :: func
         real, intent (in) ::y
      end function func
   end interface AFunc

   answer = func (x)

end subroutine fancy

end module  ProcsMod


program test_proc_ptr

  use ProcsMod

  implicit none

  interface
     function func (z)
        real :: func
        real, intent (in) :: z
     end function func
  end interface

  procedure (func), pointer :: f_ptr => null ()

  real :: answer

  f_ptr => f1
  call fancy (f_ptr, 2.0, answer)
  write (*, *) answer

  f_ptr => f2
  call fancy (f_ptr, 2.0, answer)
  write (*, *) answer


  stop

end program test_proc_ptr

The calls "call fancy (f_ptr, 2.0, answer)" look the same, but by changing the function that the function pointer f_ptr points to, a different function is passed to the subroutine fancy.

This compiles with both gfortran (versions 4.4 to 4.7) and ifort.




回答3:


I think M.S.B.'s answer describes what you mean by aliasing of functions; the Fortran terminology is "procedure pointers". As an alternative to this and Ian's answer, you can also use procedure dummy arguments (which are not necessarily pointers). Note that any procedure declaration is only supported since F2003, but gfortran 4.7 and ifort 13 both support this. It can be done with or without an (abstract) interface block:

module dummy_procedure
  implicit none

  abstract interface
    real function myfunc(x)
      real, intent(in) :: x
    end function
  end interface

contains      
  subroutine a(func)
    ! Using the interface block:
    procedure(myfunc) :: func         
    print*, 'a:', func(.5)
  end subroutine

  subroutine b(func)
    ! Using the explicit interface of a known procedure:
    procedure(f1) :: func  
    print*, 'b:', func(.5)
  end subroutine

  real function f1(x)
    real, intent(in) :: x
    f1 = 2.0 * x
  end function

  real function f2(x)
    real, intent(in) :: x
    f2 = 3.0 * x**2
  end function
end module

Now you can pass f1 and f2 directly into a and b, and the output is as expected:

program main
  use dummy_procedure
  call a(f1)  ! a: 1.0
  call a(f2)  ! a: 0.75
  call b(f1)  ! b: 1.0
  call b(f2)  ! b: 0.75
end program



回答4:


If I understand what you're trying to do:

1) Define your functions in a module.

2) Use the module.

3) Supply the function and the input data to the subroutine as separate arguments.

Here's a code that worked for me:

module iterfuncs
contains

! two example functions:
function approach_golden_ratio(a) result(agr)
  agr=1./a+1.
end function approach_golden_ratio

function approach_other_ratio(a) result(aor)
  aor=1./(a-1)
end function approach_other_ratio

end module



program use_some_functions

use iterfuncs

real :: final_res

final_res=2.3
! call subroutine with 1st function
call iterate(final_res,approach_golden_ratio)
print *,final_res

final_res=2.3
! call subroutine with 2nd function
call iterate(final_res,approach_other_ratio)
print *,final_res

end program



subroutine iterate(res,func)

use iterfuncs

do n=1,100
  res=func(res)
enddo

return

end subroutine


来源:https://stackoverflow.com/questions/13616416/calling-a-subroutine-multiple-times-with-different-function-as-argument-each-tim

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!