AS/400展望台

RPG IVのための役立つ10のヒント



ブライアン・マイヤーズ著

RPGのプロは、作業の円滑化、厄介な問題の解決、新機能の実装、そして開発したコードの効率向上や保守性の改善などを可能にする新しい秘訣やテクニックを常に探しています。新発売のRPG IVは、新しいテクニックと構造をいくつも備え、開発者が自らの書いたプログラムを最大限に活用できるようになっています。最近私が見つけた、役に立つ10 (順不同)のヒントを以下に紹介します。

1. %TRIM関数を使って文字を取り除く

RPG IVの登場以来、%TRIM関数(および%TRIML関数と%TRIMR関数)を使って文字列の端にある空白を取り除くことができるようになりました。%TRIM関数は、先頭または末尾に空白のない可変長の文字列値を返します。
IBMはV5R3で%TRIM関数を機能強化し、空白文字だけでなく任意の文字を文字列の先頭や末尾から取り除くことができるようにしました。取り除く文字列を指定するには、2番目のパラメータを追加します。

%TRIM(string:characters)

たとえば、文字フィールドAmountの値「$123.45」からドル記号($)を取り除くには、次のように関数を呼び出します。

%TRIM(Amount:'$')

2番目のパラメータには取り除きたい文字を指定します。したがってAmountの値が「$***12,345.67 」であれば、次のように関数を呼び出します。

%TRIM(Amount:'$*,')

すると「12,345.67 」という値がプログラムに返されます。この例では取り除く文字の1つとしてカンマを指定していますが、Amountの値中のカンマは文字列の途中にある(先頭や末尾にはない)ため、結果として返される値にはカンマが入っているという点に注意してください。また、返された値には末尾の空白が含まれている点にも注意してください。空白文字も取り除くには、次のように取り除く文字として空白文字も指定しなければなりません。

%TRIM(Amount:'$*, ')

2番目のパラメータをまったく指定しないと、%TRIM関数は以前の動作と同様に空白文字だけを取り除きます。

2. %DEC関数を使って文字列を数値に変換する

V5R2では、%DEC関数は文字列式を以下に示す精度でパック十進数に変換します。

%DEC(expression:digits:decimals)

今までのリリースでは、%DEC関数は数値式しか扱えませんでした。 文字列の変換に%DEC関数を使用するときは、2番目と3番目のパラメータを指定しなければなりません。たとえば、Amountという文字フィールドの値が「00123.45」であった場合、次のようなコードを書くことでこの文字列をパック十進数に変換することができます。

%DEC(Amount:7:2)

文字列値に数字以外(十進数以外)の文字が含まれている場合は、その文字を%XLATE関数を使用して空白文字に変換することができます。たとえばAmountの値が「$***12,345.67 」の場合、次のようなコードで通貨記号、アスタリスク、カンマを空白記号に変換することができます。

%DEC( %XLATE('$*,':' ':Amount) : 7 : 2 )

%DEC関数は先頭と末尾以外の空白文字を無視しますので、戻り値は7桁に切り詰められた12345.67となります。

%DEC関数は不正な数字を見つけるとステータス・コード105を返しますので、次のように簡単に捕えることができます。

MONITOR;
  Result = %DEC(%XLATE('$*,':' ':Amount)
            :7:2);
ON-ERROR 105;
  Result = 0;
ENDMON;


結果の数値をまるめるには%DECH関数を使用します。%INT関数と%INTH関数も、文字列を引数として受け取って整数値を返します。

3. %DEC関数を使って日付を数値に変換する

数値を日付に変換するには、次のように%DATE関数を使うと簡単です。

%DATE(Number:Format)

2番目のパラメータは数値で表現した日付の形式です。しかしこれまでは逆に、日付の値を数値に変換するほうが問題で、通常は文字列の結合、データ構造、%DEC関数と%CHAR関数を使用する必要がありました。

V5R3では、%DEC関数により日付、時間、タイム・スタンプなどの表現を次のように十進数値に直接変換できるようになりました。

%DEC(Date:Format)

