在子例程中将函数作为参数传递时出现分段错误

时间:2013-02-25 21:16:36

标签: fortran

我试图说明如何将函数传递给Newton Raphson过程。我成功了一个非常简单的函数(称为unefonction见下文),但它不适用于有参数的函数。第二个函数称为gaussienne,它接受​​一个参数x和两个可选参数musig。在我的牛顿拉夫森程序中,我用这种方式调用了函数:f(x)。对我来说奇怪的是,在执行过程中,程序就像可选参数sigmu一样存在,但它们不存在......因此我不明白......

这是包含函数

的模块
module fonction

  implicit none

  ! parametre pour la gaussienne
  double precision :: f_sigma = 1.d0, f_mu = 0.d0

  ! pi accessible uniquement en interne
  double precision, parameter :: pi = 3.14159265359d0

  contains

    double precision function unefonction(x)
        ! fonction : unefonction
        ! renvoie
        !    $\frac{e^x - 10}{x + 2}$

        implicit none

        ! arguments 
        double precision, intent(in) :: x

        unefonction = (exp(x) - 10.) / (x + 2.)

    end function unefonction

! * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 

    double precision function gaussienne(x, mu, sig)
        ! fonction gaussienne
        ! utilise les parametres definis dans le module si
        ! mu et sig ne sont pas passes en argument

        implicit none

        ! arguments
        double precision, intent(in)           :: x
        double precision, intent(in), optional :: mu, sig

        ! variables locales
        double precision :: norme, moy, sigma

        ! sigma
        if (present(sig)) then
            write(*,*)"sig present"
            sigma = sig
        else
            sigma = f_sigma
        end if

        ! mu
        if (present(mu)) then
            write(*,*)"mu present"
            moy = mu
        else
            moy = f_mu
        end if

        ! calcul de la gaussienne
        norme = 1.d0 / (sigma * sqrt(2.d0 * pi))
        gaussienne = norme * exp(-(x - moy)**2 / (2.d0 * sigma**2))

    end function gaussienne

end module fonction

这是包含newton raphson过程的模块

module rechercheRacine

    implicit none

    contains

        subroutine newtonRaphson(racine, f, eps, cible)

            ! recherche l'antecedant de cible

            implicit none

            ! arguments
            double precision, intent(inout)        :: racine
            double precision, intent(in), optional :: cible, eps

            ! fonction dont on cherche la racine
            double precision, external :: f

            ! variables locales
            integer          :: compteur
            double precision :: xold, xnew, delta, valcible
            double precision :: threshold, fprim, fdex

            ! precision
            if (present(eps)) then
                threshold = eps
            else
                threshold = 1.d-10
            end if

            ! valeur cible
            if (present(cible)) then
                valcible = cible
            else
                valcible = 0.d0
            end if

            write(*,*) "--------------------------------------------------------"
            write(*,*) "                      NEWTON RAPHSON"
            write(*,*) "--------------------------------------------------------"
            write(*,"('x0    = ',e16.6)") racine
            write(*,"('seuil = ',e16.6)") threshold
            write(*,"('cible = ',e16.6)") valcible
            write(*,*) "--------------------------------------------------------"
            write(*,*) "                        ITERATIONS"
            write(*,*) "--------------------------------------------------------"

            ! initialisation
            compteur = 0
            delta    = 1.d0
            xold     = racine
            write(*, '(i4,4e16.6)') compteur, f(xold), xold, 0., threshold

            ! iterations
            do while (delta > threshold .and. compteur <= 100)

                ! calcul de la fonction en xold
                fdex = f(xold) - valcible

                ! calcul de la derivee numerique
                fprim  = (f(xold + threshold) - f(xold - threshold)) / (2.d0 * threshold) 

                ! application de l'iteration de Newton Raphson
                xnew = xold - fdex / fprim
                delta = abs(xnew - xold)
                compteur = compteur + 1

                ! affichage de la convergence
                write(*, '(i4,4e16.6)') compteur, fdex, xnew, delta, threshold

                ! mise a jour de xstart
                xold = xnew
            end do

            if (delta < threshold) then
                racine = xnew
                write(*, *) '--------------------------------------------------------'
                write(*, *) '                        CONVERGE'
                write(*, *) '--------------------------------------------------------'
                write(*, *) 'A la convergence demandee, une solution est:'
                write(*, "('x = ',e20.10,'    f(x) = ', e20.10)") racine, f(racine)
                write(*, *)
            else
                write(*, *) '--------------------------------------------------------'
                write(*, *) '                      NON CONVERGE'
                write(*, *) '--------------------------------------------------------'
            end if

        end subroutine newtonRaphson

end module rechercheRacine

这是主程序:

