呼び出し可能なユーザ定義コードモジュール
この章では、ObjectScript コードのユーザ定義モジュールを作成して呼び出す方法を説明します。このようなコード単位として、ユーザ定義の関数、プロシージャ、メソッド、ルーチン、またはサブルーチンが可能です。
他の言語と同様に、ObjectScript は直接呼び出すことができる名前付きのコード・ブロックを生成できます。このようなブロックをプロシージャともいいます。ObjectScript 用語で厳密に言うと、プロシージャであるコード・ブロックは、特定の構文と構造を持ちます。
プロシージャ定義の構文は、以下のようになります。
ProcedureName(Parameters) [PublicVariables]
{
/* code goes here */
QUIT ReturnValue
}
ここで ProcedureName としているプロシージャの要素は以下のとおりです。
-
パラメータ (0 か 1) — あらゆるタイプが可能です。一般的な ObjectScript 構文と同様、プロシージャの定義時にタイプを宣言する必要はありません。既定で、パラメータは (参照渡しではなく) 値によって渡されます。指定がない限り、その範囲はプロシージャに対しローカルです。パラメータの詳細は、“プロシージャ・パラメータ” セクションを参照してください。
-
パブリック変数への参照 (0 かそれ以上) — あらゆるタイプが可能です。プロシージャは、変数の値を参照することも、設定することもできます。パブリック変数の詳細は、“プロシージャ変数” セクションを参照してください。
-
プロシージャの Public 宣言 (オプション) — 既定では、プロシージャはプライベートであるため、同じルーチン内からのみ、そのプロシージャを呼び出すことができます (ObjectScript 用語で、ルーチンとは 2 つ以上のプロシージャ、あるいはユーザ定義のコード・ブロックを含むファイルのことです)。また、プロシージャ名の後に PUBLIC キーワードを使用して、パブリック・プロシージャを生成できます。パブリック・プロシージャは他のルーチンやメソッドからも呼び出せます。パブリック・プロシージャとプライベート・プロシージャの詳細は、“パブリック・プロシージャとプライベート・プロシージャ” を参照してください。
-
コード — プロシージャ内のコードは、ObjectScript で使用できるすべての機能を備えています。プロシージャ・コードには、Basic と Java も使用できます。コードは中括弧で区切られ、プロシージャ・ブロックとも呼ばれます。
-
返り値 (オプション) — プロシージャが返す値です。標準の ObjectScript 式である必要があります。プロシージャ内のフロー制御では、計算式の値、QUIT 文、またはその両方を使用して、さまざまな返り値を指定できます。
Caché 5 以前のバージョン (とそのコード記述) に精通したユーザにとって、プロシージャを使用すると、これまでサブルーチンとユーザ定義関数で使用できたコード記述よりも高度な記述が可能です。プロシージャ・パラメータは、プロシージャでのスコープで自動的にローカルになります。これらのパラメータはプロシージャにとってプライベートであり、シンボル・テーブルとやり取りしないので、他の値を上書きしないようにする NEW コマンドは不要です。また、パブリック変数を明示的に宣言することで、例えば銀行全体の金利といったアプリケーション内のグローバル変数を参照できます。あるいは、他のアプリケーションに有効なプロシージャ内で、変数の値を生成、設定することもできます。
プロシージャは、ObjectScript ルーチンの一種です。
Caché は、多くのシステム関数を備えています。内部関数とも呼ばれるこの関数の詳細は、"ObjectScript ランゲージ・リファレンス" で説明しています。システム関数の呼び出しは、接頭語 “$” で識別されます。
プロシージャ、ルーチン、サブルーチン、関数、メソッドの概要
この章では、プロシージャを使用して、独自のコードを実装する方法について説明します。ユーザ定義の機能実装において、この方法をお勧めします。Caché ドキュメントでは、全体的にプロシージャ、ルーチン、サブルーチン、関数、メソッドについて記述しています。すべての項目が機能を共有していますが、それぞれに独自の特徴もあります。特に、Caché 5.0 リリース以前に作成されたアプリケーションには多くの場合、プロシージャとして実装されていないサブルーチンと関数が組み込まれています。この理由は、独自のコードを実装するために、異なる構造に関しその概要を提供することが重要だからです。
名前付きのユーザ定義のコード・ブロックを記述するのに、最も柔軟で強力な推奨される形式は、プロシージャです。以下はプロシージャの特徴です。
-
プライベートあるいはパブリックです。
-
ゼロ個以上のパラメータを受け取ることができます。
-
その中で生成された変数を、ローカルの範囲として自動的に維持します。
-
範囲外の変数を参照し変更できます。
-
あらゆるタイプの値を返す、あるいは値を返しません。
既定で、メソッドはプロシージャです。このため、この章のプロシージャに関する内容でもすべてメソッドについて説明します。メソッドについての詳細は、ドキュメント "Caché オブジェクトの使用法" の “メソッド” の章を参照してください。
以下はプロシージャとの比較です。
-
サブルーチンは常にパブリックで、値を返しません。
-
関数は常にパブリックで、ローカル変数を明示的に宣言する必要があります (そうしなければ、外部変数を上書きします)。また、必ず返り値があります。
-
既定で、メソッドはクラス定義の一部として指定されるプロシージャで、1 つ以上のオブジェクトあるいはクラスで呼び出すことができます。メソッドを関数として明示的に宣言すると、付随するすべての特徴も関数に属するため、これはお勧めできません。
-
ルーチンは、ObjectScript プログラムです。1 つ以上のプロシージャ、サブルーチン、関数、あるいはこれらの組み合わせになります。
ルーチン
ルーチンは、ユーザ記述コードの呼び出し可能なブロックで、ObjectScript プログラムです。ルーチンは汎用的な処理を実行します。ルーチン名は、保存時に選択する .MAC ファイル名から付けられます。ルーチンが値を返すかどうかによって、以下の構文の 1 つあるいは両方を使用して、ルーチンを呼び出すことができます。
DO ^RoutineName
SET x = $$^RoutineName
ルーチンはネームスペース内で定義されます。拡張ルーチン参照を使用し、以下のようにして現在のネームスペース以外のネームスペースで定義されたユーザ定義ルーチンを実行できます。
DO ^|"SAMPLES"|RoutineName
一般的に、ルーチンは、サブルーチン、メソッド、プロシージャのコンテナとして機能します。
ルーチンは、コード・ブロックの最初にあるラベル (タグともいいます) で識別されます。このラベルがルーチン名です。(通常) ラベルの後ろに続く括弧内には、呼び出し元のプログラムからルーチンに渡すパラメータ・リストが記述されます。
ルーチンをファイルに保存する場合、ファイル名には、アンダースコア (“_”)、ハイフン (“-”)、セミコロン (“;”) を使用できません。このような文字を含む名前は無効です。
サブルーチン
サブルーチンは、ルーチン内で名前の付いたコード・ブロックです。通常、サブルーチンはラベルから始まり、QUIT 文で終了します。パラメータを受け取ることができますが、値は返しません。サブルーチンを呼び出すには、以下の構文を使用します。
DO Subroutine^Routine
Subroutine は、Routine ファイル (Routine.MAC) 内のコード・ブロックです。
以下は、サブルーチンの形式です。
Label(parameters) // comment
// code
QUIT // note that QUIT has no arguments
サブルーチンの詳細は、従来のコードを説明している以下の "サブルーチン" セクションを参照してください。
{ } 括弧でコードと QUIT 文を囲む場合、サブルーチンはプロシージャとなるため、プロシージャとして処理されます。
関数
Caché は、"ObjectScript ランゲージ・リファレンス" で説明しているように、多くのシステム関数 (“内部” 関数とも呼ばれる) を備えています。このセクションでは、ユーザ定義 (“extrinsic”) 関数について説明します。
関数は、ルーチン内で名前の付いたコード・ブロックです。通常、関数はラベルから始まり、QUIT 文で終了します。パラメータを受け取り、値も返します。関数の呼び出しにおいて、以下の 2 つが有効な形式の構文となります。
SET rval=$$Function() /* returning a value */ DO Function^Routine /* ignoring the return value */
Function は、Routine ファイル (Routine.MAC) 内のコード・ブロックです。両方の構文形式で、拡張ルーチン参照を使用し、異なるネームスペースにある関数を実行できます。
以下は、関数の形式です。
Label(parameters)
// code
QUIT ReturnValue
{ } 括弧でコードと QUIT 文を囲む場合、関数はプロシージャとなるため、プロシージャとして処理されます。プロシージャは既定によりプライベートであるため、以下のように PUBLIC キーワードを指定することが必要になる場合もあります。
Label(parameters) PUBLIC {
// code
QUIT ReturnValue }
以下の例では、簡単な関数 (MyFunc) を指定したうえで、それを呼び出し、2 つのパラメータを渡して、さらに返される値を受け取ります。
Main ;
TRY {
KILL x
SET x=$$MyFunc(7,10)
WRITE "returned value is ",x,!
RETURN
}
CATCH { WRITE $ZERROR,!
}
MyFunc(a,b)
SET c=a+b
QUIT c
関数を呼び出すコードは返される値を無視できますが、関数の QUIT コマンドでは返される値を指定することが必要となります。引数なしの QUIT にて関数の終了を試みると、<COMMAND> エラーが生成されます。<COMMAND> エラーによって関数を呼び出したコールの場所が指定され、呼び出された関数内の引数なし QUIT コマンドのオフセット位置を指定するメッセージが続きます。詳細は "$ZERROR" を参照してください。
関数の詳細は、従来のコードを説明している以下の "関数" セクションを参照してください。
プロシージャの定義
他のプログラミング言語と同様、プロシージャは特定のタスクを実行する一連の ObjectScript コマンド (大規模なルーチン) です。If 文の構造のように、プロシージャのコードは中括弧 ({}) で囲まれています。
プロシージャは、パブリックかプライベートのいずれかで変数を定義できます。例えば、以下は “MyProc” というプロシージャの例です。
MyProc(x,y) [a,b] PUBLIC {
Write "x + y = ", x + y
}
これは、2 つのパラメータ x と y を取る “MyProc” というパブリック・プロシージャを定義しています。2 つのパブリック変数 a と b も定義しています。このプロシージャで使用される他のすべての変数 (この場合、x と y) はプライベート変数です。
既定では、プロシージャはプライベートであるため、同じルーチン内からのみ呼び出すことができますまた、プロシージャ名の後に PUBLIC キーワードを使用して、パブリック・プロシージャを生成できます。パブリック・プロシージャは他のルーチンからも呼び出せます。
プロシージャでは、定義済みのパラメータは必要ありません。パラメータでプロシージャを生成する場合、ラベルの直後に括弧で囲んだ変数リストを置きます。
プロシージャの呼び出し
プロシージャを呼び出すには、DO コマンドを発行してプロシージャを指定するか、“$$” 構文を使用して関数として呼び出します。プロシージャが他のプログラムから実行できるかどうか (パブリック)、あるいは存在するプログラム内でのみ実行できるかどうか (プライベート) を制御できます。DO コマンドで呼び出す場合、プロシージャは値を返しません。関数呼び出しとして呼び出す場合は、値を返します。“$$” 形式は最も機能的であり、一般的に好まれる形式です。
$$ 接頭語の使用法
式を使用できるコンテキスト内で、ユーザ定義関数を呼び出すことができます。以下の形式で、ユーザ定義関数を呼び出します。
$$name([param[ ,...]])
以下を示します。
-
name 関数の名前を指定します。関数の定義場所によって、以下のように名前を指定できます。
-
label は、現在のルーチンにある行ラベルです。
-
label^routine は、ディスクにある名前付きルーチンの行ラベルです。
-
^routine は、ディスクに保存されているルーチンです。ルーチンは、実行する関数のコードのみを含んでいる必要があります。
-
-
param は関数に渡される値を指定します。提供されるパラメータは、実パラメータ・リストと呼ばれます。これらは、その関数に関して定義された仮パラメータ・リストと一致する必要があります。例えば、関数コードには 2 つのパラメータがあり、1 番目は数値、2 番目は文字リテラルとします。この場合、1 番目のパラメータに文字リテラル値、2 番目に数値を指定すると、関数は不正確な値を返すか、あるいはエラーを生成する可能性があります。仮パラメータ・リストのパラメータでは、関数から NEW コマンドが必ず呼び出されます。詳細は、NEW コマンドの説明を参照してください。パラメータは、値または参照によって渡されます。詳細は、"パラメータ渡し" を参照してください。関数の仮パラメータ・リストに記載されている数より少数のパラメータを関数に渡すと、パラメータの既定値が使用されます (定義されている場合)。既定値がない場合、パラメータは未定義のままとなります。
DO コマンドの使用法
DO コマンドを使用して、ユーザ定義関数を呼び出すことができます (DO コマンドを使用して、システム関数を呼び出すことはできません)。DO コマンドで呼び出した関数は、値を返しません。つまり、関数は必ず返り値を生成しますが、DO コマンドはこの返り値を無視します。したがって、ユーザ定義関数を呼び出す DO コマンドの用途は大幅に制限されます。
DO コマンドを使用してユーザ定義関数を呼び出すには、以下の構文でコマンドを発行します。
DO label(param[,...])
DO コマンドは、label という関数を呼び出し、param で指定されたパラメータ (存在する場合) を関数に渡します。$$ 接頭語を使用しないことと、パラメータの括弧が必須であることに注意してください。label と param を指定する際には、$$ 接頭語を使用するユーザ定義関数を呼び出すときと同じ規則が適用されます。
関数は、常に値を返す必要があります。しかし、DO コマンドで関数を呼び出す場合、返された値は呼び出し元のプログラムで無視されます。
プロシージャの構文
以下はプロシージャの構文です。
label([param[=default]][,...]) [[pubvar[,...]]] [access] {
code
}
以下の構文で呼び出します。
DO label([param[,...]])
あるいは以下のようになります。
command $$label([param][,...])
以下はその説明です。
label | プロシージャ名です。標準ラベルです。必ず 1 列目から始まります。label に続くパラメータの括弧は必須です。 |
param | プロシージャに必要な各パラメータの変数です。必要なパラメータは、仮パラメータ・リスト と呼ばれます。パラメータ自体はオプションですが (何もない、1 つある、あるいは 1 つ以上の param がある)、括弧は必須です。複数の param 値がある場合、コンマによって区切られます。パラメータは、値または参照で仮パラメータ・リストに渡すことができます。ルーチンであるプロシージャにはパラメータのタイプ情報が記述されていませんが、メソッドであるプロシージャにはこの情報が記述されています。仮パラメータの最大数は 255 個です。 |
default | 直前の param に対するオプションの既定値です。各パラメータに対して既定値を提供しても、省略してもかまいません。仮パラメータに対応する実パラメータが指定されていない場合、または実パラメータが参照渡しされたときに当該のローカル変数に値がない場合は、既定値が適用されます。この既定値はリテラル、つまり引用符で囲まれた数字あるいは文字列です。null 文字列 (“”) も既定値として指定できます。null 文字列は変数を定義するため、これは既定値を指定しないこととは異なります。一方でパラメータ値が未指定の場合や既定値がない場合、変数は未定義となります。リテラル以外の既定値を指定すると、Caché は <PARAMETER> エラーを発行します。 |
pubvar | パブリック変数です。プロシージャによって使用され、他のルーチンとプロシージャで使用できる、パブリック変数の任意のリストです。これは、このプロシージャ内で定義され、他のルーチンでも使用可能な変数、あるいは他のルーチンで定義され、このプロシージャでも使用可能な変数のリストです。pubvar を指定する場合、それを角括弧で囲みます。pubvar を指定しない場合、角括弧は省略できます。複数の pubvar 値がある場合、コンマによって区切ります。Public として宣言されていない変数は、すべてプライベート変数です。プライベート変数は、プロシージャの現在の呼び出しに対してのみ有効です。その変数は、プロシージャの呼び出し時には未定義です。また、QUIT で終了する際に破棄されます。プロシージャがそのプロシージャ外のコードを呼び出すと、プライベート変数は保存されますが、制御がプロシージャに戻るまでは使用できません。また、すべての % 変数は、ここにリストされているかどうかにかかわらず、常にパブリックです。パブリック変数のリストは、このルーチンに対して指定された 1 つ以上の param を含むことができます。 |
access | プロシージャが Public か Private を宣言する任意のキーワードです。利用できる値は 2 つあります。PUBLIC キーワードは、このプロシージャがどのルーチンからでも呼び出しできることを宣言します。PRIVATE キーワードは、このプロシージャが定義されたルーチン内でのみ呼び出されることを宣言します。既定は PRIVATE です。 |
code | 中括弧で囲まれたコード・ブロックです。左中括弧 ({) は、その後ろに続く先頭の文字から少なくとも 1 つのスペースを置く、あるいは 1 行の改行が必要です。同じ行の右中括弧 (}) の後ろにはコードを記述できず、ブランクあるいはコメントのみ置くことができます。通常、右中括弧は 1 列目に記述します。このコード・ブロックは、ラベルによってのみ入力されます。 |
コマンドと引数の間には改行を挿入できません。
それぞれのプロシージャはルーチンの一部として実装され、各ルーチンは複数のプロシージャを持つことができます。
標準的な ObjectScript 構文の他に、ルーチンを管理する特殊な規則があります。ルーチン行はオプションとして、ObjectScript コードの最初にラベル (タグとも呼びます) を、最後にコメントを置くことができます。
インターシステムズでは、ルーチンの 1 行目にルーチン名を表すラベル、その後にタブまたはスペースを置いた後、ルーチンの目的を簡単に説明する短いコメントを入れることをお勧めします。行にラベルを置く場合、タブやスペースを含む残りの行からラベルを離す必要があります。つまり、スタジオを使用してルーチンに行を追加し、ラベルとタブ/スペースに続けて ObjectScript コードを入力するか、ラベルを置かずにタブかスペースに続けて ObjectScript を入力します。したがって、いずれの場合でも、それぞれの行には、最初のコマンドの前にタブかスペースが必要です。
1 行コメントを示す場合、二重スラッシュ (“//”) かセミコロン (“;”) のいずれかを使用します。コメントがコードの後ろに続く場合、スラッシュやセミコロンの前にスペースが必要です。その行にコメントしか記述しない場合、スラッシュやセミコロンの前に、タブかスペースが必要です。当然のことながら、1 行コメントは改行できません。複数行コメントの場合が、コメントの先頭に “/*” を、末尾には “*/” を付けます。
プロシージャ変数
プロシージャとメソッドは両方共、プライベート変数とパブリック変数をサポートします。以下の文はすべてプロシージャとメソッドに同等に適用されます。
プロシージャ内で使用する変数は、自動的に プライベート になります。したがって、これらは宣言する必要がなく、NEW コマンドは不要です。このプロシージャが呼び出すプロシージャの変数を共有したい場合、変数をパラメータとして他のプロシージャに渡します。
パブリック変数の宣言も可能です。パブリック変数は、このプロシージャまたはメソッドの呼び出し元と呼び出し先のすべてのプロシージャおよびメソッドで使用できます。アプリケーションで環境変数として動作するよう、比較的少数のパブリック変数をこの方法で定義します。パブリック変数を定義するには、プロシージャ名とパラメータの後に角括弧内 ([ ]) で変数をリストします。
SAMPLES>WRITE
SAMPLES>DO ^publicvarsexample
setting a
setting b
setting c
The sum is: 6
SAMPLES>WRITE
a=1
b=2
SAMPLES>
以下は、procexample.mac のコードです。
publicvarsexample
; examples of public variables
;
DO proc1() ; call a procedure
QUIT ; end of the main routine
;
proc1() [a, b]
; a private procedure
; "c" and "d" are private variables
{
WRITE !, "setting a" SET a = 1
WRITE !, "setting b" SET b = 2
WRITE !, "setting c" SET c = 3
SET d = a + b + c
WRITE !, "The sum is: ", d
}
パブリック変数とプライベート変数
プロシージャ内では、ローカル変数は “パブリック” あるいは “プライベート” のいずれででも定義できます。パブリック・リスト (pubvar) は、プロシージャ内のどの変数参照がパブリック変数のセットに追加されるかを宣言します。プロシージャ内の他の変数参照はすべて、プロシージャの現在の呼び出しによってのみ参照される、プライベート・セットに追加されます。
プライベート変数は、プロシージャの呼び出し時には未定義です。また、QUIT で終了する際に破棄されます。
プロシージャ内のコードがプロシージャ外のコードを呼び出す場合、プライベート変数は、プロシージャに戻ったときに復元されます。呼び出されたプロシージャやルーチンは、パブリック変数にアクセスできます (それ自身のプライベート変数も同様に使用できます)。したがって、pubvar はこのプロシージャで参照されるパブリック変数と、プロシージャが呼び出すルーチンによって参照できこのプロシージャで使用する変数の両方を指定します。
パブリック・リストが空の場合、すべての変数はプライベートです。この場合、角括弧は任意となります。
名前の先頭に “%” 文字が付く変数は、通常、システムや特定用途のために使用する変数です。Caché は、システムで使用するためにすべての % 変数 (%z 変数および %Z 変数を除く) を予約しています。ユーザ・コードでは、%z または %Z で始まる % 変数のみを使用してください。すべての % 変数は、暗黙的にパブリックです。これらの変数は、(ドキュメント用に) パブリック・リストに記載することもできますが、必須ではありません。
プライベート変数と NEW コマンドで生成される変数
プライベート変数は、NEW で生成される変数とは異なることに注意してください。プロシージャの変数を、他のプロシージャやサブルーチンで使用したい場合、その変数は必ず Public に宣言し、パブリック・リストに記述する必要があります。このプロシージャによってパブリック変数が導入されていれば、それに対して NEW を実行する意味があります。この場合、パブリック変数は、QUIT コマンドでプロシージャを終了するときに自動的に破壊されます。しかし、パブリック変数が保持していた以前の値は保護されます。以下はコードの例です。
MyProc(x,y)[name]{
NEW name
SET name="John"
DO xyz^abc
QUIT
}
変数はパブリックであるため、“abc” ルーチンの “xyz” プロシージャが、name の値 “John” を参照できます。name に対して NEW コマンドを実行すると、“MyProc” プロシージャを呼び出したときに既に存在しているすべての “name” というパブリック変数が保護されます。
NEW コマンドは、プライベート変数ではなくパブリック変数にのみ作用します。プロシージャ内で x がパブリック・リストになく、% 変数でない場合、NEW x または NEW (x) を指定すると違反になります。
仮リスト・パラメータをパブリックにする
プロシージャに仮リスト・パラメータ (例えば MyProc(x,y) の “x” あるいは “y”) が存在し、他のプロシージャ (あるいはこのプロシージャ外のサブルーチン) で使用したい場合、パラメータはパブリック・リストに置く必要があります。
以下はコードの例です。
MyProc(x,y)[x] {
DO abc^rou
}
y ではなく x の値が、“abc^rou” ルーチンで使用可能となります。
パブリック・プロシージャとプライベート・プロシージャ
プロシージャは、“パブリック” あるいは “プライベート” です。プライベート・プロシージャは、プロシージャが定義されたルーチン内でのみ呼び出すことができます。一方パブリック・プロシージャは、どのルーチンからも呼び出すことができます。PUBLIC と PRIVATE キーワードが省略されている場合、既定は “プライベート” です。
以下はその例です。
MyProc(x,y) PUBLIC { }
上記はパブリック・プロシージャを定義しています。
MyProc(x,y) PRIVATE { }
あるいは以下のコードを入力します。
MyProc(x,y) { }
上記 2 つのコードは、プライベート・プロシージャを定義しています。
パラメータ渡し
プロシージャの重要な特徴は、パラメータ渡しに対するサポートです。これは、プロシージャに値または変数をパラメータとして渡すための機能です。当然のことながら、パラメータ渡しは必須ではありません。例えば、パラメータ渡しとしていないプロシージャを使用して、乱数の生成や、既定以外の形式でシステム日付を返す操作などが可能です。それでも、多くの場合、プロシージャではパラメータ渡しを使用します。
パラメータ渡しを設定するには、以下を指定します。
-
プロシージャ呼び出し時の実パラメータ・リスト
-
プロシージャ定義時の仮パラメータ・リスト
Caché がユーザ定義プロシージャを実行するとき、実パラメータ・リストのパラメータが、それに対応する仮リストのパラメータに位置を基準としてマップされます。したがって、実リストの先頭にあるパラメータの値は仮リストの先頭にある変数に配置され、2 番目の値は 2 番目の変数に配置され、以降の値も同様に配置されます。これらのパラメータの照合は、名前ではなく、パラメータの位置で行われます。したがって、実パラメータに使用する変数と仮パラメータに対して使用する変数は、同じ名前を持つ必要はありません (また、通常持つべきではありません)。プロシージャでは、渡された値に該当する変数を仮リストの中で参照することにより、渡された値にアクセスします。
実パラメータ・リストと仮パラメータ・リストの間で、収録されているパラメータの数が異なっていることがあります。
-
実パラメータ・リストにあるパラメータが、仮パラメータ・リストにあるパラメータより少ない場合、仮パラメータ・リストと一致しない要素は未定義となります。以下の例のように、未定義の仮パラメータに使用するデフォルト値を指定できます。
Main /* Passes 2 parameters to a procedure that takes 3 parameters */ SET a="apple",b="banana",c="carrot",d="dill" DO ListGroceries(a,b) WRITE !,"all done" ListGroceries(x="?",y="?",z="?") { WRITE x," ",y," ",z,! }
-
実パラメータ・リストにあるパラメータが、仮パラメータ・リストにあるパラメータより多い場合は、以下の例のように、<PARAMETER> エラーが発生します。
Main /* Passes 4 parameters to a procedure that takes 3 parameters. This results in a <PARAMETER> error */ SET a="apple",b="banana",c="carrot",d="dill" DO ListGroceries(a,b,c,d) WRITE !,"all done" ListGroceries(x="?",y="?",z="?") { WRITE x," ",y," ",z,! }
仮リストにある変数が実リストにあるパラメータよりも多く、既定値が各パラメータに指定されていない場合、余った変数は未定義のままとなります。プロシージャのコードに適切な If テストを記述し、プロシージャの各参照で、使用可能な値が得られることを確認する必要があります。実パラメータ数と仮パラメータ数の一致を簡素化するには、可変個数のパラメータを指定します。
実パラメータの最大数は 254 個です。
ユーザ定義プロシージャにパラメータを渡す場合は、値渡しまたは参照渡しが可能です。同じプロシージャの呼び出しの中で、値渡しと参照渡しを混用できます。
-
プロシージャには、パラメータを値で渡すことも参照で渡すこともできます。
-
サブルーチンには、パラメータを値で渡すことも参照で渡すこともできます。
-
ユーザ定義関数には、パラメータを値で渡すことも参照で渡すこともできます。
-
システム関数には、パラメータを値でのみ渡すことができます。
値渡し
値渡しをする場合は、実パラメータ・リストでリテラル値、式、またはローカル変数 (添え字付きでも添え字なしでも可) を指定します。式を指定した場合、Caché は最初にその式を評価し、次に結果の値を渡します。変数名を使用する場合、Caché からはその変数の現在の値が渡されます。指定したすべての変数名が必ず存在し、値が設定されている必要があります。
プロシージャの仮パラメータ・リストには、添え字なしのローカル変数名を記述します。このリストで明示的な添え字付き変数を指定することはできません。一方、可変個数のパラメータを指定すると、添え字付き変数が暗黙的に作成されます。
Caché は、プロシージャで使用される非パブリック変数をすべて暗黙に生成、宣言します。したがって、呼び出し元のコードで同じ名前を持つ既存の変数は上書きされません。これらの変数の既存の値 (存在する場合) をプログラム・スタックに置きます。QUIT コマンドを呼び出すとき、Caché は各仮変数に対して暗黙の KILL コマンドを実行し、スタックから前の値を復元します。
以下の例で、SET コマンドは 3 つの異なる形式を使用して、参照先の Cube プロシージャに同じ値を渡します。
DO Start()
WRITE "all done"
Start() PUBLIC {
SET var1=6
SET a=$$Cube(6)
SET b=$$Cube(2*3)
SET c=$$Cube(var1)
WRITE !,"a: ",a," b: ",b," c: ",c,!
QUIT 1
}
Cube(num) PUBLIC {
SET result = num*num*num
QUIT result
}
参照渡し
参照渡しの場合、以下の形式を使用して、実パラメータ・リストのローカル変数名あるいは添え字なしの配列名を指定します。
.name
参照渡しでは、プロシージャでまだ参照していない段階であれば、指定された変数名または配列名が実際に存在している必要はありません。参照渡しは、配列名をプロシージャに渡すことができる唯一の方法です。なお、添え字付き変数は参照渡しできません。
-
実パラメータ・リスト : 実パラメータ・リストのローカルな変数名または配列名の前にはピリオドを記述する必要があります。これは、変数が値ではなく、参照で渡されることを指定するものです。
-
仮パラメータ・リスト : 仮パラメータ・リストでは特殊な構文を必要とせずに、参照渡しで変数を受け取ります。仮パラメータ・リストでは、ピリオドを接頭語として使用することはできません。ただし、仮パラメータ・リストの変数名の前にアンパサンド (&) を接頭語として使用することはできます。規約により、この & 接頭語の使用は、変数が参照渡しされることを示します。& 接頭語はオプションであり、処理を実行するものではありません。ソース・コードを読みやすく、かつ保守しやすくするために役立つ規約です。
参照渡しでは、実リストの各変数および配列名が、関数の仮リストの対応する変数名に結合されます。参照渡しは、参照するルーチンと関数の間を双方向で通信する効果的なメカニズムを提供します。関数が仮リストにある変数を変更すると、対応する実リスト上の参照渡しの変数も変更されます。これは、KILL コマンドにも適用されます。仮リストにある参照渡しの変数が関数によって削除されると、対応する実リスト上の変数も削除されます。
実リストで指定した変数あるいは配列名が存在しない場合、関数参照により未定義として処理されます。関数が対応する仮リストの値を変数に代入すると、実の変数あるいは配列もこの値で定義されます。
以下の例では、参照渡しを値渡しと比較しています。変数 a を値で渡し、変数 b を参照で渡します。
Main
SET a="6",b="7"
WRITE "Initial values:",!
WRITE "a=",a," b=",b,!
DO DoubleNums(a,.b)
WRITE "Returned to Main:",!
WRITE "a=",a," b=",b
DoubleNums(foo,&bar) {
WRITE "Doubled Numbers:",!
SET foo=foo*2
SET bar=bar*2
WRITE "foo=",foo," bar=",bar,!
QUIT
}
以下の例では参照渡しを使用して、変数 result を通じて参照元のルーチンと関数の間で双方向通信します。ピリオド接頭語は、result が参照渡しされることを指定します。この関数を実行すると、実パラメータ・リストに result が作成され、関数の仮パラメータ・リストにある z にバインドされます。計算された値は z に代入され、result の参照元のルーチンに渡されます。仮パラメータ・リストの z に対する & 接頭語はオプションであり、機能もありませんが、ソース・コードの明確化に役立ちます。num と powr は、参照渡しではなく値渡しであることに注意してください。以下は、値渡しと参照渡しを組み合わせた例です。
Start ; Raise an integer to a power.
READ !,"Integer= ",num QUIT:num=""
READ !,"Power= ",powr QUIT:powr=""
SET output=$$Expo(num,powr,.result)
WRITE !,"Result= ",output
GOTO Start
Expo(x,y,&z)
SET z=x
FOR i=1:1:y {SET z=z*x}
QUIT z
可変個数のパラメータ
プロシージャでは、可変個数のパラメータが使用可能であることを指定できます。そのように指定するには、vals... のように最後のパラメータ名に 3 つのドットを付加します。このドットを付加するパラメータは、パラメータのリストの最後にあるものとする必要があります。パラメータのリストに記述した唯一のパラメータであってもかまいません。この ... 構文を記述することで、1 つ以上のパラメータを渡すことができるほか、パラメータをまったく渡さないこともできます。
リストのパラメータ間、ならびにリストの最初のパラメータの前と最後のパラメータの後には、スペースと新しい行が認められています。3 つのドットの間の空白は認められていません。
この構文を使用するには、最後のパラメータ名の後に ... が続くシグニチャを指定します。この手法でメソッドに渡す複数のパラメータには、データ型による値またはオブジェクトによる値を設定できるほか、これらの設定によるパラメータを混在して使用することもできます。可変個数のパラメータの使用を指定するパラメータには、任意の有効な識別子名を設定できます。
ObjectScript では、添え字付き変数を作成することで、可変個数のパラメータを渡す処理を扱います。この場合は、渡す変数ごとに 1 つの添え字が作成されます。最上位の変数には、渡すパラメータの個数を記述し、変数の添え字には、渡す値を記述します。
これを、以下の例に示します。ここでは、invals... を仮パラメータ・リストにある唯一のパラメータとしています。ListGroceries(invals...) は、可変個数の値を値渡しで受け取ります。
Main
SET a="apple",b="banana",c="carrot",d="dill",e="endive"
DO ListGroceries(a,b,c,d,e)
WRITE !,"all done"
ListGroceries(invals...) {
WRITE invals," parameters passed",!
FOR i=1:1:invals {
WRITE invals(i),! }
}
以下の例では、2 つの定義済みパラメータの後に最後のパラメータとして args... を使用しています。このプロシージャは、少なくとも 2 つのパラメータ値を受け取る必要があり、そのうえで可変個数のパラメータを追加で受け取ることができます。
Main
SET a=7,b=8,c=9,d=100,e=2000
DO AddNumbers(a,b,c,d,e)
WRITE "all done"
AddNumbers(x,y,morenums...) {
SET sum = x+y
FOR i=1:1:$GET(morenums, 0) {
SET sum = sum + $GET(morenums(i)) }
WRITE "The sum is ",sum,!
QUIT
}
以下の例では、2 つの定義済みパラメータの後に最後のパラメータとして args... を使用しています。このプロシージャが受け取るパラメータの個数は 2 つのみで、可変個数の追加パラメータ args... は 0 です。
Main
SET a=7,b=8,c=9,d=100,e=2000
DO AddNumbers(a,b)
WRITE "all done"
AddNumbers(x,y,morenums...) {
SET sum = x+y
FOR i=1:1:$GET(morenums, 0) {
SET sum = sum + $GET(morenums(i)) }
WRITE "The sum is ",sum,!
QUIT
}
指定に応じて、AddNumbers(x,y,morenums...) は 2 個以上、255 個以下の範囲でパラメータを受け取ることができます。定義済みのパラメータに既定値を指定して AddNumbers(x=0,y=0,morenums...) とすると、このプロシージャは 0 個以上、255 個以下の範囲でパラメータを受け取ることができます。
以下の例では、唯一のパラメータとして args... を使用しています。これは参照渡しで可変個数の値を受け取ります。
Main
SET a=7,b=8,c=9,d=100,e=2000
DO AddNumbers(.a,.b,.c,.d,.e)
WRITE "all done"
AddNumbers(&nums...) {
SET sum = 0
FOR i=1:1:$GET(nums, 0) {
SET sum = sum + $GET(nums(i)) }
WRITE "The sum is ",sum,!
QUIT sum
}
変数パラメータ・リスト params... が参照によって渡されたパラメータを受け取り、params... をルーチンに渡すと、その中間ルーチンは、参照によっても渡される追加パラメータ (params 配列の追加ノード) を追加できます。以下に例を示します。
Main
SET a(1)=10,a(2)=20,a(3)=30
DO MoreNumbers(.a)
WRITE !,"all done"
MoreNumbers(¶ms...) {
SET params(1,6)=60
SET params(1,8)=80
DO ShowNumbers(.params) }
ShowNumbers(&tens...) {
SET key=$ORDER(tens(1,1,""),1,targ)
WHILE key'="" {
WRITE key," = ",targ,!
SET key=$ORDER(tens(1,1,key),1,targ)
}
}
以下の例では、この args... 構文を仮パラメータ・リストと実パラメータ・リストの両方で使用できることを示しています。この例の可変個数のパラメータ (invals...) は、ListNums に値で渡され、そこで 2 倍の値にされたうえで invals... として ListDoubleNums に渡されます。
Main
SET a="1",b="2",c="3",d="4"
DO ListNums(a,b,c,d)
WRITE !,"back to Main, all done"
ListNums(invals...) {
FOR i=1:1:invals {
WRITE invals(i),!
SET invals(i)=invals(i)*2 }
DO ListDoubleNums(invals...)
WRITE "back to ListNums",!
ListDoubleNums(twicevals...)
WRITE "Doubled Numbers:",!
FOR i=1:1:twicevals {
WRITE twicevals(i),! }
QUIT
}
可変個数のパラメータの受け入れが可能なメソッドの詳細は、"Caché オブジェクトの使用法" の “メソッド” の章にある “メソッドに使用する可変長引数” のセクションを参照してください。
プロシージャ・コード
中括弧内のコードがプロシージャのコードですが、従来の ObjectScript コードとは異なります。以下はその詳細です。
-
プロシージャに入ることができるのは、プロシージャ・ラベルの場所のみです。“label+offset” 構文を使ってプロシージャにアクセスすることはできません。
-
プロシージャのラベルは、プロシージャに対しプライベートであり、プロシージャ内からのみアクセスできます。PRIVATE キーワードは不要ですが、プロシージャ内のラベルで PRIVATE キーワードを使用できます。PUBLIC キーワードは、プロシージャ内のラベルで使用すると構文エラーが発生します。$TEXT システム関数は、プロシージャのタグ名を使用している label+offset をサポートしていますが、名前でプライベート・ラベルにアクセスできません。
-
ラベルの重複はプロシージャ内では許可されていませんが、特定の状況ではルーチン内で許可されています。特に、異なるプロシージャ内でラベルは重複して使用できます。また、同じラベルはプロシージャ内や、プロシージャが定義されたルーチン内の他の場所で表示できます。例えば、以下の 3 つの “Label1” は使用できます。
Rou1 // Rou1 routine Proc1(x,y) { Label1 // Label1 within the proc1 procedure within the Rou1 routine } Proc2(a,b,c) { Label1 // Label1 within the Proc2 procedure (local, as with previous Label1) } Label1 // Label1 that is part of Rou1 and neither procedure
-
プロシージャにルーチン名なしの Do コマンドやユーザ定義関数がある場合、ラベルが存在しているとプロシージャ内のラベルを参照します。そうでない場合、ルーチン内ではあるがプロシージャの外部にあるラベルを参照します。
-
ルーチン名を持つ DO またはユーザ定義関数がプロシージャにある場合、そのプロシージャでは必ずその外部にある行を特定します。その名前がプロシージャを含むルーチンを識別する場合も同様です。例えば以下のようになります。
ROU1 ; PROC1(x,y) { DO Label1^ROU1 Label1 ; } Label1 ; The DO calls this label
-
プロシージャに GOTO が含まれる場合、プロシージャ内のプライベート・ラベルに移動する必要があります。GOTO では、プロシージャを終了できません。
-
Label+offset 構文は、いくつかの例外はありますが、プロシージャ内ではサポートされていません。
-
$TEXT は、プロシージャ・ラベルから label+offset をサポートします。
-
Break またはエラー発生後にプロシージャに戻る方法として、GOTO label+offset を、プロシージャ・ラベルからのダイレクト・モード行数で使用できます。
-
ZBREAK コマンドでは、プロシージャ・ラベルからの label+offset を使用できます。
-
プロシージャが呼び出されたときに有効になっていた $TEST 状態は、そのプロシージャの QUIT を発行すると復元されます。
-
プロシージャの終了を意味する “}” は、行の最初の文字位置を含め、どの文字位置にでも置くことができます。コードは、行の “}” の前に記述します。その後ろには記述できません。
-
右中括弧の直前に、暗黙的な QUIT が置かれます。
-
間接指定と XECUTE コマンドは、プロシージャの範囲外に存在するかのように動作します。
-
プロシージャ範囲内の間接指定、XECUTE コマンド、および JOB コマンド
プロシージャ内の名前間接指定、引数間接指定、XECUTE コマンドは、プロシージャの範囲内では実行されません。したがって、XECUTE コマンドは、プロシージャ外にあるサブルーチンの暗黙的な DO のように動作します。
間接演算子と XECUTE は、パブリック変数にのみアクセスできます。その結果、間接演算子または XECUTE コマンドが変数 x を参照する場合、プライベート変数である x がプロシージャに存在していても、パブリック変数である x を参照します。例えば以下のようになります。
SET x="set a=3" XECUTE x ; sets the public variable a to 3
SET x="label1" DO @x ; accesses the public subroutine label1
同様に、間接指定または XECUTE で参照するラベルは、プロシージャ外のラベルです。プロシージャ内での GOTO は、その内部のラベルに移動する必要があるので、GOTO @A はプロシージャ内でサポートされていません。
間接演算と XECUTE コマンドの詳細は、このドキュメントの他の個所で説明されています。
同様に、プロシージャの範囲内で JOB コマンドを発行する場合、メソッド外の子プロセスを開始します。つまり、次のようなコードになります。
KILL ^MyVar
JOB MyLabel
QUIT $$$OK
MyLabel
SET ^MyVar=1
QUIT
子プロセスでラベルが見えるようにするには、プロシージャ・ブロックにメソッドまたはクラスを含めることはできません。
プロシージャ内でのエラー・トラップ
エラー・トラップがプロシージャ内から設定された場合、プロシージャのプライベート・ラベルに直接設定する必要があります。(これは、“+offset” やルーチン名を含む従来のコードとは異なります。この規則は、エラー・トラップの実行とは、基本的にエラー・トラップまでスタックを巻き戻し、GOTO を実行することである、という考えに一致しています)。
プロシージャ内でエラーが発生した場合、$ZERROR はプライベートの “label+offset” ではなく、プロシージャの “label+offset” に設定されます。
エラー・トラップを設定するには、通常、$ZTRAP または $ETRAP を使用します。ただし、値はリテラルにする必要があります。以下はその例です。
SET $ZTRAP = "abc"
// sets the error trap to the private label "abc" within this block
エラー・トラップの詳細は、このドキュメントの "エラー処理" の章を参照してください。
従来のユーザ定義コード
Caché にプロシージャが追加される前、ユーザ定義コードはサブルーチンと関数の形式でサポートされていました (これらは現在、プロシージャとして実装されています)。ここでは、これらの従来のユーザ定義コードについて説明していますが、これは既存のコードを説明することが主な目的です。これらを継続して使用することはお勧めしません。
サブルーチン
構文
以下は、ルーチンの構文です。
label [ ( param [ = default ][ , ...] ) ]
/* code */
QUIT
以下の構文で呼び出します。
DO label [ ( param [ , ...] ) ]
あるいは
GOTO label
label | サブルーチンの名前標準ラベルです。必ず 1 列目から始まります。label の後に続くパラメータの括弧はオプションです。パラメータが指定されたサブルーチンは、GOTO では呼び出されません。パラメータの括弧は、直前に実行されるコードがサブルーチン内に “フォールスルーする” のを防ぎます。Caché は、パラメータの括弧付きラベルに遭遇した場合 (中身が空でも)、QUIT を暗黙に実行し、実行が “フォールスルー” しないようコードを終了させます。 |
param | 呼び出し元のプログラムからサブルーチンに渡されるパラメータの値。GOTO コマンドを使用して呼び出されるサブルーチンは、param 値を持つことはできず、パラメータの括弧を使うこともできません。DO コマンドを使用して呼び出されるサブルーチンには、param 値があってもなくてもかまいません。param 値がない場合、空のパラメータの括弧は記述しても省略してもかまいません。サブルーチンに必要な各パラメータに対し、param 変数を指定します。必要なパラメータは、仮パラメータ・リスト と呼ばれます。このリストには、何も指定しなくても、1 つ以上の param を定義してもかまいません。複数の param 値がある場合、コンマによって区切られます。Caché は、参照された param 変数に対して、自動的に NEW を呼び出します。パラメータは、値または参照で仮パラメータ・リストに渡すことができます。 |
default | 直前の param に対するオプションの既定値です。各パラメータに対して既定値を提供しても、省略してもかまいません。仮パラメータに対応する実パラメータが指定されていない場合、または実パラメータが参照渡しされたときに当該のローカル変数に値がない場合は、既定値が適用されます。この既定値はリテラル、つまり引用符で囲まれた数字あるいは文字列です。null 文字列 (“”) も既定値として指定できます。null 文字列は変数を定義するため、これは既定値を指定しないこととは異なります。一方でパラメータ値が未指定の場合や既定値がない場合、変数は未定義となります。リテラル以外の既定値を指定すると、Caché は <PARAMETER> エラーを発行します。 |
code | コード・ブロックです。コード・ブロックは、通常ラベルを呼び出してアクセスします。しかし、コード・ブロック内で別のラベルを呼び出したり、label + offset の GOTO コマンドを使用しても入力 (再入力) できます。コード・ブロックでは、他のサブルーチン、関数、プロシージャを入れ子にして呼び出すことができます。入れ子になった呼び出しは、リンクされた一連の GOTO コマンドではなく、DO コマンドまたは関数呼び出しを使用して実行することをお勧めします。コード・ブロックは、通常、明示的な QUIT コマンドで終了します。必須ではありませんが、QUIT コマンドの記述をお勧めします。外部ラベルに GOTO を使用して、サブルーチンを終了することもできます。 |
概要
サブルーチンは、最初の行の 1 列目にあるラベルで識別されるコード・ブロックです。サブルーチンの実行は、通常、明示的な QUIT 文によって終了します。
サブルーチンは、DO コマンドまたは GOTO コマンドで呼び出します。
-
DO コマンドはサブルーチンを実行し、その後、呼び出し元のルーチンの実行を再開します。したがって、Caché がサブルーチンで QUIT コマンドに遭遇した場合、呼び出し元のルーチンに戻り、DO コマンドに続く次の行を実行します。
-
GOTO コマンドは、サブルーチンを実行しますが、呼び出し元のプログラムに制御を戻しません。Caché は、サブルーチンの中で QUIT コマンドに遭遇したときに実行を終了します。
DO コマンドで呼び出されたサブルーチンにパラメータを渡すことができますが、GOTO コマンドで呼び出されたサブルーチンには渡せません。値、もしくは参照によってパラメータを渡せます。詳細は、"パラメータ渡し" を参照してください。
サブルーチンと呼び出し元のルーチンは、同じ変数を使用できます。
サブルーチンは値を返しません。
関数
Caché 5 以降、関数は既定で (推奨) プロシージャとなります。ただし、プロシージャではない関数を定義することも引き続き可能です。このセクションでは、このような関数について説明しています。
構文
以下はプロシージャではない関数の構文です。
label([param[=default]][,...])
code
QUIT expression
以下の構文で呼び出します。
command $$label([param[ ,...]])
あるいは
DO label([param[ ,...]])
label | 関数名。標準ラベルです。必ず 1 列目から始まります。label に続くパラメータの括弧は必須です。 |
param | 関数に必要な各パラメータの変数です。必要なパラメータは、仮パラメータ・リスト と呼ばれます。このリストには、何も指定しなくても、1 つ以上の param を定義してもかまいません。複数の param 値がある場合、コンマによって区切られます。Caché は、参照された param 変数に対して、自動的に NEW を呼び出します。パラメータは、値または参照で仮パラメータ・リストに渡すことができます。 |
default | 直前の param に対するオプションの既定値です。各パラメータに対して既定値を提供しても、省略してもかまいません。仮パラメータに対応する実パラメータが指定されていない場合、または実パラメータが参照渡しされたときに当該のローカル変数に値がない場合は、既定値が適用されます。この既定値はリテラル、つまり引用符で囲まれた数字あるいは文字列です。null 文字列 (“”) も既定値として指定できます。null 文字列は変数を定義するため、これは既定値を指定しないこととは異なります。一方でパラメータ値が未指定の場合や既定値がない場合、変数は未定義となります。リテラル以外の既定値を指定すると、Caché は <PARAMETER> エラーを発行します。 |
code | コード・ブロックです。コード・ブロックでは、他のサブルーチン、関数、プロシージャを入れ子にして呼び出すことができます。入れ子になった呼び出しは、DO コマンドまたは関数呼び出しで実行する必要があります。GOTO コマンドでは、関数のコード・ブロックを終了できません。コード・ブロックは、式を持つ明示的な QUIT コマンドを使用してのみ終了できます。 |
expression | 有効な ObjectScrip 式で指定される関数返り値です。式を持つ QUIT コマンドは、ユーザ定義関数では必須です。式から生じる値は、関数の結果として呼び出し元に返されます。 |
概要
このセクションでは、ユーザ定義関数について説明します。ユーザ定義関数の呼び出しは、接頭語 “$$” で識別されます。(ユーザ定義関数は、外部関数 ともいいます)。
ユーザ定義関数に、Caché から提供される関数を追加できます。通常、どのプログラムからでも呼び出せる汎用的な処理を実装するために関数を使用します。
関数は、常に Caché コマンドから呼び出されます。式として評価され、呼び出し元のコマンドに単一の値を返します。例えば以下のようになります。
SET x=$$myfunc()
関数パラメータ
原則として、ユーザ定義関数はパラメータ渡しを使用します。ただし、外部に提供する値を持たない場合も、関数は機能します。例えば、乱数を生成する関数の定義や、システム日付を既定以外の形式で返す関数の定義ができます。このような場合、パラメータの括弧は、パラメータ・リストが空でも関数定義と関数呼び出しの両方に必要です。
パラメータ渡しでは、以下が必要です。
-
関数呼び出しの実パラメータ・リスト
-
関数定義の仮パラメータ・リスト
Caché がユーザ定義関数を実行するとき、実パラメータ・リストのパラメータを、対応する仮リストのパラメータの位置にマップします。例えば、実リストにある最初のパラメータの値は、仮リストにある最初の変数に配置し、2 番目の値は 2 番目の変数に配置し、以降の値も同様に配置します。これらのパラメータの照合は、名前ではなく、パラメータの位置で行われます。したがって、実パラメータに使用する変数と仮パラメータに対して使用する変数は、同じ名前を持つ必要はありません (また、通常持つべきではありません)。関数は、仮リスト内の該当する変数を参照することにより、渡された値にアクセスします。
仮リストにある変数が実リストにあるパラメータよりも多く、既定値が各パラメータに指定されていない場合、余った変数は未定義のままとなります。したがって、関数のコードには適切な If テストを記述して、各関数の参照において使用可能な値が必ず渡されることを確認する必要があります。
ユーザ定義関数にパラメータを渡す場合、値または参照で渡すことができます。同じ関数呼び出し内で、値または参照渡しの両方を混合して指定することができます。詳細は、"パラメータ渡し" を参照してください。
返り値
ユーザ定義関数の基本的な定義方法は以下のとおりです。
label ( parameters )
/* code */
QUIT expression
関数は、QUIT コマンドの後に式を含む必要があります。Caché は、QUIT コマンドに遭遇すると関数の実行を終了し、呼び出し元のプログラムに関連する式が生成する単一の値を返します。
式を含めずに QUIT コマンドを指定すると、Caché はエラーを生成します。
変数
呼び出し元のプログラムと呼び出される関数は、同じ変数一式を使用します。以下は特に考慮すべき点です。
-
Caché は仮リストの各パラメータに対し、暗黙の NEW コマンドを実行します。以下の例では、myfunc が呼び出されると x が再度初期化されます。
mainprog SET x=7 SET y=$$myfunc(99) myfunc(x) WRITE x QUIT 66
-
システムは、関数を開始した時点でシステム変数 $TEST の現在値を保存し、関数の終了時にその値を復元します。他の方法を使用して値を保存するコードを明示的に記述しない限り、関数実行中に $TEST 値を変更しても、関数の終了時にその値は破棄されます。
関数の位置
ユーザ定義関数は、それを参照するルーチン内に定義する、あるいは複数のプログラムから参照できるよう別々のルーチン内に定義できます。しかし、1 つのルーチンにすべてのユーザ定義関数を定義することをお勧めします。これにより、関数の検証や更新の際に、関数定義の検索が簡単にできます。
ユーザ定義関数の呼び出し
$$ 接頭語または DO コマンドを使用して、ユーザ定義関数を呼び出すことができます。$$ 形式は最も機能的であり、一般的に好まれる形式です。
$$ 接頭語の使用法
式を使用できるコンテキスト内で、ユーザ定義関数を呼び出すことができます。以下の形式で、ユーザ定義関数を呼び出します。
$$name([param [,...]])
以下はその説明です。
-
name 関数の名前を指定します。関数が定義される場所に応じて、以下のように名前を指定できます。
-
label — 現在のルーチン内の行ラベル
-
label^routine — ディスクにある名前付きルーチン内の行ラベル。
-
^routine — ディスクに保存されているルーチン。ルーチンは、実行する関数のコードのみを含んでいる必要があります。
ルーチンはネームスペース内で定義されます。拡張ルーチン参照を使用し、以下のようにして現在のネームスペース以外のネームスペースで定義されたルーチン内にあるユーザ定義関数を実行できます。
WRITE $$myfunc^|"SAMPLES"|routine
-
-
param は関数に渡される値を指定します。提供されるパラメータは、実パラメータ・リストと呼ばれます。これらは、その関数に関して定義された仮パラメータ・リストと一致する必要があります。例えば、関数コードには 2 つのパラメータがあり、1 番目は数値、2 番目は文字リテラルとします。この場合、1 番目のパラメータに文字リテラル値、2 番目に数値を指定すると、関数は不正確な値を返すか、あるいはエラーを生成する可能性があります。仮パラメータ・リストのパラメータでは、関数から NEW コマンドが必ず呼び出されます。詳細は、NEW コマンドの説明を参照してください。パラメータは、値または参照によって渡されます。詳細は、"パラメータ渡し" を参照してください。関数の仮パラメータ・リストに記載されている数より少数のパラメータを関数に渡すと、パラメータの既定値が使用されます (定義されている場合)。既定値がない場合、パラメータは未定義のままとなります。
DO コマンドの使用法
DO コマンドを使用して、ユーザ定義関数を呼び出すことができます (DO コマンドを使用して、システム関数を呼び出すことはできません)。DO コマンドで呼び出した関数は、値を返しません。つまり、関数は必ず返り値を生成しますが、DO コマンドはこの返り値を無視します。したがって、ユーザ定義関数を呼び出す DO コマンドの用途は大幅に制限されます。
DO コマンドを使用してユーザ定義関数を呼び出すには、以下の構文でコマンドを発行します。
DO label(param[ ,...])
DO コマンドは、label という関数を呼び出し、param で指定されたパラメータ (存在する場合) を関数に渡します。$$ 接頭語を使用しないことと、パラメータの括弧が必須であることに注意してください。label と param を指定する際には、$$ 接頭語を使用するユーザ定義関数を呼び出すときと同じ規則が適用されます。
関数は、常に値を返す必要があります。しかし、DO コマンドで関数を呼び出す場合、返された値は呼び出し元のプログラムで無視されます。