Fortran用法總結


轉載:[上古語言]Fortran用法總結 - 知乎 (zhihu.com)

 

 

 

目錄:
一、概述
二、數據類型及基本輸入輸出
三、流程控制
四、循環
五、數組
六、函數
七、文件


一、概述

1、名詞解釋

Fortran=Formula Translator/Translation
一看就知道有什么特色了:可以把接近數學語言的文本翻譯成機械語言。的確,從一開始,IBM設計的時候就是為了方便數值計算和科學數據處理。設計強大的數組操作就是為了實現這一目標。Fortran奠定了高級語言發展的基礎。現在Fortran在科研和機械方面應用很廣。

2、Fortran的主要版本及差別

按其發展歷史,Fortran編譯器的版本其實很多。現在在廣泛使用的是Fortran 77和Fortran90/95。Fortran 90/95在Fortran 77基礎上添加了不少使用的功能,並且改良了77編程的版面格式,所以編程時推薦使用90/95。鑒於很多現成的程序只有77版本,有必要知道77的一些基本常識,至少保證能夠看77程序。以下是77和90/95的一些格式上的區別。

 

Fortran 77: 固定格式(fixed format),程序代碼擴展名:.f或.for

(1)若某行以C,c或*開頭,則該行被當成注釋;
(2)每行前六個字符不能寫程序代碼,可空着,或者1~5字符以數字表明行代碼(用作格式化輸入出等);7~72為程序代碼編寫區;73往后被忽略;
(3)太長的話可以續行,所續行的第六個字符必須是”0”以外的任何字符。

Fortran 90/95:自由格式(free format), 擴展名:.f90/95

(1)以”!”引導注釋;
(2)每行可132字符,行代碼放在每行最前面;
(3)以&續行,放在該行末或下行初。

以下都是討論Fortran 90及95。

 

3、Fortran的一些特點,和C的一些不同

其實很多,在下面涉及具體方面時可以看到。這里只是大致提一些。

(1)不分大小寫
(2)每句末尾不必要寫分號
(3)程序代碼命令間的空格沒有意義
(4)不像C,Fortran不使用{ }
(5)數據類型多出了復數和邏輯判斷類型。比如復數類型
complex :: a !聲明復數的方法。復數顯然方便了科學計算,滿足了工程方面需求 a=(1.0,2.0) ! a=1+i
(6)多出了乘冪運算(**)。乘冪除了整數還可以是實數形式。如開方,開立方
a=4.0**0.5a=8.0**(1.0/3.0)
(7)數組有一些整體操作的功能;可以方便的對部分元素進行操作
(8)有些情況下可以聲明大小待定的數組,很實用的功能

 

4、Fortran的基本程序結構

先看一看所謂的”Hello Fortran”程序。
program main !程序開始,main是program的名字,完全自定義 write(*,*) "Hello" !主程序 stop !終止程序 end [program[main]] !end用於封裝代碼,表示代碼編寫完畢。[]中的內容可省略,下同。 
再看一段實用一些的程序,好有點感性認識。程序用於計算圓柱的表面積,要求輸入底面半徑和。其中展示了Fortran的一些特色用法。程序摘自維基。其實是一個叫 的網上引的維基的網頁。推薦去看看能查到不少有意思的東西。
program cylinder !給主函數起個名字  ! Calculate the area of a cylinder. ! Declare variables and constants. ! constants=pi ! variables=radius squared and height implicit none ! Require all variables to be explicitly declared !這個一般都是要寫上的。下面會進一步說明。 integer :: ierr character :: yn real :: radius, height, area real, parameter :: pi = 3.1415926536 !這是常量的聲明方法  interactive_loop: do !do循環,Fortran中的循環可以加標簽,如d前面的interactive_loop就是標簽  ! Prompt the user for radius and height and read them. write (*,*) 'Enter radius and height.' !屏幕輸出 read (*,*,iostat=ierr) radius,height !鍵盤輸入。isotat的值用判斷輸入成功否。  ! If radius and height could not be read from input, then cycle through the loop. if (ierr /= 0) then  write(*,*) 'Error, invalid input.' cycle interactive_loop !cycle 相當於C里的continue end if ! Compute area. The ** means "raise to a power." area = 2 * pi * (radius**2 + radius*height) ! 指數運算比C方便  ! Write the input variables (radius, height) and output (area) to the screen. write (*,'(1x,a7,f6.2,5x,a7,f6.2,5x,a5,f6.2)') & !"&"表示續行。這里還顯示了格式化輸出 'radius=',radius,'height=',height,'area=',area yn = ' ' yn_loop: do !內嵌的另一個do循環 write(*,*) 'Perform another calculation? y[n]' read(*,'(a1)') yn if (yn=='y' .or. yn=='Y') exit yn_loop if (yn=='n' .or. yn=='N' .or. yn==' ') exit interactive_loop end do yn_loop !結束內嵌do循環  end do interactive_loop end program cylinder 