2番目のパラメータは数値に変換したときの形式を示しています。たとえば、DueDateという日付フィールドにJune 30, 2005という値が(任意の形式で)入っている場合、次のコードで20050630という数値を取り出すことができます。

%DEC(DueDate:*ISO)

形式を省略した場合、%DEC関数は1番目のパラメータの日付の形式を使用します。%DEC関数の返す値は、選択された形式に対して適切な桁の数値となります。

4. レガシーのコードを名前つきインジケータに変換する

新しいRPG IVプログラムでは、名前つきインジケータ、インジケータ・データ構造および組み込み関数のおかげで、類似の99個の番号付きインジケータを使用する必要がほとんどなくなりました。しかし、レガシーのRPGコードをアップグレードして番号付きインジケータを新しい構成要素で置き換えるのは現実的ではなく、エラーが起こりやすくなるのではと思われるでしょう。

幸い「どちらか一方を選択しなければならない」という問題ではありません。同じ条件に対して番号付きインジケータも名前付きインジケータもどちらも割り当てることができ、番号付きインジケータかそれに対応する名前付きインジケータをプログラム中で等価的に使用できるようになっています。図1に示すようにデータ構造とポインタを使用するだけです。

図1のインジケータ・データ構造は内部の配列*Inと同じアドレスを指していますので、番号付きインジケータと同じ記憶域を共有しています。この例では、プログラムはインジケータ03を参照するのに番号と名前のいずれでも参照できます。名前の場合はExit (Indicatorsデータ構造の3バイト目)となります。これはデータ構造中のほかのインジケータでも同じです。

5. system()関数と一緒にコマンドを実行する

RPGプログラムからCLコマンドを実行するときは、QCMDEXC APIかQCAPCMD APIを使用するのが一般的です。しかし、Cのランタイム・ライブラリ関数system()を使用するとより便利です。system()関数はコマンド文字列をコマンド・プロセッサに渡しますが、コマンド文字列の長さや他のパラメータを渡す必要はありません。

system()関数を呼び出すには、system()関数をコマンド文字列のポインタに渡すだけです。図2 のAに、お勧めのプロトタイプと必要となるH仕様を記載しました。プログラム中でコマンドを実行する必要が生じたら、このプロトタイプを参照してください。コマンド文字列は変数、リテラル、名前付き定数あるいは式のいずれかです。図2のコードの残りの部分は典型的な使用方法を示しています。

戻り値でsystem()関数の呼び出しが成功したか失敗したかがわかります。成功した場合は戻り値が0、失敗した場合は1となります。文字列へのヌル・ポインタを渡した場合はsystem()関数は-1を返し、コマンド・プロセッサは呼び出されません。

system()関数の呼び出しが失敗した場合(つまり戻り値が1の場合)、グローバル変数_EXCP_MSGIDにCPFのメッセージIDがセットされます。図3の強調表示した部分のコードのように、この変数を自分のプログラムにインポートして、エラーの具体的内容をチェックできます。

system()関数を使用するには、プログラムをコンパイルあるいはバインドする際に、バインド・ディレクトリQC2LEを参照しなければなりません。図2ではH仕様の中でQC2LEを指定しています。

6. コンパイル時配列を置換する

コンパイル時配列は、ソース・メンバー内でコードされたデータを使用して配列要素に値を読み込みます。配列の定義時にCTDATAキーワードをコーディングすると、その配列はコンパイル時配列であることを示し、データは**CTDATA文の後、ソースの最後にコードされます。

コンパイル時配列をコーディングするこの方法の1つの欠点は、配列の定義と配列のデータを、場合によっては数千行というRPGコードを間に挟んだ2つの分離した部分として配列をコーディングしなければならないということです。モジュール化されたコーディング環境では、密接に関連したコード同士はソース・メンバー中でも物理的に近くに配置したほうが良いでしょう。

図4は、配列の定義とそれに関係したデータを、1つのデータ構造中に統合したコンパイル時配列をコーディングするもう1つの方法を示しています。Days配列はDaysdataデータ構造と同じ記憶域にオーバーレイされていますので、配列要素にはDaysDataからの値が含まれることになります。

プログラムがデータ構造サブフィールドを名前で参照することがなければ、そのデータ構造サブフィールドには名前が付いている必要はありません。そのため、Daysdataデータ構造の各フィールドには名前を付けず、データ構造中のデータを初期化するだけで構いません。

7. 配列データ構造を使用する

複数回繰り返しデータ構造は、その定義のOCCURSキーワードに示されている通り、最大32,767回まで繰り返されるデータ構造です。RPGプログラムは、%OCCUR関数あるいはOCCUR演算コードを使用して、現在のデータ構造にデータをセットするかデータ構造からデータを取得するというように、一度に1つのデータ構造を処理できます。複数回繰り返しデータ構造を使用すると、一度に1つのコピーを処理しながら、複雑なデータのコピーを複数保存することができます。

V5R2では、複数回繰り返しデータ構造を新しい構成要素である配列データ構造に効率よく置き換えました。配列データ構造は、DIMキーワードを使用して定義し、繰り返し回数を指定した修飾データ構造です。配列データ構造は複数回繰り返しデータ構造に似ていますが、インデックスを指定するのに通常の配列記法を使用するという点と、同じ文から複数の繰り返しを参照できる点が異なります。

図5に配列データ構造の使用法を示しました。Regionslsデータ構造は50個の要素(つまり繰り返しが50回)の配列データ構造です。このデータ構造は修飾データ構造でなければならないということに注意してください。つまり、各サブフィールド名をデータ構造名で修飾しなければならないということです。データ構造中のサブフィールドを処理する際は、従来の配列記法のようにインデックスを参照することができます。たとえば、Regionsls(25).NameはRegionslsの25回目の繰り返しのNameサブフィールドを参照します。

配列データ構造を使用すると配列でもあるサブフィールドを定義することができ、効果的に多次元配列を使用することができます。図5に示した例では、MonthlyslsはRegionsls配列データ構造のサブフィールドとして定義された配列になっています。この例はMonthlysls配列の特定のインデックスを参照する方法を示しています。

8. レコード形式データ構造を使用する

V5R2では、LIKERECキーワードを使用して、レコード形式と同じレイアウトを使用したデータ構造やデータ構造サブフィールドを簡単に作成できます。2番目のパラメータはオプションで、レコード形式中のどのフィールドをデータ構造中に含めるのかを選択することができます。

・ LIKEREC(format:*ALL)は、レコード形式中のすべてのフィールドをデータ構造に含めるという意味になります。
・ LIKEREC(format:*INPUT)は、入力可能なフィールドすべてを含めるという意味になります。これがデフォルトです。
・ LIKEREC(format:*OUTPUT)は、出力可能なフィールドすべてを含めるという意味になります。
・ LIKEREC(format:*KEY)は、キー・フィールドだけを順番に含めるという意味になります。

図6は、LIKERECキーワードを使用して、Ordrec形式からのキー・フィールドだけを含んだOrderkeyデータ構造を定義している例です。このプログラムはレコード形式名を知っていなければなりません(つまり、適切なレコード形式を含むファイル用のF仕様がなければなりません)。F仕様がレコード形式の名前を変更する場合は、変更になった名前の(内部)形式名を使用します。

LIKERECデータ構造は修飾データ構造ですが、その定義にQUALIFIEDキーワードを指定することはできません。サブフィールドは修飾名(たとえばOrderkey.Customer )で参照します。

修飾データ構造中のサブフィールド用にLIKERECキーワードを含めることもできますので、データ構造中にデータ構造を入れ子にすることもできます。この場合、サブフィールドは自動的に修飾されますので、OrderDetail.Orderkey.Customerという名前のサブフィールドを参照する場合もあります。

これまで外部記述データ構造を使用してLIKERECと同様の定義をしていたのであれば、EXTNAMEキーワードにも新しい追加のパラメータがサポートされてレコード形式に名前を付け、データ構造中のフィールドを制限できる(たとえば、EXTNAME(Ordfile:Ordrec:*KEY))のは朗報でしょう。しかし、EXTNAMEを使用してデータ構造中にもう一つのデータ構造を入れ子にすることはできません。

