プログラムと下位ルーチン

このドキュメントでは、ルーチン、つまり下位プログラムとして呼び出すことができるオブジェクトタイプについて説明します。

ヘルプルーチンとマップは他のオブジェクトからも呼び出されますが、厳密にはルーチンではないため、個別のドキュメントで説明しています。「ヘルプルーチン」および「マップ」を参照してください。

基本的に、プログラム、サブプログラムおよびサブルーチンは、データの受け渡し方法やお互いのデータエリアを共有できるかどうかなどがそれぞれ異なります。したがって、どのオブジェクトタイプをどの目的に使用するかの決定は、アプリケーションのデータ構造に大きく依存します。

このドキュメントでは、次のトピックについて説明します。


モジュラーアプリケーション構造

一般に、Natural アプリケーションは単一の巨大なプログラムで構成されるのではなく、複数のオブジェクトモジュールに分割されます。これらの各オブジェクトは、管理可能なサイズの機能ユニットであり、各オブジェクトは明確に定義された方法でアプリケーションの他のオブジェクトに接続されています。これにより、適切に構造化されたアプリケーションが提供されるため、開発およびその後のメンテナンスが大幅に容易かつ迅速に行うことができるようになります。

メインプログラムの実行中には、他のプログラム、サブプログラム、サブルーチン、ヘルプルーチン、およびマップを呼び出すことができます。続いてこれらのオブジェクトが他のオブジェクトを呼び出すことができます。例えば、サブルーチンが別のサブルーチンを呼び出すこともできます。したがって、アプリケーションのオブジェクト指向構造は非常に複雑になり、複数のレベルに拡張される可能性があります。

呼び出されるオブジェクトの複数レベル

呼び出される各オブジェクトは、それを呼び出したオブジェクトのレベルの 1 つ下のレベルになります。つまり、下位オブジェクトが呼び出されるたびに、レベル番号は 1 つ増加します。

直接実行されるプログラムはすべてレベル 1 です。メインプログラムによって直接呼び出されるサブプログラム、サブルーチン、マップ、ヘルプルーチンはレベル 2 になります。このサブルーチンがサブルーチンを呼び出すと、呼び出されたサブルーチンはレベル 3 になります。

別のオブジェクト内から FETCH ステートメントで呼び出されたプログラムは、メインプログラムとして分類され、レベル 1 から動作します。ただし、FETCH RETURN で呼び出されたプログラムは、下位プログラムとして分類され、呼び出し元オブジェクトの 1 つ下のレベルが割り当てられます。

次の図は、呼び出されるオブジェクトの複数レベルの例と、これらのレベルがどのように数えられるかを示しています。

現在実行中のオブジェクトのレベル番号を確認する場合は、システム変数 *LEVEL を使用できます。詳細については『システム変数』ドキュメントを参照してください。

下位ルーチンを呼び出すときの処理フロー

下位ルーチン、つまりサブプログラム、外部サブルーチン、プログラムまたは関数をそれぞれ呼び出す CALLNATPERFORM、または FETCH RETURN ステートメントまたはファンクションコールが実行されると、呼び出し元オブジェクトの実行は中断され、該当する下位ルーチンの実行が始まります。

下位ルーチンの実行は、END ステートメントに到達するまで、または ESCAPE ROUTINE または ESCAPE MODULE ステートメントの実行によって下位ルーチンの処理が停止されるまで継続します。

いずれの場合も、呼び出し元オブジェクトの処理は、下位ルーチンを呼び出すために使用された CALLNATPERFORM または FETCH RETURN ステートメントの次のステートメントから続行します。

ファンクションコールの場合、呼び出し元オブジェクトのその後の処理は、ファンクションコールを含むステートメントで継続されます。

例:

プログラム

プログラムは単独で実行し、テストできます。

  • ソースプログラムをカタログ化(コンパイル)して実行するには、システムコマンド RUN を使用します。

  • すでにカタログ化オブジェクトとして存在するプログラムを実行するには、システムコマンド EXECUTE を使用します。

