彭國倫Fortran95學習筆記(一)第八章至第十六章

基本讀了一遍,把剩下的總結完。

第八章 函數

基本的調用不解釋了,這裏強調一些細節。

  1. 可以在子函數最後使用end表示函數的結束,不過實際上使用retrun是標準的用法,並且可以選擇讓函數在執行到一般的時候return返回。
  2. Fortran77不支持遞歸,但是Fortran90支持,不過遞歸的調用需要額外的命令,由於遞歸調用的計算速度很慢,科學計算中並不推薦使用,參考Page195
  3. Fortran中的傳參均爲傳址調用(by reference),subroutinefunction均是傳址,但是一般不建議function中改變傳參的值。如果所傳的參數值發生改變,那麼主程序中的變量值也會變化,這點和matlab或者C中均不同。
  4. subroutinefunction的區別,後者有返回值,另外function調用之前要先聲明real,external :: add。注意這裏的externcal不是C語言中防止重複編譯的關鍵字,是聲明add是一個函數而不是變量。另外函數聲明也可以不用單獨寫出,例如下面
program ex0808
 implicit none
 real a,b
 real add
 add(a,b) = a+b
 write(*,*) add(a,3.0)
end 

全局變量common

基本的用法不講了,這裏強調一下全局變量是根據相對位置關係對應的,一個common中寫入多個變量時,一定要注意順序

int a,b,c
common /abc/ a,b,c

common的賦初值不能直接像數組一樣使用DATA,而是要用block data,具體用法如下,類似subroutine一樣寫在程序之外單獨一塊:

program ex0812
...
end

block data
implicit none
  integer a,b,c
  common /abc/ a,b,c
  data a,b,c /1,2,3/
end block data

實際上在程序中用一般的賦值方式對common中的變量賦值就可以進行初始化,這一用法的用意除了標準化之外,執行順序也不同。block data中聲明的初值,在主程序調用之前就已經賦值了。所以這也要求程序中只能有聲明和賦初值相關,不能出現程序命令,而且附加parameter在這裏賦初值也不支持。

函數中的變量

實際上也屬於函數本身的注意事項,但是和變量特別相關。

  1. 函數所傳的參數類型一定要對應,否則可能出現float類型的變量被以int類型的編碼讀取,數據是錯誤的。
  2. 如果所傳參數爲數組,則傳輸的是數組的第一個元素的地址。例如傳遞一個4個元素的一維數組a時,子函數中的變量定義可以是integer ainteger a(3)integer a(4)integer a(2,2)。甚至子函數中數組長度超過主程序中,語法都是允許的,數據讀取時是根據連續內存讀取的,所以並不影響使用。但是編譯在這裏不進行長度校對,如果編程人員不小心,就會出現很多數組操作改變其他變量值的問題,即下標溢出(實操吃過很多次虧,這個一定要小心)

變量的生存週期

即一般子函數中變量的生存週期,只維持到當前子函數執行結束。如果希望像C中靜態變量一樣,子函數退出後,變量中的值保留,就需要使用關鍵字save

integer :: cout = 1
save cout

而不使用save關鍵字的子函數,在有的編譯器中,變量也不會在每次執行後清零。

傳遞函數

即子函數傳參時,傳遞的參數中含有子函數名,這樣的子函數,需要在主程序中聲明,例如

real,external :: func !聲明func是一個自定義函數
real,intrinsic :: sin !聲明sin是庫函數

call ExecFunc(func)

特殊參數的使用方法

兩種技巧,

  • 設置賦值參數屬性,設置某些參數是隻讀不能改變的
integer,intent(in) :: a   !子函數中值不允許改變
integer,intent(out):: b   !子函數中值必須改變

這種技巧可以幫助我們進行語法檢查,減少調試時的工作量

  • 不指定個數的參數傳遞,這個並不對應重載,而是C語言裏面傳參的默認值,這裏要用到option這個關鍵字
subroutine sub(a,b)
...
integer :: a
integer,optional :: b
if (present(b)) then
   ....
else
  ...
end if
return
end

而其中的present(b)表示是否傳入了參數b。從而實現不定個數傳遞

函數接口interface其實是命令語句之前的聲明語句,在特殊返回值或者調用參數不固定時會被要求使用,具體參閱書中Page189,這裏只給出一個例子:

program ex0825
implicit none
  interface 
    function reandom10(lbound,ubound)
    implicit none
    real :: lbound,ubound
    real :: reandom10(10) !函數的返回值爲數組
    end function
  end interface
  ...
