Fortran知识点

来源:互联网 发布:数据挖掘技术有哪些 编辑:程序博客网 时间:2024/05/21 01:43

       Fortran中子函数所带参数叫虚参,如subroutine sub(da)  da叫做虚参,而对应的主调函数call sub(aa)   aa叫做实参,在此直说明一下让人费解的假定大小数组说明符。

       所谓的假定大小数组说明符是指:在子程序中,可以使用“*”号作为虚参数组的数组说明符中最后一个维定义符的上届。它的作用是所定义的虚数组的大小和与之对应的实参数组大小完全相同,也就是说,当子程序未被调用时,虚数组的大小是假定的,假定与所对应的实参数组大小相同。

       注:这种假定大小数组说明符只能在子程序中对虚参数组使用。

interface 功能详解

本文详细介绍了interface用作接口界面块、函数重载和操作符重载时的用法。
接口界面块:Interface 功能详解
说明:子程序可看做无返回值的函数,为了方便叙述,如未特别说明,文中将函数(function)和子程序(subroutine)统称为函数。

函数的接口信息用于告诉编译器应该如何正确调用该函数,它包括参数和返回值的数量、类型等信息。因此每个函数都必须具有相应的接口信息,缺省情况具有隐式声明,而使用interface则可显式声明函数的接口信息。

Interface 的主要功能:
1、明确外部函数(external procedure) 或虚函数(dummy procedure)的接口信息,包括:输入输出参数的类型和属性、返回值类型、函数属性;
2、定义通用过程名,即函数重载(overload);
3、操作符(+,-,*,/,et al)和赋值符(=)重载。
 
下面将分别对以上三种功能进行说明。
 
1、接口界面块

    内部函数(contains)、模块(module)中的函数,以及Fortran标准函数 (如:sind、abs等) 均自动包含显式接口,不需要也不能再次声明接口信息,因此上述情况不在讨论之中。我们建议将外部函数封装在module中使用。
    外部函数缺省具有隐式接口,对一些常规函数,用户不必显示声明其接口信息,编译器也能正确识别。但当外部函数的形参具有ALLOCATABLE, ASYNCHRONOUS, OPTIONAL, POINTER, TARGET, VALUE, VOLATILE属性时,必须显示声明接口信息。下列情况之一也必须使用接口界面块:

● 外部函数返回值为指针、数组或可变长度字符串;
● 形参为数组片段;
● 参数个数不确定或包含可选参数;
● 改变参数传递顺序;
● 子程序中扩展了赋值号的使用范围。
 
    接口界面块的使用较为简单,在接口界面块(interface-end interface)之间写入函数形参类型说明语句,即去掉所有可执行语句后的剩余部分。下面的例子给出了函数返回数组时以及具有可选参数时使用interface的例子:
01program Fcode_cn
02  integer::i=0, j=1, k=2, m(2)
03   
04  interface  ! 接口块
05    subroutine sub1(i,j,k)
06      integer,optional::k
07      integer i,j
08    end subroutine
09     
10    function func1(j,k)
11      integer j, k
12      integer func1(2)
13    end function
14  end interface
15  
16  = func1(j,k)
17  print*, m  ! m=3,-1
18   
19  call sub1(i,j,k)
20  print*, i  ! i=3
21   
22  call sub1(i,j)
23  print*, i  ! i=1
24   
25  pause
26end
27  
28function func1(j,k)
29  integer j, k
30  integer func1(2)
31  func1(1) = + k
32  func1(2) = - k
33end function
34  
35subroutine sub1(i,j,k)
36  integer,optional::k
37  integer i,j
38  if( present(k) ) then
39    = + k
40  else
41    = j
42  end if
43end subroutine

2、函数重载

某些情况下,我们需要对不同类型或不同数量的参数做相似或相同的操作,由于参数类型、数量不同,我们需要编写不同的函数来处理。比如求绝对值,如果参数是4字节整数,我们需要调用iabs函数;如果参数是4字节或8字节实数,我们需要分别调用abs或dabs函数。

由于需要记住多个功能相同或相近的函数,增加了我们使用这些函数的难度,同时也增加了出错的可能性,比如将实数传递给iabs函数。

上述函数的功能相同,只是参数类型或个数不同,那么可否使用同一个函数名来执行它们呢?当然可以,这就是函数重载。函数重载允许通过调用通用过程名来执行特定函数。当用户调用通用过程名时,编译器首先检查传入参数的类型和数量,再调用与之匹配(类型和数量相同)的特定函数来执行具体任务。

例如我们建立通用过程名abs来求绝对值,用户在任何情况都只需调用abs,编译器会自动选用合适的特定函数执行对应操作:当传入参数是4字节整数,就调用iabs函数;如果是8字节实数,则调用dabs函数。

