Show page source of 制御構造 #51854

= 制御構造
 * MK SCriptの制御構造は、条件分岐、繰り返し、ノードオブジェクト(旧名パラメータ付きノード)の3種類が存在します。
 * さらに、繰り返しは前置条件型と後置条件型の2つから成り立ちます。

== 条件分岐
 * 条件式として記載された内容が真の場合、内部ブロックの処理を実行し、条件分岐構造全体の評価を終了します。
 * 条件式が偽の場合、次の条件式の評価に移ります。
 * すべての条件式が偽の場合、else 節が存在すれば、else節内部ブロックの処理を実行し、条件分岐構造全体の評価を終了します。
 * else節が存在しない場合は、その時点で条件分岐構造全体の評価を終了します。
 * 表記法
{{{
if <条件式> then
  内部ブロック
[
elseif <条件式> then
  内部ブロック
]*             # 0 回以上記載
[
else
  内部ブロック
]?             # 0もしくは1回記載
end
}}}
 * 例
{{{
if x == 10 then
  Console << "x == " << x << "\n"
elseif x < 10 then
  Console << "x < " << 10 << "\n"
else
  Console << "x > " << 10 << "\n"
end  
}}} 
== 後置条件句
 * 右辺の条件式が真の時のみ左辺の式を実行します。
 * 左辺には、単一式のみ記載することが可能です。
 * 左辺式の実行結果を取得することはできません(return節で呼び出し元に値を返す場合を除く)
{{{
  Console.println( "error" ) if x < 0
  return -1 if x < 0
  raise MyException.new() if x < 0
  break if x > 5
  continue if x % 2 == 0
}}}


== 繰り返し
 * 繰り返しは、条件式が真の間、内部ブロックが繰り返し実行されます。
 * 前置条件型は、最初に条件評価を行い、条件式が真であった場合のみ、内部ブロックを実行します。
 * 後置条件型は、内部ブロックを一度実行したのち、最初の条件式評価を行います。
 * 繰り返しの内部ブロック内では、break、continue を記述することができます。
   * breakは、最も内側の繰り返し内部ブロックから脱出します。
   * continueは、最も内側の繰り返し構造の先頭にジャンプします。前置条件型の場合、条件式を再評価します。
 * 表記法
{{{
# 前置条件型
while <条件式>
  内部ブロック
end

# 後置条件型
do
  内部ブロック
end while 条件式
}}}
 * 例
{{{
# 前置条件型
while x != 10
  x = x + 1
end

while x != 10
  if x == 5 then
    break          # breakで繰り返し内部ブロックを抜ける
  end
  x = x + 1
end

while x != 10
  x = x + 1
  if x == 5 then
    continue     # continueで、繰り返し構造の先頭(条件評価)から処理をやり直します。
  end
  y = y + 1
end

# 後置条件型
do
  x = x + 1
end while x != 10
}}}
{{{
do
  x = x + 1
  if x == 5 then
    continue     # continueで、繰り返し構造の先頭(内部ブロックの先頭)から処理をやり直します。
  end
  y = y + 1
end while x != 10
}}}

== ノードオブジェクト
 * ノードオブジェクトは、制御構造を抽象化したオブジェクトです。
 * ノードオブジェクトを使用すると、メソッド、ブロック構造、式をオブジェクトとして保持し、後で必要なときに呼び出すことができるようになります。
 * ノードオブジェクトは、内部定義クラス Node にカプセル化されます。
