文(Statements)

関数、オペレータ等の本体は文( statements )により構成されます。文とはKLプログラムの実際に何かをなす際の部品の一部です。文は式の評価を成し、条件により他の文を実行し、さらには他の複文を条件に合致するまで反復処理したりすることができます。

この章の残りの部分では、KL関数定義の内部に使用することのできる、「文」のいくつかのバリエーションについて記述します。文は以下の2つのカテゴリ分けされます。単純な文と、複雑な文です。

単純な文

単純な文とは、条件のチェックをしない、もしくはネストしたスコープを作らない文のことです。単純な文は常に ; (セミコロン)文字で終わります。

警告

KLでも、C言語属においても、文末のセミコロン付け忘れは一般的な文法エラーですね。

式文(Expression Statements)

どの式、; (セミコロン)で終わるものは全て文です。式を評価しその結果となる値(返り値がある場合)をすべて無視することで、文は動作します。最も一般的な式文は関数呼び出しです。(例えば report)

/*
** Example: Expression Statements
*/

operator entry() {
  // A very useless expression statement:
  // evaluates 2 + 2 then discards the
  // result
  2 + 2;

  // A more useful expression statement:
  // evaluates the report function, which
  // causes text to appear
  report("Hello!");
}

並列実行文(PEX文)

並列実行文( parallel execution statement, 略記 PEX statement,)により、KLプログラムからオペレータを一般的な手法により複数回呼び出します。どの PEX文も、呼び出しには1つオペレータを必要 (参照 演算子) とし、呼び出し回数の指定も同様に必要です。さらに任意の数の、オペレータに渡す追加パラメータをとることも可能です。

PEX文は以下の形式:

operatorName<<<countExpr>>>(optionalArg1, optionalArg2, ...);

operatorName は最初のパラメータが IndexcountExprSize 型の式、オペレータが呼び出される回数を表すものであります。

オペレータを countExpr 回 呼びだします。可能であれば重奏し並列に呼び出します。この際第一引数を 0 から countExpr-1 まで変化させます。与えられたオプショナルな追加の引数は一個一個渡されます。オペレータは与えられた引数と互換性のあるパラメータ(キャスト含め)リストを持つ必要があります。可読性のため、KLでは特別なシンタックスを用意し、オペレータに初期引数 Index を追加します。この文法は並列実行の文法に倣います。

// The following operator definitions are equivalent:

operator foo<<<index>>>(String string, UInt32 array[]) { /*...*/ }
operator foo(Index index, String string, UInt32 array[]) { /*...*/ }

KLそして Fabric Engine は、PEX文におけるオペレータ呼び出しを、実行マシン上の利用可能コアに対し可能な限り均等に割り当てようとします。しかしどの呼び出しにどれだけの時間が掛かるかは事前に不明なため、PEXの効率は各呼び出しが同等の所要時間の場合に最大効率となります。

プログラムの練習としてPEXの再帰呼び出しを試す価値があります。

警告

KLは、追加引数に異なるコアより同時に渡される変数への変更を保護しません。あるPEX文において、特定のオペレータの2つの呼び出しが、同じ値を同時に変更しないように保証するのはプログラマの責任の範疇となります。このような同値同時変更を許してしまうと、追跡困難なバグの温床となり、ひいてはプログラムを破綻させます。

警告

PEXには生来オーバーヘッドが存在します。そのため些細なオペレータ呼び出しをPEXで行うよりは、直列実行のほうが効率的な場合があります。並列実行(Parallel executions)は大規模なワークロードにおいて最も効果的です。
/*
** Example: PEX Operations
*/

operator setArrayElementDesc<<<index>>>(io String array[]) {
  array[index] = "index=" + index;
}

operator entry() {
  String array[];
  array.resize(16);
  setArrayElementDesc<<<array.size()>>>(array);
  report(array);
}

空文(Empty Statement)

; (セミコロン) 単独で、何もしない文となります。文を必要とするが、何もしないで良い場合に使用します。