プログラムは、他のオブジェクトから FETCH または FETCH RETURN ステートメントで呼び出すこともできます。呼び出し元オブジェクトは、別のプログラム、サブルーチンサブプログラム関数ヘルプルーチン、またはマップの処理ルールのいずれでもかまいません。

  • プログラムが FETCH RETURN で呼び出されると、呼び出し元オブジェクトの実行は終了されるのではなく中断され、フェッチされたプログラムは下位プログラムとしてアクティブになります。FETCH されたプログラムの実行が終了すると、呼び出し元オブジェクトが再びアクティブになり、その実行は FETCH RETURN ステートメントの次のステートメントから継続します。

  • プログラムが FETCH で呼び出されると、呼び出し元オブジェクトの実行は終了し、FETCH されたプログラムがメインプログラムとしてアクティブになります。呼び出し元オブジェクトが、フェッチされたプログラムの終了時に再びアクティブになることはありません。

以下では次のトピックについて説明します。

FETCH RETURN で呼び出されるプログラム

FETCH RETURN で呼び出されるプログラムは、呼び出し元オブジェクトが使用するグローバルデータエリア(GDA)にアクセスできます。

また、あらゆるプログラムには独自のローカルデータエリア(LDA)があり、そのプログラム内だけで使用されるフィールドを定義します。さらに、プログラムはアプリケーション独立変数(AIV)にアクセスできます。詳細については、『ステートメント』ドキュメントの「アプリケーションに依存しない変数の定義」を参照してください。

ただし、FETCH RETURN で呼び出されたプログラムが独自のグローバルデータエリア(GDA)を持つことはできません。

FETCH で呼び出されるプログラム

メインプログラムとして FETCH で呼び出されるプログラムは、上記の図に示したように、通常は独自のグローバルデータエリアを設定します。ただし、呼び出し元オブジェクトが設定したものと同じグローバルデータエリアを使用することもできます。

注意:
ソースプログラムは、RUN ステートメントで呼び出すこともできます。『ステートメント』ドキュメントの RUN ステートメントを参照してください。

サブルーチン

通常、サブルーチンは、アプリケーション内の異なるオブジェクトで使用される機能を実装します。

サブルーチンを構成するステートメントは、DEFINE SUBROUTINE... END-SUBROUTINE ステートメントブロック内に定義する必要があります。

サブルーチンは PERFORM ステートメントによって呼び出されます。

サブルーチンには、インラインサブルーチン外部サブルーチンがあります。

  • インラインサブルーチン
    インラインサブルーチンは、このサブルーチンを呼び出す PERFORM ステートメントが含まれるオブジェクト内に定義されるものです。

  • 外部サブルーチン
    外部サブルーチンは、このサブルーチンを呼び出すオブジェクトの外部にある別のサブルーチンタイプのオブジェクト内に定義されるものです。

同じオブジェクト内で繰り返し実行されるコードブロックがある場合は、インラインサブルーチンを使用すると便利です。次に、DEFINE SUBROUTINE ステートメントブロック内でこのブロックを 1 度だけコードし、いくつかの PERFORM ステートメントで呼び出すだけです。

以下では次のトピックについて説明します。

インラインサブルーチン

インラインサブルーチンは、タイプがプログラム、ファンクション、サブプログラム、サブルーチン、またはヘルプルーチンのオブジェクト内に含めることができます。

インラインサブルーチンで使用できるデータ

オブジェクトに含まれるインラインサブルーチンは、そのオブジェクト内のすべてのデータフィールドにアクセスできます。

外部サブルーチン

外部サブルーチン、つまりサブルーチンタイプのオブジェクトは単独では実行できません。これは、他のオブジェクトから呼び出す必要があります。呼び出し元オブジェクトは、プログラム、関数、サブプログラム、サブルーチン、ヘルプルーチン、またはマップの処理ルールのいずれでもかまいません。

外部サブルーチンで使用できるデータ

外部サブルーチンは、呼び出し元オブジェクトが使用するグローバルデータエリア(GDA)にアクセスできます。

また、PERFORM ステートメントを使用して、呼び出し元オブジェクトから外部サブルーチンにパラメータを渡すことができます。これらのパラメータは、サブルーチンの DEFINE DATA PARAMETER ステートメント、またはサブルーチンが使用するパラメータデータエリア(PDA)に定義する必要があります。

また、外部サブルーチンは、そのサブルーチン内のみで使用するフィールドを定義するローカルデータエリア(LDA)を持つことができます。ただし、外部サブルーチンが独自のグローバルデータエリア(GDA)を持つことはできません。

外部サブルーチンはアプリケーション独立変数(AIV)にもアクセスできます。詳細については、『ステートメント』ドキュメントの「アプリケーションに依存しない変数の定義」を参照してください。