=== メソッドのオブジェクト化
 * メソッドをオブジェクト化するには、メソッド名を変数に代入します。
 * インスタンスメソッドの場合には、右辺のメソッド名に[@]を付加します。
 * クラスメソッドの場合には、右辺のメソッド名に[@@]を付加します。
{{{
def method1( )
end
x = @method1

def class.method2( )
end
x = @@method2
}}}

 * 別オブジェクトのインスタンスメソッドや、クラスメソッドをオブジェクト化するには、対象オブジェクトを . 左辺に記載します。
{{{
class C0
  def class.method()
  end
  def instance_method( )
  end
end
p = C0.new()
x = p.@instance_method
y = C0.@@method
}}}
 * オブジェクト化されたメソッドを呼び出すには、ノードオブジェクトに定義されている invoke メソッドを呼び出すか、メソッドオブジェクトに直接パラメータを渡します。
{{{
def method( )
end
x = @method
x.invoke( )   # method( ) の呼び出し
x( )          # これも method の呼び出し

def method_2( a, b, c )
  return a + b + c
end
x = @method_2
Console << x.invoke( 1, 2, 3 ) << "\n"  # method_2( 1, 2, 3 )の呼び出し
Console << x( 1, 2, 3 ) << "\n"         # これもmethod_2( 1, 2, 3 )の呼び出し
}}}

=== ブロックのオブジェクト化
 * ブロックのオブジェクト化は無名関数と似ています。
    * 違いは、ブロックが宣言されたスコープのローカル変数へのアクセスが可能なことです。
 * return を使用して、値を返すことも可能です。
 * ブロックオブジェクトは、block ... end で囲まれたブロック構造を変数に代入することで宣言します。
{{{
x = block
  if mode = 1 then
    return method_1( )
  elseif mode == 2 then
    return method_2( )
  end
end
}}}
 * ブロックオブジェクト呼び出し時にパラメータを渡すことも可能です。
 * パラメータ付きでブロックオブジェクトを宣言するには、| param1, param2 ... | block ... end のように、| | で括りブロック定義前にパラメータリストを定義します。
{{{
y = |a, b, c| block
  if mode = 1 then
    Console << a 
  elseif mode == 2 then
    Console << b
  else
    Console << c
  end
end
}}}
 * ブロックは、宣言されたスコープとローカル変数を共有します。
   * ブロック内で初めて参照された変数のスコープは、ブロック内部のみとなります。
   * パラメータとして渡された変数のスコープは、ブロック内部のみとなります。
     * 宣言スコープ内のパラメータ名と同名変数を参照するために owner. を使用することができます。
   * 上記以外の変数は宣言されたスコープの変数参照となります。
{{{
c = 10
y = |a,b| block
  return a + b + c
end

z = |a,b,c| block
  return a + b + c + owner.c
end
}}}

 * 呼び出しは、メソッドオブジェクトと同様に行えます。
{{{
x = block
  if mode = 1 then
    method_1( )
  elseif mode == 2 then
    method_2( )
  end
end
x( )           # x に代入されたブロックを呼び出す
x.invoke( )

y = |a, b, c| block
  if mode = 1 then
    Console << a 
  elseif mode == 2 then
    Console << b
  else
    Console << c
  end
end
y( 1, 2, 3 )         # ブロック引数として、1, 2, 3 を渡し、ブロックを呼び出す。
y.invoke( 1, 2, 3 )
}}}
 * 宣言と同時に呼び出すことも可能です。ただしこの場合、invokeを明示的に記載します。
{{{
|a, b, c| block
  if mode = 1 then
    Console << a 
  elseif mode == 2 then
    Console << b
  else
    Console << c
  end.invoke( 1, 2, 3 )
}}}

 * ブロック内での returnは、呼び出されたブロックから抜けるだけです。呼び出し元メソッドから抜けることはありません。
 * break, continue はブロック呼び出しをまたぐことはできません。

=== エクスプレッション(式)オブジェクト
 * 式オブジェクトは、評価式を{ } で括る事で宣言します。
 * パラメータ付きで式オブジェクトを宣言するには、| param1, param2 ... | block ... end のように、| | で括り式定義前にパラメータリストを定義します。
{{{
a = 1
b = 2
x = { a + b + c }
y = | a, b | { a + b + owner.a + owner.b }
}}}

 * 呼び出しは、メソッドオブジェクト、ブロックオブジェクトと同様に行えます。
{{{
a = 1
b = 2
x = { a + b + c }
y = | a, b | { a + b + owner.a + owner.b }
x()
y(3,4)
}}}