6

我正在做一个需要在 Fortran 中实现一些数值方法的项目。为此,我需要编写一些递归函数。这是我的代码。

!     
! File:   main.F95
!

RECURSIVE FUNCTION integrate(n) RESULT(rv)
    IMPLICIT NONE
    DOUBLE PRECISION :: rv
    INTEGER, INTENT(IN) :: n
    DOUBLE PRECISION, PARAMETER :: minusone = -1.0
    IF (n == 1) THEN
        rv = 10 !exp(minusone)
        RETURN
    ELSE
        rv = 1 - (n * integrate(n - 1))
        RETURN
    END IF
END FUNCTION integrate

RECURSIVE FUNCTION factorial(n) RESULT(res)
    INTEGER res, n
    IF (n .EQ. 0) THEN
        res = 1
    ELSE
        res = n * factorial(n - 1)
    END IF
END

PROGRAM main
    DOUBLE PRECISION :: rv1
    PRINT *, factorial(5)
    PRINT *, integrate(2)

    !READ *, rv1

END PROGRAM main

对于这个程序,输出是:

         NaN
       1

如果我更改打印语句的顺序(第 30 和 31 行),输出将是:

         1
-19.000000

输出应该是(对于原始打印语句顺序):

  120  
  -19 

我从 Wikipedia Fortran 95 语言功能页面中获取了阶乘函数。

  • 编译器:带有 Cygwin 的 gfortran 4.5.3
  • IDE:Netbeans 7.0.1
  • 平台:Windows 7
4

3 回答 3

10

正如其中一条评论所提到的,更好的解决方案是将您的子例程和函数放入一个模块中,然后从您的主程序中使用该模块。这将使调用者知道这些过程的接口——在 Fortran 术语中是“显式”的。编译器不仅会正确处理函数的类型,它还将能够检查调用中的参数和被调用者中的参数(“虚拟参数”)之间的类型一致性以确保一致性。

如果您使用尽可能多的调试选项,编译器将帮助您发现错误。使用 gfortran,尝试: -O2 -fimplicit-none -Wall -Wline-truncation -Wcharacter-truncation -Wsurprising -Waliasing -Wimplicit-interface -Wunused-parameter -fwhole-file -fcheck=all -std=f2008 -pedantic -fbacktrace

module factorial_procs

   IMPLICIT NONE

contains

   RECURSIVE FUNCTION integrate(n) RESULT(rv)
       DOUBLE PRECISION :: rv
       INTEGER, INTENT(IN) :: n

       IF (n == 1) THEN
           rv = 10
           RETURN
       ELSE
           rv = 1 - (n * integrate(n - 1))
           RETURN
       END IF
   END FUNCTION integrate

   RECURSIVE FUNCTION factorial(n) RESULT(res)
       INTEGER res, n
       IF (n .EQ. 0) THEN
           res = 1
       ELSE
           res = n * factorial(n - 1)
       END IF
   END

end module factorial_procs

PROGRAM main

    use factorial_procs

    implicit none

    PRINT *, factorial(5)
    PRINT *, integrate(2)

END PROGRAM main

您可能会发现您只能通过使用常规整数的直接乘法来计算非常小的整数的阶乘。一种解决方法是使用更大的整数类型,例如,

integer, parameter :: BigInt_K = selected_int_kind (18)

就像您可以现代化并使用 selected_real_kind 而不是双精度一样。

于 2012-08-05T16:56:01.350 回答
8

您的函数编写正确。问题出在主程序中,您没有显式声明integratefactorial函数的类型,因此您有隐式类型,在这种情况下factorial是假定的REAL并且integrate是假定的INTEGER。出于某种原因,您的编译器没有警告您类型不匹配。我的做了:

$ gfortran recurs.f90 
recurs.f90:26.22:

    PRINT *, integrate(2)
                      1
Error: Return type mismatch of function 'integrate' at (1) (INTEGER(4)/REAL(8))
recurs.f90:27.22:

    PRINT *, factorial(5)
                      1
Error: Return type mismatch of function 'factorial' at (1) (REAL(4)/INTEGER(4))

您应该将主程序更改为:

PROGRAM main
    IMPLICIT NONE
    DOUBLE PRECISION, EXTERNAL :: integrate
    INTEGER, EXTERNAL :: factorial
    PRINT *, factorial(5)
    PRINT *, integrate(2)
END PROGRAM main

注意IMPLICIT NONE线。此声明语句将禁用任何隐式类型,如果不是所有变量和函数都被显式声明,编译器将抛出错误。这是每个 Fortran 程序中非常重要的一行,如果你拥有它,你就会自己找出问题所在,因为它会迫使你在程序中显式声明所有内容。

现在的输出是:

         120
  -19.0000000000000     

正如预期的那样。

附带说明一下,DOUBLE PRECISION类型声明不如使用指定REALKIND参数灵活,例如REAL(KIND=myRealKind). 请参阅有关如何KIND正确使用此问题的答案:Fortran 90 kind parameter

于 2012-08-05T12:48:31.047 回答
0
I would like to highlight some points while using RECURSIVE functions or non recursive function. 

1. 确保函数与调用程序有显式接口。这可以通过将函数放入模块和 USE 关联来实现。这在上述答案中进行了解释。2.您可以使用INTERFACE与主调用程序建立显式接口,这在您有很少数量的具有隐式接口的函数时很有用,例如您的情况。示例如下。

PROGRAM main
IMPLICIT NONE
  INTERFACE 
   FUNCTION factorial(n)
     INTEGER:: factorial 
     INTEGER, INTENT(IN):: n
   END FUNCTION factorial

  FUNCTION integrate(n)
    DOUBLE PRECISION:: integrate
    INTEGER, INTENT(IN):: n
  END FUNCTION integrate
END INTERFACE 

PRINT *, factorial(5) 
PRINT *, integrate(2)

END PROGRAM main

请注意,定义 kind 参数然后使用 KIND 子句总是更好!@milancurcic解释

还有另一种非常简单的方法可以解决您的问题:只需在主程序中定义阶乘和迭代,如下所示,您就可以开始了

PROGRAM main
IMPLICIT NONE
    DOUBLE PRECISION :: integrate
    INTEGER:: factorial
    PRINT *, factorial(5)
    PRINT *, integrate(2)

END PROGRAM main 
于 2016-02-15T11:48:11.077 回答