end

內部函數,Pure函數,elemental函數

第一個爲某個子函數爲另外一個子函數專屬調用時,可以使用contain直接寫在函數體中。後面兩個爲並行使用。

Module

可以用來封裝程序模塊,功能和include有點像,但是並不是純粹的添加代碼段,而是具有一定的語法。主要用途有聲明全局變量,聲明type結構體,聲明函數等。在後面第11章會有詳細的介紹。

第九章 文件

writeread的使用不重複了,這裏強調後面出現的新知識。

inquire(file=filename,exist=alive)

Page237,可以檢查一個文件是否存在

open(unit = fileld, file=filename, access="direct", &
   form="formatted",recl=6,status="old")

Page254,使用direct模式讀取文件,允許在文件中的讀取位置進行跳躍,而非按順序讀取。

open(unit = fileld, file=filename, access="direct", form="unformatted",&
      access="direct",recl=1,status="replace")

Page258,讀取二進制文件時,通過recl=1設置讀取字段的單位長度,1代表爲1bytes還是4bytes要看不同的編譯器。

write(unit=string,fmt="(I2,'+',I2,'=',I2)") a,b,a+b
write(*,*) string

Page260,成爲internal file(內部文件),將字符串生成並賦值給字符串變量,然後字符串可以正常輸出。

integer :: a=1,b=2,c=3
namelist /na/ a,b,c
write(*,nml=na)

可以將na名下的所有變量按順序輸出,不用使用do循環。

第十章 指針

和C一樣的指針的概念,但是具體的語法約定有很多的不同,先給出最基礎的使用方式
方式一

integer, target:: a=1 !聲明一個可以當成目標的變量
integer,pointer:: p  !聲明一個可以指向整數的指針
p=>a !將指針指向變量a的地址
a=2  !改變變量的值
p=3 !改變地址的值,注意,是值,不是地址

方式二

integer,pointer :: p
allocate(p)
p=100

需要注意的就是fortran的指針變量賦值時,也是改變指向的地址的值,這點和C不同。另外就是使用動態數組記得使用deallocate(p)釋放內存。爲了方便使用,還有三個工具函數。

  • associated(pointer,[target]) 檢查指針是否設置指向
  • p=>null()讓指針指向一個空地址
  • nullify(pointer1,[pointer2,…])功能同上,fortran90以上支持

指針數組

這個蠻厲害的,感覺比C要強大

integer,target :: b(5)=(/1,2,3,4,5/)
integer,pointer :: a(:)

a=>b !數組b的所有值都給a
a=>b(1:3) !只把數組b的前三個元素給a
a=>b(1:5:2) !把第1,3,5個元素給a

或者使用動態數組

integer,pointer :: a(:)
allocate(a(5))
a=(/1,2,3,4,5/)
write(*,*) a
deallocate(a)

但注意allocate聲明成指針數組時,生存週期不止在當前函數結束,一定要deallocate釋放內存。

指針與數組

指針可以是數組的參數或者返回值,但是需要使用接口interface。另外指針參數聲明時不需要intent關鍵字。給一個例子Page282

program ex1007
  ...
  integer,pointer :: p(:)
  interface 
    function getmin(p)
      integer,pointer :: p(:)
      integer,pointer :: getmin
    end function
  end interface
  ...
  getmin(p)
end 

指針的基本應用

指針在fortran中可以方便數組中固定元素的訪址,以及較大數據的交換給出例子分別如下

b=>a(5,5,5)
a(5,5,5) = 1 !常規寫法,但是找到這個元素需要經過地址計算
b=1   !速度更快
type person
...
end type

type(person):: a,b,temp
type(person),pointer :: pa,pb,pt
!正常交換交換的數據較多
temp = a
a = b
b = temp
!指針長只有4bytes,交換指針要比交換數據本身更快
!但是注意此時a,b本身是否交換了需要再驗證一下
pt=>pa
pa=>pb
pb=>pt

指針的高級應用

其實還是蠻基礎的,就是數據結構常見的內容,鏈表,雙向鏈表和環形鏈表。

第十一章 Module及面向對象

基礎的用法給出一個,

module bank
  implicit none
  private money
  public LoadMoney,SaveMoney,Report
  integer::money = 1000000
  contains
    subroutine LoadMoney(num)
    ...
    end subroutine
  ...

program main
use bank
...

