バージョン 6.3.3
 —  プログラミングガイド  —

ユーザー定義関数

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


ユーザー定義関数について

関数を使用すると、サブプログラムと同様に、データを受信したり、データを変更したり、結果を呼び出し側モジュールに渡したりすることができます。 サブプログラムを上回る関数の利点として、追加の一時変数を必要とすることなく、直接ステートメントおよび式でファンクションコールを使用できることが挙げられます。

通常、関数に渡されたパラメータに応じて関数内で結果が作成され、その結果が呼び出し側のオブジェクトに戻されます。 呼び出し側のモジュールに別の値を戻す場合は、パラメータを使用してこれを行います。「サブプログラム」を参照してください。

ファンクションコードが完全に実行された後、コントロールは呼び出し側のオブジェクトに戻され、プログラムはファンクションコールの後に続くステートメントを続行します。

詳細については、以下も参照してください。

Top of page

ファンクションコールとサブプログラムコールの違い

以下の 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

Top of page

ファンクション定義(DEFINE FUNCTION)

ファンクション定義には、ファンクションが呼び出されたときに実行される Natural コードが含まれています。 サブプログラムと同様に、Natural オブジェクトを作成する必要があります。この場合は、ファンクション定義を含む "ファンクション" タイプのオブジェクトを作成します。 ファンクション定義は、Natural ステートメント DEFINE FUNCTION を使用して作成します。

ファンクションコール自体は、実行可能コードを含むどのオブジェクトタイプでも構いません。

Top of page

プロトタイプ定義(DEFINE PROTOTYPE)

ファンクションコールをコンパイルするためには、戻り値の format-length/array-definition に関する情報が Natural に必要です。 その後、この情報はプロトタイプ定義のコンパイラから使用できるようになります。 この定義は、Natural ステートメント DEFINE PROTOTYPE を使用して作成します。 戻されるパラメータの定義を含めることもできます。この定義は、その後コンパイル時にチェックされます。

Natural では、ランタイム時に初めて "呼び出し側" のオブジェクトと "呼び出された" オブジェクト間が接続されるため、コンパイル時にどのファンクションタイプの戻り値で処理しているのかがコンピュータで認識されることはありません。 これは、ファンクションを含むオブジェクトが必ずしもコンパイル時に存在する必要はないためです。 このため、コンパイル時に format-length/array-definition を生成プログラムに組み込めるよう、プロトタイプ定義が作成されます。

プロトタイプ定義には決して実行可能コードが含まれないことに注意してください。 プロトタイプ定義には、ファンクションコールに関する情報、つまり戻り値または戻されるパラメータの format/length/array-definition のみが含まれます。

Top of page

記号および変数ファンクションコール

変数ファンクションコールを定義するには、常に DEFINE PROTOTYPE VARIABLE ステートメントを使用する必要があります。 使用しなかった場合、ファンクションコールは暗黙的な記号ファンクションコールであると想定されます。

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

Top of page

自動または暗黙的なプロトタイプ定義(APT)

明示的プロトタイプ定義(EPT)も PT 節も存在しない場合、生成プログラムでプロトタイプ定義の検索が行われます。 詳細については、下記の「可能なプロトタイプ定義の組み合わせ」を参照してください。

Top of page

プロトタイプキャスト(PT 節)

特定のファンクションの該当プロトタイプを見つけるために、Natural によってファンクション名を持つプロトタイプが検索されます。 それ以外の場合、ファンクションコールは記号ファンクションコールであると想定されます。 この場合、ファンクションコールでキーワード PT= を使用して、ファンクション "署名" を定義する必要があります。

Top of page

戻り値の中間結果(IR 節)

この節を使用すると、明示的または暗示的なプロトタイプ定義を使用しないで、ファンクションコールの戻り値のフォーマットおよび長さを指定することができます。つまり、中間結果を明示的に指定できます。 詳細については、「ファンクションコール」の intermediate-result-definition を参照してください。

Top of page

可能なプロトタイプ定義の組み合わせ

以下の表では、DEFINE PROTOTYPE ステートメントとファンクションコールで使用可能な節、またはそのいずれかを使用した場合に可能なさまざまな構文の組み合わせに基づいて、プロトタイプ定義の影響を説明しています。 以下の組み合わせは、属するファンクションコールにのみ有効なファンクションプロトタイプ部分を定義するために使用できます。

ケース 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)は起動されません。

結論として、上記のケースから以下の一般的ルールが導き出されます。

Top of page

再帰的なファンクションコール

ファンクションを再帰的に呼び出す場合、ファンクションプロトタイプがファンクション定義に含まれているか、または 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

Top of page

ステートメントおよび式でのファンクションの動作

オペランドの代わりに、ステートメントまたは式で直接ファンクションコールを使用することができます。 ただし、使用できるのは、オペランドが修正不可能な場合のみです。

すべてのファンクションコールは、コンパイル時に分析される構文の順序に従って実行されます。 ファンクションコールの結果は内部一時変数に保存され、ステートメントまたは式に渡されます。

この一定の順序により、例えばステートメントの出力に意図しない影響を与えることなく、ファンクション内で標準出力を実行することができます。

例:

プログラム:

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

Top of page

ステートメントとしてのファンクションの使用

ファンクションは、ステートメントおよび式とは関係なく、ステートメントとして呼び出すこともできます。 この場合、戻り値(定義されていると仮定)は考慮されません。

ただし、オプションのオペランドリストの後に独立したファンクションを宣言する場合、オペランドリストの後にセミコロンを付けて、ファンクションコールがオペランドリストの一部ではないことを明確にする必要があります。

例:

プログラムオブジェクト:

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

Top of page