プログラムの正規化は昔から必要だと言われている。
つまりGOTO命令はできるだけ使わずに
DO-END, IF-ELSE-ENDIFなどで構造化せよという
ことである。
_
ところがこのプログラマーはSETLL & READ が良くわかっていないようだ。
複数のキー・フィールドを持つ索引ファイルに最上位のキー・フィールドだけで
SETLLをかけても良いのだが一般的にはすべてのキー・フィールドに
*LOCALをセットしてからSETLLするほうが確実である。
■ 正しい読取りファイルを選択する
まず最初にやるべきことは読取るフィァイルとして正しい適切な論理ファイルを
選択することである。
このプログラマーが選択した論理ファイルのキーは
*--------------------------------------------------------------------------+ C UKKEY6 KLIST C KFLD UKT050 起票店 CD C KFLD UKT001 本支店 CD C KFLD UKT004 取引先 CD C KFLD UKT002 処理日付 C KFLD UKT008 手形番号 *--------------------------------------------------------------------------+
であるが実際の処理を見ていると
「起票店 CD」を指定してなおかつ指定された「処理日付」以下のデータを読み取って
処理日付順に印刷することである。
そうであればこの論理ファイルのキーは処理の目的に対して適切ではなく
正しくは
*--------------------------------------------------------------------------+ C UKKEY6 KLIST C KFLD UKT050 起票店 CD C KFLD UKT002 処理日付 C KFLD UKT001 本支店 CD C KFLD UKT004 取引先 CD C KFLD UKT008 手形番号 *--------------------------------------------------------------------------+
をキーとする論理ファイルを使用するべきである。
そうでないと処理対象でないレコードを読み飛ばさなければならない無用な処理が
発生するからである。
後述の処理を見ればそれが現れている。
このときにプログラマーは自分が指定した論理ファイルが適切でなかったと
気づくべきであった。
最初の論理ファイルの選択から間違っていた。
■ READE を知らないと
READE(=Read Euqal) とは同じキーのレコードだけを読取る命令であり
同じキーのレコードが無くなるとシステムはEOF(=end of file)と同じ処理を
してくれるのでとてもわかりやすい。
READするときはできるだけREADEを使うようにしたほうが
安全で確実なキーを読取ることができる。
このプログラマーは READEを知らないらしく次のように記述している。
READE を使わないと
C SELECT C* 処理終了 C *IN99 WHENEQ *ON READ END C UKT050 ORNE UKKEY1 自店終了 C LEAVE C* 読み飛ばし処理 C UKT002 WHENGT P@DAY 日付 OVER C ITER
とこのように冗長な処理になってしまうのだが READE を使うと
読み飛ばしなどの処理は不要である。
C SETOFF 50 C UKKEQL READE UKTEGL06 50 C 50 LEAVE
のように見た目にもスッキリした処理になる。
またご覧のようにIF文で条件を尋ねるべきをSELECT-WHENを使っているので
経験豊かなプログラマーが見ると頭が混乱してしまうような書き方をしている。
■ SETLL & READE とは
複合キーの最下位の値にアクセス・ポインターをセットしてから
読取るのが基本である。
*-------------------------------------------------------------------------- C UKKEY6 KLIST C KFLD UKT050 C KFLD UKT001 C KFLD UKT004 C KFLD UKT002 C KFLD UKT008 *-------------------------------------------------------------------------- C Z-ADD P@TEN UKT050 C MOVE *LOVAL UKT001 C MOVE *LOVAL UKT004 C MOVE *LOVAL UKT002 C MOVE *LOVAL UKT008 C UKKEY6 SETLL UKTEGL06 *-------------------------------------------------------------------------- C UKKEQL KLIST C KFLD UKT050 *--------------------------------------------------------------------------+ C DO *HIVAL DO-*HIVAL : C SETOFF 50 C UKKEQL READE UKTEGL06 50 C 50 LEAVE : C ENDDO DO-*HIVAL
■ 正規化した読取りとは
ある印刷伝票は1ページに7行を印刷することができる専用用紙である。
この用紙に明細が行単位で1レコードになっているデータを読み取って
印刷する場合、どのような処理として記述すればよいだろうか?
このプログラムがその命題であったのだが
C UKKEY1 SETLL UKTEGL06 C *IN99 DOWEQ *OFF C READ UKTEGL06 99 C* C*<< 読取り条件 >> C SELECT C* 処理終了 C *IN99 WHENEQ *ON C UKT050 ORNE UKKEY1 C LEAVE C* 読み飛ばし処理 C UKT002 WHENGT P@DAY C ITER
としているが
C *IN99 DOWEQ *OFF
のような条件でDO-LOOPしているので
C READ UKTEGL06 99
の命令の直後にも *IN99=*ONの状態の処理が入ってしまう。
そこでこれを防ぐため
C* 処理終了 C *IN99 WHENEQ *ON C UKT050 ORNE UKKEY1 C LEAVE
として処理の終了の記述をしなければならないことになっている。
それでは
C *IN99 DOWEQ *OFF
の意味はなく
C DO *HIVAL
でも良かったのだ。
_
さらに
C READ UKTEGL06 99 C 99 LEAVE
であればもっとスッキリ短くまとまっていた。
C *IN99 IFEQ *ON C UKT050 ORNE UKKEY1 C LEAVE C ENDIF
のほうがマシであるが SELECTを使っているので
C* 読み飛ばし処理 C UKT002 WHENGT P@DAY C ITER
というような錯乱した書き方になっててしまっている。
C DO *HIVAL C READ UKTEGL06 99 C 99 LEAVE
のほうがはるかに自然でまとまっていて意図がよくわかる。
このプログラマーの記述は自分も混乱して読み手にも錯乱をもたらし
バグを誘発する書き方である。
■ 数字だったり文字だったり
処理をくわしく眺めていくと設計上の問題まで
見つかった。
それはフィールドの定義を文字フィールドとして定義したり
あるときは数字フィールドとして定義したり一貫性が無いことである。
演算の中で同じ意味のフィールドをあるときは文字フィールドとして
定義していてまた別のときは数字フィールドとして定義している箇所が
いくつも見つかった。
原因は数字フィールドの定義が理解できていない。
数字しか代入しないから数字フィールドとして定義するのは誤まりである。
例えば
長さ 1桁の店区分というフィールドを 1S 0 という数字フィールドとして
定義している。
無用なフィールドを数字フィールドとして定義してしまうと
次のような問題が発生する。
・DSPFに表示するときそのまま表示すると符号の部分も含めて2バイトで
表示されてしまう。
・しかも未入力の場合 0が表示されてしまう。
・この2つの問題を避けるために数字の編集が毎回必要になる。
・0-9まで店コードを使ってしまうと店が増えてA, B, …を追加で指定することが
できない。
従って数字を入力するからち言って数字フィールドとして定義するのは
誤まりである。
数字として定義すべきなのはこのサイトでも説明したように
数量、金額、日付、時刻
だけである。
_
■ テーブルを誤解して使う
この人のテーブルの使い方にも理解できないところがある。
E TABA 4 4 1 0 TABB 3 0 店区分 : C*<UKTEGP KEY> C P@TEN LOKUPTABA TABB 91 受手 マスター C Z-ADDTABB UKKEY1 30 : ** TABA / TABB 011022032042
のようにテーブル: TABA/ TABB をどのプログラムでも頻繁に定義して利用している。
TABA は 1, 2, 3, 4 でありそれに対応する TABB は
011, 022, 032, 042 である。
であれば
D HDR S 3 0 DIM(4) CTDATA PERRCD(4) : C EVAL EKKEY1 = HDR(P@TEN) : ** 011022032042
とすればよい。店コードは 011, 022, 032, 042 であるのだが
実際の入力操作ではそれを簡単にするために 1, 2, 3, または 4を
指定させているようなのだがそれならば
1, 2, 3, 4の順で 011, 022, 032, 042 を並べればよいだけの話で
わざわざテーブルを作ってLOOKUPする必要はない。
つまりテーブルとして定義してLOOKUPする必要なんかない。
この人はテーブルの意味をよく理解できていないのか
ことの問題の本質が理解できていないようである。
テーブルとは先頭からTABxxという名前のフィールドとして定義されているが
ほとんど使う場面はない。
数十年以上RPGの開発をしてきたが独立してからはテーブルを使ったことは
一度もない。
それほど使用頻度は少ないのに頻繁に利用されているので調べてみれば
案の定であった。
このプログラムはわずか450ステップほどであったが書き直してみると220ステップにしか
ならなかった。
このようなプログラマーが1000ステップを超えるプログラムを書けば
まさしくスパゲッティ状態のプログラムになることだろう。
驚くことにこのプログラムはこの特約店が販売しているパッケージ製品である。
プログラムは動作すればよいというものではない。
簡潔で無駄のないそれでいて読み手にも理解されやすいプログラムを心がけるべきである。
プログラムを書いた本人であっても1年もすれば自分が書いた意味も忘れてしまうのである。
自分自身も第三者のなり読み手の一人になる日は遠くないのだ。
美しく自然でシンプルなプログラムにすべきでありRPGとはそう書けるようにできている。
RPGにおかしな特殊な書き方が要ると思っていたらそれは間違いである。
この誤解がとても多く難しくおかしなテクニックが必要であると思っている人が
とても多いようだ。
Teqniqueを紹介しているサイトが言うのは変と思われるかも知れないが
このサイトで変な書き方を紹介したことは一度もないはずである。
おかしな書き方を薦めているのでは決してない。
RPGに特殊なテクニックは必要ないし特殊なテクニックを必要としたのなら
そのプログラムは間違っている。
あえてねじ曲げた書き方をする必要は全くないのだ。
RPGとはそのような誰もがやさしく書ける言語である。
それを難しくしてしまうのは書き手である。
_