制御フロー
メソッドや関数が単純か複雑かに関係なく、開発者は3つのプログラミング構造のうち、1つ以上を常に使用します。 プログラミング構造は、メソッド内でステートメントが実行される順序を決定する実行フローをコントロールします。 3つのタイプの構造があります:
-
シーケンシャル: シーケンシャル構造は単純な線形構造です。 シーケンスとは、4Dが最初から最後まで次々に実行する一連のステートメントです。 オブジェクトメソッドで頻繁に使用される1行から成るルーチンはもっとも簡単なシーケンシャル構造の例です。 例:
[People]lastName:=Uppercase([People]lastName)
-
分岐: 分岐構造は、条件をテストし、その結果に基づいて異なる流れにメソッドを導きます。 条件は true または false に評価されるブール式です。
If...Else...End if
構文は分岐構造の一例で、処理フローを二つに分岐します。Case of...Else...End case
構文も分岐構造の一つで、処理フローをもっとたくさん分岐することができます。 -
ループ: メソッドの作成にあたって、何度も同じ処理を繰り返すことがあります。 これに実現するために、4D は以下のループ構造を備えています:
ループを制御する方法には、条件が満たされるまでループする方法と、指定した回数だけループする方法の2通りがあります。 各ループ構造はいずれの方法にも用いることができますが、While
ループと Repeat
ループは条件が満たされるまで繰り返す場合に、For
ループは指定した回数だけループする場合の利用に適切です。 For each...End for each
ループは両方を組み合わせることが可能で、オブジェクトやコレクション内でループするために設計されています。
注: 4Dはプログラム構造 (If/While/For/Caes of/Repeat/For each) を512レベルまで入れ子で記述できます。
If...Else...End if
If...Else...End if
による制御フロー構造の正式な構文は以下のようになります:
If(Boolean_Expression)
statement(s)
Else
statement(s)
End if
Else
部分はオプションであり、省略して以下のように記述できます:
If(Boolean_Expression)
statement(s)
End if
If...Else...End if
構造は、条件 (ブール式) が true か false かによって、処理の選択肢を2つメソッドに与えます。 ブール式が true の場合は、テストのすぐ後のステートメントを実行し、 ブール式が FALSE の場合には、Else 文のすぐ後のステートメントを実行します。 任意の Else
が省略されていた場合、End if
のすぐ後のステートメント (あれば) へと実行が続行されます。
ブール式は常に全体が評価されるという点に注意してください。 たとえば、以下のような場合:
If(MethodA & MethodB)
...
End if
この場合、両方のメソッドが true である場合に限り、式は true になります。 しかしながら MethodA が false であっても、4Dは_MethodB_ も評価するため、これは時間の無駄になります。 この場合には、以下のような構造を使用するほうが賢明といえます:
If(MethodA)
If(MethodB)
...
End if
End if
上記の結果はほぼ同じで、MethodB は必要な場合にのみ評価されます。
例題
// ユーザーに名前の入力を求めます
$Find:=Request("名前を入力してください")
If(OK=1)
QUERY([People];[People]LastName=$Find)
Else
ALERT("名前が入力されませんでした")
End if
Tip: 一方の条件に実行ステートメントがない分岐処理を書くこともできます。 下のようなコードはどちらも有効です:
If(Boolean_Expression)
Else
statement(s)
End if
または:
If(Boolean_Expression)
statement(s)
Else
End if
Case of...Else...End case
Case of...Else...End case
による制御フロー構造の正式な構文は以下のようになります:
Case of
:(Boolean_Expression)
statement(s)
:(Boolean_Expression)
statement(s)
.
.
.
:(Boolean_Expression)
statement(s)
Else
statement(s)
End case
Else
部分はオプションであり、省略して以下のように記述できます:
Case of
:(Boolean_Expression)
statement(s)
:(Boolean_Expression)
statement(s)
.
.
.
:(Boolean_Expression)
statement(s)
End case
If...Else...End if
と同様に、Case of...Else...End case
構造も処理の選択肢をメソッドに与えます。 If...Else...End
との違いは、Case of...Else...End case
構造が複数のブール式を評価し、その中から最初に true となるステートメントを実行することです。
ブール式の前にはそれぞれコロン (:
) を付けます。 コロンとブール式の組み合わせをケースと呼びます。 例えば以下の行はケースです:
:(bValidate=1)
最初に true になったケースに続く (次のケース までの) ステートメントだけが実行されます。 true になるケースがない場合、どのステートメントも実行されません (Else
文が指定されていない場合) 。
最後のケースの後に Else 文を含むことができます。 すべてのケースが FALSE の場合に、Else
文の後のステートメントが実行されます。
例題
下記の例は数値変数を判定し、対応する数字をアラートボックスに表示します:
Case of
:(vResult=1) // 数値が1の場合
ALERT("一です。") // 1のアラートボックスを表示します
:(vResult=2) // 数値が2の場合
ALERT("二です。") // 2のアラートボックスを表示します
:(vResult=3) // 数値が3の場合
ALERT("三 です。") // 3のアラートボックスを表示します
Else // 数値が1,2,3のいずれでもない場合
ALERT("一、二、三のいずれでもありません。")
End case
比較するために、同じことを If...Else...End if
構文で記述すると以下のようになります。
If(vResult=1) // 数値が1の場合
ALERT("一です。") // 1のアラートボックスを表示します
Else
If(vResult=2) // 数値が2の場合
ALERT("二です。") // 2のアラートボックスを表示します
Else
If(vResult=3) // 数値が3の場合
ALERT("三です。") // 3のアラートボックスを表示します
Else // 数値が1,2,3のいずれでもない場合
ALERT("一、二、三のいずれでもありません。")
End if
End if
End if
Case of...Else...End case
構造は、最初に true になったケースだけを実行します 。 2つ以上のケースが true の場合は、最初に true になったケースのステートメントだけを実行します。
したがって、階層的なテストを実行するときには、階層上で低い位置にある条件がテスト順序で先に記述されていることを確認する必要があります。 以下の例では、ケース2が true の場合、ケース1も必ず true であるため、ケース1は後に位置すべきです。 このままの順序では、ケース2のステートメントはけっして実行されません:
Case of
:(vResult=1)
...
// ステートメントなど
:((vResult=1) & (vCondition#2)) // このケースが判定されることはありません
...
// ステートメントなど
End case
vResult = 1の判定により他の条件を見る前に分岐するので、第2のケースが判定されることはありません。 コードが正しく実行されるためには次のように書きます:
Case of
:((vResult=1) & (vCondition#2)) // このケースが先に判定されます
...
// ステートメントなど
:(vResult=1)
...
// ステートメントなど
End case
さらに階層的なテストを実行したい場合、コードも階層化する必要があります。
Tip: 分岐構造において、ケースに続くステートメントの記述は必須ではありません。 下のようなコードはどちらも有効です:
Case of
:(Boolean_Expression)
:(Boolean_Expression)
...
:(Boolean_Expression)
statement(s)
Else
statement(s)
End case
または:
Case of
:(Boolean_Expression)
:(Boolean_Expression)
statement(s)
...
:(Boolean_Expression)
statement(s)
Else
End case
または:
Case of
Else
statement(s)
End case
While...End while
While...End while
による制御フロー構造の正式な構文は以下のようになります:
While(Boolean_Expression)
statement(s)
{break}
{continue}
End while
While...End while
ループは、ブール式が true である限り、ループ内のステートメントを実行し続けます。 ループの始めにブール式を評価し、ブール式が FALSE の場合にはループをおこないません。
break
および continue
ステートメントについては 後述します。
一般に、While...End while
ループに入る手前で、ブール式で判定する値を初期化しておきます。 通常はブール式が true になるように設定してからループに入ります。
ブール式は、ループ内の要素を使って設定されなければなりません。そうでなければ、ループは永久に続くでしょう。 以下の例では、NeverStop がいつも true であるので、ループは永久に続きます。
NeverStop:=True
While(NeverStop)
End while
このようにメソッドの実行が制御不能になった場合には、トレース機能を使用し、ループを止めて、問題点を追跡することができます。 メソッドのトレース方法については、エラー処理 の章を見てください。
例題
CONFIRM("新規レコードを追加しますか?") // ユーザーに確認します
While(OK=1) // 利用者が望む限りループします
ADD RECORD([aTable]) // 新規にレコードを追加します
End while // ループは必ず End while によって終わります
この例では、まずループに入る前に CONFIRM
コマンドによりシステム変数 OK
がセットされます。 ユーザーがダイアログボックスで OK ボタンをクリックすると、システム変数 OK
に1がセットされ、ループを開始します。 それ以外の場合はシステム変数 OK
に0が設定され、ループをスキップします。 ループに入ると、ADD RECORD
コマンドはループを続けます。これは、ユーザーがレコードを保存した時点で、システム変数 OK
に1が設定されるからです。 ユーザーが最後のレコードを取り消した (保存しない) 時点で、システム変数 OK
に0がセットされ、ループは終了します。
Repeat...Until
Repeat...Until
による制御フロー構造の正式な構文は以下のようになります:
Repeat
statement(s)
{break}
{continue}
Until(Boolean_Expression)
Repeat...Until
ループは、While...End while ループと似ていますが、まずループの後でブール式を判定する点が異なります。 つまり、Repeat...Until
ループは最低でも1回は必ずループを実行しますが、While...End while
ループは最初のブール式が FALSE である場合には、ループを1回も実行しません。
もう一つの While...End while
ループとの相違点は、 Repeat...Until
はブール式が true になるまでループを続行することです。
break
および continue
ステートメントについては 後述します。
例題
以下の例を、While...End while
ループの例と比較してください。 ブール式を、初期化しておく必要がない点に注目してください。システム変数 OK
を初期化する CONFIRM
コマンドはありません。
Repeat
ADD RECORD([aTable])
Until(OK=0)
For...End for
For...End for
による制御フ ロー構造の正式な構文は以下のようになります:
For(Counter_Variable;Start_Expression;End_Expression{;Increment_Expression})
statement(s)
{break}
{continue}
End for
For...End for
ループは、カウンター変数によりループを制御します:
- カウンター変数 Counter_Variable は、数値変数 (実数または倍長整数) で、Start_Expression に指定した値に初期化されます。
- ループを実行するたびに、任意の引数である Increment_Expression の値がカウンター変数に加算されます。 Increment_Expression を指定しない場合、増分値は1になります。
- カウンターが End_Expression の値を超えた時点で、ループを停止します。
重要: Start_Expression、End_Expression、Increment_Expression の値は、ループの始めに一度だけ評価されます。 これらの数値が変数で指定されている場合、ループ内でその変数の値を変更してもループは影響を受けません。
Tip: 特別な目的のために、カウンター変数 Counter_Variable の値を変更することができます。ループ内でカウンター変数を変更すると、ループはその影響を受けます。
- 通常、Start_Expression は End_Expression より小さい。
- Start_Expression と End_Expression が等しい場合、1回だけループがおこなわれます。
- Start_Expression が End_Expression より大きい場合、Increment_Expression に負の値を指定しない限り、ループはおこなわれません。 次に例を示します。
break
および continue
ステートメントについては 後述します。
基本的な使用例
- 以下の例は、100回の繰り返しをおこないます:
For(vCounter;1;100)
// なんらかの処理
End for
- 以下の例は、配列 anArray の全要素に対して処理をおこないます:
For($vlElem;1;Size of array(anArray))
// 各配列要素に対する処理
anArray{$vlElem}:=...
End for