/*
** Example: The Empty Statement
*/

function foo(io MyType myType) {
  for (; returnsFalseWhenDone(myType); )
    ; // Do nothing
}

変数宣言文

変数宣言文( variable declaration statement )とは、その文のある最も内側のスコープに新しい変数を1つ(以上)導入します。これらの変数は、実行が同スコープ内である場合、スコープの残りから可視であり続け、外れると破棄され ―適切であればデストラクタが実行されます。

変数の宣言には型が必要です。複数の変数の宣言では、同一文内に , (カンマ)で区切ります。

変数の名前は、配列や辞書の仕様に従います。 配列 and 辞書(Dictionaries) の詳細情報を参照してください。

変数は、宣言され初期値を設定する際、コンストラクトもしくは代入による割当が可能です。 コンストラクタ 参照。

/*
** Example: Variable Declaration Statements
*/

operator entry() {
  Scalar a;
  report(a);
  Integer b[], c[2][2], d;
  report(b);
  report(c);
  report(d);
}

注釈

現在KLでは、変数を宣言したその文において、配列を初期化することはできません。

定数宣言文

定数宣言文( constant declaration statement )は variable declaration statement と似ていますが、 const キーワードを頭に付加します。文法はグローバル定義された名前付き定数と同様です。 名前付き定数 参照

return

return 文には2種の形式があります:

  • 値を返す関数において、 return 文は呼び出し元の関数へと、文に与えられた値を即座に返します。返却される値の型は、関数定義に指定された型へと暗黙的なキャストが行われます。
  • 値を返さない関数において、 return 文は値を取らず、呼び出し元関数へと即座に戻ります。
/*
** Example: The return Statement
*/

// Function that returns a value
function String markupName(String name) {
  return "My name is '" + name + "'";
}

// Function that does not return a value
function suffixIndexDesc(io String strings[], Size index) {
  if (index >= strings.size())
    return;
  strings[index] += " (was at index " + index + ")";
}

throw

throw 文は、例外を投げます。KLコードの実行を即座に中止し、呼びだし元の環境へ(Fabric Engine あるいは the KL tool) 例外を返し、ユーザに表示されます。throw 文は、パラメータを1つ取ります。このパラメータはどんな値でも構いません。この値は、呼び出し元環境へと渡される前に、自動的に文字列へと変換されます。

注釈

KL内で例外を捕捉(catch exception)することはまだできません。64bit Windows での LLVM に例外ハンドリングが対応していないためです。
/*
** Example: The throw Statement
*/

operator entry() {
  report("Before throw");
  throw "Exception";
  report("After throw");
}

/*
** Output:
(stdin):9:3: warning: code is unreachable

Before throw
Error: Exception
KL stack trace:
[ST] 1 kl.internal.String.SetErrorDataPtrAndLength.AS0()
[ST] 2 function.setError.R.St()
[ST] 3 operator.entry() (stdin):8
[ST] 4 kl.internal.entry.stub.cpu()

*/

複雑な文

複雑な文( complex statement )とは、異なる文を一纏めにし条件により異なる文を実行する文です。複雑な分は、単純な文、他の複雑な分、そして複数の式により構成します。

合成文(Compound Statements)

KLソースコード中、単文を挿入可能場所であれば、複数の文を挿入可能です。 { (左中括弧) and } (右中括弧)で包みます。このような影響があります:

  • この文は、1つの文であるかのように実行されます。
  • 新しい、ネストされたスコープが導入されます。 Scoping Rules 参照

条件文

KLには2種類の条件文があります。 if 文と switch 文です。

if

if キーワードにより条件文を開始します。必要に応じ else 句を含めることができます。JavaScript や C言語同様 ifelse 文は連続できます。

/*
** Example: The if Statement
*/

function String desc(Integer n) {
  if (n == 0)
    return "zero";
  else if (n == 1)
    return "one";
  else
    return "lots";
}

switch

switch...case 構文により、一連の if...else 文をコンパクトにします。これもJavaScript、C言語同様。

