基本讀了一遍,把剩下的總結完。
第八章 函數
基本的調用不解釋了,這裏強調一些細節。
- 可以在子函數最後使用
end
表示函數的結束,不過實際上使用retrun
是標準的用法,並且可以選擇讓函數在執行到一般的時候return
返回。 - Fortran77不支持遞歸,但是Fortran90支持,不過遞歸的調用需要額外的命令,由於遞歸調用的計算速度很慢,科學計算中並不推薦使用,參考Page195
- Fortran中的傳參均爲傳址調用(by reference),
subroutine
和function
均是傳址,但是一般不建議function
中改變傳參的值。如果所傳的參數值發生改變,那麼主程序中的變量值也會變化,這點和matlab或者C中均不同。 subroutine
和function
的區別,後者有返回值,另外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
在這裏賦初值也不支持。
函數中的變量
實際上也屬於函數本身的注意事項,但是和變量特別相關。
- 函數所傳的參數類型一定要對應,否則可能出現
float
類型的變量被以int
類型的編碼讀取,數據是錯誤的。 - 如果所傳參數爲數組,則傳輸的是數組的第一個元素的地址。例如傳遞一個4個元素的一維數組
a
時,子函數中的變量定義可以是integer a
,integer 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章會有詳細的介紹。
第九章 文件
write
和read
的使用不重複了,這裏強調後面出現的新知識。
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了,但是僅僅是能夠運行。接下來花一點時間將程序改寫得更加結構化一些。