Fortran學習筆記6(函式、子程式)
程式程式碼中,常常會在不同的地方重複用到某一功能和重複某一程式碼,這個時候就要使用函式。函式包括內嵌函式,自定義函式,子程式。
子程式Subroutine
子程式之所以叫子程式,是因為在沒有經過呼叫之前,它不會被執行。(當然,如果把編譯器對主函式的呼叫也看做同樣的話則另當別論。)
包含子程式的程式的格式:
program main
......
......
call sub_1(...)
......
call sub_2(...)
......
end program main
subroutine sub_1(...)
...
end subroutine sub_1
subroutine sub_2(... )
...
end subroutine sub_2
主程式並不一定要放在最開始,它可以放在程式的任何地方(但是最好放在最開始或者結尾,如果你放在了一大段程式的中間……何苦為難自己!)子程式之間也可以互相呼叫。甚至可以自己呼叫自己,這個演算法就叫遞迴,但是Fortran77不支援。呼叫語句為call。
有關子程式,一個很重要的概念是,子程式獨立的擁有屬於自己的變數申明。主程式和子程式,不同的子程式之間的變數名可以相同,他們互不干擾。除了變數獨立,子程式還獨立擁有自己的行程式碼。
在子程式的呼叫中會涉及到引數傳遞。Fortran中的引數傳遞使用的是傳址呼叫。也就是說,實參和形參公用一塊記憶體,兩者改變其中之一,另一者也會發生變化,(其實是形參暫時的借住在實參家裡。)
自定義函式Function
自定義函式的執行和上面的子程式沒有什麼兩樣,也需要呼叫才能被執行,也可以獨立宣告變數,引數傳遞的方法和子程式相同。儘管如此,自定義函式和子程式還是有兩點不同:
- 呼叫自定義函式前要宣告。
- 自定義函式呼叫後會返回一個數值。
下面是一個簡單的程式:用牛頓法來解方程。
program main
implicit real*8(a-z)
external f,g
integer::max
integer::i,k=1
real*8::x0,x1,x2,dx,tol,f,g
open(unit=11,file ='fin.txt')
open(unit=12,file='fout.txt')
read(11,*)x0,max,tol
do
x1=x0-f(x0)/g(x0)
dx=abs(x1-x0)
if(dx<=tol) exit
k=k+1
if(k>=max) exit
x0=x1
end do
write(12,*)x0
end program main
function f(x)
implicit none
real*8::f,x
f=x**3+2*x**2+10*x-20
end function
function g(x)
implicit none
real*8::g,x !,dx
g=3*x**2+4*x+10
end function
下面是一個更簡單的例子:(摘自彭國倫《Fortran95程式設計》一書)
program main
implicit none
real::a=1,b
real add
add(a,b)=a+b
! 簡單的子程式直接寫在主程式裡面,並且不用call呼叫。並且這樣的函式只能在本主函式中使用,其他地方不能呼叫。
write(*,*)add(a,3.0)
stop
end program
寫函式的時候請注意,儘量不要去改變傳入的引數的數值,最好另開闢一個記憶體來儲存要用的應變數,而不要隨意去改變傳入的自變數。
全域性變數COMMON
不同的程式之間,也就是在不同的函式之間,除了可以通過傳遞引數的方法來共享記憶體,還可以通過全域性變數來讓不同程式中宣告出來的變數使用相同的記憶體位置。
program main
implicit none
integer::a,b
common a,b
a=1
b=2
call sub()
end program
subroutine sub()
implicit none
integer::num1,num2
common num1,num2
write(*,*)num1,num2
return
end subroutine sub
由於全域性變數使用的“地址對應”的方法在程式中共享資料,所以在程式設計時常常會出現一些不方便,比如在主程式中聲明瞭6個全域變數,而你在子程式中只需要使用第六個全域變數,但是你不得不把前五個都寫出來。這樣很麻煩,一個幼小的解決辦法是將全域變數分割槽。
program main
implicit none
integer::a,b,c,d
common/group1/ a,b
common/group2/ c,d
a=1
b=2
c=3
d=4
call sub_1()
call sub_2()
end program
subroutine sub_1()
implicit none
integer::num1,num2
common/group1/ num1,num2
write(*,*)num1,num2
return
end subroutine sub_1
subroutine sub_2()
implicit none
integer::num1,num2
common/group2/ num1,num2
write(*,*)num1,num2
return
end subroutine sub_2
BLOCK DATA
關於COMMON設定初值,不能直接在子程式或者主程式中使用data來設定初值,要在block data程式模組中使用data命令來設定初值。BLOCK data很像一段子程式,它也是一段獨立的程式模組,也擁有自己的變數宣告,不過它不需要別人呼叫就可以自己執行。事實上,這段程式會在主程式執行前生效,不過它的功能只在於設定全域性變數的初值,不能有其他執行命令出現。具體語法如下:
block data name ! name可以省略
implicit none ! 最好不要省略
integer... ! 宣告變數
real...
common ... ! 把變數放在公共空間
common/group1/... ! 公共空間分割槽
data var1,var2,...,varn/value1,value2,...,valuen/ ! data語句賦初值
...
...
end block data name ! 可以只寫end或end block data
還有一點要注意,全域性變數不能宣告成常量。所以block data中不能出現parameter。
實際的例子如下:
program main
implicit none
integer::a,b
common a,b
integer::c,d
common/group1/c,d
integer::e,f
common/group2/e,f
write(*,*)a,b,c,d,e,f
stop
end program main
block data name ! name可以省略
implicit none ! 最好不要省略
integer::a,b
common a,b
data a,b/1,2/
integer::c,d
common/group1/ c,d
data c,d/3,4/
integer::e,f
common/group2/e,f
data e,f/5,6/
end block data name ! 可以只寫end或end block data
小結
COMMON語法在資料共享式會出現很多限制和不足,建議不要使用而採用module語句來實現資料共享。