プロジェクトメソッドには、適切な名前を付ける必要があります。フォームメソッドやオブジェクトメソッドはフォームやオブジェクトと密接に関連付けられていますが、プロジェクトメソッドはどこにでも使用できます。データベースの特定のオブジェクトに付属しているわけではありません。プロジェクトメソッドはその実行方法や使用方法に応じて、次のような役割を果たします:
メニューメソッド サブルーチン、関数 プロセスメソッド イベント処理メソッド エラー処理メソッド これらの用語はプロジェクトメソッドを、それがなんであるかで識別するのでなく、何を行うで識別しています。
メニューメソッド は、カスタムメニューから呼び出されるプロジェクトメソッドです。これは、ユーザのアプリケーションの流れを管理します。メニューメソッドは、必要とされる場所での分岐、フォームの表示、レポートの生成、ユーザデータベースの一般的な管理といった制御を行います。
サブルーチン は、処理の下請け的なプロジェクトメソッドです。他のメソッドから呼ばれて、リクエストされた処理を実行します。関数は、呼び出し元のメソッドに値を返すサブルーチンです。
プロセスメソッド は、プロセスの開始時に呼び出されるプロジェクトメソッドです。このプロセスは、プロセスメソッドが実行されている間だけ存続します。プロセスに関する詳細は の節を参照してください。メニューに属するメニューメソッドのプロパティとして新規プロセス開始 をチェックした場合も、プロセスメソッドとして開始されます。
イベント処理メソッド は、イベントを処理するプロセスメソッドとして、分離されたプロセス内で実行されます。通常、開発者はイベント管理の大部分を4Dに任せます。例えば、データ入力中、4Dがキーストロークやクリックを検出し、それから正しいオブジェクトとフォームメソッドを呼び出します。このため開発者は、これらのメソッド内でイベントに対し適切に応答できるのです。一方、開発者がイベントを直接操作したい場合があります。例えば(レコードをブラウズする ループ等)処理時間の長い操作を実行する場合、「Ctrl+.(ピリオド)」キー(Windows)や「command+.(ピリオド)」キー(Macintosh)を押して、その操作への割り込みを行いたいとします。この場合、開発者はイベント処理メソッドを使用する必要があります。詳細はON EVENT CALL コマンドの説明を参照してください。
エラー処理メソッド は、割り込みを実行するプロジェクトメソッドです。エラーや例外が起こる度に、エラー管理メソッドがインストールされたプロセス内で実行されます。詳細はON ERR CALL コマンドの説明を参照してください。
アプリケーションモードでカスタムメニューを選択すると、そのメニューに関連付けられたメニューメソッドが実行されます。メニューエディタを使用して、メニューコマンドにメソッドを割り当てます。メニューが選択されると、それに対応するメニューコマンドが実行されます。この手順は、データベースをカスタマイズする主要な方法の一つです。特定の処理を実行するメニューメソッドを割り当てたカスタムメニューを作成することで、データベースをカスタマイズすることができます。詳細は、4D Design Referenceのメニューエディタの説明を参照してください。
カスタムメニューコマンドにより、単一または複数の処理を実行することができます。例えば、データの入力処理を実行するメニューは、以下の2つの処理を実行するメソッドを呼び出すことができます。まず適切な入力フォームを表示します。次にユーザがキャンセルするまでの間ADD RECORD コマンドによるデータ入力を繰り返します。
連続した処理の自動化は、プログラミング言語の強力な機能の1つです。カスタムメニューを使用すると、処理を自動化することができ、データベースのユーザにより多くのガイダンスを提供することができます。
プロジェクトメソッドを作成すると、それは同じデータベースシステムの言語の一部になります。プロジェクトメソッドは、4Dに組み込まれたコマンドと同様に呼び出すことができます。このように使用されるプロジェクトメソッドをサブルーチンと呼びます。
サブルーチンは、以下のようなメリットがあります。
重複したコードをなくす メソッドの役割を明確にする メソッドの変更を容易にする コードをモジュール化する 例えば、顧客データベースがあるとします。データベースをカスタマイズしていくうちに同じ処理を繰り返し行うことに気づいたとします。それは顧客を検索してレコードを修正するという一連の作業です。そのコーディングは以下のようになっています:
サブルーチンを使用しなければ、顧客レコードの修正を実行するたびにコードを作成しなければなりません。10箇所で同じ処理が必要になると、同じコーディングを10回も行わねばなりません。サブルーチンを使用すれば1回コーディングするだけで済みます。これがコーディングの重複をなくすというサブルーチンの第一の利点です。
先ほど説明したコードがMODIFY CUSTOMER と呼ばれるメソッドであるならば、他のメソッドの中でメソッドの名前を使って実行できます。例えば、顧客のレコードを修正し、それからレコードをプリントするために、以下のようなメソッドを書くことができます:
この機能はメソッドを劇的にに簡素化します。例においてMODIFY CUSTOMER メソッドがどのように動作するか知る必要がなく、何を行うかを知っていればよいのです。これは、メソッドをサブルーチン化にすることにより処理内容が明確になる、二番目のメリットです。また、これにより作成したメソッドは4D言語を拡張したことになります。
このデータベース例で、顧客を検索する方法を変更する必要がある場合、10か所ではなく、たった1つのメソッドを変更するだけで済みます。これがサブルーチンを使うもう一つの理由です。
サブルーチンを使ってコードをモジュール化します。これはコードをモジュール(サブルーチン)に分割することを意味し、それぞれは論理的な仕事を実行します。アカウントデータベースの以下のコードを見てみましょう:
FIND CLEARED CHECKS
RECONCILE ACCOUNT
PRINT CHECK BOOK REPORT
データベースを知らない人でも、このプログラムが何をしているかはわかります。各サブルーチンの処理手順を知る必要はありません。各サブルーチンは長く、複雑な処理で構成されていることもありますが、そのサブルーチンが何を実行するのかだけを知っていれば十分です。
プログラムを論理的な処理単位やモジュールにできるだけ分割することをお勧めします。
メソッドにデータを渡す必要がしばしば発生します。これは引数によって容易にできます。
引数 は、メソッド内で行う処理に必要なデータです。引数という用語はこのマニュアルの随所で使用されています。引数は、4Dコマンドへデータを渡す場合にも使用します。以下の例は、文字列“Hello”という引数をALERT コマンドへ渡します:
引数は、メソッドに対しても同じように渡すことができます。例えばメソッドDO SOMETHING が3つの引数を受け取る場合、このメソッドを呼び出すには以下のように書きます:
DO SOMETHING (WithThis;AndThat;ThisWay)
引数は、セミコロン (;) で区切ります。
サブルーチン (呼び出されるメソッド) 内で、それぞれの引数の値は自動的に、順に番号が付けられたローカル変数 ($1 , $2 , $3 ...) に格納されます。ローカル変数の番号は、引数の順序を表わします。
このローカル変数/引数は呼び出しメソッド から渡された実際のフィールドや変数、式ではなく、渡された値を保持しているだけです。
サブルーチン内で、他のローカル変数と同様にこれらの引数 ($1 , $2... ) を使用できます。
Note : しかしながら、引数として渡した変数の値を変更するコマンドを使用する場合 (例えばFind in field )、$1 , $2 などの直接使用することはできません。まず標準のローカル変数等にコピーする必要があります (例: $myvar :=$1 )。
引数はローカル変数であるため、サブルーチン内でのみ使用可能で、サブルーチンの終了時にクリアされます。このためサブルーチンは、引数として渡された実際のフィールドや変数の値を呼び出しメソッドレベルで変更できません。例えば:
DO SOMETHING が表示するアラートボックスには“WILLIAMS”と表示され、MY METHOD が表示するアラートボックスには“williams”と表示されます。メソッドは引数$1 の値をローカルに変更します。しかしこの変更はMY METHOD で引数として渡されたフィールド [People]Last Name の値に影響しません。
メソッドDO SOMETHING でフィールドの値を変更する方法には2通りあります:
1. メソッドにフィールドを渡すのではなく、フィールドへのポインタを渡します:
ここで、引数はフィールドではなく、フィールドへのポインタです。DO SOMETHING メソッド内では、$1 はフィールドの値ではなく、フィールドへのポインタです。$1 で参照 されるオブジェクト (上記のコードでは$1 ->) は、実際のフィールドです。その結果、参照されたオブジェクトの変更はサブルーチンのスコープを超え、実際にフィールドに影響が及びます。この例題では、両方のアラートボックスに“WILLIAMS”と表示されます。
ポインタに関する詳細は、 の節を参照してください。
2. メソッドDO SOMETHING に処理を行わせるだけではなく、値を返すようにコードを書き換えることができます:
[People]Last Name:=DO SOMETHING ([People]Last Name)
ALERT ([People]Last Name)
$0 :=Uppercase ($1 )
ALERT ($0 )
サブルーチンから値が返される2番目の手法は関数と呼ばれ、次の項で説明します。
上級プログラミング : サブルーチン内の引数にはローカル変数
$1 ,
$2 ...を使用してアクセスできます。さらに引数を省略可能とし、
${...} シンタックスを使用して参照することもできます。詳細は
Count parameters 関数の説明を参照してください。
メソッドからデータを返すこともできます。値を返すサブルーチンを関数 と呼びます。
値を返す4Dコマンドや4Dプラグインコマンドも関数と呼びます。
以下の行は、文字列のデータ長を返すLength 関数を用いたステートメントです。このステートメントは、Length 関数がMyLength という変数に値を返します。
MyLength :=Length ("How did I get here?")
どのようなサブルーチンでも値を返すことができます。返す値をローカル変数$0 に格納します。
例えばUppercase4 という以下の関数は、始めの4文字を大文字に変換した文字列を返します:
以下は、Uppercase4 関数を使用するメソッドの例です:
NewPhrase :=Uppercase4 ("This is good.")
変数NewPhrase には“THIS is good.”が格納されます。
戻り値 $0 はサブルーチン内でローカル変数です。サブルーチンの中では$0 を通常のローカル変数と同様に使用できます。例えば、前例のDO SOMETHING において、$0 は最初に大文字に変換した$1 の値を割り当てられ、その後ALERT コマンドの引数として使われました。サブルーチンの中では、他のローカル変数と同じ方法で$0 を使うことができます。サブルーチンが終わる時の$0 の値を呼び出し元のメソッドに戻すのは4Dの役割です。
プロジェクトメソッドは、自分自身を呼び出すことができます。例えば:
メソッドAがメソッドBを呼び出し、メソッドBはメソッドAを呼び出します。 メソッドAは自身を呼び出すことができます。 これは再帰呼び出し と呼ばれています。4D言語は再帰呼び出しを完全にサポートしています。
例題を見てみましょう。以下のフィールドから成る[Friends and Relatives] テーブルがあります: - [Friends and Relatives]Name - [Friends and Relatives]ChildrensName こ の例題では、フィールドの値は重複しない、すなわち同じ名前の人間はいないとします。名前を指定することで、以下のような文を作成します:“A friend of mine, John who is the child of Paul who is the child of Jane who is the child of Robert who is the child of Eleanor, does this for a living!”:
1. この文を以下のように作成できます:
$vsName :=Request ("Enter the name:";"John")
If (OK=1)
QUERY ([Frie nds and Relatives];[Frie nds and Relatives]Name=$vsName )
If (Records in selection ([Frie nds and Relatives])>0)
$vtTheWholeStory :="A friend of mine, "+$vsName
REPEAT
QUERY ([Frie nds and Relatives];[Frie nds and Relatives]ChildrensName=$vsName )
$vlQueryResult :=Records in selection ([Frie nds and Relatives])
If ($vlQueryResult >0)
$vtTheWholeStory :=$vtTheWholeStory +" who is the child of "+[Frie nds and Relatives]Name
$vsName :=[Frie nds and Relatives]Name
End if
Until ($vlQueryResult =0)
$vtTheWholeStory :=$vtTheWholeStory +", does this for a living!"
ALERT ($vtTheWholeStory )
End if
End if
2. 以下の方法でも作成できます:
$vsName :=Request ("Enter the name:";"John")
If (OK=1)
QUERY ([Frie nds and Relatives];[Frie nds and Relatives]Name=$vsName )
If (Records in selection ([Frie nds and Relatives])>0)
ALERT ("A friend of mine, "+Genealogy of ($vsName )+", does this for a living!")
End if
End if
再帰関数Genealogy of は以下の通りです:
$0 :=$1
QUERY ([Frie nds and Relatives];[Frie nds and Relatives]ChildrensName=$1 )
If (Records in selection ([Frie nds and Relatives])>0)
$0 :=$0 +" who is the child of "+Genealogy of ([Frie nds and Relatives]Name)
End if
Genealogy of メソッドが自分自身を呼び出していることに注目してください。
最初に挙げた方法は反復性のアルゴリズム です。2番目に挙げた方法は再帰呼び出しのアルゴリズム です。
前述の例題のようなコードを実装する場合、反復性や再帰呼び出しを使用してメソッドを書くことができるということに注目してください。一般的に、再帰呼び出しは、より明瞭で、読みやすく、維持しやすいコードを提供します。ただし、この使用は必須ではありません。
4D内での再帰呼び出しの代表的な使用方法は以下の通りです:
例題と同じく、お互いに関連するテーブル内でのレコードの取り扱い。 FOLDER LIST コマンドとDOCUMENT LIST コマンドを使用して、ディスク上にあるドキュメントとフォルダをブラウズする。フォルダにはフォルダとドキュメントが含まれており、サブフォルダはまたフォルダとドキュメントを含むことができます。重要 : 再帰呼び出しは、必ずある時点で終了する必要があります。例えばGenealogy of メ ソッドが自身の呼び出しを止めるのは、クエリがレコードを返さないときです。この条件のテストをしないと、メソッドは際限なく自身を呼び出します。 (メソッド内で使用される引数やローカル変数と同様に) 再帰呼び出しを行う容量が一杯になると、最終的に4Dは“スタックがいっぱいです”エラーを返します 。