/*
** Example: The if Statement
*/

function String descNumber(Integer i) {
  switch (i) {
    case 0:
      return "zero";
    case 1:
      return "one";
    case 2:
    case 3;
    case 4:
      return "a few";
    default:
      return "many";
  }
}

ループ文

KLでは4つのループ分があります。 “C-style” ループ, while ループ, do-while ループ, dictionary ループです。

“C-Style” ループ

C-style loop の形式:

for (startStmt; checkExpr; nextExpr)
  bodyStmt

startStmt の場には文(空でも可)を, checkExprnextExpr は任意に式を置き, bodyStmt は文を置きます。ループは以下のように動作します:

  • 新しいネストされたスコープを作ります。ループ完了で破棄します。
  • startStmt 文を実行します。
  • checkExpr 式があれば評価します。結果の値を Boolean に変換します。 false であればループを終了します。
  • bodyStmt 文を実行します。
  • nextExpr 式があれば評価します。結果の値は無視します。checkExpr 式の評価へと実行ステップが戻されます。

startStmt は文であるため、ここに変数を宣言することが可能です。ループ終了とともにこの変数はスコープからはずれます。ループに束縛されたインデックス変数を startStmt に宣言することは一般的な慣習です。

/*
** Example: C-Style Loops
*/

operator entry() {
  for (Index i=0; i<10; ++i)
    report("i is now " + i);
}

While ループ

while loop の形式:

while (checkExpr)
  bodyStmt

checkExpr の場には式を、 bodyStmt 文を置きます。以下のように動作します:

  • checkExpr 式を評価します。結果の値を Boolean に変換します。false であればループを終了します。
  • bodyStmt 文を実行します。
  • 実行ステップを checkExpr 式の評価まで戻します。
/*
** Example: while Loops
*/

operator entry() {
  Integer i = 0;
  while (i < 10) {
    report("i is now " + i);
    ++i;
  }
}

/*
** Output:

i is now 0
i is now 1
i is now 2
i is now 3
i is now 4
i is now 5
i is now 6
i is now 7
i is now 8
i is now 9

*/

Do-While ループ

do-while loop の形式

do
  bodyStmt
while (checkExpr);

checkExpr の場には式を、 bodyStmt 文を置きます。以下のように動作します:

  • bodyStmt 文を実行します。
  • checkExpr 式を評価します。結果の値を Boolean に変換します。false であればループを終了します。
  • bodyStmt 文の実行まで、実行ステップを戻します。
/*
** Example: do-while Loops
*/

operator entry() {
  Integer i=0;
  do {
    report("i is now " + i);
    ++i;
  } while (i<10);
}

/*
** Output:

i is now 0
i is now 1
i is now 2
i is now 3
i is now 4
i is now 5
i is now 6
i is now 7
i is now 8
i is now 9

*/

Dictionary ループ

dictionary ループは、その辞書の全てのキーと値 ―もしくはキーか値それぞれ単独で反復処理します。辞書のループについては 辞書(Dictionaries) 参照

ループ制御文

ループ本体において、 breakcontinue 文を使用し、現在のループサイクルを途中で終了させることができます。

break

break 文により、最も内側のループを即座に終了します。 break 文はループの外で使用するとエラーになります。

/*
** Example: The break Statement
*/

operator entry() {
  // Only loops 5 times
  for (Integer i=0; ; ++i) {
    report("Loop " + i);
    if (i == 5)
      break; // exit the innermost loop
  }
}

/*
** Output:

Loop 0
Loop 1
Loop 2
Loop 3
Loop 4
Loop 5

*/

continue

continue 文によりループの最も内側の、次の反復へと即座にジャンプします。ループの外で使用するとエラーとなります。

/*
** Example: The continue Statement
*/

operator entry() {
  // Only prints 7
  for (Integer i=0; i<10; ++i) {
    if (i != 7)
      continue; // continue to next iteration of loop
    report("Iteration " + i);
  }
}

/*
** Output:

Iteration 7

*/