秋の訪れとともにやって来たRPGの新機能、パート2
前回の記事 では、2020年秋のリリースでRPGに加わった新機能のいくつかについて概略を説明しました。その際に記載できなかったいくつかの機能について、今回と次回の記事で取り上げます。
ここでは、新たなFOR-EACHループ構造について紹介します。これは、PHPで出会って以来ずっと、RPGにあればと思っていた機能でした。簡単に言えば、FOR-EACHは、自動的に配列を繰り返し処理し、一度に1つの要素を「供給」します。ここで「配列」と言うのは、データ構造配列、動的配列、さらには前回の記事で紹介した、%LIST BIFによって作成される新たな一時的な配列も含め、あらゆる種類の配列のことを言います。
以下は、FOR-EACHを使用して、単純な独立型配列のすべての要素を処理する、実に単純な例です。
dcl-s nameArray char(10) Dim(100);
dcl-s name like(nameArray);
// The old way
For element = 1 to %elem(nameArray);
Dsply nameArray(element);
EndFor;
// The For-Each way
For-Each name in nameArray;
Dsply name;
EndFor;
FOR-EACHが各要素をnameフィールドに抽出するため、添え字を使用してループ内の要素にアクセスする必要がないことに注目してください。単純な配列を処理するときは大した問題ではありませんが、複数フィールドDS配列を処理するときには、処理がはるかに簡素化されることになります。後ほど、これについての例を示します。
しかし、FOR-EACHが本領を発揮するところは、新たな 動的にサイズ指定される配列で使用するときです。RPGは、配列内にアクティブな要素がいくつあるか把握しているため、その情報を使用してループの処理を制御することができます。
新たな動的配列サポートの私のお気に入りの使い方の1つは、SQL結果セットの処理での使用ですので、次の例でSQLを使用してみましょう。私が気に入っているのは、動的配列とFOR-EACHを組み合わせて使用すると、実際、ループを制御するのにSQLERRD(3)の値を使用する必要がないことです。以下に、シンプルな例を示します。
Dcl-Ds results ExtName('ANIMALS') qualified dim( *Auto : 1000 ) End-ds;
Dcl-Ds animal likeDs(results);
Dcl-s type like(results.TYPE);
Dsply ( 'What type of animal do you want?' ) ' ' type;
Exec SQL
Declare animalsCursor cursor for
Select * from ANIMALS where type = :type;
Exec SQL
Open animalsCursor;
Exec SQL
Fetch from animalsCursor for 1000 rows into :results;
Dsply ( 'Found ' + %Char( %Elem( results ) ) + ' matching animals' );
For-Each animal in results;
Dsply ( 'Id: ' + %Char(animal.Id) + ' - Name: ' + animal.name );
EndFor;
また、簡略化されたフィールド参照と私が呼んでいたものも、ここで見ることができます。手作業でループをコーディングしていたとしたら、animal.Idやanimal.nameなどの参照は、results(element).Idやresults(element).nameとコーディングしなければなりませんでした。タイピングが多くなればなるほど、それだけエレガントでなくなります。
ただし、私にとっては、単にタイピングの削減ということだけではありません。コードの意図、すなわち、この配列内のすべてのアクティブな要素を処理するつもりだ、ということがより明確になるという意味で、可読性も向上することになります。
この組み合わせがSQLの処理をどれほど簡単にすることができるかということについて、お分かりいただけたでしょうか。カーソルを使用しなければならないことを心配する必要がないとしたら、どれだけ良いか少し考えてみてください。ターゲットとなるDSに複数行SELECTを行うだけというのは素晴らしいのではないでしょうか。そうする方が、はるかに簡単であり、他の言語で行われていることにより近いでしょう。折しも、そう感じるのは私だけでなかったようで、この機能を提供するようIBMに要望するRFEが挙げられています。IBMでは、このような要望が獲得する得票数にかなり注目しているため、私に賛同していただける方は、この機能強化に投票していただければと思います。このRFEは、https://www.ibm.com/developerworks/rfe/execute?use_case=viewRfe&CR_ID=141665でご覧になれます。
FOR-EACHはすべての配列処理のニーズにぴったり合うのか
ご覧の通り、FOR-EACHは、配列のすべての要素を処理するとき、特に動的配列を処理するときには、見事に動作します。しかし、フィルされてこなかった従来の(すなわち固定サイズの)RPG配列についてはどうでしょうか。FOR-EACHの登場以前は、アクティブな要素の数の最大値を上限として使用して、普通のFORループでこれをコーディングするのが一般的でした。そのような状況でFOR-EACHを使用することもできますが、%SUBARRを使用して配列の範囲を制限する必要があるため、結果として生じるコードはそれほどエレガントではありません。結局、そのような状況で古いアプローチを使用してもよいのですが、以下の比較例を見比べて、それぞれお選びいただければと思います。
// Traditional FOR loop approach
For element = 1 to nameCount;
Dsply nameArray(element);
EndFor;
// Using FOR-EACH with a partially filled array
For-each name in %Subarr(nameArray: 1: nameCount);
Dsply name;
Endfor;
次回予告
これらの機能強化に関する、最後にして3本目となる記事では、文字から数値への変換を支援するために設計された、新たな2つのコンパイラー オプションについて取り上げる予定です。それらは%DECなどのBIFの処理を改善するだけでなく、XMLおよびJSONの処理も支援することをお分かりいただけると思います。