【入門編】ON ZERODIVIDEによる例外トラップ – PL/Iの基本構文とデータ制御実践ガイド

こんにちは!メインフレームの世界へようこそ。
JavaやCOBOLといった他のプログラミング言語の経験がおありなら、「基幹システムの要であるPL/I(ピーエルアイ)」という名前を聞いて、少し身構えてしまっているかもしれませんね。

「なんだか古い言語だし、ルールが厳しそう……」
「エラーが起きたらシステムが派手にクラッシュするんじゃないか……」

そんな風に不安を感じていらっしゃるかもしれませんが、どうか安心してください。今日一緒に紐解いていく「ON ZERODIVIDE(ゼロ除算の例外トラップ)」の仕組みを知れば、PL/Iがいかに懐の深い、プログラマに優しい言語であるかがきっと分かりますよ。

それでは、リラックスして扉を開けてみましょう!

—

1. 他の言語とはちょっと違う?PL/Iの「懐の深さ」とキーワードの秘密

JavaやC言語を使っていると、`if` や `while`、あるいはエラー処理の `try-catch` など、「予約語(プログラミング言語があらかじめ予約していて、変数名に使えない言葉)」の多さに悩まされることがありますよね。

ところが、PL/Iには原則として「厳格な予約語」というものが存在しません。

どういうことかと言うと、極端な話、エラー処理の構文に出てくるような言葉すら、あなたが「変数名」として使おうと思えば使えてしまうのです。コンパイラは前後の文脈(コンテキスト)を読んで、「あ、ここで言う `ZERODIVIDE` は変数じゃなくて、割り算のゼロエラーのことね」と賢く判断してくれます。

この「何でも受け入れてくれる柔軟さ」こそが、PL/Iが長年、金融や流通の巨大な基幹システムを支え続けてこられた理由の一つなんです。

—

2. ゼロ除算(ZERODIVIDE)って、現実のシステムではどういう時に起きるの?

プログラミングの基礎で最初に習うことの一つが「ゼロで割ってはいけない」というルールですよね。電卓でも `E`(エラー)になります。

しかし、夜間のバッチ処理が何百万件ものデータを猛烈なスピードで処理している現場では、以下のような「予期せぬゼロ」がひょっこり顔を出すことがあります。

  • 前日のマスタメンテ漏れで、売上個数を割る「店舗数」がゼロになっていた
  • 集計用の分母となるデータが、ファイル破損によってブランク(空白)や全ゼロで初期化されていた

Javaであれば `ArithmeticException` が飛んでプログラムが強制終了し、COBOLであれば `FILE STATUS` や異常終了(ABEND)でバッチ全体が止まってしまいます。

では、PL/Iはどうするのでしょうか? ここで登場するのが、今回の主役である `ON ZERODIVIDE` です。

—

3. 実践! `ON ZERODIVIDE` でエラーを華麗にキャッチする

PL/Iでは、万が一ゼロ除算が発生したときに「どうするか(リカバリ)」をあらかじめ宣言しておくことができます。Javaの `try-catch` ブロックを、もっと直感的に、プログラム全体(あるいは特定のブロック)の守護神として配置するイメージですね。

実際のコードを見てみましょう。大文字ベースで書かれた、少しレガシーな雰囲気を味わってみてください。

1
TEST_CALC: PROC OPTIONS(MAIN);

DCL TOTAL_SALES DEC FIXED(9,2) INIT(150000.00); / 総売上金額 /
DCL SHOP_COUNT DEC FIXED(3) INIT(0); / 店舗数(ここにゼロが入っている!) /
DCL AVG_SALES DEC FIXED(9,2); / 平均売上 /
DCL ERROR_FLG CHAR(1) INIT(‘0’); / エラー検知フラグ /