Fortran程序的主要結構就是這樣了。一般還會有些module的部分在主函數前,函數在主函數后。

二、數據類型及基本輸入輸出

1、數據類型,聲明及賦初值

(1)integer: 短整型kind=2, 長整型kind=4

integer([kind=]2) :: a=3
如果聲明成integer:: a,則默認為長整型。
!”::” 在聲明並同時賦初值時必須要寫上;類型名后面有形容詞時也必須保留::;其他情況可略去
!所謂形容詞,可以看一下這個。比如聲明常數
realparameter :: pi=3.1415926
parameter就是形容詞。

(2)real:單精度kind=4(默認),雙精度kind=8

real([kind=]8) :: a=3.0
還有指數的形式,如1E10為單精度,1D10為雙精度

(3)complex 單精度和雙精度

complex([kind=]4) b

(4)character

character([len=]10) c !len為最大長度

(5)logical

logical*2 :: d=.ture. (等價於logical(2)::d=.ture.)

(6)自定義類型type:類似於C中的struct

Fortran 77中給變量賦初值常用DATA命令,可同時給多個變量賦初值
data a,b,string /1, 2.0, 'fortran'/
與C不同的是,Fortran中變量不聲明也能使用,即有默認類型(跟implicit命令有關)。按
照默認的定,以i,j,k,l,m,n開頭的變量被定義為integer,其余為real。取消該設置需在程序聲明部分之前implicit none。彭國倫建議一般都使用該語句。
另一點關於聲明的不同是Fortran有”等價聲明”:
integer a,b equivalence(a,b)
使得a,b使用同一塊內存。這樣可以節省內存;有時可精簡代碼。如:equivalence(很長名字的變量如三維數組的某個元素,a),之后使用a來編寫程序就簡潔多了。

 

2、基本輸入輸出

輸入:

read(*,*) a !從鍵盤讀入 

輸出:

write(*,*) "text" !在屏幕上輸出。Fortran 77用' text'。Fortan 90中一般" "和' '都可 print *"text" !只能用於屏幕輸出
(*,*)完整寫為(unit=*,fmt=*)
其中unit為輸入/輸出位置,如屏幕,文件等;fmt為格式。如這兩項都寫成*,則按默認的方式進行,即上面描述的。print后面的*表示按默認格式輸出。

 

三、流程控制

1、運算符

(1)六個關系運算符

(2)五大邏輯運算符

! 僅.NOT.連接一個表達式,其余左右兩邊都要有表達式(可以是logical類型的變量)
!.EQV.:當兩邊邏輯運算值相同時為真, .NEQV.:當兩邊邏輯運算值不同時為真

 

2、IF

(1)基本 :

if(邏輯判斷式) then …… end if 

如果then后面只有一句,可寫為

if(邏輯判斷式) …… !then和end if可省略

