数多くのBIFの中からーアップデート
前回の記事では、久し振りの「A Bevy Of BIFs(数多くのBIFの中から)」シリーズとして、このファミリーに最も新しく加わった%ScanRplについて取り上げました。記事を書き終えて程なくして、%SubArrなど、近頃、数多くのBIFが追加されたことや、%Trimxのように、人目に留まらなかったように思われる、既存のBIFに対するいくつかの機能強化があったことを実感しました。この記事では、そのような、これまで取り上げてこなかった事柄について取り上げようと思います。
%Trim: トリミング対象の文字を指定する
まずは、皆さんよくご存じと思われるBIF、%Trimから見て行きましょう。先に進む前に、指摘しておきたいことがあります。ここでは%Trimと記しますが、これには、Trimファミリー全体、すなわち、%Trim、%TrimL、および%TrimRが含まれます。
一方、皆さんあまりご存じでないと思われるのは、%Trimでは、どの文字を切り取りたいかを正確に指定できるようにする、オプションの2番目のパラメーターを使用できるようになったことです。これは、先頭にアスタリスクや通貨記号が付いた数値が出てくるCSVファイルまたはWeb サービスからのデータを処理するときに実に便利です。たとえば、$250.95や***125などです。以下のサンプル コードは、この新たなオプションが、そのような値をどのようにして「クリーン アップ」できるようにするかを示しています。
このコードを実行すると、フィールドtargetは123.45という値を受け取って長さは6になり、この時点で、後の数値計算での使用のために%Dec()による処理が可能になります。
dcl-s charAmt2 char(20) Inz('***$123.45');
dcl-s target varchar(20);
target = %Trim( charAmt2 : ' *$' );
ブランクが取り除かれるためには(サンプルでは後続ブランク)、除去対象となる文字列にスペースを含める必要があった点に注目してください。結果として生じる値を%Dec()で使用することだけが目的という場合は、このBIFは先行および後続スペースを自動的に無視するため、これは必要ではなかったでしょう。
%Addr: *Dataオプション
オンライン フォーラムに投稿されているサンプルのうち、これを利用していないものの多さからすると、多くの人がこれを見落としているに違いないと私は思っています。
V5R4で導入された%Addr BIFの*Dataオプションは、可変長フィールドのデータ部のアドレスを返し、自動的に2(または4)バイト カウントのヘッダー部をバイパスします。私は、文字列を繰り返し構築するプログラムで大いにこの機能を活用しています。たとえば、CSVファイル、JSON、HTML、およびXMLなどへ書き込まれることになるレコードです。こうした種類の文字列は、固定長フィールドと比べて、可変長フィールドで構築するほうがはるかに効率的ですが、このオプションが導入されるまでは、APIに渡す前にアドレスに手動で2(または4)を追加する必要がありました。その結果として、次のようなコードに出くわすかもしれません。
pParmData = %Addr(ParmData) + 2;
この新たなオプションを使用することで、次のようにコーディングできるようになります。
pParmData = %Addr(ParmData: *Data);
このコードは、後にコードの維持管理を担当する誰にとっても、簡潔で読みやすいだけでなく、はるかに安全でもあります。
なぜそうなのでしょうか。ParmDataは元々50,000バイト長で、後に、サイズを100,000バイトに増やす必要があるとします。この時点で、この変更処理を担当するプログラマーが、100,000バイトの可変長フィールドでは、2バイトではなく、4バイトのヘッダーが必要であることを認識していることを願う必要があります。結果として、単にフィールド長を変更するだけでは不十分です。また、「+ 2」を「+ 4」へ変更することも忘れてはなりません。そうしないと、作成されるアドレスは正しいものではなくなります。最初のプログラマーが*Dataオプションを使用していれば、このことは問題にならなかったでしょう。コンパイラーが自動的に正しいアドレスを算出してくれただろうからです。
配列を使用しませんか
配列が当たり前に使用されているPHPでコーディングをするようになってから、RPGプログラマーは、そうするべきであるほど配列を使用していないということに気付かされました。しかし、配列を実際に活用しているプログラマーの間でさえも、%SubArr BIFは、気付かれることなく、あっと言う間に通り過ぎてしまったようです。V7.4リリースの登場までは、RPGには、PHPのような言語が行う意味での動的配列を使用する機能はありませんでした。けれども、%SubArrを使用することで、固定長配列によって生じるいくつかの問題は解決できるようになります。
%SubArrは、SORTAなどの命令で配列の一部分に対してのみ処理が行われるように指定することを可能にします。そのため、たとえば、配列myArrayをロードしていて、countフィールドにおけるアクティブな要素の数をトラッキングする場合は、次のように、%SubArrを使用することで、その値を後で使用することができます。
SORTA %SubArr( myArray : 1 : count );
これにより、配列のアクティブな部分だけをソートすることが可能になります。これは、より高速であるだけでなく、ソートの後で、「それらが邪魔にならないようにしておく」ために、事前に高い(または低い)値をすべての配列要素にロードしておく必要もなくなります。
また、%SubArr()は、配列の一部分を別の部分に割り当てるのに使用することもできます。古いMOVEA命令が使用される方法と、いくつかの点で似ています。以下の1つ目の例では、arrayZoneの要素5~9が、arrayPackの要素1~5へコピーされます。2つ目の例では、arrayZoneの要素1、2、および3が、arrayPackの要素2、3、および4にコピーされます。
dcl-s arrayPack packed(5) dim(5);
dcl-s arrayZone zoned(7:2) dim(10);
dcl-s i int(5);
// Fill up arrayZone elements
for i = 1 to %Elem(arrayZone);
arrayZone(i) = i * 3.3333;
EndFor;
// Copy elements 5 - 9 of arrayZone to elements 1 - 5 of arrayPack
arrayPack = %SubArr( arrayZone : 5 );
// Copy elements 1, 2 & 3 of arrayZone into elements 2, 3 & 4 of arrayPack
%SubArr( arrayPack : 2 : 3 ) = arrayZone;
配列の話の途中ですが . . .
V7.1でデータ構造配列に対してlookup命令を実行する機能がようやく備わったことは、気付かれないまま過ぎ去ってしまったのかもしれません。DS配列はV5R2の頃からありましたが、それらを検索するには、bsearch()などのAPIに頼る必要がありました。
bsearchは、複雑なルックアップでは今でも役割がありますが、単純なルックアップ操作では、必要とされなくなっています。V7.1以降では、以下の例のように、直接DS配列に対してルックアップを実行することができます。配列全体が検索されることを示すために、配列の指標として(*)が使用されていることに注目してください。
dcl-s element int(5);
dcl-s product char(7);
dcl-ds productInfo Dim(999) Qualified;
productCode Like(product);
description Varchar(60);
price Packed(7:2);
cost Packed(7:2);
end-ds;
product = 'A1234456';
element = %LookUp( product : productInfo(*).productCode );
コーディングがはるかに簡単です。唯一のマイナス面は、現在のところ、サポートされているのは基本的な%LookUp BIFのみであり、%LookUpGEなどの「ファミリー」の他のメンバーはサポートされていない点です。