皆さん、こんにちは!IBMメインフレームの世界へようこそ。
JavaやCOBOLといったモダン、あるいはビジネス向けの言語をバリバリ書いてきた方にとって、初めて見るPL/I(ピーエルワン)のコードは、どこか異世界の呪文のように映るかもしれませんよね。「なんだこの独特な書き方は…」と、黒い画面(3270エミュレータ)の前で固まってしまった方もいるのではないでしょうか。
でも、どうぞご安心ください。怖がる必要はまったくありません。今回は、PL/Iのデータ制御のなかでも、特にメモリの節約と高速なフラグ管理に不可欠な「BIT属性のパック形式と論理演算」について、一緒に優しく紐解いていきましょう。他言語の知識がある皆さんなら、本質を掴むのはあっという間です!
—
1. そもそもPL/Iの「識別子」と「予約語」の優しいお話
本題に入る前に、PL/Iのちょっとユニークな性格について触れておきます。JavaやC言語では、`if` や `while`、`class` といった「予約語」が決まっていて、これらを変数名(識別子)として使うことはできませんよね。
ところが、PL/Iの世界はもっと寛大です。PL/Iには厳密な意味での「予約語」が存在しません。
どういうことかというと、コンパイラは前後の文脈(コンテキスト)を見て、「あ、ここは変数名だな」「ここは命令文だな」と空気を読んで判断してくれます。
例えば、極端な話ですが `IF` という名前の変数を作ったとしても、書き方さえ間違えなければコンパイラは怒りません(※後々のメンテナンスや、他の人が読んだときの混乱を考えると、わざわざそんな紛らわしい名前をつけるのはお勧めしませんが!)。この「人間味のある(?)柔軟さ」が、PL/Iの面白さであり、同時にレガシーコードを奥深いものにしている理由の一つです。
—
2. メモリの魔術!BIT(n)がメモリ上でパックされる仕組み
さて、ここからが今回のメインディッシュです。
JavaやCOBOLで「フラグ(真偽値)」を扱うとき、どうしていますか? Javaなら `boolean` 型(通常1バイト=8ビット)、COBOLなら `PIC X(1)`(1バイト=8ビット)を使うのが一般的ですよね。
しかし、メインフレームの世界では、かつてメモリ(主記憶)は非常にお高くて貴重な資源でした。「たった一つのフラグを表すために、わざわざ1バイト(8個分のスイッチ)全部消費するなんて、もったいない!」と考えた先人たちの知恵が、BIT属性です。
バイトの隙間を許さない「パック形式」
PL/Iの `BIT(n)` は、n個のビット(0か1)を、文字通りビット単位で隙間なく詰め込む(パックする)データ型です。
- `BIT(1)` を8個宣言すると、合計で 8ビット = たったの1バイトしかメモリを消費しません。
- もしこれを個別の変数にバラバラに持たせても、コンパイラやストレージの境界調整(アライメント)のルールに従いつつ、極限までメモリを節約できるように配置されます。
イメージとしては、「細長い一列の電球のスイッチ盤」を思い浮かべてみてください。1つのスイッチ(ビット)が ON(1) か OFF(0) かを表し、それがズラリと並んでいる状態です。
—
3. 実践!PL/IでのBITデータ宣言と論理演算
では、実際にPL/IでどのようにBITデータを扱い、論理演算(AND, OR, NOT)を行うのか、サンプルコードを見てみましょう。
実務のバッチプログラムやマイグレーション調査でよく見かけるような、フラグ制御のコードを書いてみました。
1
/ ———————————————— /
/ BIT属性と論理演算のサンプルプログラム /
/ ———————————————— /
TEST_BIT_SAMPLE: PROC OPTIONS(MAIN);
/ 変数の宣言 /
/ 8ビットのスイッチ盤を2つ用意します /
DCL FLG_A BIT(8) INIT(‘11001010’B); / 初期値:左から順にビットを指定 /
DCL FLG_B BIT(8) INIT(‘10101100’B); / 初期値:リテラルの末尾の ‘B’ はビットを意味します /
DCL FLG_RES BIT(8); / 演算結果を格納するエリア /
DCL MSG CHAR(50); / 画面・ログ出力用メッセージ /
/ — 1. 論理積 (AND) 演算 — /
/ 両方とも1の場所だけが1になります(共通の条件チェックに便利!) /
FLG_RES = FLG_A & FLG_B;
MSG = ‘AND演算結果: ‘ || FLG_RES;
PUT SKIP LIST(MSG);
/ — 2. 論理和 (OR) 演算 — /
/ どちらか一方が1であれば1になります(フラグの合算・追加に) /
FLG_RES = FLG_A | FLG_B;
MSG = ‘OR演算結果: ‘ || FLG_RES;
PUT SKIP LIST(MSG);
/ — 3. 否定 (NOT) 演算 — /
/ ビットを反転させます(ON/OFFの切り替えに) /
FLG_RES = ^FLG_A;
MSG = ‘NOT演算結果: ‘ || FLG_RES;
PUT SKIP LIST(MSG);
/ — 4. ビットの切り出し(Substring機能) — /
/ PL/IではSUBSTR関数を使って、特定のビットだけをピンポイントで操作できます /
/ FLG_Aの左から3番目のビットが1かどうかを判定する /
IF SUBSTR(FLG_A, 3, 1) = ‘1’B THEN
PUT SKIP LIST(‘FLG_Aの3ビット目は ON です!’);
ELSE
PUT SKIP LIST(‘FLG_Aの3ビット目は OFF です!’);
END TEST_BIT_SAMPLE;
コードのポイント解説
1. 初期値のリテラル(`’11001010’B`)
文字の末尾についている `B` は、「これは文字(CHAR)ではなく、ビット列ですよ」とコンパイラに教えるためのPL/I流の書き方です。直感的でわかりやすいですよね。
2. 論理演算子
- `&` (アンパサンド)= AND(論理積)
- `|` (バーティカルバー)= OR(論理和)
- `^` (サーカムフレックス、環境によっては¬を使うことも)= NOT(否定)
JavaやC言語(`&&`, `||`, `!`)とは少し記号が違いますが、意味はまったく同じです。
3. `SUBSTR` によるビット操作
文字列だけでなく、BIT型に対しても `SUBSTR(変数, 開始位置, 長さ)` が使えます。 「特定のフラグ群の中から、第3ビット目の状態だけを知りたい」といった要件のとき、わざわざ複雑なシフト演算を書かずに一発でスパッと切り出せるのは、PL/Iの非常に強力で便利なところです。
—
4. まとめ:レガシーの仕様も、怖くない!
今回は、BIT属性のパック形式と論理演算についてお話ししました。
- BIT(n) は、メモリを極限まで節約するための「ビット単位のスイッチ盤」。
- 予約語を持たない柔軟なPL/Iだからこそ、データ構造の定義もシンプル。
- `&`, `|`, `^` や `SUBSTR` を使えば、他の言語のフラグ制御と同じ感覚でスマートに操れる。
「メインフレームのプログラム」と聞くと、なんだか難解で近寄りがたいブラックボックスのように感じるかもしれませんが、一つひとつの仕様を紐解いていけば、当時のプログラマたちの「限られたリソースをいかに効率よく使うか」という熱い工夫や優しさが詰まっていることに気づきます。
皆さんのレガシー移行やバッチ改修の現場で、この記事が少しでも「あ、そういうことか!」という安心感とヒントになれば幸いです。それでは、次回のメインフレーム・アーキテクチャ解説もお楽しみに!
