nまでの整数のランダムシーケンス

(以下の図では、表示されているメソッドが説明と食い違っている場合がありますが、ブラウザをリロードすると改善します。)

自然数n(n>0)の1からnまでのランダムな数列を配列に入れて出力する例です。

実行例はこちら。(Runボタンを押してみてください。UI-PANEの上のコンソールウィンドウに出力されます。)

AppクラスのMainlineメソッドがスタートポイントで、上からAppクラスのインスタンスが投入されてきてスタートします。ちなみに、VPGLのチャートは、データフローグラフです。(フローチャートのような)制御フローグラフではありません。

Number::RNDSEQ(ユーザー定義)は、投入されている整数n(この場合は10(CONSTは、何が入力されても、オペランドの定数を出力するノードです))までのシーケンス配列を出力し、ALRTでコンソールに出力し、そのオブジェクトが到達するSTOPノードの実行によって系が停止します。

Number::RNDSEQは、Number::MKSEQ(ユーザー定義)でnまでの整列された数列[1 2 3 4 … n]を作成し、配列の配列[ [1 2 3 4 … n] [] ]を作ります。そのあと、一番目の配列からランダムに値を取り除いて、取り除いた値を二番目の配列の先頭に追加して配列を伸ばしていき、一番目の配列が空になったら(ここまでArray::MOVRND(ユーザー定義))戻り値の二番目の要素の配列を値として返します。

T::MKSEQ(n)は、再帰的に定義実装されています。VPGLでは、ループにすると、下から上にデータを戻すパスが必要なので、スペースの節約のため再帰呼び出しを常用します。

  • n<=0ならば, 空の配列[]をかえす。
  • n>=1ならば、T::MKSEQ(n-1)の値の先頭に、nをArray::PUSH(システム定義済み)で追加して、その結果を返す。

です。

Array::MOVRND([配列1, 配列2])も再帰的に定義されています。

  • 配列1が空ならば、入力をそのまま出力する。
  • 配列1が空でなければ、
    1. m: (0<=m<配列1の要素数)を乱数で決定して、
    2. 配列1の中からArray:REMBER(m)=>[要素, 残りの配列]-ユーザー定義を使って、m番目の要素を取り除き、
    3. 取り除いた要素を配列1の先頭にArray:PUSH(システム定義)で追加して、
    4. [m番目が取り除かれた配列1 要素が追加された配列2]の形の配列に対して再帰的にMOVRNDを適用した結果を返す。

です。

Array::Rember(m) => [m番目の要素 m番目の要素が取り除かれた配列] も再帰的にに定義されています。

上(D1)からは配列(空でないことが必須)、左(A4)からmが入ってきます。出力は、出力0(G4)から、取り除いたm番目の要素が、出力1(D7)から、m番目の要素が取り除かれて長さが1短くなった配列が出力されます。チャートは以下のロジックが作図されています。

  • m<=0ならば、[配列の0番目の要素 残りの配列]を出力します。
  • m>1ならば、
    e : 配列の0番目の要素(HEAD(システム定義)でとりだし)
    l : eを取り除いて、長さが1短くなった配列(REST(システム定義))とし、
  • 出力として
    出力0 : l->REMBER(m-1)の出力0
    出力1 : (l->REMBER(m-1)の出力1)->PUSH(e)
    を出力します。

途中、C2のSWITCHで二つの入力を同時に振り分けるために、C1で[入力0 入力1]の配列を作って、SWITCHの振り分け後にそれぞれの要素をArray::GET(システム定義)で取り分けたのちに処理しています。(このあたりの決まりごとのような冗長さ言語仕様の改善で何とかしたいのですが…)