Subprogram

通常、サブプログラムは、アプリケーション内の異なるオブジェクトで使用される機能を実装します。

サブプログラムは単独では実行できません。これは、他のオブジェクトから呼び出す必要があります。呼び出し元オブジェクトは、プログラム、関数、サブプログラム、サブルーチン、またはヘルプルーチンのいずれでもかまいません。

サブプログラムは、CALLNAT ステートメントを使用して呼び出されます。

CALLNAT ステートメントが実行されると、呼び出し元オブジェクトの実行は中断され、サブプログラムが実行されます。このサブプログラムの実行後は、呼び出し元オブジェクトの実行が CALLNAT ステートメントの次のステートメントから続行されます。

サブプログラムで使用可能なデータ

CALLNAT ステートメントを使用して、パラメータを呼び出し元オブジェクトからサブプログラムに渡すことができます。これらのパラメータは、呼び出し元オブジェクトからサブプログラムが使用できる唯一のデータです。これらは、サブプログラムの DEFINE DATA PARAMETER ステートメント、またはサブプログラムが使用するパラメータデータエリア(PDA)に定義する必要があります。

また、サブプログラムは、その中で使用するフィールドを定義する独自のローカルデータエリア(LDA)を持つことができます。

サブプログラムがサブルーチンやヘルプルーチンを呼び出す場合は、そのようなサブルーチンやヘルプルーチンと共有する独自のグローバルデータエリア(GDA)を設定することもできます。

さらに、サブプログラムはアプリケーション独立変数(AIV)にアクセスできます。詳細については、『ステートメント』ドキュメントの「アプリケーションに依存しない変数の定義」を参照してください。

関数

通常、関数は、アプリケーション内の異なるオブジェクトで使用される機能を実装します。

関数は、Natural が提供する標準システム関数(関連ドキュメントを参照)ではなく、ユーザー定義の機能を提供します。

関数は、呼び出し元オブジェクトによって使用される結果値を返します。結果値は、関数で使用可能なデータから計算されます。

ファンクションオブジェクトには、DEFINE FUNCTIONEND ステートメントで定義される単一の関数が含まれます。

関数自体はファンクションコールによって呼び出されます。

関数で使用可能なデータ

ファンクションコールを使用して、パラメータを呼び出し元オブジェクトから関数に渡すことができます。これらのパラメータは、呼び出し元オブジェクトから関数が使用できる唯一のデータです。DEFINE FUNCTION ステートメントで定義する必要があります。

また、関数は、その関数内で使用するフィールドを定義する独自のローカルデータエリア(LDA)を持つことができます。ただし、関数が独自のグローバルデータエリア(GDA)を持つことはできません。

ファンクションはアプリケーション独立変数(AIV)にアクセスできます。詳細については、『ステートメント』ドキュメントの「アプリケーションに依存しない変数の定義」を参照してください。

必要な場合は、DEFINE PROTOTYPE ステートメントを使用して関数を呼び出すオブジェクトの結果とパラメータレイアウトを定義できます。

graphics/pgfunction.png

詳細については、「ファンクションコール」を参照してください。

外部サブルーチン、サブプログラム、関数の比較

このセクションでは、外部サブルーチン、サブプログラム、関数の機能の比較をまとめています。

以下は、すべての場合で同じです。

  • ルーチンロジックを形成するプログラミングコードは、Natural ライブラリに格納されている別のオブジェクトにコード化されます。

  • パラメータは、DEFINE DATA PARAMETER ステートメントを使用してオブジェクト内で定義します。

外部サブルーチン、サブプログラム、および関数の違いを次の表に示します。

対象 外部サブルーチン Subprogram 関数
名前の最大長 32 文字 8 文字 32 文字
グローバルデータエリア(GDA)の使用 GDA を呼び出し側と共有します GDA のインスタンスを作成します GDA は許可されていません。
コンパイル時に呼び出されたオブジェクトの定義に照らした、渡されたパラメータのフォーマット/長さのチェック チェックされるのは、コンパイラオプション PCHECKON に設定されている場合のみ: チェックされるのは、コンパイラオプション PCHECKON に設定されている場合のみ: コンパイル時にカタログ化されたファンクションオブジェクトが存在する場合にのみチェックされる
呼び出し元 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 

ファンクション F#ADD の定義

** 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 

呼び出される側のサブプログラム FUNCAX04

** 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