HOME    BRMSブログ  No.13 ルールシートのプロセッシングモード

BRMS徹底活用ブログ

No.13 ルールシートのプロセッシングモード

2017.03.24 Progress Corticon

本エントリーは株式会社アシスト様が寄稿したエントリー(https://www.ashisuto.co.jp/product/category/brms/progress_corticon/column/detail/brmstech13.html)を転載したものとなります。

No.13 ルールシートのプロセッシングモード

2016年4月からProgress製品を担当しています棚橋浩志です。弊社では年度初めにその年の目標として漢字一文字を発表します。私は『脱』を発表して、脱・手続き型プログラミング言語、脱・オブジェクト指向型プログラミング言語を目標に Corticon に取り組んで行きたいと思います。

さて今回はルールシートでのプロセッシングモードについて記事にしてみます。

バブルソート

アルゴリズムの代名詞でもあるバブルソートですが、代名詞という冠が付くだけに私も社内で javascript の新人教育で使っています。構成する要素のインデックスを隣同士で比較して、条件に適合すれば入れ替えるという処理になります。
では反復処理演算子 for を使用してプログラムを作成しましょう。 ここでは10個の数字配列(要素)を昇順にソートする仕様でjavascript のプログラムを作成してみましょう。

var warray = new Array( 100 , 90 , 80 , 70 , 60 , 50 , 40 , 30 , 20 , 10 );
for (var wcnt02= 0 ; wcnt02 < warray.length - 1 ; wcnt02++)
{
        if(warray[wcnt02] > warray[wcnt02 + 1])
        {
            var wint = warray[wcnt02];
            warray[wcnt02] = warray[wcnt02 + 1];
            warray[wcnt02 + 1] = wint;
        }
}

注意点は
1) 比較回数は、要素数 - 1 になる点warray.length - 1
2) 値は一時領域に待避して入れ替えをする点var wint = warray[wcnt02]
3) この反復処理1回で最大値が配列最後の要素 warray[warray.length]に移動される点
になります。
3)は次のステップに重要な事象で、1回の反復処理では最大値が配列最後の要素に1つ決まるだけなので、この反復処理をさらに反復処理させる必要があります。
そしてこの反復処理させる回数は、1回反復処理が行われると1つ最大値(最小値)が決定されるため、1回ずつ減らして行く必要があることです。これを実装するとつぎのようになります。

var warray = new Array( 100 , 90 , 80 , 70 , 60 , 50 , 40 , 30 , 20 , 10 );
for (var wcnt01= 0 ; wcnt01 < warray.length - 1 ; wcnt01++)
{
        for (var wcnt02= 0 ; wcnt02 < warray.length - wcnt01 - 1 ; wcnt02++)
        {
            if(warray[wcnt02] > warray[wcnt02 + 1])
            {
                var wint = warray[wcnt02];
                warray[wcnt02] = warray[wcnt02 + 1];
                warray[wcnt02 + 1] = wint;
            }
        }
}

Corticonでのバブルソート (手続き型プログラミング風)

Corticonでこのバブルソートを実装して見ましょう。
ところで javascript の新人教育ではこのバブルソートはプログラミング技法を学ぶことが主旨なのですが、毎回この主旨を無視したある意味勉強熱心な生徒がいます。それはArrayオブジェクトのsortメソッドを使って課題を10分程度で仕上げて、さらには「作りましたが動きません!」と質問に来ます。・・・今回もまたか・・・と思います。
(Arrayオブジェクトのsortメソッドは文字列のソートに特化したメソッドなので、数字をソートする場合にはもうひとひねり必要です)
手続き型プログラミング編という題目に沿えば、上のjavascriptスクリプト・コードをCorticonのルールシートで実装することが望ましいのですが、Corticon でも sortedBy演算子があるのでこちらを使用したルールを作成してみましょう。

語彙:proceduralSort.ecore

画像1

まず入出力の定義を語彙ファイルで作成します。 10個の要素をソートすると仮定して、sortエンティティ int01属性 から int10属性 まで整数型で作成しました。
そして要素10個を並び替える作業領域を作成してソートするための語彙を追加します。実装方法はいろいろあると思いますが、作業領域が動的に作成できて、sortedBy演算子を使用できるようにつぎのように作成しました。

語彙:proceduralSort.ecore

画像2

つぎにルールシートを作成します。語彙ファイルと同様に実装方法はいろいろあると思いますがつぎのように作成してみました。

ルールシート:init.ers

init.ers ではまず要素を格納する器を作成します。

画像3

アクションA行でwork.integer01 = 0と初期化して、アドバンス推論自己トリガ(上図の赤枠)を使用して、アクションC行でtemp.index = work.integer01 および temp.ranking=-1 の値を持つ10個の器を作成します。
このようにアドバンス推論自己トリガは、「手続き型プログラミング」の概念でいうところのカウンターで数字をインクリメントさせてループさせたい場合に使用します。ルールシートではアクションE行でカウンターを設定しています。
今一度 JavaScript のプログラム・コードを参照ください。
for (var wcnt01= 0 ; wcnt01 < warray.length - 1 ; wcnt01++)
で定義されているスクリプトと同じ動きをします。「手続き型プログラミング」をしてきた人には非常にわかりやすいプロセッシングモードだと思います。

ルールシート:init2.ers

器を作成したら、その器に要素を入れる init2.ers を作成します。

画像4

