このドキュメントでは、次のトピックについて説明します。
関数を使用すると、サブプログラムと同様に、データを受信したり、データを変更したり、結果を呼び出し側モジュールに渡したりすることができます。 サブプログラムを上回る関数の利点として、追加の一時変数を必要とすることなく、直接ステートメントおよび式でファンクションコールを使用できることが挙げられます。
通常、関数に渡されたパラメータに応じて関数内で結果が作成され、その結果が呼び出し側のオブジェクトに戻されます。 呼び出し側のモジュールに別の値を戻す場合は、パラメータを使用してこれを行います。「サブプログラム」を参照してください。
ファンクションコードが完全に実行された後、コントロールは呼び出し側のオブジェクトに戻され、プログラムはファンクションコールの後に続くステートメントを続行します。
詳細については、以下も参照してください。
Natural ファンクションオブジェクトタイプ
Natural ステートメント DEFINE FUNCTION
、DEFINE PROTOTYPE
以下の 2 つの例は、ファンクションコールとサブプログラムコールの使用法の違いを示しています。
以下の例は、ファンクションコールを使用するプログラムオブジェクト、DEFINE FUNCTION
ステートメントで作成したファンクション定義を含むファンクションオブジェクト、および DEFINE PROTOTYPE
ステートメントで作成したコピーコードオブジェクトで構成されています。
プログラムオブジェクト:
/* Excerpt from a Natural program using a function call INCLUDE C#ADD WRITE #ADD(< 2,3 >) /* function call; no temporary variable necessary END
ファンクションオブジェクト:
/* Natural function definition DEFINE FUNCTION #ADD RETURNS (I4) BY VALUE DEFINE DATA PARAMETER 1 #SUMMAND1 (I4) BY VALUE 1 #SUMMAND2 (I4) BY VALUE END-DEFINE #ADD := #SUMMAND1 + #SUMMAND2 END-FUNCTION END
コピーコードオブジェクト("C#ADD" など):
/* Natural copycode containing prototype DEFINE PROTOTYPE #ADD RETURNS (I4) DEFINE DATA PARAMETER 1 #SUMMAND1 (I4) BY VALUE 1 #SUMMAND2 (I4) BY VALUE END-DEFINE END-PROTOTYPE
サブプログラムを使用して同じ機能を実現する場合、一時変数を使用する必要があります。
以下の例は、サブプログラムオブジェクトを呼び出すプログラムオブジェクトで構成され、一時変数が使用されています。
プログラムオブジェクト:
/* Natural program using a subprogram DEFINE DATA LOCAL 1 #RESULT (I4) INIT <0> /* temporary variable END-DEFINE CALLNAT 'N#ADD' USING #RESULT 2 3 /* result is stored into #RESULT WRITE #RESULT /* print out the result of the subprogram END
サブプログラムオブジェクト("N#ADD" など):
/* Natural program using a subprogram DEFINE DATA PARAMETER 1 #RETURN (I4) BY VALUE RESULT 1 #SUMMAND1 (I4) BY VALUE 1 #SUMMAND2 (I4) BY VALUE END-DEFINE #RETURN := #SUMMAND1 + #SUMMAND2 END
ファンクション定義には、ファンクションが呼び出されたときに実行される Natural コードが含まれています。 サブプログラムと同様に、Natural オブジェクトを作成する必要があります。この場合は、ファンクション定義を含む "ファンクション" タイプのオブジェクトを作成します。 ファンクション定義は、Natural ステートメント DEFINE FUNCTION
を使用して作成します。
ファンクションコール自体は、実行可能コードを含むどのオブジェクトタイプでも構いません。
ファンクションコールをコンパイルするためには、戻り値の format-length/array-definition に関する情報が Natural に必要です。 その後、この情報はプロトタイプ定義のコンパイラから使用できるようになります。 この定義は、Natural ステートメント DEFINE PROTOTYPE
を使用して作成します。 戻されるパラメータの定義を含めることもできます。この定義は、その後コンパイル時にチェックされます。
Natural では、ランタイム時に初めて "呼び出し側" のオブジェクトと "呼び出された" オブジェクト間が接続されるため、コンパイル時にどのファンクションタイプの戻り値で処理しているのかがコンピュータで認識されることはありません。 これは、ファンクションを含むオブジェクトが必ずしもコンパイル時に存在する必要はないためです。 このため、コンパイル時に format-length/array-definition を生成プログラムに組み込めるよう、プロトタイプ定義が作成されます。
プロトタイプ定義には決して実行可能コードが含まれないことに注意してください。 プロトタイプ定義には、ファンクションコールに関する情報、つまり戻り値または戻されるパラメータの format/length/array-definition のみが含まれます。
変数ファンクションコールを定義するには、常に DEFINE PROTOTYPE VARIABLE
ステートメントを使用する必要があります。 使用しなかった場合、ファンクションコールは暗黙的な記号ファンクションコールであると想定されます。
このトピックの詳細については、「ファンクションコール」を参照してください。
明示的プロトタイプ定義(EPT)も PT
節も存在しない場合、生成プログラムでプロトタイプ定義の検索が行われます。 詳細については、下記の「可能なプロトタイプ定義の組み合わせ」を参照してください。
特定のファンクションの該当プロトタイプを見つけるために、Natural によってファンクション名を持つプロトタイプが検索されます。 それ以外の場合、ファンクションコールは記号ファンクションコールであると想定されます。 この場合、ファンクションコールでキーワード
PT=
を使用して、ファンクション "署名" を定義する必要があります。
この節を使用すると、明示的または暗示的なプロトタイプ定義を使用しないで、ファンクションコールの戻り値のフォーマットおよび長さを指定することができます。つまり、中間結果を明示的に指定できます。 詳細については、「ファンクションコール」の intermediate-result-definition を参照してください。
以下の表では、DEFINE PROTOTYPE
ステートメントとファンクションコールで使用可能な節、またはそのいずれかを使用した場合に可能なさまざまな構文の組み合わせに基づいて、プロトタイプ定義の影響を説明しています。 以下の組み合わせは、属するファンクションコールにのみ有効なファンクションプロトタイプ部分を定義するために使用できます。
明示的な DEFINE PROTOTYPE 定義(EPT)
記号または変数ファンクションコール、パラメータ定義、戻り値定義を決定できます。
プロトタイプキャスト(PT 節)
パラメータ定義、戻り値定義を決定できます。
戻り値の中間結果(IR 節)
戻り値定義を決定できます。
ケース | DEFINE PROTOTYPE での明示的プロトタイプ定義(EPT)
|
ファンクションコールの PT 節(PT)
|
ファンクションコールの IR 節(IR)
|
GP からのプロトタイプ定義の自動読み込み(APT) | プロトタイプの動作 | |
---|---|---|---|---|---|---|
1 | x | x | x | - | SV(EPT)、PS(PT)、R(IR) | |
2 | - | x | x | - | S、PS(PT)、R(IR) | |
3 | x | - | x | - | SV(EPT)、PS(EPT)、R(IR) | |
4 | - | - | x | x | S、PS(APT)、R(IR) | |
5 | x | x | - | - | SV(EPT)、PS(PT)、R(PT) | |
6 | - | x | - | - | S、PS(PT)、R(IR) | |
7 | x | - | - | - | SV(EPT)、PS(EPT)、R(EPT) | |
8 | - | - | - | x | S、PS(APT)、R(APT) |
上記の意味は次に示すとおりです。
EPT | 明示的な DEFINE PROTOTYPE ステートメント。
|
---|---|
PT | プロトタイプキャスト(PT 節)。
|
IR | 戻り値の中間結果(IR 節)。
|
APT | 外部 GP 経由の自動プロトタイプ定義。 |
S | 記号ファンクションコール。 |
V | 変数ファンクションコール。 |
SV(EPT) | 明示的プロトタイプ定義によって、記号ファンクションコールが実行されるか、変数ファンクションコールが実行されるかが決定されます。 |
R(IR) | ファンクションコールの IR 節によって戻り変数(R)が定義されます。
|
R(PT) | ファンクションコールの PT 節によって戻り変数(R)が定義されます。
|
R(EPT) | 明示的な DEFINE PROTOTYPE ステートメントによって戻り変数(R)が定義されます。
|
PS(PT) | ファンクションコールの PT 節によって、パラメータ署名(PS)(戻り値定義なしのパラメータ定義)が定義されます。
|
PS(EPT) | 明示的な DEFINE PROTOTYPE ステートメントによって、パラメータ署名(PS)(戻り値定義なしのパラメータ定義)が定義されます。
|
PS(APT) | 生成プログラム(GP)からプロトタイプ定義を読み込むことよって、パラメータ署名(PS)が自動的に定義されます。 |
例えば、上記の表のケース 1 の動作は以下のとおりです。
明示的な DEFINE PROTOTYPE
ステートメント(EPT)が使用され、ファンクションコールで PT
および IR
節が定義された場合、どのよう動作になるでしょうか。
EPT 定義によって、記号ファンクションコールが実行されるか、変数ファンクションコールが実行されるかが決定されます。 DEFINE PROTOTYPE VARIABLE
が事前に定義されている場合、変数ファンクションコールが想定されます。 パラメータ署名(戻り値定義なしの、すべてのパラメータのフォーマットおよび長さの定義)が PT
節によって定義され、戻り値のフォーマットおよび長さがファンクションコールの IR
節によって定義されます。 この場合、自動プロトタイプ定義(APT)は起動されません。
結論として、上記のケースから以下の一般的ルールが導き出されます。
変数ファンクションコールの場合、常に、コールの明示的プロトタイプ定義(EPT)が存在している必要があります。
PT
節では、記号ファンクションコールであるか変数ファンクションコールであるかは決定されません。
PT
節の定義によって、パラメータおよび戻り値の EPT 定義が上書きされます。
IR
節の定義によって、戻り値定義が上書きされます。
EPT も PT
節も存在しない場合、生成プログラムでプロトタイプ定義の検索が行われます(自動プロトタイプ定義)。
ファンクションを再帰的に呼び出す場合、ファンクションプロトタイプがファンクション定義に含まれているか、または INCLUDE
ファイルで挿入される必要があります。
ファンクションオブジェクト:
/* Function definition for calculation of the math. factorial DEFINE FUNCTION #FACT RETURNS (I4) BY VALUE DEFINE DATA PARAMETER 1 #PARA (I4) BY VALUE LOCAL 1 #TEMP (I4) END-DEFINE /* Prototype definition is necessary INCLUDE C#FACT /* Program code IF #PARA=0 #FACT := 1 ELSE #TEMP := #PARA - 1 #FACT := #PARA * #FACT(< #TEMP >) END-IF END-FUNCTION END
コピーコードオブジェクト("C#FACT" など):
/* Prototype definition is necessary DEFINE PROTOTYPE #FACT RETURNS (I4) DEFINE DATA PARAMETER 1 #PARA (I4) BY VALUE END-DEFINE END-PROTOTYPE
プログラムオブジェクト:
/* Prototype definition INCLUDE C#FACT /* function call WRITE #FACT(<12>) END
オペランドの代わりに、ステートメントまたは式で直接ファンクションコールを使用することができます。 ただし、使用できるのは、オペランドが修正不可能な場合のみです。
すべてのファンクションコールは、コンパイル時に分析される構文の順序に従って実行されます。 ファンクションコールの結果は内部一時変数に保存され、ステートメントまたは式に渡されます。
この一定の順序により、例えばステートメントの出力に意図しない影響を与えることなく、ファンクション内で標準出力を実行することができます。
プログラム:
/* Natural program using a function call INCLUDE CPRINT PRINT 'before' #PRINT(<>) 'after' END
ファンクションオブジェクト:
/* Natural function definition /* function returns integer value 10 DEFINE FUNCTION #PRINT RETURNS (I4) WRITE '#PRINT' #PRINT := 10 END-FUNCTION END
コピーコード("CPRINT" など):
DEFINE PROTOTYPE #PRINT END-PROTOTYPE
標準出力に送られる結果は以下のとおりです。
#PRINT before 10 after
ファンクションは、ステートメントおよび式とは関係なく、ステートメントとして呼び出すこともできます。 この場合、戻り値(定義されていると仮定)は考慮されません。
ただし、オプションのオペランドリストの後に独立したファンクションを宣言する場合、オペランドリストの後にセミコロンを付けて、ファンクションコールがオペランドリストの一部ではないことを明確にする必要があります。
プログラムオブジェクト:
/* Natural program using a function call DEFINE DATA LOCAL 1 #A (I4) INIT <1> 1 #B (I4) INIT <2> END-DEFINE INCLUDE CPROTO WRITE #A #B #PRINT_ADD(< 2,3 >) /* function call belongs to operand list just in front of it WRITE '******' WRITE #A #B; /* semicolon separates operand list and function call #PRINT_ADD(< 2,3 >) /* function call doesn't belong to the operand list END
ファンクションオブジェクト:
/* Natural function definition DEFINE FUNCTION #PRINT_ADD RETURNS (I4) BY VALUE DEFINE DATA PARAMETER 1 #SUMMAND1 (I4) BY VALUE 1 #SUMMAND2 (I4) BY VALUE END-DEFINE #PRINT_ADD := #SUMMAND1 + #SUMMAND2 PRINT '#PRINT_ADD =' #PRINT_ADD END-FUNCTION END
コピーコードオブジェクト("CPROTO" など):
/* Natural copycode containing prototype DEFINE PROTOTYPE #PRINT_ADD RETURNS (I4) DEFINE DATA PARAMETER 1 #SUMMAND1 (I4) BY VALUE 1 #SUMMAND2 (I4) BY VALUE END-DEFINE END-PROTOTYPE