制御構造

  • 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)