条件a行のtemp.indexの値を条件にして、アクションA行のtemp.valueに各列の値を代入します。続いてプロセッシングモードをアドバンス推論に設定しました。 init.ers のようにアドバンス推論自己トリガに設定して、カウンターを設定しても同じ動作をしますが、 temp.valueの値は必ず取得されたデータで更新されるという前提があるためにプロセッシングモードをアドバンス推論にしました。反対にtemp.valueの値が更新されないことがある場合には、プロセッシングモードをアドバンス推論自己トリガに設定してカウンターを組み込む必要があります。何故ならアドバンス推論は『データの状態に変化がないと、ループ反復は止まります。』 (Corticon Studio:ルール モデリング ガイド P156参照)という仕様のため、ループの課程で1回でもデータを更新しないことがある場合にはループ処理を中断するからです。

ルールシート:sort_init.ers

これまでの2つのルールシートでソートを実行する準備が整いましたので、ソートの処理をルールシートに作成します。
まずはsortedBy -> firstを使用するために sort_init.ers でカウンターを初期化します。

画像5

ルールシート:sort.ers

つぎにアクションA行でカウンターを使用して sortedBy -> first で順位を決定します。この部分も実装方法はいろいろあると思いますが、以下 sort.ers を作成しました。

画像6

init.ers temp.ranking-1に初期化しているのでArray.rankingの値が0未満とフィルタで設定して、Array -> sortedBy(value) -> first.ranking = work.integer01を実行します。 このArray -> sortedBy(value) -> first.ranking sort.ers で初回しか実行されません。そのためこのルールに アドバンス推論 アドバンス推論自己トリガを設定しても、同じ値を保持します。そのためにルールフローで、この sort.ers にループ設定をして、ループの都度 Array -> sortedBy(value) -> first.ranking が実行されるようにします。ソート処理はこの sort_init.ers sort.ers 2つのルールシートで完結します。

ルールシート:init2.ers

最後に入出力用の語彙に、ソートした結果を戻すルールシート sort_end.ers を作成します。

画像7

この処理はちょうど init2.ers の逆の処理になります。ループが反復されても必ずどこかの値を変化させるという前提でプロセッシングモードをアドバンス推論に設定します。

ルールフロー:proceduralSort.erf

以下ルールフローを作成します。

画像8

注意点は 先に説明した sort.ers にループの設定をします。

テストシート:proceduralSort.ert

バブルソートのルールが完成したのでテストを行ってみましょう。

画像9

問題なくソートが実行されましたが、膨大なルールの集大成になってしまいました。Corticonには先頭データを操作するまたは最後のデータを操作する演算子はありますが、中程のデータに対して順列を保持して操作するといった演算子はありません。
JavaScriptのソースで言えば、外側のfor文は制御できるが、入れ子になっている内側のfor文の処理は苦手ということになります。

一方でバブルソートに付きまとう無駄な処理がこのワークフローにも含まれています。
例えば JavaScript で定義すれば

var warray = new Array( 10 , 20 , 30 , 40 , 50 , 60 , 70 , 80 , 100 , 90 );

という要素の場合には最後の2つの要素を1回交換するだけで済みますが、比較処理は実に46回行われます。言い換えれば『46回比較処理をした結果10個の要素がソートされます』という話になり、要素交換が主たる処理なのか要素比較が主たる処理なのかわからなくなります。 これが手続き型プログラミングの長所でもあり短所でもあります。

そこで脱・手続き型プログラミングということでCorticonならではの実装をしてみましょう。

Corticonでのバブルソート (Corticon風)

Corticonでのバブルソート (手続き型プログラミング風)で、プロセッシングモードの アドバンス推論アドバンス推論自己推論の違いは理解頂けたでしょうか。
アドバンス推論は『データの状態に変化がないと反復が強制停止する』モードであり、 アドバンス推論自己推論は『カウンターを使用して、データの状態に変化がなくても反復を強制停止させない』モードとなります。
(アドバンス推論自己トリガにカウンターが記述されていない場合には、アドバンス推論として動きます)

このCorticonの特性を利用すると実は簡単にバブルソートがつくれてしまいます。もうおわかりになりましたか?バブルソートの根本的な処理は『要素を比較して交換する』ということです。交換処理がなくなったら処理は終了です。(=『データの状態に変化がないと反復が強制停止する』)。46回比較する必要はないのです。早速実装してみましょう。

語彙:sort.ecore

まずは語彙ファイルを作成します。手続き型プログラミング風同様に入出力の定義をします。

画像10

語彙ファイルは以上です。驚かれるかもしれませんがソートのための項目追加はtemp属性だけです。

ルールシート:sort.ers

つぎにルールシートを作成します。

画像11

『左右の要素を比較して左の値が大きければ交換する』ということを記載しただけです。なお値の交換には値を sort.temp に一時待避させて移動します。これはプログラミングの鉄則です。そしてこのルールシートにアドバンス推論を設定して完成です。データの状態に変化がなくなったらCorticonは反復処理を強制停止します。またルールシートが1つなのでルールフローで制御する必要もありません。

テストシート:sort.ert

ではテストを実施してみましょう。

画像12

問題なくソートが実行されました。手続き型プログラミング風はここまで簡単にはなりません。

最後に

実は手続き型プログラミング風のルールの作成にはまたしても3日以上費やしました。振り返れば思うように動かないことに時間をとられ調査して、課程で別の問題に突き当たりと、開発さながらの状況になりました。一方Corticon風のルールはテストの実施まで1時間でした。今回の結果には驚きを隠せなかったと同時に、前回も指摘した脱・手続き型プログラミングの思考回路でルールを作成する必要があると実感しました。 その結果開発工数も大幅に削減され Corticon をちょっぴり見直しました。

皆様も是非『脱・手続き型プログラミング』思考で Corticonをご活用下さい。