下面用求绝对值的例子说明函数重载功能。为与Fortran内在函数abs区别开来,我们在函数名后添加“_f”。
01module abs_module
02  implicit none
03  interface abs_f
04  module procedure abs_f, dabs_f, iabs_f
05  end interface
06contains
07function abs_f(x)
08real(4) abs_f, x
09if( x<0.0_4 ) then
10  abs_f = -x
11else
12  abs_f = x
13end if
14end function abs_f
15  
16function dabs_f(x)
17real(8) dabs_f, x
18if( x<0.0_8 ) then
19  dabs_f = -x
20else
21  dabs_f = x
22end if
23end function dabs_f
24  
25function iabs_f(x)
26integer(4) iabs_f, x
27if( x<0 ) then
28  iabs_f = -x
29else
30  iabs_f = x
31end if
32end function iabs_f
33end module abs_module
34  
35program Fcode_cn
36use abs_module
37real(4):: x=-2.0_4
38real(8):: y=-3.0_8
39integer(4)::z=-4
40  
41print*, abs_f(x)
42print*, dabs_f(y), abs_f(y)
43print*, iabs_f(z), abs_f(z)
44  
45pause
46end
 
执行结果:
2.000000
3.00000000000000  3.00000000000000
4  4
 
    代码分析:我们使用interface创建了一个函数重载,其通用过程名(interface之后的标识符)为abs_f,包含三个特定过程abs_f, dabs_f, iabs_f。需要注意,通用过程名可以与其中一个特定过程名一致,也可以不一致;但特定过程名之间必须不同。每个特定过程的形参类型和数量不能完全一致。

  interface abs_f
     module procedure abs_f, dabs_f, iabs_f
  end interface

在程序执行过程中,调用通用过程名时,编译器首先检查实参的类型和数量,并与特定过程的接口信息相匹配。如果匹配成功,则调用相应特定函数(故dabs_f(y)和abs_f(y)的结果是一致的);否则编译器会报错。

比如调用 abs_f(12_8), 由于我们没有给出针对8字节整数求绝对值的特定函数,编译器则会报错。
 
3、操作符和赋值符(=)重载

常规运算中,我们经常用到算术操作符(+,-,*,/,**)、关系操作符(<,<=,>,>=,==,/=)以及赋值符(=),同时也会发现其使用具有一定的局限性:只能对特定的数据类型进行运算,不能直接用于派生数据类型。

比如两个时间相减,我们不能直接使用“-”进行操作,而需要编写特定的函数。针对这一问题,Fortran90引入了操作符重载功能。我们先看下面一段代码:

01module time_class
02  implicit none
03  type time  !定义一个时间类结构体
04    integer(1) hour, minute, second
05  end type
06  interface operator(-) !重载操作符-
07    module procedure timeMinus
08  end interface
09  interface assignment(=) !重载赋值符=
10    module procedure assign_time
11  end interface
12  interface operator(.minus.) !自定义操作符.minus.
13    module procedure timeMinus
14  end interface
15contains
16! 两个时间相减
17function timeMinus(time1,time2)
18type(time),intent(in)::time1, time2
19type(time) timeminus
20integer n
21n=(time1.hour-time2.hour)*3600 + (time1.minute-time2.minute)*60 +(time1.second-time2.second)
22if(n<0) n=n+3600*24
23timeminus.second=mod(n,60)
24n=n/60
25timeminus.minute=mod(n,60)
26timeminus.hour=n/60
27end function
28! 如果time类数据正确,将其赋值给res;否则输出错误提示
29subroutine assign_time(res,time1)
30type(time),intent(in):: time1
31type(time),intent(out):: res
32if(time1.hour>=0 .and. time1.hour<=23) then
33  if(time1.minute>=0 .and. time1.minute<=59) then
34    if(time1.second>=0 .and. time1.second<=59) then
35      res.hour=time1.hour
36      res.minute=time1.minute
37      res.second=time1.second
38      return
39    end if
40  end if
41end if
42write(*,*) "time类数据错误."
43end subroutine
44end module
45  
46program fcode_cn
47  use time_class
48  type(time) time1,time2,time3
49  time1=time(2,3,58)
50  time2=time(23,12,7)
51  time3=time2-time1 !重载操作符-
52  print*,time3
53  time2=time3 .minus. time1 !自定义操作符.minus.
54  print*,time2
55  time1=time(25,5,6) !重载赋值符=
56pause
57end
 
模块time_class中定义了一个时间类结构体以及两个函数,函数timeMinus用于操作符“-”重载和自定义操作符,子程序assign_time用于赋值符“=”重载。注意,操作符重载只能使用function,而赋值符重载只能使用subroutine,自定义操作符需位于两个dot之间。

程序一开始使用赋值符重载功能对time1和time2进行赋值,由于数据无误,正常执行;
接下来,分别使用重载操作符“-”和自定义操作符“.minus.”进行两个时间相减,得到相应结果;
最后对time1赋值时,由于数据有误,输出错误提示。

执行结果:

21  8  9
19  4  11


Time类数据错误.
 

0 0
原创粉丝点击