逆ポーランド記法電卓(RPN電卓)を作ってみました

  副題「名機 YHP−25 の再現」


◆前説不要の方は本題へ跳んで下さい。

 

私のパソコン歴はシャープの「X1」に始まるのですが、プログラミング歴はもう少し古く、最初は下の写真にある横河・ヒューレット・パッカード(OKOGAWA・EWLETT・ACKARD=YHP)製の「YHP−25 mini」というプログラミング電卓でした。その後シャープの「BASIC電卓」などで楽しんで、それからパソコンに移りました。

↑当時の価格で5万円以上だったように思うのですが・・・。月給に比して高かったですよぉ。(テキサス・インスツルメンツからは「TI−80」というプログラミング電卓が出ていましたっけ。)

プログラミングステップ数はたったの「49ステップ」です。4段のスタックと6本のメモリー、条件分岐とGOTOによるループ、そんな機能でしたが、楽しくて楽しくて、はまりました。アセンブリ言語のミニ版のようなものでした。

電気力線の作図、等電位線の作図、微分方程式の簡単な数値解法でいろいろな運動の解析、ニュートン法による求根・・・かなり長い間夢中になったものです。

電卓からはx,yの座標を出力させ、手でグラフ用紙にプロットする、というスタイルで1枚のグラフを完成するのに1ヵ月近くかかったこともあったと覚えています。現在のプログラミング言語からは考えられないことでしょう。

正三角形の各頂点に等しい大きさの正の電荷を置いたとき、正三角形の中心部での電位の面は「峠」になるのか「盆地」になるのかを解いて、「盆地」になる解を得た時は感激しましたっけ。

さて、プログラミングのことはここではこれ以上立ち入らないことにして。

 

この電卓は電卓としての機能もユニークなものなのです。現在でも電卓としてはユニークでしょう。コンピュータのプログラミングの世界でこそ有名ですが、「逆ポーランド記法(RPN)」による演算なのです。なんだそりゃ?と思う方もおられると思いますが、上の写真をよく見て下さい。

「=」キーがないのです。テンキーの脇に四則演算のキーがあります。「−」と「7」の上にまたがった形で「ENTER↑」という横長の大きなキーがあります。これがこの電卓の「キーポイント」なのです。

詳しくは、ちょっと先で説明することにして、昔話・思い出話を先に終えましょう。

 このYHP−25は、表示部は赤い発光ダイオードで、消費電力が500mWありました。当時の電池にとってこの消費電力は結構シビアなものでした。

 そこで、マニュアルには、表示部に「0」を表示するよりも「1」を表示するほうが発光させるセグメントが少なくて消費電力が小さくなるので、長時間電源をONにしておくときは、「1」を表示させるようにと書いてありました。

プログラミングを学ぶときに、数字の「0」は問題ないですが、文字を扱うときの「””」(null)というのは、最初は気持の悪いものでしょう。

 YHP−25でプログラミングの勉強を始めたときの私にとっては、「NOP」というのが扱いにくかったことを覚えています。

No Operation」のことです。「何もしない」という記述です。私の兄貴が「HITAC−10」かなにかで卒論を書いたコンピュータの専門家でしたから、「NOP」の意味を教えてもらったものでした。

 

↓現在使っている「hp−11C」という電卓です。「横河」が抜けて「ヒューレット・パッカード」だけになってしまいました。

これももう10数年使っていますが、YHP−25とおなじ「RPN」電卓です。

教師の現役時代、定期テストのころになると、ずぼらな生徒がよく「電卓持込可の試験なんですけど、先生、電卓貸して下さい」とやってきたものです。

 私が嫌味たっぷりに「いいよ。イコールキーのない電卓が使いこなせるんだったらどうぞ。」とこの電卓を出して見せると、しばらく電卓のキー配列を眺めて、「やっぱいい、先生」と引き下がっていったものです。

「自分のやりたいように計算をする」という点で、この電卓はとても優れています。レジスタもメモリーもユーザーに解放されていて、自由にコントロールできるからです。

25 miniも11Cも、2変数統計ができます。最小自乗法による近似直線の傾きと切片、相関係数、などがストレートに求まるのです。理系の実験データ処理には必須アイテムですね。


 

さて、今回、久しぶりに何かプログラムで遊んでみたいな、と思ったときに、このRPN電卓をエクセルのVBAで再現してみようか、と思いつきました。

 


さてここからしばらく「RPN」の説明をしましょう。

 

まず、ウィキペディアから引用します。

http://ja.wikipedia.org/wiki/%E9%80%86%E3%83%9D%E3%83%BC%E3%83%A9%E3%83%B3%E3%83%89%E8%A8%98%E6%B3%95

 逆ポーランド記法(Reverse Polish Notation, RPN)とは、演算子を操作対象の後に記述する記法。後置記法(Postfix Notation) とも言う。たとえば、

1 + 2(1 に 2 を足す)は

1  2  +

と書く。このような記法を採用したものとしてHP社の電卓などが有名である。この書き方は日本語の文法とよく似ている。この語順を日本語に読み替えた、日本語版のForth的な言語がMindである。Mindだと「1 と2 とを 足す」というように、日本語の普通の書き方のように記述することができる。

 この記法を計算機等の入力に採用した場合かっこ操作が必要なくなる。例えば(1 + 2) × (3 + 4)を計算する場合、数式をそのまま入力する方式の計算機ではこれら全ての記号を入力する必要があるが、逆ポーランド記法を採用した場合は「1 2 + 3 4 + *」と入力すればよい(1 と 2 とを 足したものと 3 と 4 とを 足したものとを 掛ける)。

 これを実現するには、計算機内部では単純なスタック構造を構成すればよいので構造も複雑にはならない。そのためソフトウェア初心者の練習課題として、この逆ポーランド記法の電卓シミュレータを作ることがよく行われる。

 一方、この方法では数と数との間を区切る記号(デリミタが必要である。上の例では1と2との間の空白、3と4との間の空白がそれである。一般的な電卓で用いられる1 + 2 =という方法(中置記法)は、演算子及び等号そのものがデリミタとして働くため、デリミタを明示する必要は無い。

 

というわけです。ここに2つの重要なキーワードが出ています。一つは「スタック」、もう一つは「デリミタ」です。(私はターミネータと呼びたいと思います。理由はYHP25miniのマニュアルがそうだったから、という単純なことです。)

 

「スタック」って何だ?という方のために。またまたウィキペディアから。

http://ja.wikipedia.org/wiki/%E3%82%B9%E3%82%BF%E3%83%83%E3%82%AF

 スタックは情報工学における抽象データ型であり、コンピュータで用いられる基本的なデータ構造の1つで、データを後入れ先出し (LIFO: Last In First Out;  FILO: First In Last Out) の構造で保持するものである。

机の上の「未決箱」に書類を積み上げることを考えて下さい。一番上に一番新しい書類が載っていて、一番下に一番古い書類がつぶされています。途中から書類を抜き出そうとすると書類の山が「崩壊」するので、一番上からしか取り出せないものとします。

 これこそ、「スタック」なのです。そもそもが「スタック=stack」とは英語で「積み重ね」のことです。

こんなイメージ図がよく使われます。

 スタックと呼ばれる一連の積み重なった「容器」に、上からデータを「PUSH」して入れていく、取り出すときは上から「POP」して取り出す、というものです。

データを管理するために、「スタックポインタ」という索引をつけておくことが必要です。

お菓子のF家さんの工場で、仕入れた原材料をどんどん積み上げてゆき、使う時は上から順に取り出してお菓子を作っていた、というのは、「スタック」の実践例(もちろん皮肉です)です。

 Last In First Out ということは別な言い方をすれば、First In Last Out ということですから、積み重ねの下の方が「期限切れ」になるのは当たり前ですね。

データ構造としてはもうひとつ重要なのがあります。それは

 irst n irst ut(FIFO) という方式です。

 これは「行列待ち」です。最初に列に並んだ人が最初に呼ばれなければ「暴動」、ですよね。

それぞれに特性があって、使い分けられます。

 

さて、ウィキペディアではスタックを使った計算動作の例題として

 (1+2)*(4+5)=

 があげられています。普通のスタックのイメージは上に私が描いた上から押し込んで上から取り出すというものです。

ところがここでまた、私のつまらぬコダワリが出てきて。「YHP25mini のスタックはそういうイメージではなかった」と、なるんですねぇ。

 YHP25mini のスタック・イメージは、下から押し上げて、使うときは下へ引きおろす、というものなのです。

    ↓こんな感じですね。

下からx,y,z,tという名が付いています。(tは多分topのtだと思います。)

データの出入り口はxです。入力される数値はまずxに入ります。(電卓で表示されているのは常にxの内容です。)

「ENTER」キーを押すと、xの数値はyに押し上げられます(xにもそのままその数値は残りますので、yにコピーされるといってもいいです。)

第2の数値が入力され始めると、いったんxに残されていた数値は消去されて新たな数値を受け入れます。これで、2つの数値がxとyにあることになります。

ここで演算キーを押すと、xとyの間で演算が行われてその結果がxに入ります。yにあった値は消費されます。

 

↓「1 ENTER 2 +」の時の動作です。

 

↓続いて、「4 ENTER 5 +」です。演算終了後に数値入力が始まると、xにあった数値をまずyに押し上げてから、新たな数値をxに受け入れます。

 

↓最後に「×」です。

いかがでしょうか?イメージはつかめたでしょうか?「フーン、そんなものなのかぁ」で結構です。(プログラミング教室ではありませんので。)

 

数と数との間を区切る記号(デリミタ」という言葉がウィキペディアにありました。

 数値が二つ順番に入ってきますので、先の数値の終りを知らせてやらないと困るわけです。

 YHP25 mini では、その操作を「ターミネートする」といっていたと思います。

 「terminate : <行動・状態などを>終える・終結する」ということですので。

上の例で見たように、数値1の入力が終わったときに ENTER キーを押すと、ターミネートが起こって、入力が終結しxの値が確定するとともに、yにその値を押し上げます。

 そうして、数値2が入力され始めると、xの値を消して新しい数値を受け入れます。

数値2の入力の終りには演算子が来ますので、演算子自体がターミネートを行う必要があります。ですから演算子の動作の冒頭に「ターミネータ」を置く必要があることはお分かりでしょう。

ところが、同じようにxに数値があるときでも、その数値が演算終了後の数値であるなら、新たな数値入力が始まったとき、xにあった値をyに押し上げてから新しい値を受け入れなければなりません。ですから、xに数値があるとき、ターミネートされた後なのか、演算が終了した後なのか、状態を知る必要が生じます。

微妙ですが、プログラムを作る上では、重要な「状態の違い」です。

 


[余分]

「逆ポーランド記法」というのがあるからには、「逆」ではない「ポーランド記法」というのもあるのでしょうか?

 あるんですねえ。またまたウィキペディアから引用します。

http://ja.wikipedia.org/wiki/%E3%83%9D%E3%83%BC%E3%83%A9%E3%83%B3%E3%83%89%E8%A8%98%E6%B3%95 

 ポーランド記法(ポーランドきほう、Polish Notation)とは、演算子を操作対象の前に記述する記法。前置記法(ぜんちきほう、prefix notation)ともいう。演算子と対象が前後することを除けば逆ポーランド記法と同じものである。名前の由来はポーランド人の論理学者ヤン・ウカシェヴィチ (Jan Łukasiewicz 1878-1956) が考案したことによる。

 

この記法では、「1 + 2 =」は、「+ 1 2」と書かれます。

 上の例の「(1+2)*(4+5)=」は「× (+ 1 2) (+ 4 5)」となりますね。

YHP25mini のマニュアルでは「ルカシェヴィッチ」と書いてあったように思います。「ルカシェヴィッチ記法」とも書いてあったように思います。

 ポーランド語はまったく知りませんので、きっと「ウカシェヴィチ」が正しいのでしょうね。

演算子が先にあるというとちょっと変な気もしますが、「add A B」というような書き方がありましたよね、アセンブリ言語で。「BにAを足す」というような意味でしたね。

ちなみに、普通に「1+2」というように表記する方法は、中置記法(infix notation)といいます。

 

完全代数演算

 こういう用語があるのかどうかよくは知らないのですが、一時「完全代数演算電卓」というものがありました。(今もあるのかどうか、よく知りません。)

この電卓ではまったく数式どおりに入力していくのですが、「=」キーが押された時点で演算子の優先順位を電卓が判断して、演算を実行するというものだったと思います。演算子の優先順位の変更はもちろんカッコで行います。それはまあいいとして、おもしろいのが・・・

普通の電卓でも「sin」とか「cos」とか「log」とか「√」とかいった、現在表示されている数値一つに対して働きかける演算子というか関数では、先に数値を入れておいて、後から関数を入れますね。

 「sin(30)」が計算したくても、「30度のサインを取って」などと呟きながら「30 sin」とキーを押すでしょ。

 どうも、これがHPの持つ電卓の特許としてのRPNに抵触して、特許使用料を払わなければならなかったといううわさなのです。

 「数値を入れてから、『働き』を押す」というやり方ですね。

ところが、ほんとうに「sin 30 =」と押す電卓を作ったわけです。これならHPの特許から独立できるという話でした。

 「2の√」ではなくて「√ 2」なのです。面白いけれど、戸惑いましたね。

さて、今、どうなったのか?最近電卓を買いませんので、知りません。

 


やっと本題です。

逆ポーランド記法電卓(RPN電卓)を作ってみました。「名機 YHP−25 の再現」です。

 

●動作の基本はYHP−25 mini として、その動作を思い出しながらつくりましたが、実際には今はもう動かないので、不審な時は11−Cの方の動作を真似ました。

もちろんプログラミング機能は再現していません。

10の指数表示での入力も面倒くさいので省略しました。悪しからず。

 (「EEX」=nter Exponentialというやつです。)

 

↓こんな設計です。

 

入力は数字キーをマウスでクリックすることで行います。(キーボードからのテンキー入力はできません。)

 1クリックずつ確実にクリックして下さい。結構入力が跳ぶようです。

数値は「文字」として受け入れて文字列変数に格納しています。

スタックとメモリーの表示は「ラベルの caption(文字)」として行っています。

数値1を入力したら「ENTER」キーをクリックッしてから数値2を入力して下さい。

ターミネートが起こると、文字列変数を「Val()」関数で数値に変換して、倍精度実数の数値変数に格納します。

その後、四則演算キーをクリックすれば答えがxに表示されます。

y,z, t およびMem が薄く黄色になっているのは、内容が見えるようにしてはありますが、操作対象ではないという意味です。

 実際の電卓では、x の内容のみが表示されます。

STO」 「STO+」はいわゆるメモリーへの格納(Store)です。「STO」はメモリーの内容を新たな値で書き換えます。「STO+」は現在のメモリーの内容に新たな値を加えたものをメモリーに格納します。

メモリーの内容は、「ゼロをストアする」以外の方法でクリアすることはできません。

メモリー内容の呼び出しは「RCL」(= Recall)で行って下さい。

「−」キーは演算子ですから、負の数値の入力には使えません。負の数値を入力したい場合は数値入力後に「CHS」キーをクリックして下さい。

 Change Sign です。

Clear」はスタック全体をクリアし、「Clear_x」はxだけをクリアします。

「←」は、数値入力中にクリックすると、1クリックで1文字前へBack Space して訂正ができます。

 演算終了後などにクリックした時は「Clear_x」と同じ動作をします。

x⇔y」はxとyの内容の入れ替えを行います。

R↓」は、Roll Down です。yの値をxにおろして、zをyに、tをzにおろし、xの値はzに上げられます。

通常はtに値があるときに、2項演算などが行われてyの値が消費されたときは、yにはzの値が降り、zにはtの値が降り、tの値はそのまま保存されます。いいかえると、zにはtの値がコピーされます。(tは空になりません。)

 この性質を利用すると、掛け算や足し算の「定数機能」的な使用ができます。

起動時の角度単位は「度」です。ラジアンに切り替えるときはオプションボタンをクリックして下さい。

度でサインを取り、ラジアンでアークサインを取る、といった使い方で度からラジアンへの変換もできます。

π の値が必要な時は「Pi」キーをクリックして下さい。

1/x」はオマケのようなものです。n乗根を求めるときに便利かな、と思ってつけておきました。

x^2」もオマケです。「数値 ENTER ×」で自乗は求まります。

関数機能はVBAが持っているものをそのまま使っています。(自分でテーラー展開などやって求めているわけではありません。)

 当然、変数の変域や関数の値域はVBAの仕様そのままです。

 

いろいろ試してみてはいるのですが、バグだらけだろうと思います。もしバグを見つけましたら・・・

「それは仕様だ」

 と思って下さい。どこかの大メーカーの製品のように(^^;)

 

今回、公開するバージョンを「RPN電卓 Generation_U_0」として固定したいと思います。

 VBAのエディタからプログラムは簡単に変更できますので、仕様変更やバグ取りを行ったら、バージョンをカウントアップして下さい。ヨロシク。

 もちろん、問い合わせには応じます。

 

VBAプログラムの解説はしません。そう複雑なことはしていないので、よく読めば分かると思います。

一つだけ。

 私は、自作のプロシージャを呼び出すときに必ず「Call ステートメント」を使って呼び出しています。VB(A)の言語仕様としては、Callは省略できますし、省略した方が何となくスマートでいいのですが、可読性のためです。

昔(20年以上も前です)、自分自身が、他人の書いたプログラムを読んでプログラミング言語を必死になって勉強しているとき、その言語処理系がもともと持っているものなのか、プログラマーが作ってどこかに書き込んであるものなのか、なかなか区別がつかずに苦労したものでした。

 そういう経験から、「これは自作のプロシージャです」ということを明瞭にしておきたくてこんな書き方をしています。

 ですから、Callで呼んでいるプロシージャは、VB(A)がもともともっているステートメントではないので、プログラムの中に必ず書かれています。安心してお読み下さい。

 


★VBAのプログラムをエディタでいじるためにエクスポートして、いろいろやっていたときに、右クリックメニューに「開く」があったので、何気なく「開いた」ところ、Visual Basic 6.0が立ち上がってきてビックリしました。(エクセルが立ち上がってくると思い込んでいたのと、正直のところVB6.0をインストールしてあること自体をほとんど忘れていたからです。)

 で、コンパイルしてみたら、ホンの2,3カ所手直しするだけでコンパイルできてしまったのです。

 というわけで、VBでコンパイルしたものも一緒に圧縮ファイルに入れておきますので、遊んでみて下さい。

 

 

★RPN電卓のダウンロードはここからどうぞ。

 「RPN_Calc.lzh」

 です。

 解凍すると、「RPN電卓generation_U_0.xls」と「RPN_Calculator.exe」が出てきます。

 お楽しみ下さい。

 

◆このページのTOPへ戻る