(2) 多重判斷:

if(條件1then …… else if(條件2then …… else if(條件3then …… else …… end if

(3) 嵌套:

if(邏輯判斷式) then  if(邏輯判斷式) then  if(邏輯判斷式) then  else if(邏輯判斷式) then …… else …… end if  end if end if

(4) 算術判斷:

      program example implicit none  real c write (*,*) "input a number" read (*,*) c if(c) 10,20,30 !//10,20和30為行代碼,根據c小於/等於/大於0,執行10/20/30行的程  10 write (*,*) "A" goto 40 !//goto可實現跳到任意前面或后面的行代碼處,但用多了破壞程序結  20 write (*,*) "B" goto 40 30 write (*,*) "C" goto 40 40 stop   end program example

 

3、SELECT CASE

類似於C的switch語句

select case(變量) case(數值1!//比如case(1:5)代表1<=變量<=5會執行該模塊  …… !//case(1,3,5)代表變量等於1或3或5會執行該模塊 case(數值2!//括號中數值只能是integer,character或logical型常量,不能real型  …… case default …… end case

 

4、PAUSE, CONTINUE

pause暫停程序執行,按enter可繼續執行
continue貌似沒什么用處,可用作封裝程序的標志

 

四、循環

(1)DO

do counter=初值, 終值, /減量 !//counter的值從初值到終值按增/減量變,  …… !//counter每取一個值對應着一次循環。增/減量不寫則認為1  …… …… !//循環主體也沒有必要用{}  …… end do
Fortran 77中不是用end do來終止,而是下面這樣子:
do 循環終行行代碼 counter=初值, 終值, /減量 …… 行代碼 …… !//這是do的最后一行

(2)DO WHILE

do while(邏輯運算) …… …… end do
類似於C中的while(邏輯運算) {……}。
一開始那個計算圓柱表面積的程序中,應該也算是這一類。不過它是通過內部的if語句來控制循。看來也是可以的,不過在這本書上沒看到這樣寫。其實應該也可以歸於下面這種。

(3)沒看到和C里面的do{……}while(邏輯運算); 相對應的循環語句.

不過可以這樣,保證至少做一循環:
do while(.ture.) …… …… if(邏輯運算) exit end do
exit就好比C里面的break。C里的continue在Fortran里是cycle

(4)Fortran的一個特色:帶署名的循環

可以這樣,不易出錯:
outer: do i=1,3 inner: do j=1,3 …… end do inner end do outer
還可以這樣,很方便:
loop1: do i=1,3 loop2: do j=1,3 if(i==3) exit loop1 !exit終止整個循環loop1 if(j==2) cycle loop2 !cycle跳出loop2的本次循環,進行loop2的下次循環 write(*,*) i,j end do loop2 end do loop1
還有一些循環主要用於Fortran中的數組運算,為Fortran特有,很實用。

 

五、數組

1、數組的聲明

和C不同的是,Fortran中的數組元素的索引值寫在()內,且高維的也只用一個(),如
integer a(5) !聲明一個整型一維數組 real :: b(3,6) !聲明一個實型二維數組
類型可以是integer, real, character, logical或type。最高可以到7維。
數組大小必須為常數。但是和C語言不同,Fortran也有辦法使用大小可變的數組,方法如:
integer, allocatable :: a(:) !聲明小可變經過某個途徑得知所需數組大小size之后,用下面的語句: allocate(a(size)) !配置內存空間
之后該數組和通過一般方法聲明的數組完全相同。
與C不同,Fortran索引值默認為從1開始,而且可以在聲明時改變該規則:
integer a(-3:1) ! 索引值為-3,-2,-1,0,1 integer b(2:3,-1:3) !b(2~3,-1~3)為可使用的元素

2、數組在內存中的存放

和C不同,Fortran中的數組比如a(2,2)在內存中存放順序為a(1,1),a(2,1),a(1,2),a(2,2)。原則是放低維的元素,再放高維的元素。此規則稱為column major。

3、賦初值

(1)最普通的做法:

integer a(5) data a /1,2,3,4,5/ integer :: a(5)=(/1,2,3,4,5/) integer :: a(5)=5,則5個元素均為5
對於integer :: a(2,2)=(/1,2,3,4/)
根據數組元素在內存中存放的方式,等價於賦值
a(1,1)=1,a(2,1)=2,a(1,2)=3,a(2,2)=4

(2)利用Fortran的特色:隱含式循環。看例子就明白了。

integer a(5) integer i data (a(i),i=2,4)/2,3,4/ !(a(i),i=2,4)表示i從2到4循環,增量為默認值1
還可以這樣:
integer i integer :: a(5)=(/1,(2,i=2,4),5/) !五個元素分別賦值為1,2,2,2,5 integer :: b(5)=(/i, i=1,5/) !五個元素分別賦值為1234
還可以嵌套
data ((a(i,j),i=1,2),j=1,2)=/1,2,3,4/ !//a(1,1)=1,1(2,1)=2,a(1,2)=3,a(2,2)=4

4、操作整個數組

設a,b為相同類型、維數和大小的數組
a=5 !所有元素賦值為5 a=(/1,2,3/) !這里假設a為一維,a(1)=1,a(2)=2,a(3)=3 a=b !對應元素賦值,要求a,b,c維數和大小相同,下同 a=b+c a=b-c a=b*c a=b/c a=sin(b) !內部函數都可以這樣用

5、操作部分數組元素

a為一維數組
a(3:5)=(/3,4,5/) !a(3)=3,a(4)=4,a(5)=5 a(1:5:2)=3 !a(1)=3,a(3)=3,a(5)=3 a(3:)=5 !a(3)以及之后的所有元素賦值為5 a(1:3)=b(4:6) !類似於這種的要求左右數組元素個數相同 a(:)=b(:,2) !a(1)=b(1,2),a(2)=b(2,2),以此類推

6、WHERE

where形式上類似於if,但只用於設置數組。設有兩個同樣類型、維數和大小的數組a,b
where(a<3) b=a !a中小於3的元素賦值給b對應位置的元素 end where 
再如:where(a(1:3)/=0) c=a !略去了end where,因為只跟了一行where可嵌,也可類似do循環有署名標簽。

7、FORALL

有點像C中的for循環:
forall(triplet1[,triplet2 [,triplet3]],mask)
其中triplet形如i=2:6:2,表示循環,最后一個數字省略則增量為1
例如: forall(i=1:5,j=1:5,a(i,j)<10) a(i,j)=1 end forall
又如: forall(i=1:5,j=1:5,a(i,j)/=0) a(i,j)=1/a(i,j)
forall也可以嵌套使用,好比C中for循環的嵌套。

六、函數

Fortran中函數分兩類:子程序(subroutine)和自定義函數(function)。自定義函數本質上就是學上的函數,一般要傳遞自變量給自定義函數,返回函數值。子程序不一定是這樣,可以沒有返值。傳遞參數要注意類型的對應,這跟C是一樣的。

1、子程序

目的:把某一段經常使用的有特定功能的程序獨立出來,可以方便調用。習慣上一般都把子程序放在主程序結束之后。
形式:
subroutine name (parameter1, parameter2) !給子程序起一個有意義的名字。可以傳遞參數,子程序無返回值與自定義函數不同之處。括號內也可以空着,代不傳遞參數。 implicit none integer:: parameter1, parameter2 !需要定義一下接收參數的類型。 …… !接下來的程序編寫跟主程序沒有任何別。 …… return !跟C不同,這里表示子程序執行后回到調用它的地方繼續執行下面的程序。不一定放在最后。 !可以放在子程序的其他位置,作用相同;子程序中return之后的部分不執行。還可省略不寫 end [subroutine name]
調用:使用call命令直接使用,不需要聲明。在調用處寫:
call subroutine name(parameter1,parameter2)
注意點:
a.子程序之間也可相互調用。直接調用就是了,像在主程序中調用子程序一樣。
b.傳遞參數的原理和C中不同。Fortran里是傳址調用(call by address/reference),就是傳遞時用參數和子程序中接收時用的參數使用同一個地址,盡管命名可以不同。這樣如果子程序的執行改子程序中接收參數的值,所傳遞的參數也相應發生變化。
c.子程序各自內部定義的變量具有獨立性,類似於C。各自的行代碼也具有獨立性。因此各個子程序主程序中有相同的變量名、行代碼號,並不會相互影響。

2、自定義函數

和子程序的明顯不同在於:需要在主程序中聲明之后才能使用。調用方式也有差別。另外按照慣例用函數不去改變自變量的值。如果要改變傳遞參數的值,習慣上用子程序來做。
聲明方式:
real, external :: function_name
一般自定義函數也是放在主程序之后。
形式:
function function_name(parameter1, parameter2) implicit none real:: parameter1, parameter2 !聲明函數參數類型,這是必需的 real::function_name !聲明函數返回值類型,這是必需的 …… …… function_name=. !返回值的表達式,以函數名返回 return !跟C不同,這里表示子程序執行后回到調用它的地方繼續執行下面的程序。可以不寫 end 
也可以這樣直接聲明返回值類型,簡潔些:
real function function_name(parameter1, parameter2) implicit none real:: parameter1, parameter2 !這個還是必需的 …… …… function_name=. !返回值表達式 return end 
調用:
function_name(parameter1,parameter2)
不需要call命令。
自定義函數可以相互調用。調用時也需要事先聲明。
總之,調用自定義函數前需要做聲明,調用子程序則不需要。

3、關於函數中的變量

(1)注意類型的對應。Fortran中甚至可以傳遞數值常量,但只有跟函數定義的參數類型對應才會到想要的結果。如call ShowReal(1.0)就必須用1.0而不是1。
(2)傳遞數組參數,也跟C一樣是傳地址,不過不一定是數組首地址,而可以是數組某個指定元素地址。比如有數組a(5),調用call function(a)則傳遞a(1)的地址,調用call function(a(3))則遞a(3)的地址。
(3)多維數組作為函數參數,跟C相反的是,最后一維的大小可以不寫,其他維大小必須寫。這決於Fortran中數組元素column major的存放方式。
(4)在函數中,如果數組是接收用的參數,則在聲明時可以用變量賦值它的大小,甚至可以不指定大小。例如:
subroutine Array(num,size) implicit none integer:: size integer num(size) !可以定義一個數組,其大小是通過傳遞過來的參數決定的。這很實用。 …… …… return end
!注意這里與在主程序中聲明數組時,數組大小需要用常數來規定大小的規則不太一樣,而這里也可以用參數變量賦值其數組大小。
(5)save命令:將函數中的變量值在調用之后保留下來,下次調用此函數時該變量的值就是上次保的值。只要在定義時加上save就行:
integer, save :: a=1
(6)傳遞函數(包括自定義函數、庫函數、子程序都是可以的)。類似於C中的函數指針需要在主程序和調用函數的函數中都聲明作為參數傳遞的函數。如
real, external :: function !自定義函數 real, intrinsic :: sin !庫函數 external sub !子程序
(7)函數使用接口(interface):一段程序模塊。以下情況必需:
a.函數返回值為數組
b.指定參數位置來傳遞參數時
c.所調用的函數參數個數不固定
d.輸入指標參數時
e.函數返回值為指針時。
具體用法結合例子容易看懂。例子都很長。看書吧。

4、全局變量

功能就不用說了。
原理:根據聲明時的相對位置關系而取用,不同與C中根據變量名使用。
如果在主程序中定義:
integer :: a,b common a,b !就是這樣定義全局變量的
在子程序或自定義函數中定義:
integer :: c,d common c,d
則a和c共用相同內存,b和d共用相同內存。
全局變量太多時會很麻煩。可以把它們人為歸類,只需在定義時在common后面加上區間名。如
common /groupe1/ a, common /group2/ b
這樣使用時就不必把所有全局變量都列出來,再聲明common /groupe1/ c就可以用a、c全局變量了。
可以使用block data程序模塊。在主程序和函數中不能直接使用前面提到的data命令給全局變量賦初值。可以給它們各自賦初值;如果要使用data命令必須要這樣:
block data [name] implicit none integer a,b,c real d,e common a b c common /group1/ d,e data a,b,c,d,e /1,2,3,4.0,5.0/ end [block data [name]]

5、Module

Module不是函數。它用於封裝程序模塊,一般是把具有相關功能的函數及變量封裝在一起。用法很單,但能提供很多方便,使程序變得簡潔,比如使用全局變量不必每次都聲明一長串,
寫在Module里調用就行了。Module一般寫在主程序開始之前。
形式:
module module_name …… …… end [module [module_name]]
使用:在主程序或函數中使用時,需要在聲明之前先寫上一行:
use module_name.
Module中有函數時必須在contains命令之后(即在某一行寫上contains然后下面開始寫函數,多所有函數都寫在這個contains之后)。並且module中定義過的變量在module里的函數中可直接使用,函數之間也可以直接相互調用,連module中的自定義函數在被調用時也不用先聲明。

6、include放在需要的任何地方,插入另外的文件(必須在同一目錄下)。如:

include 'funcion.f90'

 

七、文件

1、文本文件

Fortran里有兩種讀取文件的方式,對應於兩種文件
順序讀取:用於文本文件
直接讀取:用於二進制文件
這里只摘錄關於文本文件的讀取。一般模式如下。
character(len=20)::filenamein="in.txt", filenameout="out.txt" !文件名 logical alive integer::fileidin=10,fileidout=20 !10,20是給文件編的號,除1,2,5,6的正整數都可,因為2、6是默認的輸出位置(屏幕),1、5是默認的輸入位置(鍵盤) integer::error real::in,out !下面這一段用於確認指定名字的文件是否存在 inquire(file=filenamein, exist=alive) !如果存在,alive賦值為0 if(.NOT. alive) then  write(*,*) trim(filenamein), " doesn't exist."!trim用於刪去filenamein中字串!后面的stop多余空格,輸出時好看些 end if  open([unit=]fileidin, file=filenamein, status="old") open([unit=]fileidout,file=filenameout[,status="new"]) !unit指定輸入/輸出的位置。打開已有文件一定要用status="old";打開新文件用status="new"; !不指定status,則默認status="unknown",覆蓋已有文件或打開新文件……  read([unit=]fileidin, [fmt=]100,iostat=error )in !error=0表示正確讀入數據。 100 format(1X,F6.3) !按一定格式輸入輸出,格式可以另外寫並指定行代碼,也可以直接寫在read/write中 write(([unit=]fileidout, "(1X,F6.3)")out close(fileidin) close(fileidout) !1X代表一個空格。F6.3代表real型數據用占6個字符(含小數點),其中小數點后三位。 !常用的還有I3,用於整型數據,共占三個字符;A8,字符型,占8個字符。換行用 /
二進制文件的讀取有所不同。不再列舉。

2、內部文件

另一個很實用的讀寫功能是內部文件(internal file)。看看這個例子就明白了。
integer::a=1,b=2 character(len=20)::string write(unit=string,fmt="(I2,'+',I2,'=',I2)")a,b,a+b write(*,*)string
則結果輸出1+2=3。反過來也是可以的:
integer a character(len=20)::string="123" read(string,*)a write(*,*)a
則輸出123。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM