Casio Basic入門33
最終更新:2015/01/25
Chapter 6
前回: Casio Basic入門32 を見る
◆ Chapter 6 の目標: プログラムを速くする
入力ボックスの改良と拡張
前回は、INPI Ver 1.2 へのバージョンアップを行いました。今回は、さらに高速化して Ver 2.0 を作ります。
1) INPI Ver 1.0 から Ver 1.2 への変更
※ 遅い処理を極量排除する。
2) INPI Ver 1.2 から Ver 2.0 への変更
※ プログラムの内部仕様(ロジック)変更を行い、その高速化を実感してもらう。
3) INPI Ver 2.0 を基に、INP Ver 2.0 を作るための機能拡張
※ 小数入力への対応方法を紹介する。
4) INP Ver 2.0 を基に、IN Ver 2.0 を作るための機能拡張
※ 負の数入力への対応方法を紹介する。
Chapter6-2
ロジックの見直しでプログラムを高速化する
前回作成した INPI Ver 1.2 (ファイル名: INPI12)
Ver 1.0 (INPI10) | Ver 1.2 (INPI12) | |||
処理種別 (時間) | 個数 | 時間 (ミリ秒) | 個数 | 時間 (ミリ秒) |
倫理演算 (10ミリ秒) | 7 | 70 | 3 | 30 |
比較演算 (11.5ミリ秒) | 14 | 161 | 9 | 103.5 |
合 計 | 231 | 133.5 |
今回は、1つめの If (I≧1 And I≦9) Or K=25 の場合の処理に含まれる以下のコードに着目し、
C≠1 Or A≠25⇒Isz C
C=1⇒K→A
これらの処理を見直して、さらに高速化を試みます。
結論から言えば、これらの処理をほとんど削除できました。先ずは、今回作った Ver 2.0 のコードを Ver 1.2 と並べてみます。変更する部分は赤文字で示しています。
Ver 1.2 と Ver 2.0 のループ全体の論理演算と比較演算の数をまとめてみます。
Ver 1.2 (INPI12) | Ver 2.0 (INPI20) | |||
処理種別 (時間) | 個数 | 時間 (ミリ秒) | 個数 | 時間 (ミリ秒) |
論理演算 (10ミリ秒) | 3 | 30 | 2 | 20 |
比較演算 (11.5ミリ秒) | 10 | 115 | 7 | 80.5 |
合 計 | 145 | 100.5 |
単なる目安としても、論理演算と比較演算の回数を減らすことで、応答性の大幅な向上(約50%向上)が期待できるわけです。
キー入力回数の測定
実際に前回と同じ方法で、INBOX TEST プログラムを使って、今回も入力応答性を実際に調べてみました。
INPI バージョン | 10桁入力所要時間 | 1秒あたりの入力回数 |
1.0 | 2.8秒 | 3.5回 |
1.2 | 2.2秒 | 4.5回 |
2.0 | 1.4秒 | 7.1回 |
この測定は、非常に個人差があるので、比較の意味しかありませんが、Ver 1.2 に対してVer 2.0 の応答性は、実際に事前の推定通り約50% 向上したと言えます。最初の Ver 1.0 に対しては2倍の入力応答性が得られました。
実際に使ってみると、かなり高速化されたと感じると思います。
今回の Ver 2.0 では、論理演算と比較演算を減らすために、プログラムのロジック(仕様)を変更しています。それによって、今回着目している上記2行の部分から、論理演算と比較演算を完全に取り去り、Isz C のみにできました。
Ver 1.x のロジック
入力ボックスでは、主に2つのことを行います。
1) 入力操作に応じて、内容を表示する
2) 入力操作に応じて、結果を変数に保存する
Ver 1.0 では、計算して更新した値を変数Z に代入し、そのZ を Locate コマンドで表示しています。入力結果と表示が常に同じであることが保証されるので、この方法を採用しました。
キー入力するたびに、何桁目を入力しているかをプログラムで管理しています。変数C がこれから入力する桁数を示し、C=0 は何も入力されていない状態、C=1 は1桁、C=2 は2桁入力されている状態を示します。
INPI は、10桁以上入力できない仕様にしていました。Z が11桁以上になると、fx-5800Pの仕様のため、Z を表示する際に 指数表示になり、桁数の管理ができなくなることが、その理由です。そこで、桁数の最大を 10 としていました。入力ボックスの表示桁数は変数D で管理しているので、D が 10 を超えると D=10 とするようにしています。
キー入力を進めてゆき、現在の桁数C が D を超える場合は、それ以上入力できないように管理しています。[DEL] キーで消去する場合も、現在の桁数C を減らしてゆき、C=0 となれば、それ以上削除できないようにしています。
このように、変数C を用いた桁数の管理は、正常動作に必要です。
さて、例えば 001234 と6桁入力した場合、Z の値は1234 となり、桁数が2つ異なってしまい、桁数管理がうまくゆかなくなります。そこで、最初の桁(=一番左の桁)には、0 を入力できないようにしています。そのための処理が、今回着目した部分(下記)です。
C≠1 Or A≠25⇒Isz C
C=1⇒K→A
INPI を正負小数入力に拡張すると、小数点やマイナス記号も、最初の桁(=一番左の桁)に入力させないように抑制しなければならなくなり、小数点の位置や負号の有無の管理と併せて、この入力抑制処理がさらに膨らんで複雑になることが容易に想像され、実際に作ってみると、かなり複雑になってしまいます。
要するに、入力結果の数Z をそのまま表示させる方法を諦めて、他の方法に変更しなければ、処理をシンプルにして、論理演算や比較演算を減らすことができないと考えました。
Ver 2.0 のロジック
そこで、入力操作に応じた「表示」 と 「変数への保存」を独立させることにしました。
その結果、001234 と入力すれば、表示は 001234 となり、現在の桁数を管理する変数C には 6 が格納されます。そして、変数Z には 1234 と保存されます。変数Z を表示することを止めて、入力キーコードから変換したキーの数字をキー入力のたびに表示するようにました。
これにより、桁数管理の整合性をとるための、上記の2行の処理は不要になります。それに伴って、変数A を使うことがなくなり、変数 A を使うために、プログラムの最初で A→Z[1] とし、最後で Z[1]→A とする処理も不要になりました。
さらに、このロジックでは、Locate コマンドを使った表示処理で、指数表示の影響を考える必要がなくなり、最大桁数の管理も不要になりました。必要なら fx-5800P の最大桁数16桁を入力ボックスの桁数にすることも可能になります。但し、INPI を呼び出す時に、プログラムによっては、最大桁数の管理を行う必要が出てきます。これは作るプログラム次第で考えれば良いので、入力ボックスのプログラムで考える必要はありません。
最初から、これを思いつけば苦労は無かったのですが、しかたありません。むしろ、不適切なロジックに皆さんを付き合わせてしまって、大変申し訳なく思っています。ハードウェア性能をうまく引き出せば、凝ったプログラムも可能になります。今回は、悪いお手本とそれを改善するところをご覧頂きたいと思います。
INPI Ver 2.0 プログラムの説明
個々の処理について、説明を加えました。赤文字で書いている処理については個別に説明します。
EngOff [初期設定]:通常の整数表示のために EngモードをOff
0→Z [初期設定]:入力結果を代入する変数を 0 で初期化
87→DimZ [初期設定]:全てのキーコードを代入する領域確保
1→Z[35]:2→Z[36] [初期設定]:テンキーの数字とキーコードの関連付け
2→Z[37]:4→Z[21] [初期設定]:テンキーの数字とキーコードの関連付け
5→Z[22]:6→Z[23] [初期設定]:テンキーの数字とキーコードの関連づけ
7→Z[31]:8→Z[32] [初期設定]:テンキーの数字とキーコードの関連づけ
9→Z[33] [初期設定]:テンキーの数字とキーコードの関連づけ
If E=2:Then [初期表示]:入力モードインジケータ E=2の時
Locate 6,4,"<EXE>:ENTER" [初期表示]:このように表示
Else If E=1 [初期表示]:入力モードインジケータ E=1の時
Then
Locate 15,4,"▶t" [初期表示]:▶ を表示させたいので先ずは▶t
Locate 16,4,"E" [初期表示]:これで ▶E と表示
IfEnd:IfEnd
For 1→I To D [初期表示]:指定桁数D だけ繰り返す
Locate X+I-1,Y,">" [初期表示]:指定座標(X, Y) から D桁だけ > を表示
Next
0→C [初期設定]:現在の入力桁を管理する変数C を 0 で初期化
Do [ループ]: LpWhile K≠47 との間のループ
Do [キーコード取得]:何かキーが押されるまでループが回る
Getkey→K [キーコード取得]:キ-コードを変数K に代入
LpWhile K=0 [キーコード取得]:何もキーが押されない時ループ継続
Z[K]→I [キーコード取得]:キーコードを変数 I に代入, 1-~9以外は 0
If (I≧1 And I≦9) Or K=25 [テンキーが押下時:数字を一桁ずつ得てZ再計算]
Then
If C<D:Then ⇒現在の入力桁 C が最大桁数 D 以上では何も処理しない
10Z+I→Z ⇒入力数を更新して変数Z に代入
Locate X+C,Y,I ⇒指定座標(X, Y) のXからC桁目に I を表示
Isz C ⇒桁数管理変数C を1つ増やす。C+1→C よりも Isz C が高速
IfEnd
Else If K=34 [DELキーが押下時:数字を右から一桁ずつ削除してZ再計算]
Then
If C:Then ⇒桁数管理変数C が 0 の時は何もしない ⇒ 削除しない
Int(Z÷10)→Z ⇒Z を 10 で割った結果の整数部を得て、Z に代入
C-1→C ⇒桁数が1つ減るので、現在の桁 C を1つ減らす、Dsz C 不可
Locate X+C,Y,">" ⇒削除した数字の跡に > を表示
IfEnd
IffEnd:IfEnd
LpWhile K≠47 [ループ] EXEキーが押されない限り Lbl 0 へ戻る
For 1→I To D-C [後処理]:入力桁を示す >>>> を消去
Locate X+C+I-1,Y," " [後処理]:入力数の1つ右から最大桁数までスペース表示
Next
If E=2:Then [後処理]:入力モードインジケータ E=2の時
Locate 6,4," " [後処理]:インジケータをスペース11個で上書き
Else If E=1 [後処理]:入力モードインジケータ E=1の時
Then
Locate 15,4," " [後処理]:インジケータをスペース2個で上書き
IfEnd:IfEnd
0→DimZ [後処理]:配列変数の領域を解放
Return [メインルーチンへ戻る]
INPI Ver 2.0 の使い方
メインルーチンにおいて、INPI を呼び出す直前に、必要な変数設定を行います。
- X: 入力ボックス表示開始のX座標
- Y: 入力ボックス表示開始のY座標
- D: 入力ボックスの桁数 (最大入力桁数)
- E: 入力モードインジケータの設定
・E=2: 画面右下に <EXE>:ENTER と表示
・E=1: 画面右下に ▶E と表示
・E=0 あるいはそれ以外: インジケータの表示なし
[EXE] キーで入力確定すると、入力値が変数 Z に格納されて INPI が終了するので、メインルーチンでは INPI 呼び出し直後に Z を適切な変数に代入しておく。
例)
3→X:2→Y:6→D:1→E
Prog "INPI20"
Z→A
INPI では、上記の変数以外に、変数 C、I、K を使います。これら3つの変数に加えて上記の5つの変数は、INPI で使用され値が変わります。メインルーチンでは、これら5つの変数を使えますが、INPI を呼び出すたびに変更されても良い変数として使ってください。
INPI Ver 2.0 の入力桁数制限について
入力ボックスの桁数の制限は、Locate コマンドの制限に依存します。つまり、画面の桁数が16桁なので、最大16桁まで使えます。
但し、入力した数値をプログラムで計算に使う場合は、fx-5800P の計算精度に留意する必要があります。
1回の計算につき10桁目の誤差が±1となります。計算結果が10桁以上になると(Norm 2 指定の場合)指数形式になり、その場合は仮数部の最下位桁の精度が±1になります。一回の計算では、10桁未満であれば計算精度が保証されるわけですが、計算を繰り返す際に上記の桁数制限を受ける場合は、そこで誤差が発生します。
但し10桁の制限は、実はPCで使える開発環境でも、多くの場合同様ですから、fx-5800P に限った問題ではありません。この点は意外に認識されないことが多いのですが、その意味で fx-5800P は十分な精度を持っていると言えます。
なお、INPI では、プログラム内で敢えて桁数制限を設けないことにします。そもそも INPI を使って10桁の計算をすることは殆ど無いと思いますし、柔軟な仕様にしておく方が何かと良いと思うからです。
(注) INPI Ver 1.0 および Ver 1.2 では変数 Z をそのまま 表示させる仕様であったので、桁数制限が10桁でした。従って、入力桁数が10桁以上にならないように、プログラム内で制限をかけていました。Ver 2.0 では、この点がより柔軟になっています。
INPI Ver 2.0 での入力値 Z の更新方法と表示方法について
上のプログラムで、赤文字で示した部分が、本プログラムの一番重要なところなので、説明します。
Ver 2.0 では、キー操作ごとに入力値 Z を更新計算します。リアルタイム表示は変数 I を使い、Z を用いません。
これらの動作は、現在の入力位置 C と 最大桁数 D を用いて制御しています。
テンキー入力時の 入力値 Z の更新
[2] [0] [1] [4] と順次入力する場合をみてみます。
[2] キーを押すと、Z=2 となります。次に [0] で Z=20 とし、さらに [1] で Z=201、[4] で Z=2014 としたいわけです。Z を10倍し、テンキーの番号 (変数 I )を加えれば、Z を更新できることが分かります。つまり、
10Z+I→Z
が Z の更新計算となります。
DEL キー入力時の入力値 Z の更新
Z が 2014 の時、[DEL] キーを押ると、最下位の 4 を削除する仕様です。つまり、Z=201 としたいわけです。2014 から 201 にするには、2014 を 10 で割って、整数部を取り出せば良いので、
Int(Z÷10)→Z
が、Z の更新計算となります。
テンキー入力時の表示の変更
入力ボックスの表示開始座標は(X, Y)で、指定桁数(最大桁数)は、D です。そして、現在の入力桁を、C としています。テンキーの番号は変数 I です。
最初に [2] を押すと、最上位(一番左の桁)に 2 を表示する仕様です。入力した直後は、C=0 です。
X | X+1 | X+2 | X+3 | X+4 | X+5 |
2 | > | > | > | > | > |
Locate X, Y, I とすれば良いですね。そして、現在の入力桁 C は1つ増やして(インクリメントして)、1 になります。
続いて [0] を押すと、左から2桁目に 0 を表示したいので、表示桁は1つ右にズレます。表示行はそのまま同じです。
Locate X+C,Y,I
とれば、良さそうですね。
X | X+1 | X+2 | X+3 | X+4 | X+5 |
2 | 0 | > | > | > | > |
表示後、C をインクリメントします。C+1→C とすれば良いのですが、Isz C とする方法もあります。前回紹介した各処理の時間比較の結果、Isz C は C+1→C よりもかなり速いことが分かっているので、高速化が目的の INPI Ver 2.0 では、Isz C を採用します。
さらに、[1] キーを押すとき、
Locate X+C,Y,I
Isz C
で、うまくゆくことが分かります。
X | X+1 | X+2 | X+3 | X+4 | X+5 |
2 | 0 | 1 | > | > | > |
続けて、[4] キーを押すと、
Locate X+C,Y,I
Isz C
で、問題無いことが分かります。
入力ボックス4桁目表示直後 (C=3)
X | X+1 | X+2 | X+3 | X+4 | X+5 |
2 | 0 | 1 | 4 | > | > |
そして、Isz C を実行して C は 4 となります。
DEL キー入力時の表示の変更
2014 と入力されている状態で、[DEL] キーを押すと、末尾の 4 を消去する仕様です。末尾の 4 を消去するには、4 に > を上書きします。
2014 と入力されている状態では、C=4 となっています。そこで、先ず最初に C を1つ減らし(ディクリメントし) C=3 とした後
Locate X+C,Y,">"
とすれば、良いことが分かります。
X | X+1 | X+2 | X+3 | X+4 | X+5 |
2 | 0 | 1 | > | > | > |
さて、C のディクリメントの方法は、C-1→C とするか、Dsz C とする方法があります。高速化のためには、Dsz C が良いのですが、これだと問題があります。
Dsz C は、ディクリメント後 C が 0 になると、次の処理を飛ばして、2つめの処理までジャンプする命令です。入力ボックスを完全に消去する時、C=1 となっているので、この状態でディクリメントすると C=0 となります。すると Dsz C により次の処理を飛び越えてしまうので問題です。従って、ここでは C-1→C を採用します。
C-1→C
Locate X+C,Y,">"
ループの見直し
Ver 1.2 では Lbl 0 ~ Goto 0 をループに使っていました。プログラムの制御構造※ が複雑になると、Lbl / Goto をループに使うよりも Do / LpWhile や While / WhileEnd ループを使う方がプログラムが速くなることが経験的に分かっています。プログラムが長くなるほど、その効果は大きくなります。実のところ、今回の INPI Ver 2.0 では、Lbl 0 ~ Goto 0 を Do ~ LpWhile K≠47 に変更しても、実際の測定では明かな差は出ていません。しかし、これより複雑な制御構造を持つプログラムでは、その差が出てくる可能性があるので(実際に差がでてきます)、今回は Do / LpWhile ループに変更しておきます。
※ 制御構造とは、プログラムを構成する命令やコマンドを実行する順序のことです。プログラムは通常上から下へ実行され、If 文や⇒命令、Dsz/Isz命令 による条件分岐、Goto/Lbl などの無条件分岐や Do/While/For 文などのループ処理、そして Prog コマンド によるサブルーチン呼び出しがある時だけ、この原則を破ってすぐ下の処理ではなくて、別のところへジャンプします。分岐やループの処理の無いプログラムは最も単純な制御構造を持っています。分岐やループ、サブルーチン呼び出しが多くなるほど制御構造が複雑になります。
INPI Ver 2.0 の仕様
高速化のため、Z の更新とリアルタイム表示を完全に独立させるようにロジックを変更したので、INPI Ver 2.0 は、入力仕様が Ver 1.2 と異なっています。
Ver 1.2 では、最上位に 0 の入力をさせないようにしていましたが、このために余計な処理を加えていました。Ver 2.0 ではこの処理を取り去っています。
fx-5800P を普通の関数電卓として使う時、0123 と入力すると、そのまま表示されます。そして [EXE] キーを押せば 123 と表示されます。fx-5800P の仕様と同じで問題ない筈です。
INPI Ver 2.0 でも同様に、0000123 と入力すると、そのまま表示されます。そして [EXE] キーで確定すると Z に 123 と格納され、メインルーチンに戻ります。
さらに、Z の更新とリアルタイム表示を独立させた結果、入力ボックスのプログラム上の桁数制限が無くなりました、fx-5800P で使う場合は、Locate コマンドの制限が1行あたり16桁なので、入力ボックスの桁数は16桁に制限されます。fx-9860GII で使う場合は、Locate コマンドの制限が1行あたり21桁なので、入力ボックスの桁数は21桁に制限されます。こうして、柔軟性と移植性が得られました。
使用方法
メインルーチンから以下の書式で呼び出します。
△→X:△→Y:△→D:△→E
Prog "INPI"
Z→▽
※ △や▽は、任意の数、但し使用機種の画面範囲内に収まるように設定する。
参考 fx-5800P: 1≦X≦16、1≦Y≦4、X+D≦16
fx-9869GII: 1≦X≦21、1≦Y≦7、X+D≦21
- X: 入力ボックス表示開始桁
- Y: 入力ボックス表示開始行
- D: 入力ボックス桁数
- E: 入力ボックスインジケータの選択
E=2: 画面右下に <EXE>:ENTER と表示
E=1: 画面右下に ▶E と表示
E=(上記以外): インジケータを表示しない
- Z: 入力ボックスで確定した数値が代入される
入力ボックス内部では、上記5つの変数以外に、C、I、配列変数を用いています。以上8個の変数は、入力ボックスを呼び出すたびに代入されている数値が変更されます。メインルーチンでこれら変数を使っても問題はありませんが、これら8個の変数は入力ボックスを呼び出すために変更されても良い変数として使ってください。特に配列変数は、入力ボックス内で87個の領域確保を行い、メインルーチンに戻る前に領域解放を行いますので、それを念頭に置いてメインルーチンで使用のこと。
今回作った INPI Ver 2.0 を基に、次回から 小数入力ができるように拡張してゆきます。
つづく...
⇒ Casio Basic入門34 / 目次
応援クリックをお願いします。励みになるので...
keywords: fx-5800P、CasioBasic、入力ボックス, プログラミング入門、プログラム関数電卓
リンク集 | ブログ内マップ
- 関連記事
-
- Casio Basic入門10 2015/01/07
- Casio Basic入門34 2015/01/02
- Casio Basic入門33 2014/12/31
- Casio Basic入門18 2014/04/05
- Casio Basic入門17 2014/03/29