/ ————————————————– /
/ ゼロ除算が発生したときの「身代わり(ONユニット)」を定義する /
/ ————————————————– /
ON ZERODIVIDE BEGIN;
DISPLAY(‘【警告】ゼロ除算を検知しました。処理を継続します。’);
AVG_SALES = 0; / ゼロで割ってしまった場合のデフォルト値を代入 /
ERROR_FLG = ‘1’; / 異常があったことをフラグに記憶 /
END;

DISPLAY(‘— 売上集計バッチ処理を開始します —‘);

/ わざと SHOP_COUNT(0)で割り算を実行する危険な瞬間! /
AVG_SALES = TOTAL_SALES / SHOP_COUNT;

/ もしエラーが起きなければここを通る /
IF ERROR_FLG = ‘0’ THEN
DISPLAY(‘平均売上の計算に成功しました。’);

DISPLAY(‘処理を安全に終了します。お疲れ様でした。’);

END TEST_CALC;

このコードの動きのポイント

1. `ON ZERODIVIDE BEGIN; … END;`
これがセーフティネットです。この宣言をしておくと、プログラムのどこかで「ゼロ除算」の悲劇が起きた瞬間、プログラムがクラッシュする代わりに、この中の処理が自動的に呼び出されます。
2. 安全なリカバリ
ONユニットの中で `AVG_SALES = 0;` とリカバリ値をセットし、さらに `ERROR_FLG` を立てることで、バッチ処理全体の強制終了(システムダウン)を防いでいます。
3. 処理の継続
PL/Iの優しさはここからです。例外処理が終わったあと、特別な指定をしない限り、エラーが起きた文の次の行から処理を何食わぬ顔で再開してくれます(※コンパイラのオプションや状況によって挙動は異なりますが、基本思想として「倒れないこと」を最優先します)。

—

4. エラー発生箇所を特定するテクニック(実務の知恵)

「エラーが起きたことは分かったけれど、何万行もある巨大なプログラムの『どこ』でゼロ除算が起きたのか分からないと、修正できないよ!」

その通りですよね。実務の現場では、どのレコード、どの計算式でゼロになったのかを特定することが極めて重要です。

そんなときは、PL/Iの組込み関数である `ONSOURCE` や `ONCHAR`、あるいはメインフレームのデバッグ機能と組み合わせます。また、実務的な防衛プログラミングとしては、以下のように「割る前に自分でチェックする(事前ガード)」を併用するのがプロの技です。

1
/ 事前チェックで未然に防ぐ堅牢なスタイル /
IF SHOP_COUNT = 0 THEN DO;
DISPLAY(‘エラー: 店舗数がゼロのため、計算をスキップします。’);
AVG_SALES = 0;
END;
ELSE DO;
AVG_SALES = TOTAL_SALES / SHOP_COUNT;
END;

「なんだ、自分で `IF` でチェックすればいいなら、`ON ZERODIVIDE` なんて使わなくてもいいのでは?」と思われるかもしれませんが、予期せぬデータ不整合や、複雑に入り組んだサブルーチン間での計算では、すべての箇所に `IF` を書くのが難しい場合もあります。そんなときの「最後の砦(ラスト・ディフェンス・ライン)」として、`ON ZERODIVIDE` は絶大な安心感を与えてくれるのです。

—

5. まとめ

いかがでしたでしょうか?
PL/Iの `ON ZERODIVIDE` は、一見すると少し特殊な構文に見えますが、要するに「エラーが起きたときに、パニックにならずどう対処するかをあらかじめ決めておく優しい仕組み」です。

  • 予約語にとらわれない、柔軟で懐の深い文法規則
  • 万が一のゼロ除算も、`ON` ユニットでスマートに捕捉・リカバリ
  • 基幹システムを止めないための強い味方

レガシーなメインフレームの世界も、こうして一つずつ紐解いていけば、決して怖くありません。むしろ、システムを安定稼働させるための先人たちの知恵がギュッと詰まっていて、触れば触るほど奥深い面白さがありますよ。

日々のマイグレーション調査やバッチ改修の作業が、少しでも楽しく、スムーズに進むよう応援しています!

タイトルとURLをコピーしました