跳转到内容

Fortran/语言扩展

来自维基教科书,自由的教科书

过程重载

[编辑 | 编辑源代码]

与其他几种语言一样,Fortran 90 及更高版本支持根据传递的参数从例程列表中选择适当的例程。此选择在编译时完成,因此不受运行时性能损失的影响。此功能可以通过使用模块和接口块来访问。

在以下示例中,指定了一个模块,该模块包含一个接口函数 f,它可以处理各种类型的参数。

module extension_m
    implicit none
    private
    public f  ! Only the interface f is accessable outside the module.

    interface f  ! The overloaded function is called "f".
          module procedure f_i  ! "f(x)" for integer argument "x" will call "f_i"
          module procedure f_r  ! "f(x)" for real    argument "x" will call "f_r"
          module procedure f_z  ! ...        complex ....                   "f_z"
   end interface
contains
   integer function f_i(x) result (y)
      integer, intent (in) :: x

      y = x**2 - 1
   end function 

   real function f_r(x) result(y)
      real, intent (in) :: x

      y = x**2 - 1.0
   end function 

   complex function f_z(x) result(y)
      complex, intent (in) :: x

      y = x**2 - 1.0
   end function 
end module

现在,使用此模块的程序可以访问单个接口函数 f,该函数接受整数、实数或复数类型的参数。函数的返回类型与输入类型相同。这样,该例程很像 Fortran 标准中定义的许多内在函数。下面给出一个示例程序

program main
    use extension_m
    implicit none
    complex :: xz, yz
    integer :: xi, yi
    real    :: xr, yr

    xi = 2
    xr = 2.0
    xz = 2.0
    yi = f(xi)
    yr = f(xr)
    yz = f(xz)
end program

内在函数

[编辑 | 编辑源代码]

可以扩展内在函数。这类似于重载运算符。

这里我们将通过扩展 sqrt 函数来演示这一点。内在函数没有为整数类型的参数实现。这是因为没有明确的想法如何定义非整数类型的结果(例如 ,但如何定义 )。我们在这里实现了一种方法,其中结果始终是最接近的整数。

module sqrt_int_m
    implicit none
    private
    public sqrt
    ! use intrinsic sqrt for data types which are not overloaded
    intrinsic :: sqrt
    ! extend sqrt for integers
    interface sqrt
        module procedure sqrt_int
    end interface 
contains
  pure integer function sqrt_int(i)
    integer, intent (in) :: i

    sqrt_int = nint(sqrt(real(i)))
  end function 
end module 

program main
    use sqrt_int_m
    implicit none
    integer :: i

    ! sqrt can be called by real and integer arguments
    do i = 1, 7
        print *, "i, sqrt(i), sqrt(real(i))", i, sqrt(i), sqrt(real(i))
    end do
end program

派生数据类型

[编辑 | 编辑源代码]

Fortran 90 及更高版本支持创建新的数据类型,这些数据类型是现有类型的组合。在某些方面,这类似于数组,但组件不必全部是相同类型,并且它们是通过名称而不是索引引用的。这种数据类型必须在该类型变量之前声明,并且声明必须在范围内才能使用。下面给出了一个简单二维向量类型的示例。

type :: vec_t
   real :: x,y
end type

可以像声明其他任何变量一样声明此类型的变量,包括变量特征,如指针或维度。

type (vec_t) :: a,b
type (vec_t), dimension (10) :: vecs

使用派生数据类型,Fortran 语言可以扩展为表示比原始类型表示的更多样化的数据类型。

运算符重载

[编辑 | 编辑源代码]

运算符可以重载,以便派生数据类型支持标准操作,从而有可能扩展 Fortran 语言,使其具有行为类似于本机类型的新类型。

赋值运算符 = 可以重载。我们将通过以下示例来演示这一点。这里,我们定义了如何在左侧逻辑类型和右侧整数之间执行赋值。

module overload_assignment_m
    implicit none
    private
    public assignment (=)

    interface assignment (=)
        module procedure logical_gets_integer
    end interface
contains
    subroutine logical_gets_integer(tf, i)
        logical, intent (out) :: tf
        integer, intent (in)  :: i

        tf = (i == 0)
    end subroutine
end module

program main
    use overload_assignment_m
    implicit none
    logical :: tf

    tf = 0
    print *, "tf=0:", tf  ! Yields: T
    tf = 1
    print *, "tf=1:", tf  ! Yields: F
end program

内在运算符

[编辑 | 编辑源代码]

可以重载内在运算符,如 +,-,*

在以下示例中,我们将重载 * 运算符以用作逻辑 .and.

module overload_asterisk_m
    implicit none
    private
    public operator (*)

    interface operator (*)
        module procedure logical_and
    end interface
contains
    pure logical function logical_and(log1, log2)
        logical, intent (in) :: log1, log2

        logical_and = (log1 .and. log2)
    end function
end module

program main
  use overload_asterisk_m
  implicit none
  logical, parameter :: T = .true., F = .false.

  print *, "T*T:", T*T  ! Yields: T
  print *, "T*F:", T*F  ! Yields: F
  print *, "F*T:", F*T  ! Yields: F
  print *, "F*F:", F*F  ! Yields: F
end program

新运算符

[编辑 | 编辑源代码]

可以创建新的自创建运算符。

我们通过以下示例来演示这一点:我们创建了一个一元运算符 .even. <int>,它在给定的 integer 为偶数时输出一个 logical,以及一个二元运算符 <reals> .cross. <reals>,它执行两个 real 向量的标准叉积。

module new_operators_m
    implicit none
    private
    public operator (.even.)
    public operator (.cross.)

    interface operator (.even.)
        module procedure check_even
    end interface

    interface operator (.cross.)
        module procedure cross_product
    end interface
contains
    pure logical function check_even(i)
        integer, intent (in) :: i

        check_even = (modulo(i, 2) == 0)
    end function

    function cross_product(x, y) result(z)
        real, intent (in) :: x(3), y(3)
        real :: z(3)

        z(1) = x(2)*y(3) - x(3)*y(2)
        z(2) = x(3)*y(1) - x(1)*y(3)
        z(3) = x(1)*y(2) - x(2)*y(1)
    end function
end module

program main
    use new_operators_m
    implicit none
    integer :: i
    real    :: x(3), y(3)

    do i = 1, 6
        print *, "i:", i, "even?", .even. i
    end do
    print *

    x = [ 1, 2,  3]
    y = [-1, 2, -3]
    print *, 'x', x
    print *, 'y', y
    print *, 'x cross_product y', x .cross. y
end program
华夏公益教科书