跳转到内容

Fortran/Fortran 过程和函数

来自 Wikibooks,开放世界中的开放书籍

函数和子程序

[编辑 | 编辑源代码]

在大多数程序中,一段代码块通常会在多个地方重复使用。为了最大程度地减少代码重复并便于维护代码,此类代码块应放置在函数或子程序中。Fortran 函数类似于数学函数,它接收一个或多个参数作为输入,并返回单个输出值。Fortran 子程序是一段代码块,它对输入变量执行一些操作,并且作为调用子程序的结果,输入变量被修改。

包含函数调用的表达式

! func1 is a function defined elsewhere.
! It takes an integer as an input and returns another integer as the output.
a = func1(b)

对子程序的调用

! sub1 is a subroutine defined elsewhere.
! sub1 performs some operation on input variables e and f.
call sub1(e, f)
! Now e or f, or both (or neither) may be modified.

许多编程语言不区分函数和子程序(例如 C/C++、Python、Java)。纯函数式编程语言(例如 Haskell)只允许函数,因为子程序在某些情况下可以修改输入变量作为副作用,这会使代码复杂化。

函数比子程序更简单。函数必须返回单个值,并且可以像write语句一样从表达式中调用,在if声明if (function) then中调用,等等。子程序不返回值,但可以通过其参数返回多个值,并且只能用作独立命令(使用关键字call)。

在 Fortran 中,可以使用函数来返回值或值数组。以下程序调用一个函数来计算一个整数的平方和立方的和。

function func(i) result(j)
    integer, intent (in) :: i ! input
    integer              :: j ! output

    j = i**2 + i**3
end function

program main
    implicit none
    integer :: i
    integer :: func

    i = 3
    print *, "sum of the square and cube of", i, "is", func(i)
end program

参数iintent (in)属性意味着i不能在函数内部更改,相反,返回值j具有自动的intent (out)。请注意,func的返回类型需要声明。如果省略了它,一些编译器将无法编译。Open64 将在警告的情况下编译生成的代码,但行为是不确定的。

另一种公式(与 F77 兼容)是

      FUNCTION func_name(a, b)
          INTEGER :: func_name
          INTEGER :: a
          REAL    :: b
          func_name = (2*a)+b
          RETURN
      END FUNCTION
    
      PROGRAM cows
          IMPLICIT NONE
          INTEGER :: func_name
          PRINT *, func_name(2, 1.3)
      END PROGRAM

func_name的返回类型仍然需要声明,如上所示。唯一的区别是func_name内部如何引用func_name的返回类型。在这种情况下,返回变量与函数本身具有相同的名称。

递归函数可以声明,例如下面显示的那样,以便代码可以编译。

recursive function fact(i) result(j)
    integer, intent (in) :: i
    integer :: j
    if (i==1) then
        j = 1
    else
        j = i * fact(i - 1)
    end if
end function fact

子程序

[编辑 | 编辑源代码]

子程序可用于通过其参数返回多个值。它使用call语句调用。这是一个例子。

subroutine square_cube(i, isquare, icube)
    integer, intent (in)  :: i              ! input
    integer, intent (out) :: isquare, icube ! output

    isquare = i**2
    icube   = i**3
end subroutine

program main
    implicit none
    external square_cube ! external subroutine
    integer :: isq, icub

    call square_cube(4, isq, icub)
    print *, "i,i^2,i^3=", 4, isq, icub
end program

在声明需要传入或传出的函数和子程序内的变量时,可以添加意图。默认情况下,没有意图检查 - 这可能会导致编译器无法检测到错误编码。

intent (in) - 可以在过程中使用虚拟参数的值,但不能修改。

intent (out) - 可以在过程中设置虚拟参数,然后修改它,并将值返回给调用方。

intent (inout) - 可以在过程中使用和修改虚拟参数的初始值,然后将其返回给调用方。

关于函数与子程序的更多信息

[编辑 | 编辑源代码]

不同的函数结果定义

[编辑 | 编辑源代码]

函数可以以不同的形式定义其结果的数据类型:作为单独的变量或通过函数名称。

参见下面的示例

function f1(i) result (j)
  !! result's variable:  separately specified
  !! result's data type: separately specified
  integer, intent (in) :: i
  integer              :: j
  j = i + 1
end function

integer function f2(i) result (j)
  !! result's variable:  separately specified
  !! result's data type: by prefix
  integer, intent (in) :: i
  j = i + 2
end function

integer function f3(i)
  !! result's variable:  by function name
  !! result's data type: by prefix
  integer, intent(in) :: i
  f3 = i + 3
end function

function f4(i)
  !! result's variable:  by function name
  !! result's data type: separately specified
  integer, intent (in) :: i
  integer              :: f4
  f4 = i + 4
end function

program main
  implicit none
  integer :: f1, f2, f3, f4

  print *, 'f1(0)', f1(0) ! output: 1
  print *, 'f2(0)', f2(0) ! output: 2
  print *, 'f3(0)', f3(0) ! output: 3
  print *, 'f4(0)', f4(0) ! output: 4
end program

过程必须通过模块use或通过将它们指定为external过程来包含。external只提供一个隐式接口,该接口不如显式接口好,因为编译器不知道参数的数量,也不知道它们的类型。因此,它在编译时不能产生警告(与从模块use提供的显式接口形成对比,参见 Fortran/OOP in Fortran)。

subroutine square_cube(i, isquare, icube)
    integer, intent (in)  :: i              ! input
    integer, intent (out) :: isquare, icube ! output

    isquare = i**2
    icube   = i**3
end subroutine

integer function pow4(i)
    integer, intent (in) :: i

    pow4 = i**4
end function

program main
    implicit none
    external square_cube    ! external subroutine (only implicit interface)
    integer :: pow4         ! external function (only implicit interface)
    integer :: i, isq, icub

    i = 5
    call square_cube(i, isq, icub)
    print '(A,4I5)', "i,i^2,i^3,i^4=", i, isq, icub, pow4(i)
end program

纯过程

[编辑 | 编辑源代码]

函数和子程序都可以修改其输入变量。子程序必须修改输入变量,因为它们不返回值。函数不必修改,但默认情况下允许修改输入变量。函数可以使用所有输入变量的intent属性转换为纯函数,该属性没有任何副作用,并且通过关键字pure进一步加强。pure关键字施加了额外的限制,这些限制基本上阻止了函数具有任何副作用

pure函数的示例。

pure real function square(x)
    real, intent (in) :: x

    square = x*x
end function

program main
    real :: a, b, square

    a = 2.0
    b = square(a)
    ! After invoking the square(.) pure function, we can be sure that
    ! besides assigning the output value of square(a) to b,
    ! nothing else has been changed.
end program

关键字参数

[编辑 | 编辑源代码]

如果通过其虚拟名称指定输入参数,则可以使用任何顺序。只要调用过程具有目标过程的接口块(如果通过module使用模块来包含函数,则会自动创建该块),就可以做到这一点。

还有一种混合方法,其中一些参数通过位置指定,其余参数通过其虚拟名称指定。

给出一个例子

real function adder(a,b,c,d)
    real, intent (in) :: a, b, c, d
    adder = a+b+c+d
end function

program main
    interface
        real function adder(a,b,c,d)
            real, intent (in) :: a, b, c, d
        end function
    end interface

    print *, adder(d=1.0, b=2.0, c=1.0, a=1.0)  ! specify each parameter by dummy name
    print *, adder(1.0, d=1.0, b=2.0, c=1.0)    ! specify some parameters by dummy names, other by position
end program

可选参数

[编辑 | 编辑源代码]

参数可以设置为optional。可以使用内在函数present检查是否设置了特定参数。

下面给出一个例子。

real function tester(a)
    real, intent (in), optional :: a
    if (present(a)) then
        tester = a
    else
        tester = 0.0
    end if
end function 

program main
    interface
        real function tester(a)
            real, intent (in), optional :: a
        end function 
    end interface

    print *, "[no args] tester()   :", tester()    ! yields: 0.0
    print *, "[   args] tester(1.0):", tester(1.0) ! yields: 1.0
end program

接口块

[编辑 | 编辑源代码]

如果一个过程有另一个过程作为哑参数,则需要指定它的类型,就像其他参数的类型一样。interface 块用于这种情况。它由包含其参数定义的过程语句组成。

注意,每个接口块都有自己的作用域。因此,如果需要访问外部值,则需要显式加载它们。这可以通过importuse 语句实现。

下面给出一个例子。

function tester(a)
    real, intent (in) :: a
    real :: tester

    tester = 2*a + 3
end function tester

program main
    interface
        function tester(a)
            real, intent (in) :: a
            real :: tester
        end function tester
    end interface

    print *, "tester(1.0):", tester(1.0) ! yields: 5.0
end program main

保存属性

[编辑 | 编辑源代码]

通过显式给出save 属性,可以在过程调用之间保存变量的值。

下面给出一个例子。

subroutine f()
    implicit none
    integer, save :: i = 0

    i = i + 1
    print *, "value i:", i
end

program main
    implicit none
    interface
        subroutine f()
            integer, save :: i = 0
        end
    end interface

    call f()  ! yields: 1
    call f()  ! yields: 2
    call f()  ! yields: 3
end program main

可以为不同的输入参数创建具有相同名称的泛型函数,类似于abs 函数,它适用于整数、实数和复数数据类型。

以下示例说明如何创建添加两个整数或字符字符串的函数add

module add_mod
    implicit none
    private
    public :: add

    interface add
        procedure add_int, add_char
    end interface add
contains
    pure function add_int( x, y )
        integer, intent (in) :: x, y
        integer :: add_int

        add_int = x+y
    end function add_int

    pure function add_char( x, y )
        character (len=*), intent (in) :: x, y
        character (len=len(x)+len(y)), allocatable :: add_char

        add_char = x // y
    end function add_char
end module add_mod

program main
  use add_mod
  implicit none

  print *, "add ints: ", add( 1, 2 )
  print *, "add chars: ", add("abc", "def")
end program main

可以将抽象类型的类型绑定过程设置为deferred,以便需要在派生类型中重新实现它。有关更多信息,请参阅有关抽象类型的部分。

可以创建操作任意维度的参数的过程。elemental 关键字用于定义对单个对象(例如整数)的操作,并且自动处理一般情况。

给出了任意长整数维度的加法示例。

pure elemental function add_int(x, y)
    integer, intent (in) :: x, y
    integer :: add_int
    add_int = x + y
end function add_int

program main
    implicit none

    interface
        pure elemental function add_int(x, y)
            integer, intent (in) :: x, y
            integer :: add_int
        end function add_int
  end interface

  print *, "add ints:", add_int(1, 2) ! yields: 3
  print *, "add arrays:", add_int([1, 2], [2, 3]) ! yields: 3   5
end program main
华夏公益教科书