和C語言中的類不同,不需要實例,use module之後直接調用裏面的函數就可以了,這一點其實更加直觀。而關鍵字private表示只有module內部可以使用,public表示主程序中也可以調用,並且關鍵字對函數和變量都可以使用。另外module中可以通過contain添加函數。module中的變量默認的生存週期爲整個函數,所以money變量在每次調用之後值改變但不會清零。

另外強調的一點是,module中函數之外聲明的變量,函數本身可以使用。即money變量可以在子函數LoadMoney中使用,這不需要傳參。這和C語言中的類又很像。

use的使用細節

直接給實例

program main
  use A, aa=>va !把module A中的變量aa改名爲va使用
  use B, only:vc !只用module B中的變量vc
  use C, only:bb=>bc !前兩者的組合

另外,module是支持嵌套的,這和類的繼承思想類似,但private聲明的部分不會被繼承。

再論interface接口

除去前面函數的參數或者返回值比較特殊時需要使用interface之外,該命令還可以被用來實現重載(overload,2333骨王)和自定義操作符,直接給出兩者例子

module MA
interface show
   module procedure show_int
   module procedure show_character
end interface   
contains
   !兩個子函數的具體實現
   ...   
end module

program main
...
call show(1)
call show("abc")
...
end
module MA
  ...
  interface operator(+)
    module procedure add
  end interface
contain
   integer function add(a,b)
   ...
   end function
end module

program main
...
type(ta) a,b,c
c = a+b
...
end

就是說自己定義了結構體情形下+號代表的意義,就可以很方便的使用了,另外這個操作符可以是任意符號,不一定非得是當前存在的。

第十二章 編輯器的高級使用

首先講了一下Visual Fortran如何進行調試,這部分略過。後半部分講了優化,這是需要留意的知識。提升執行速度書中羅列了幾種,避免重複運算等算法相關的優化這裏不提。

  • 表達式的選擇,加法比乘法快,乘法比乘冪快
2*A=A+A
A**2=A*A
X**2+2*X+3 = ((X+2)*X)+3
  • 整數與浮點數運算的選擇,整數運算比浮點數快,整數浮點的純運算比整數浮點混起來快
I = 1+1
F= 1.0+1.0

E = (real(a)/c)*(real(b)/d)
E = real(a*b)/(c*d)
  • 訪問速度,常量數字以及parameter訪存比一般的變量要快,數組訪存需要經過額外的運算
integer,parameter :: d = 2.0
c = 2.0
a = b/2.0
a = b/c !這個最慢
a = b/d
  • 利用cache,提高cache訪存預測的命中率,需要使用儘可能連續的內存空間,do循環數組時從第一個下標開始向後,C語言則反過來
do k = 1,kmax
do j = 1,jmax
do i = 1,imax
    s = s+ A(i,j,k)
end do
end do
end do
  • 減少程序代碼的跳轉、轉向,就是儘量少用條件和循環語句。

最後說下,提高運算速度是好事,但是可讀性有時候更加重要,佔主要運算時間的語句進行執行速度優化即可。

與其他語言的鏈接

思想就是將代碼中添加註釋,並且要調整函數使兩者的函數編碼等匹配。不過書中主要是將win下如何混編介紹了,linux下未講。不太用,寫了也記不住,真的需要的時候回來補充。

第十三章 計算機繪圖

跳過,作圖用MATLAB它不香嗎,Tecplot它不香嗎,ParaView它不香嗎。

第十四章 數值方法

跳過,參考數學系大一必修的計算方法課程,或者一般研究生學的數值方法,要比這裏詳細。

第十五章 算法與數據結構

跳過,C語言版本的學過,Fortran版本的現在真的有地方會用嘛…

第十六章 IMSL函數庫

新知識,IMSL是一個module模板,如果當前編譯器內置的話,直接在main函數中use即可。它包含幾個模塊

  • 線性代數,矩陣的相乘轉置求逆等
  • 矩陣函數,各種分解,求行列式,秩
  • 求解線性系統,lin_sol_gen(A,B,X)求A*X=B,但是具體算法未給出
  • 求解非線性方程
  • 求微積分
  • 求常微分方程
  • 插值與曲線近似
  • 最小二乘

結束!!!終於!!!還是有點長的。

最近完成了一個後臺階的算例是從.f文件改寫成.f90了,但是僅僅是能夠運行。接下來花一點時間將程序改寫得更加結構化一些。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章