原著者 : Phillip Andrews (UKMO), Gerard Cats (KNMI/HIRLAM), David Dent (ECMWF), Michael Gertz (DWD), Jean Louis Ricard (Meteo France)
訳者 : 保坂征宏、吉村裕正(いずれも気象研気候)
注意。
!-------------------------------------------------------------- !<節の数字><節のタイトル> !--------------------------------------------------------------である。
<>中に適切に書くこと。
! <コメント>である。
<>中に適切に書くこと。
PROGRAM MAIN PARAMETER ( IMAX = 100 ) .. CALL SUB ( A , IMAX ) .. STOP END C----------------------- SUBROUTINE SUB ( A , IMAX ) DIMENSION DYNAMIC ( IMAX ) ! 自動配列 ... RETURN ENDであろう。]
ルーチンとは、 サブルーチン・関数・モジュール・プログラムのような Fortran プログラム単位を意味する。 ここで示す規則は、 共通のスタイルのルールをつくることによって、 うまく構造化されたプログラミングの実践を促進し、 管理労力を簡単にし、 交換するコードの可読性を高めるためのものである。
以下の項のうちのあるものは、 Fortran90 では使わないほうがよい機能・冗長な機能である。 また他の幾つかの項では、コードの維持管理を困難にする ようなプログラミング上の悪習と考えられる機能を禁止している。
上記以外の GOTO 文の使用はおそらく IF,CASE,DO WHILE,EXIT,CYCLE文を使うことで 回避することができる。 GOTO の使用が避けられない場合は、 プログラムの流れを説明するコメントを明示し、 同様のコメントが付されたCONTINUE文へジャンプするようにせよ。
これは C プログラミングではよく行なわれることだが、 混乱を招く。 また、 その関数は副作用を持たないと知っていれば、 コンパイラが最適化をするかも知れない。 分散並列計算機向けにデザインされた Fortran 90 の変形である HPF ( High Performance Fortran ) は このような実装[副作用がない場合に、より最適化をすることか?]を 認めている。
サブルーチンに引数を渡す際、暗黙的に配列の形状を変更することは禁止。 実際には標準で禁じられているにもかかわらず、 FORTRAN 77 では、n 次元配列をサブルーチンに渡し、 サブルーチン内では例えば 1 次元配列として扱うことは 慣習としてよく行なわれていた。 この慣習は Fortran 90 では禁止だが、INTERFACE ブロックを伴わない 外部ルーチンの場合は依然可能である。 このような引渡し方は、メモリ上でのデータ配置を仮定することで はじめて有効になるので、超並列計算機上では機能しない可能性が大きい。 そこでこれを行なうことを禁止する。
基本的な精神は、 読みやすく維持しやすい、 移植しやすいコードを書くことである。 コードは、予見できない変形を見込んで、 できる限り一般的な方法で書かれているべきである。 実際にはこのことは、 コーディングの時間と手間はやや長くなることを意味する。 しかし、この余分な労力のおかげで、 そのソフトが生き続ける間、維持コストは減じるであろう。
! distribute input data if(myid==0) then ! node 0 sends data to all other nodes. <= コメント行1文字字下げ call MPI_SEND(...) <= コード行2文字字下げ else ! all nodes except 0 receive data. <= コメント行1文字字下げ call MPI_RECV(...) <= コード行2文字字下げ end if] [ 実際問題、上の例のコメントの字下げの仕方って、わかりやすいかなあ? ]
たとえば
! Initialize Variables x=1 MEANINGFUL_NAME=3.0 SILLY_NAME=2.0のかわりに、
! Initialize variables x = 1 MeaningfulName = 3.0 SillyName = 2.0とせよ。
同様に、 式を式として認識しやすく読みやすいように努めよ。 継続行では、 前の行の最後に作用素を書くのではなく、 適当に字下げされた作用素ではじめるようにする方が読みやすい。
Fortran 90 で導入された新たな特色の使い方に愚かなやり方があるのは 避けられない。 当然我々はそのような使い方は禁止したいし、 それ以外の実践は推奨したい。 しかし そのような推奨の完璧なリストを作るには Fortran 90 についてある程度の経験を持たねばならないだろう。 以下のルールはそのような経験のもとで修正・拡張されなければならないだろう。
はじめに
もし、省略可能引数やキーワード引数(optional or keyword arguments)を 使用するなら、f90のルーチン間で、明示的引用仕様宣言 (Explicit interface blocks)が必要になる。 それにより、また、CALLで指定された(specified)引数の型や形状や 数がサブプログラム自身で指定されたものと同じであるかをコンパイラーが チェックすることが可能になる。 付け加えて、コンパイラーのいくつか(例えばCray f90のコンパイラー)は、 呼び出されたサブプログラムがf90で書かれているかどうか(このことにより どのように配列の情報がサブルーチンに引き渡されるかが変わる)を決定する ために、引用仕様宣言の存在を使用する。 このように、一般的にはf90のルーチン間で明示的引用仕様宣言を与えるのが 望ましい。 そうするにはいくつかの方法があり、それらの各々は、プログラム設計 (program design)やコード管理(code management)や構成の制御(configuration contro l)さえも関連(implications)をもっている。 以下のセクションでは、3つの主要な選択肢(options)について議論する。
選択肢(option) I: 明示的にコードされた引用仕様宣言(Explicitly Coded Interface Blocks)
引用仕様宣言は、特に呼び出されるルーチンから引数リストの宣言部分を コピーすることにより、呼び出すルーチンに明示的に書かれる。 この直接的なアプローチは、しかし、いくつかの欠点がある。 まず初めに、もし呼び出されるルーチンの引数リストが変わると引用仕様宣言は CALL文とともに更新しなければならないので、呼び出すルーチンを維持管理するのに 必要な作業が増大し、望ましくない。 更に、呼び出すルーチン内の引用仕様宣言が実際に更新されていて呼び出される ルーチンの実際の引用仕様(interface)と同じになっているかどうかの保証がない。
選択肢 II: モジュール(Module)内の明示的にコードされた引用仕様宣言
あるパッケージのすべてのルーチンの引用仕様宣言がモジュールの中に明示的に 書かれ、このモジュールがパッケージのすべてのルーチンに使用(use)される。 これは、すべてのルーチンの引用仕様の記述(the interface specification)を見つけ る のに、一つのルーチンを調べればよいという長所がある。これは、呼び出される すべてのルーチンのソースコードを個別に調べるより簡単である。 しかし、ルーチン自身やそれへのCALL文に加えて、引用仕様宣言をまだ維持管理 しなければならない。引用仕様宣言を含むモジュールを自動的に作り出すように、 プログラムや例えばunix scriptを書くことはできるけれども。
選択肢 III: 自動引用仕様宣言(Automatic Interface Blocks)
Fortran 90コンパイラは、Contains文に続くルーチンの間で、自動的に明示的に 引用仕様宣言を供給することができる。 引用仕様宣言はまた、モジュールを使用(use)することによって、どのルーチンにも 供給することができる。 このように、引用仕様宣言を実際には書かずに、コンパイラーによって全ての ルーチン間に明示的引用仕様宣言が供給されるようなシステムを設計することが可能で ある。 このようにする一つの方法は、 f90のモジュールのレベルで コードを「モジュール化(modularise)」することである。 すなわち、 関連するコードを一つのモジュール内のContains文以降に 一緒に置くことである。 モジュールBの中のルーチンbを呼び出すモジュールAの中のルーチンaは、ルーチンb への明示的引用仕様を自動的に供給されるようにするために、ただ、モジュールBを Useしなければならない。 明らかに、もしルーチンbが代わりにモジュールAのなかにあれば、Use文は必要ない。 このアプローチの一つの結果は、ひとつのモジュールとそれに含まれるすべての ルーチンは一つのコンパイルユニットを構成するということである。 もし、モジュールが大きいか、もしくは一つのパッケージ内の各々のモジュールが パッケージ内の他の多くのモジュールをUseするルーチンを含んでいるなら (その場合には、一つのモジュール内の一つのルーチンを変更することにより 実質的にパッケージ全体の再コンパイルが必要になる)、これは欠点になるだろう。 他方で、コンパイルユニットの数が非常に減れば、コンパイルや構成(configuration) の コントロールシステムが単純になる。
結論
選択肢 II) や III) は両方、 明示的引用仕様宣言の問題に実行可能な解決法を与える。 選択肢 III) は、コンパイラーが引用仕様宣言を与える仕事を行い、 プログラミングのオーバーヘッド(overheads)が減少し、 同時に使用される引用仕様宣言が正しいことが 保証されるので、おそらくより望ましい。 どの選択肢が選択されても、 プログラム設計と同様にコード管理や構成の制御 (configuration control)に重大な影響を与える。
1dArrayA(:) = 1dArrayB(:) + 1dArrayC(:) 2dArray(:, :) = scalar * Another2dArray(:, :)
2dArray(:, 2:len2) = scalar & * ( Another2dArray(:, 1:len2 -1) & - Another2dArray(:, 2:len2) & )
Integer, parameter :: single & ! single precision kind. = selected_real_kind(6,50) Integer, parameter :: double & ! double precision kind. = selected_real_kind(12,150)
このモジュールは、すべての変数が適切な種別(kind)で宣言されるように、 すべてのルーチンで使用(USE)することができる。例えば、
Real(single), pointer :: geopotential(:,:,:) ! Geopotential height fields
これらの標準が守られること、 特に文書がソフトウエアと同時に更新され続けることと ソフトウエアができるかぎり移植可能な形で書かれること、 を保証することは重要である。 もしこれらの標準が守られないならば、 コードの交換は難しくなるだろう。 QA Fortran [QA Fortran って何?]のような これらの標準に対する従順さをテストするための ツールを作らねばならないかも知れない。 これは今後の課題である。
一つの選択肢は コード交換とコード外解説の中心データベースを 立ち上げることであろう。 受け入れるかどうかという判断基準は、 この文書に書かれている標準を満たすことに置くことになろう。 これはもちろん、 データベース維持のためのハードウエア、ソフトウエア、人を確保するための 資金を必要とする。
その反対の状態として、 各々の機関が独自の強制戦略を採用し。 他機関に提供可能なソフトウエアのリストを示す、 という選択肢もある。
最善の解は Mosaic とインターネットを使う分散システムをデザインすること かもしれない。
23/3/94: Draft Version 0.1 Phillip Andrews (UKMO)
22/4/94: Draft Version 0.2 Phillip Andrews (UKMO)
7/6/94: Draft Version 0.3 Phillip Andrews (UKMO)
31/7/94: Draft Version 0.4 Phillip Andrews (UKMO)
29/9/94: Draft Version 0.5 Phillip Andrews (UKMO)
14/10/94: Draft Version 0.6 Phillip Andrews (UKMO)
Renumbered Version 1.0
20/6/95: Version 1.1 Phillip Andrews (UKMO)
標準ヘッダはこの付録で与えられる。 テンプレートとして書かれている。 テキスト中の < > ブラケットは ユーザが適当な文に置き換えること。
!+ <A one line description of this program> ! Program <NameOfProgram> ! Description: ! <Say what this program does> ! ! Method: ! <Say how it does it: include references to external documentation> ! <If this routine is divided into sections, be brief here, ! and put Method comments at the start of each section> ! ! Input files: ! <Describe these, and say in which routine they are read> ! Output files: ! <Describe these, and say in which routine they are written> ! Current Code Owner: <Name of person responsible for this code> ! History: ! Version Date Comment ! ------- ---- ------- ! <version> <date> Original code. <Your name> ! Code Description: ! Language: Fortran 90. ! Software Standards: "European Standards for Writing and ! Documenting Exchangeable Fortran 90 Code". ! Declarations: ! Modules used: Use, Only : & ! Imported Type Definitions: ! Imported Parameters: ! Imported Scalar Variables with intent (in): ! Imported Scalar Variables with intent (out): ! Imported Array Variables with intent (in): ! Imported Array Variables with intent (out): ! Imported Routines: ! <Repeat from Use for each module...> Implicit None ! Include statements ! Declarations must be of the form: ! <type> <VariableName> ! Description/ purpose of variable ! Local parameters: ! Local scalars: ! Local arrays: !- End of header ---------------------------------------------------------------
!+ <A one line description of this subroutine> ! Subroutine <SubroutineName> & ! (<InputArguments, inoutArguments, OutputArguments>) ! Description: ! <Say what this routine does> ! ! Method: ! <Say how it does it: include references to external documentation> ! <If this routine is divided into sections, be brief here, ! and put Method comments at the start of each section> ! ! Current Code Owner: <Name of person responsible for this code> ! ! History: ! Version Date Comment ! ------- ---- ------- ! <version> <date> Original code. <Your name> ! ! Code Description: ! Language: Fortran 90. ! Software Standards: "European Standards for Writing and ! Documenting Exchangeable Fortran 90 Code". ! ! Declarations: ! Modules used: Use, Only : & ! Imported Type Definitions: ! Imported Parameters: ! Imported Scalar Variables with intent (in): ! Imported Scalar Variables with intent (out): ! Imported Array Variables with intent (in): ! Imported Array Variables with intent (out): ! Imported Routines: ! <Repeat from Use for each module...> Implicit None ! Include statements: ! Declarations must be of the form: ! <type> <VariableName> ! Description/ purpose of variable ! Subroutine arguments ! Scalar arguments with intent(in): ! Array arguments with intent(in): ! Scalar arguments with intent(inout): ! Array arguments with intent(inout): ! Scalar arguments with intent(out): ! Array arguments with intent(out): ! Local parameters: ! Local scalars: ! Local arrays: !- End of header ---------------------------------------------------------------
!+ <A one line description of this function> ! Function <FunctionName> & (<InputArguments>) & Result (<ResultName>) ! The use of result is recommended ! but is not compulsory. ! Description: ! <Say what this function does> ! ! Method: ! <Say how it does it: include references to external documentation> ! <If this routine is divided into sections, be brief here, ! and put Method comments at the start of each section> ! ! Current Code Owner: <Name of person responsible for this code> ! ! History: ! Version Date Comment ! ------- ---- ------- ! <version> <date> Original code. <Your name> ! ! Code Description: ! Language: Fortran 90. ! Software Standards: "European Standards for Writing and ! Documenting Exchangeable Fortran 90 Code". ! ! Declarations: ! Modules used: Use, Only : & ! Imported Type Definitions: ! Imported Parameters: ! Imported Scalar Variables with intent (in): ! Imported Scalar Variables with intent (out): ! Imported Array Variables with intent (in): ! Imported Array Variables with intent (out): ! Imported Routines: ! <Repeat from Use for each module...> Implicit None ! Declarations must be of the form: ! <type> <VariableName> ! Description/ purpose of variable ! Include statements: ! Function arguments ! Scalar arguments with intent(in): ! Array arguments with intent(in): ! Local parameters: ! Local scalars: ! Local arrays: !- End of header ------------------------------------------------------------
!+ <A one line description of this module> ! Module <ModuleName> ! ! Description: ! <Say what this module is for> ! ! Current Code Owner: <Name of person responsible for this code> ! ! History: ! ! Version Date Comment ! ------- ---- ------- ! <version> <date> Original code. <Your name> ! ! Code Description: ! Language: Fortran 90. ! Software Standards: "European Standards for Writing and ! Documenting Exchangeable Fortran 90 Code". ! ! Modules used: ! Use, only : & ! Imported Type Definitions: ! Imported Parameters: ! Imported Scalar Variables with intent (in): ! Imported Scalar Variables with intent (out): ! Imported Array Variables with intent (in): ! Imported Array Variables with intent (out): ! Imported Routines: ! <Repeat from Use for each module...> ! Declarations must be of the form: ! <type> <VariableName> ! Description/ purpose of variable Imlicit none ! Global (i.e. public) Declarations: ! Global Type Definitions: ! Global Parameters: ! Global Scalars: ! Global Arrays: ! Local (i.e. private) Declarations: ! Local Type Definitions: ! Local Parameters: ! Local Scalars: ! Local Arrays: ! Operator definitions: ! Define new operators or overload existing ones. Contains ! Define procedures contained in this module. End module <ModuleName> !- End of module header付録 C: 例
This example subroutine has been taken from the UKMO's variational assimilation code. Some additional style rules have been applied such as placing one argument per line and commenting the argument with its intent.
!+ Allocates and calculates the LS field Vtheta Subroutine Var_LSVtheta & ( LocalFS, & ! in LS ) ! inout ! Description: ! Allocates and calculates the linearization state derived field Vtheta: ! i.e. the virtual potential temperature. ! ! Method: ! See UMDP 101 section 3.1.3. ! ! Owner: Phil Andrews ! ! History: ! Version Date Comment ! ------- ---- ------- ! 0.1 01/09/94 Original code. Phil Andrews ! 0.2 26/10/94 Changed references from theta to thetaL and corrected ! 0.2 the calculation of Vtheta. Mike Thurlow. ! 0.3 07/02/95 Test of FieldStatus added. Phil Andrews. ! 1.4 11/05/95 Tracing added. JB ! ! Code Description: ! Language: Fortran 90. ! Software Standards: "European Standards for Writing and ! Documenting Exchangeable Fortran 90 Code". ! ! Parent module: VarMod_LS ! ! Declarations: ! Modules used: Use VarMod_PFInfo, only : & ! Imported routines: Var_DeallocateModel, & ! Imported Type Definitions: ModelDump_type, & ! Stores LS PF or Adj states. ModelDumpHeader_type, & ! Structures use within Header_type, & ! ModelDump_type XYgrid_type, & ! ditto Zgrid_type, & ! ditto ! Imported Parameters: FieldStatus_absent ! FieldStatus code for absent Use VarMod_Constants, only : & ! Imported Parameters: InvGasRatioMinusOne Use VarMod_Trace, only : & ! Imported routines: Var_TraceEntry, & Var_Trace, & Var_TraceExit, & ! Imported scalars: UseTrace, & TraceNameLen Implicit none !* Subroutine arguments ! Scalar arguments with intent(in): Integer, intent(in) :: LocalFS ! value to use for FieldStatus ! Scalar arguments with intent(inout): Type (ModelDump_type), intent(inout) & ! linearization state :: LS !* End of Subroutine arguments ! Local parameters: Character (len=TraceNameLen), parameter & :: RoutineName = "Var_LSVtheta" ! Local scalars: Integer :: qlevels ! Number of q levels Integer :: Tlevels ! Number of temperature levels !- End of header -------------------------------------------------------------- If (LS % header % Vtheta % FieldStatus == FieldStatus_absent) then !----------------------------------------------------------------------------- ![1.0] Initialize: allocate space, calculate required fields etc... !----------------------------------------------------------------------------- If (UseTrace) call Var_TraceEntry(RoutineName) qlevels = LS % header % qT % z % TopLev Tlevels = LS % header % thetaL % z % TopLev ! Allocate LS % Vtheta: Allocate (LS % Vtheta & (1:LS % header % thetaL % x % len, & 1:LS % header % thetaL % y % len, & 1:LS % header % thetaL % z % TopLev & ) ) ! Set header values for LS % Vtheta: LS % header % Vtheta = LS % header % thetaL LS % header % Vtheta % FieldStatus = LocalFS ! Obtain derived LS fields as necessary: Call Var_LSqc & ( LocalFS +1, & ! in LS ) ! inout Call Var_LStheta & ( LocalFS +1, & ! in LS ) ! inout !----------------------------------------------------------------------------- ![2.0] Calculate LS % Vtheta: !----------------------------------------------------------------------------- ! lowest to top wet level: LS % Vtheta(:,:,1:qlevels) = LS % theta(:,:,1:qlevels) & * ( 1.0 & + ( InvGasRatioMinusOne & * ( LS % qT(:,:,:) - LS % qc(:,:,:) ) & ) ) ! Top wet level +1 to top dry level: LS % Vtheta(:,:,qlevels+1:Tlevels) = LS % thetaL(:,:,qlevels +1:Tlevels) ! Tidy up: Call Var_DeallocateModel & ( LocalFS +1, & ! in LS ) ! inout ! That's it! If (UseTrace) call Var_TraceExit(RoutineName) End if End subroutine Var_LSVtheta
The following program demonstrates how new operators can be defined in Fortran 90. It also shows how to extend the existing operators and the assignment operation.
!+ Extend assignment and + operators, & define two new ops ! Module Operators ! ! Description: ! ! Module to extend the assignment operation so that it will ! convert characters to an integer, extend the + operator to ! add logical values, and define two new operators. ! ! Current Code Owner: A N Other ! ! History: ! Version Date Comment ! ------- ------ ------- ! 1.0 5/5/95 Original code. (A N Other) ! ! Code Description: ! Language: Fortran 90. ! ! The procedure named must be a subroutine with one ! intent(OUT) argument and one intent(in) argument. Implicit none ! Override operators: Interface assignment(=) Module procedure Chars_to_Integer End interface ! Similarly the next interface will arrange for Add_Logicals ! to be called if two logical values are added together. The ! procedure named must be a function with two intent(in) ! arguments. The result of the addition is the result of the ! function. Interface operator(+) Module procedure Add_Logicals End interface ! Define new operators: ! This creates a new operator .Half. It is a unary operator, ! because Divide_By_Two only takes one argument. Interface operator(.Half.) Module procedure Divide_By_Two End interface ! This creates a new operator .JPlusKSq. It is a binary ! operator, because J_Plus_K_Squared takes two arguments. Interface operator(.JPlusKSq.) Module procedure J_Plus_K_Squared End interface Contains ! Define procedures contained in this module subroutine Chars_to_Integer(int, int_as_chars) ! Subroutine to convert a character string containing ! digits to an integer. Character(len=5), intent(in) :: int_as_chars Integer, intent(OUT) :: int Read (int_as_chars, FMT = '(I5)') int End subroutine Chars_to_Integer Function Add_Logicals(a, b) result(c) ! Description: ! Function to implement addition of logical values as an ! OR operation. Logical, intent(in) :: a Logical, intent(in) :: b Logical :: c c = a .OR. b End function Add_Logicals Function Divide_By_Two(a) result(b) Integer, intent(in) :: a Integer :: b b = a / 2 End function Divide_By_Two Function J_Plus_K_Squared(j, k) result(l) Integer, intent(in) :: j Integer, intent(in) :: k Integer :: l l = (j + k) * (j + k) End function J_Plus_K_Squared End module Operators !- End of module header !+ Test program for operator module ! Program Try_Operators ! Description: ! Program to test the operators defined in the module ! Operators ! ! Current Code Owner: A N Other ! History: ! Version Date Comment ! ------- ------ ------- ! 1.0 5/5/95 Original code. (A N Other) ! ! Code Description: ! Language: Fortran 90. ! Modules used: Use Operators Implicit none ! Local scalars: Character(len=5) :: string5 = '-1234' ! Char. to integer Integer :: i_value = 99999 ! and half operator Integer :: half_value ! test variables. Integer :: n ! Test variables Integer :: l = 4 ! for add & square Integer :: m = 8 ! operation. Logical :: x = .false. ! Test variables Logical :: y = .true. ! for logical Logical :: z = .false. ! addition ops. !- End of header i_value = string5 ! Convert from a string to an integer. z = x + y ! Add together some logicals. Print *, string5, i_value, x, y, z half_value = .Half. i_value ! Use user def unary operator n = l .JPlusKSq. m ! Use user def binary operator Print *, i_value, half_value, l, m, n End program Try_Operators
This program shows how to define a generic function (one which is defined for arguments of more than one type). It also illustrates how explicit interfaces may be required when using subroutines or functions, and how these are provided automatically if the subroutines or functions are placed in a module.
!+ Generic function to return a cube of a number ! Module Generic_Cube ! ! Description: ! Module to define a generic function called Cube which ! returns the cube of a number. The function is defined for ! both integer and real arguments so, for example, Cube(2) ! and Cube(4.263) will both work. ! ! Current Code Owner: A N Other ! ! History: ! Version Date Comment ! ------- ------ ------- ! 1.0 5/5/95 Original code. (A N Other) ! ! Code Description: ! Language: Fortran 90. Implicit none ! The interface says that when the generic function Cube is ! used the compiler should either call R_Cube or I_Cube ! depending on the type of the argument. Interface Cube Module procedure R_Cube, I_Cube End interface Contains Function R_Cube(value) result(res)! Compiler knows to call Real :: value ! this with real args Real :: res ! because 'value' is a = value * value * value ! declared as Real. End function R_Cube Function I_Cube(value) result(res)! Compiler knows to call Integer :: value ! this with integer args Integer :: res ! because 'value' is a = value * value * value ! declared as Integer. End function I_Cube End module Generic_Cube !- End of module header !+ Print the cube roots numbers. ! Program Cubes ! Description: ! Program to print the cubes and cube roots of some numbers. ! ! Current Code Owner: A N Other ! ! History: ! Version Date Comment ! ------- ------ ------- ! 1.0 5/5/95 Original code. (A N Other) ! ! Code Description: ! Language: Fortran 90. Use Generic_Cube ! Make the generic function Cube ! available in this program Implicit none ! Local scalars: Integer :: i = 8 ! Integer test value Real :: a = 8.01 ! Real test value ! Interface to define a generic function Cube_Root which ! returns the cube root of an integer or real number. ! Because the functions R_Cube_Root and I_Cube_Root are not ! in a module, the compiler doesn't know the types of their ! arguments when it compiles the main program. This means we ! have to give them explicitly in the interface, which is ! laborious and error prone. The moral of the tale is - use ! a module! Interface Cube_Root Function R_Cube_Root(value) result(res) Real :: value Real :: res End function R_Cube_Root Function I_Cube_Root(value) result(res) Integer :: value Integer :: res End function I_Cube_Root End interface ! Print the cubes and cube roots of some numbers using the ! Cube and Cube_Root generic functions. Write (*, "(3(A, I9.3))") "i: ", i, " Cube: ", Cube(i), & " Cube root: ", Cube_Root(i) Write (*, "(3(A, F9.4))") "a: ", a, " Cube: ", Cube(a), & " Cube root: ", Cube_Root(a) End program Cubes !+ Return the cube root of a real number ! Function R_Cube_Root(value) result(res) ! Description: ! Function returning the cube root of a real number. ! ! Current Code Owner: A N Other ! ! History: ! Version Date Comment ! ------- ------ ------- ! 1.0 5/5/95 Original code. (A N Other) ! ! Code Description: ! Language: Fortran 90. Implicit none ! Scalar arguments with intent(in) Real :: value ! Input value ! Local scalars Real :: res ! Result !- End of header res = value ** (1.0 / 3.0) End function R_Cube_Root !+ Return the cube root of an integer ! Function I_Cube_Root(value) result(res) ! Description. ! Function returning the integer part of the cube root of ! its integer argument. ! ! Current Code Owner: A N Other ! ! History: ! Version Date Comment ! ------- ------ ------- ! 1.0 5/5/95 Original code. (A N Other) ! ! Code Description: ! Language: Fortran 90. Implicit none ! Scalar arguments with intent(in) Integer :: value ! Input value ! Local scalars Integer :: res ! Result !- End of header res = int(Real(value) ** (1.0 / 3.0)) End function I_Cube_RootLast updated: 23 October 1996 by cpjones@meto.gov.uk | © Crown Copyright 1996