このドキュメントでは、ルーチン、つまり下位プログラムとして呼び出すことができるオブジェクトタイプについて説明します。
ヘルプルーチンとマップは他のオブジェクトからも呼び出されますが、厳密にはルーチンではないため、個別のドキュメントで説明しています。「ヘルプルーチン」および「マップ」を参照してください。
基本的に、プログラム、サブプログラムおよびサブルーチンは、データの受け渡し方法やお互いのデータエリアを共有できるかどうかなどがそれぞれ異なります。したがって、どのオブジェクトタイプをどの目的に使用するかの決定は、アプリケーションのデータ構造に大きく依存します。
このドキュメントでは、次のトピックについて説明します。
一般に、Natural アプリケーションは単一の巨大なプログラムで構成されるのではなく、複数のオブジェクトモジュールに分割されます。これらの各オブジェクトは、管理可能なサイズの機能ユニットであり、各オブジェクトは明確に定義された方法でアプリケーションの他のオブジェクトに接続されています。これにより、適切に構造化されたアプリケーションが提供されるため、開発およびその後のメンテナンスが大幅に容易かつ迅速に行うことができるようになります。
メインプログラムの実行中には、他のプログラム、サブプログラム、サブルーチン、ヘルプルーチン、およびマップを呼び出すことができます。続いてこれらのオブジェクトが他のオブジェクトを呼び出すことができます。例えば、サブルーチンが別のサブルーチンを呼び出すこともできます。したがって、アプリケーションのオブジェクト指向構造は非常に複雑になり、複数のレベルに拡張される可能性があります。
呼び出される各オブジェクトは、それを呼び出したオブジェクトのレベルの 1 つ下のレベルになります。つまり、下位オブジェクトが呼び出されるたびに、レベル番号は 1 つ増加します。
直接実行されるプログラムはすべてレベル 1 です。メインプログラムによって直接呼び出されるサブプログラム、サブルーチン、マップ、ヘルプルーチンはレベル 2 になります。このサブルーチンがサブルーチンを呼び出すと、呼び出されたサブルーチンはレベル 3 になります。
別のオブジェクト内から FETCH
ステートメントで呼び出されたプログラムは、メインプログラムとして分類され、レベル 1 から動作します。ただし、FETCH RETURN
で呼び出されたプログラムは、下位プログラムとして分類され、呼び出し元オブジェクトの 1 つ下のレベルが割り当てられます。
次の図は、呼び出されるオブジェクトの複数レベルの例と、これらのレベルがどのように数えられるかを示しています。
現在実行中のオブジェクトのレベル番号を確認する場合は、システム変数 *LEVEL
を使用できます。詳細については『システム変数』ドキュメントを参照してください。
下位ルーチン、つまりサブプログラム、外部サブルーチン、プログラムまたは関数をそれぞれ呼び出す CALLNAT
、PERFORM
、または FETCH RETURN
ステートメントまたはファンクションコールが実行されると、呼び出し元オブジェクトの実行は中断され、該当する下位ルーチンの実行が始まります。
下位ルーチンの実行は、END
ステートメントに到達するまで、または ESCAPE
ROUTINE
または ESCAPE
MODULE
ステートメントの実行によって下位ルーチンの処理が停止されるまで継続します。
いずれの場合も、呼び出し元オブジェクトの処理は、下位ルーチンを呼び出すために使用された CALLNAT
、PERFORM
または FETCH RETURN
ステートメントの次のステートメントから続行します。
ファンクションコールの場合、呼び出し元オブジェクトのその後の処理は、ファンクションコールを含むステートメントで継続されます。
プログラムは単独で実行し、テストできます。
ソースプログラムをカタログ化(コンパイル)して実行するには、システムコマンド RUN
を使用します。
すでにカタログ化オブジェクトとして存在するプログラムを実行するには、システムコマンド EXECUTE
を使用します。
プログラムは、他のオブジェクトから FETCH
または FETCH RETURN
ステートメントで呼び出すこともできます。呼び出し元オブジェクトは、別のプログラム、サブルーチン、サブプログラム、関数、ヘルプルーチン、またはマップの処理ルールのいずれでもかまいません。
プログラムが FETCH RETURN
で呼び出されると、呼び出し元オブジェクトの実行は終了されるのではなく中断され、フェッチされたプログラムは下位プログラムとしてアクティブになります。FETCH
されたプログラムの実行が終了すると、呼び出し元オブジェクトが再びアクティブになり、その実行は FETCH RETURN
ステートメントの次のステートメントから継続します。
プログラムが FETCH
で呼び出されると、呼び出し元オブジェクトの実行は終了し、FETCH
されたプログラムがメインプログラムとしてアクティブになります。呼び出し元オブジェクトが、フェッチされたプログラムの終了時に再びアクティブになることはありません。
以下では次のトピックについて説明します。
FETCH RETURN
で呼び出されるプログラムは、呼び出し元オブジェクトが使用するグローバルデータエリア(GDA)にアクセスできます。
また、あらゆるプログラムには独自のローカルデータエリア(LDA)があり、そのプログラム内だけで使用されるフィールドを定義します。さらに、プログラムはアプリケーション独立変数(AIV)にアクセスできます。詳細については、『ステートメント』ドキュメントの「アプリケーションに依存しない変数の定義」を参照してください。
ただし、FETCH RETURN
で呼び出されたプログラムが独自のグローバルデータエリア(GDA)を持つことはできません。
メインプログラムとして FETCH
で呼び出されるプログラムは、上記の図に示したように、通常は独自のグローバルデータエリアを設定します。ただし、呼び出し元オブジェクトが設定したものと同じグローバルデータエリアを使用することもできます。
注意:
ソースプログラムは、RUN
ステートメントで呼び出すこともできます。『ステートメント』ドキュメントの RUN
ステートメントを参照してください。
通常、サブルーチンは、アプリケーション内の異なるオブジェクトで使用される機能を実装します。
サブルーチンを構成するステートメントは、DEFINE SUBROUTINE
... END-SUBROUTINE
ステートメントブロック内に定義する必要があります。
サブルーチンは PERFORM
ステートメントによって呼び出されます。
サブルーチンには、インラインサブルーチンと外部サブルーチンがあります。
インラインサブルーチン
インラインサブルーチンは、このサブルーチンを呼び出す PERFORM
ステートメントが含まれるオブジェクト内に定義されるものです。
外部サブルーチン
外部サブルーチンは、このサブルーチンを呼び出すオブジェクトの外部にある別のサブルーチンタイプのオブジェクト内に定義されるものです。
同じオブジェクト内で繰り返し実行されるコードブロックがある場合は、インラインサブルーチンを使用すると便利です。次に、DEFINE SUBROUTINE
ステートメントブロック内でこのブロックを 1 度だけコードし、いくつかの PERFORM
ステートメントで呼び出すだけです。
以下では次のトピックについて説明します。
インラインサブルーチンは、タイプがプログラム、ファンクション、サブプログラム、サブルーチン、またはヘルプルーチンのオブジェクト内に含めることができます。
オブジェクトに含まれるインラインサブルーチンは、そのオブジェクト内のすべてのデータフィールドにアクセスできます。
外部サブルーチン、つまりサブルーチンタイプのオブジェクトは単独では実行できません。これは、他のオブジェクトから呼び出す必要があります。呼び出し元オブジェクトは、プログラム、関数、サブプログラム、サブルーチン、ヘルプルーチン、またはマップの処理ルールのいずれでもかまいません。
外部サブルーチンは、呼び出し元オブジェクトが使用するグローバルデータエリア(GDA)にアクセスできます。
また、PERFORM
ステートメントを使用して、呼び出し元オブジェクトから外部サブルーチンにパラメータを渡すことができます。これらのパラメータは、サブルーチンの DEFINE DATA
PARAMETER
ステートメント、またはサブルーチンが使用するパラメータデータエリア(PDA)に定義する必要があります。
また、外部サブルーチンは、そのサブルーチン内のみで使用するフィールドを定義するローカルデータエリア(LDA)を持つことができます。ただし、外部サブルーチンが独自のグローバルデータエリア(GDA)を持つことはできません。
外部サブルーチンはアプリケーション独立変数(AIV)にもアクセスできます。詳細については、『ステートメント』ドキュメントの「アプリケーションに依存しない変数の定義」を参照してください。
通常、サブプログラムは、アプリケーション内の異なるオブジェクトで使用される機能を実装します。
サブプログラムは単独では実行できません。これは、他のオブジェクトから呼び出す必要があります。呼び出し元オブジェクトは、プログラム、関数、サブプログラム、サブルーチン、またはヘルプルーチンのいずれでもかまいません。
サブプログラムは、CALLNAT
ステートメントを使用して呼び出されます。
CALLNAT
ステートメントが実行されると、呼び出し元オブジェクトの実行は中断され、サブプログラムが実行されます。このサブプログラムの実行後は、呼び出し元オブジェクトの実行が CALLNAT
ステートメントの次のステートメントから続行されます。
CALLNAT
ステートメントを使用して、パラメータを呼び出し元オブジェクトからサブプログラムに渡すことができます。これらのパラメータは、呼び出し元オブジェクトからサブプログラムが使用できる唯一のデータです。これらは、サブプログラムの DEFINE DATA
PARAMETER
ステートメント、またはサブプログラムが使用するパラメータデータエリア(PDA)に定義する必要があります。
また、サブプログラムは、その中で使用するフィールドを定義する独自のローカルデータエリア(LDA)を持つことができます。
サブプログラムがサブルーチンやヘルプルーチンを呼び出す場合は、そのようなサブルーチンやヘルプルーチンと共有する独自のグローバルデータエリア(GDA)を設定することもできます。
さらに、サブプログラムはアプリケーション独立変数(AIV)にアクセスできます。詳細については、『ステートメント』ドキュメントの「アプリケーションに依存しない変数の定義」を参照してください。
通常、関数は、アプリケーション内の異なるオブジェクトで使用される機能を実装します。
関数は、Natural が提供する標準システム関数(関連ドキュメントを参照)ではなく、ユーザー定義の機能を提供します。
関数は、呼び出し元オブジェクトによって使用される結果値を返します。結果値は、関数で使用可能なデータから計算されます。
ファンクションオブジェクトには、DEFINE FUNCTION
と END
ステートメントで定義される単一の関数が含まれます。
関数自体はファンクションコールによって呼び出されます。
ファンクションコールを使用して、パラメータを呼び出し元オブジェクトから関数に渡すことができます。これらのパラメータは、呼び出し元オブジェクトから関数が使用できる唯一のデータです。DEFINE FUNCTION
ステートメントで定義する必要があります。
また、関数は、その関数内で使用するフィールドを定義する独自のローカルデータエリア(LDA)を持つことができます。ただし、関数が独自のグローバルデータエリア(GDA)を持つことはできません。
ファンクションはアプリケーション独立変数(AIV)にアクセスできます。詳細については、『ステートメント』ドキュメントの「アプリケーションに依存しない変数の定義」を参照してください。
必要な場合は、DEFINE PROTOTYPE
ステートメントを使用して関数を呼び出すオブジェクトの結果とパラメータレイアウトを定義できます。
詳細については、「ファンクションコール」を参照してください。
このセクションでは、外部サブルーチン、サブプログラム、関数の機能の比較をまとめています。
以下は、すべての場合で同じです。
ルーチンロジックを形成するプログラミングコードは、Natural ライブラリに格納されている別のオブジェクトにコード化されます。
パラメータは、DEFINE DATA
PARAMETER
ステートメントを使用してオブジェクト内で定義します。
外部サブルーチン、サブプログラム、および関数の違いを次の表に示します。
対象 | 外部サブルーチン | Subprogram | 関数 |
---|---|---|---|
名前の最大長 | 32 文字 | 8 文字 | 32 文字 |
グローバルデータエリア(GDA)の使用 | GDA を呼び出し側と共有します | GDA のインスタンスを作成します | GDA は許可されていません。 |
コンパイル時に呼び出されたオブジェクトの定義に照らした、渡されたパラメータのフォーマット/長さのチェック | チェックされるのは、コンパイラオプション PCHECK が ON に設定されている場合のみ:
|
チェックされるのは、コンパイラオプション PCHECK が ON に設定されている場合のみ:
|
コンパイル時にカタログ化されたファンクションオブジェクトが存在する場合にのみチェックされる |
呼び出し元 | PERFORM ステートメントによって呼び出される
|
CALLNAT ステートメントによって呼び出される
|
ファンクションコールによって呼び出される
ファンクションコールは、ステートメントで、読み取り専用オペランドの代わりに使用できます。ファンクションコールはステートメントとしても使用できます。 |
コンパイル/実行時に呼び出されるオブジェクトの決定 | コンパイル時に決定 | CALLNAT ステートメントに使用されるオペランドに応じて、コンパイル時または実行時に決定される
|
ファンクションコールに使用されるオペランドに応じて、コンパイル時または実行時に決定される |
ステートメントでの結果値の使用 | 結果の値は、ステートメント内のオペランドとして使用されるパラメータに割り当てられている必要があります。 | 結果の値は、ステートメント内のオペランドとして使用されるパラメータに割り当てられている必要があります。 | ファンクションコールの結果は、ファンクションコールを含むステートメントでオペランドとして使用されます。 |
次の例では、ファンクションコールとサブプログラムの呼び出しを比較しています。
次の例は、ファンクションを呼び出すプログラムと、DEFINE FUNCTION
ステートメントで作成されたファンクション定義を示しています。
** Example 'FUNCAX01': Calling a function (Program) ************************************************************************ * WRITE 'Function call' F#ADD(< 2,3 >) /* Function call. /* No temporary variables needed. * END
** Example 'FUNCAX02': Calling a function (Function) ************************************************************************ DEFINE FUNCTION F#ADD RETURNS #RESULT (I4) DEFINE DATA PARAMETER 1 #SUMMAND1 (I4) BY VALUE 1 #SUMMAND2 (I4) BY VALUE END-DEFINE /* #RESULT := #SUMMAND1 + #SUMMAND2 /* END-FUNCTION * END
代わりにサブプログラム呼び出しを使用してファンクションコールの例と同じ機能を実装するには、一時変数を指定する必要があります。
以下の例は、サブプログラムを呼び出すプログラムであり、一時変数が使用されています。
** Example 'FUNCAX03': Calling a subprogram (Program) ************************************************************************ DEFINE DATA LOCAL 1 #RESULT (I4) INIT <0> END-DEFINE * CALLNAT 'FUNCAX04' #RESULT 2 3 /* Result is stored in #RESULT. * WRITE '=' #RESULT /* Print out the result of the /* subprogram. * END
** Example 'FUNCAX04': Calling a subprogram (Subprogram) ************************************************************************ DEFINE DATA PARAMETER 1 #RESULT (I4) BY VALUE RESULT 1 #SUMMAND1 (I4) BY VALUE 1 #SUMMAND2 (I4) BY VALUE END-DEFINE * #RESULT := #SUMMAND1 + #SUMMAND2 * END