program main

    ! contient la subroutine newtonRaphson
    use rechercheRacine

    ! contient la fonction
    use fonction

    implicit none

    double precision :: racine, eps, cible

    ! appel de la subroutine newtonRaphson
    ! sans la valeur cible : cible (defaut = 0)
    ! sans la precision : eps (defaut 1d-10)
    racine = 1.d0
    call newtonRaphson(racine, unefonction)

    ! --------------------------------------------------------

    ! appel de la subroutine newtonRaphson
    ! avec pour cible 10
    racine = 1.d0
    eps    = 1.d-14
    cible  = 10.d0
    call newtonRaphson(racine, unefonction, eps, cible)

    ! --------------------------------------------------------

    ! parametre de la gaussienne
    f_sigma = 2.d0
    f_mu = 5.d0
    ! appel de la subroutine newtonRaphson
    ! passage des arguments sous la forme clef = valeur
    cible = 0.1d0
    racine = 2.d0
    call newtonRaphson(cible = cible, f = gaussienne, racine = racine)

end program main

主程序适用于名为unefonction的函数,但它不适用于gaussienne函数。

以下是错误消息:

Program received signal SIGSEGV: Segmentation fault - invalid memory reference.

Backtrace for this error:
#0  0x7F1B6F5890F7
#1  0x7F1B6F5896D4
#2  0x7F1B6EEEB49F
#3  0x4009D2 in __fonction_MOD_gaussienne at mod_fonction.f90:54
#4  0x40104D in __rechercheracine_MOD_newtonraphson at mod_racine.f90:59
#5  0x4016BA in MAIN__ at main.f90:40
Erreur de segmentation (core dumped)

我认为invalid memory reference是由于程序确实存在,好像存在可选参数sigmu,因此在它们不存在的情况下查找它们。

1 个答案:

答案 0 :(得分:6)

是的,问题是你传递的函数确实需要三个参数而不是一个参数。如果您更改子例程external中的f newtonRaphson声明

double precision, external :: f

到显式界面(描述,你如何真正使用它):

interface
  double precision function f(x)
    double precision, intent(in) :: x
  end function f
end interface

由于参数数量不匹配,您的代码甚至无法编译。

它们是将“参数”传递给从例程f调用的函数newtonRaphson的不同方法:

  • 您可以期望f有两个intent(in)个参数而不是一个:除了值x之外,它还可以采用一个真实数组,它可以是任意大小,可能包含必要的参数。这需要以下界面:

    interface
      double precision function f(x, params)
        double precision, intent(in) :: x
        double precision, intent(in) :: params(:)
      end function f
    end interface
    

    那些不需要参数的函数(如unefonction)不会使用第二个参数的内容,而其他函数(如gaussienne)将从中获取参数。

  • 您可以使newtonRaphson期望具有类型绑定过程的给定可扩展类型(class)返回给定x值的值。然后,您可以创建此类型的abritrary扩展,它可以根据存储为扩展类型中的字段的某些参数来计算给定x值的值。然后程序看起来如下(我剥离了几个部分),但它需要Fortran 2003编译器:

    module rechercheRacine
      implicit none
    
      type, abstract :: calculator
      contains
        procedure(getvalue_iface), deferred :: getvalue
      end type calculator
    
      interface
        double precision function getvalue_iface(self, x)
          import :: calculator
          class(calculator), intent(in) :: self
          double precision, intent(in) :: x
        end function getvalue_iface
      end interface
    
    contains
    
      subroutine newtonRaphson(racine, f, eps, cible)
        double precision, intent(inout)        :: racine
        class(calculator), intent(in)          :: f
        double precision, intent(in), optional :: cible, eps
    
        do while (delta > threshold .and. compteur <= 100)
          fdex = f%getvalue(xold) - valcible
          :
        end do
    
      end subroutine newtonRaphson
    
    end module rechercheRacine
    
    
    module fonction
      use rechercheRacine
      implicit none
    
      type, extends(calculator) :: unefonction
      contains
        procedure :: getvalue => unefonction_getvalue
      end type unefonction
    
      type, extends(calculator) :: gaussienne
        double precision :: mu = 0.0d0, sigma = 1.0d0
      contains
        procedure :: getvalue => gaussienne_getvalue
      end type gaussienne
    
    contains
    
      double precision function unefonction_getvalue(self, x)
        class(unefonction), intent(in) :: self
        double precision, intent(in) :: x
        unefonction_getvalue = (exp(x) - 10.) / (x + 2.)
      end function unefonction_getvalue
    
      double precision function gaussienne_getvalue(self, x)
        class(gaussienne), intent(in) :: self
        double precision, intent(in) :: x
    
        :
        gaussienne_getvalue = norme * exp(-(x - moy)**2 / (2.d0 * self%sigma**2))
    
      end function gaussienne_getvalue
    
    end module fonction
    
    
    program main
      use rechercheRacine
      use fonction
      implicit none
    
      type(unefonction) :: fone
      type(gaussienne) :: fgauss
    
      :
      call newtonRaphson(racine, fone)
      call newtonRaphson(cible = cible, f = fgauss, racine = racine)
    
    end program main