必要があってREXXのプログラムを書くことになったのですが、アセンブラーでは当たり前のように使えるアドレス・ポインターが使えない。同じ構造のデータ・テーブルが複数あって、それを探索するサブルーチンを作りたい。アセンブラーだと探索するテーブルのアドレスをサブルーチンに渡せば事足りるけど、REXXのようにアドレスを扱わない言語だとそれができない。テーブルには名前があるのでそれを引数で渡せばとも思ったが、それはテーブルの場所(先頭アドレス)が渡るのではなく名前そのものが引数になってしまい上手く行かない。同じ処理を行うサブルーチンを、それぞれのテーブル用に名前を変えて何個も作れば簡単なのだが、本当にみんなそんなことをしているのかと疑問に思った。
ネットを探しまくり、value関数の使い方で実現できることがわかった。備忘録代わりに残しておきます。変数エリアの場所を示すアドレスを間接的に使って値を取り出す方法とも言えます。
プログラムは複数のデータ・テーブルを持つ。各テーブルはデータは違うが構造は同じ。入力されたパラメーターが正しいか(テーブルに登録されているか)を調べるサブルーチンを作りたい。チェックしたい値(キー)と、どのテーブルを探すかをサブルーチンに渡し、サブルーチンそのものは1つで各テーブル共用に作りたい。そういう場合にサブルーチンでは渡されたテーブル名を、名前の文字列そのものではなくテーブルの場所を示す引数として処理したい。そういう場合に参考にできます。C言語でいうポインターとは違うけど、引数をポインターっぽく処理する例かな。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 |
/* REXX */ /* Application Data Table-1 */ APT1.0 = 7; /* Num of Table Entries */ APT1.1 = 2698; APT1.2 = 3875; APT1.3 = 4033; APT1.4 = 5901; APT1.5 = 7739; APT1.6 = 8706; APT1.7 = 9999; /* Application Data Table-2 */ APT2.0 = 3; /* Num of Table Entries */ APT2.1 = NYZ; APT2.2 = XEZ; APT2.3 = JGZ; /* Main Procedures */ ARG key; rc = TableSearch(key, APT1); if rc = 1 then SAY "Input Key is correct in Table-1:" key else SAY "Input Key is invalid in Table-1:" key; rc = TableSearch(key, APT2); if rc = 1 then SAY "Input Key is correct in Table-2:" key else SAY "Input Key is invalid in Table-2:" key; exit; /* Sub Routines */ /* ------------------------------------- */ /* TableSearch(arg1, arg2) */ /* arg1 --> search value */ /* arg2 --> Table Name */ /* */ /* rc ----> 0: Table Entry is not found. */ /* 1: Table Entry is found. */ /* ------------------------------------- */ TableSearch: ts_found = 0; parse arg ts_value, ts_table; do i = 1 to value(value(ts_table).0); if (value(value(ts_table).i) = ts_value) then do; ts_found = 1; leave; end; end; return ts_found; 同じ処理だけどCやREXXに慣れてなければこっちの方がわかりやすいかな。 TableSearch: ts_found = 0; parse arg ts_value, ts_table; ts_loopn = value(value(ts_table).0); do i = 1 to ts_loopn; ts_entry = value(value(ts_table).i); if (ts_entry = ts_value) then do; ts_found = 1; leave; end; end; return ts_found; |
TableSearchというサブルーチンは2つの引数を持つ。1つは探したいキーの値(例えば入力されたパラメーター値など)、もう1つが探す対象となるテーブル名。value関数を入れ子で使うと、テーブル名に添え字を付けてテーブル内の値を取り出せるようである。REXXは専門ではないので細かいことは追求してないけど、こういう発想って感心します。変数の値を参照するのはいちいちvalue関数使わなくてもできるし、いつ使うんでしょ、とも思うが、それを重ねて使うとこうなるとは。
エントリーを探すループの中に「SAY value(ts_table) i value(value(ts_table).i);」を入れてみるとvalue関数の動きがわかりやすい。これで次のようにテーブル名を直接プログラムの中に書いたサブルーチンをテーブル毎に書くようなことをしなくても済みます。
1 2 3 4 5 6 7 8 9 10 |
TableSearch1: ts_found = 0; parse arg ts_value; do i = 1 to APT1.0; if (APT1.i = ts_value) then do; ts_found = 1; leave; end; end; return ts_found; |