9. キー・リストを置換する

I/O操作の一部(CHAIN、DELETE、READE、READPE、SETGT、SETLLなど)は検索引数を使用して処理対象のレコードを識別します。これまで、検索引数はフィールド名かまたはKLIST演算コードおよびKFLD演算コードを使って定義したキー・リストでした。V5R1の新しいフリー形式の仕様では、KLISTまたはKFLD演算で合成キーを定義することはできません(ただし、フリー形式の仕様は固定形式の計算で定義されたキー・リストを処理することはできます)。

V5R2ではより優れた方法が採用されました。フリー形式の仕様に対しては、検索用の引数として値のリストを括弧でくくることができるようになりました。

Chain (Company:Custnbr) Custrec;

この例では、Custrecレコードを取り出すための合成キーとしてCompanyとCustnbrを使用しています。V5R2では、値のリストを作成する際にはファイル名ではなくレコード形式名を使わなければなりませんでしたが、V5R3ではこの制限がなくなりました。

合成キー中の値はすべて、式でもあるいは手続きの呼び出しでも構いません。

Chain (Company:Custnbr+1) Custrec;
Chain (Company:GetNextOrder(Company:Custnbr)) Orderrec;


上の例の2行目では、GetNextOrderが手続き名です。この例では、検索引数の2番目の値として手続きの呼び出しが完全なパラメータ付きで組み入れられています。

合成リストをコーディングする際のフリー形式のもう1つのオプションは、V5R2の%KDS (Key Data Structure)関数を使用して、キーとして使用するデータ構造を指名する方法です。図6の強調表示した部分が%KDS関数の使用例です。この例では、Orderkeyデータ構造はLIKERECデータ構造であり、Ordrec形式のキー・フィールドだけを使用しています。SETLL操作はこのデータ構造を検索引数として使用します。キー・データ構造中のフィールド数よりも少ないフィールド数を検索引数に使用させたい場合は、任意のサブフィールド数を指定することができます。図6では、READE操作は検索引数としてOrderkey構造の最初の2つのフィールドしか使っていません。V5R2では、%KDS関数を使用する際にはファイル名ではなくレコード形式名を使わなければなりませんでしたが、V5R3ではこの制限がなくなりました。

10. データ構造をパラメータとして渡す

データ構造全体をプロトタイプ・パラメータとして手続きに渡すことができます。まず、通常のデータ構造を作成します。次に、プロトタイプ中にLIKEDSキーワードを入れて、作成したデータ構造と同じレイアウトのパラメータを定義します。図7のAに、手続き呼び出し中でプロトタイプをコーディングする方法を示します。

呼び出された手続き中で、LIKEDSパラメータ中のサブフィールド名をパラメータ名とみなして、そのサブフィールドにアクセスできます。図7のBに、データ構造を受け取ってそのサブフィールドを呼び出された手続き中で使用している例を示します。

データ構造をパラメータとして渡すときは、参照データ構造をベースとしたLIKEDSデータ構造でもあるパラメータ(たとえば図7のQualname)を渡すか、または参照データ構造自体を渡すのが一番良いでしょう。しかし、パラメータの長さが正しい限り、任意の文字列フィールドを渡すこともできます。

データ構造をパラメータとして渡す以外に、手続きからの戻り値としてデータ構造を受け取ることもできます。戻り値中のサブフィールドを処理するには、その値を呼び出している手続き中のデータ構造に割り当てます。同様のテクニックを使用して、LIKERECキーワードを使ってパラメータを渡したりレコード形式を真似た戻り値を定義したりすることができます。

ブライアン・マイヤーズ氏はベル・データ鰍フ業務提携先、Penton Media, Inc.が発行するiSeries NEWS誌のテクニカルエディターで、RPG IV Jump StartやProgramming in RPG IV(ともに29th Street Press社版)などの著書があります。同氏はiSeriesに関する講義を、サイトやDVD、iSeries Networkの遠隔学習プログラムで提供しています。



↑このページのトップへ
TOPPAGE

BELLDATA, Inc. Copyright reserved.