e-Gadget - プログラム関数電卓 2015年01月

Casio Basic入門の記事のチェック

お知らせ

Casio Basic 入門の過去の記事を最初から読み直して、分かりにくい表現や記載ミスがあれば、順次修正を行っています。

掲載しているプログラムは、全て実際に実行して確認した内容を記載しているのですが、記載ミスがあれば意味がありません。これまでもミスを見つけた時は随時修正してきましたが、改めて最初からじっくりと読み直して、記事のデバッグを行うことにしました。

さらに、新製品の登場や、fx-9860GII を入手して確認したことなど、更新すべき内容についても、合わせて修正します。

大きな修正を行った時はこれまでのように、記事の冒頭右側修正日を記載します。合わせて、常にトップに表示されるページで、チェックの進み具合をお知らせします。




[2015/01/30 追記]
現在公開している最新のエントリー Caso Basic入門37 までチェックが終わっています。プログラムコードの記載ミス、説明の抜け、混乱させる間違った説明が見つかりました。これらは全て修正しています。




管理人: Krtyski (やす)
関連記事

テーマ : プログラム関数電卓
ジャンル : コンピュータ

Casio Basic入門32

Casio Basic入門
<目次>

誤字脱字・記載ミスや分かりにくい表現は随時追記・修正します

最終: 2015/01/25

 4. CasioBasicを使ってみる(続き)

前回: Casio Basic入門31 (Chapter 5)を見る


お詫び

※ 本Chapter 6 は、Chapter 3 の続編で、Chapter 3 で作ったプログラムを高速化するのが 本Chapter 6 の目的です。なお、Chapter 3 自由自在に入力する Caso Basic入門17Casio Basic入門18 を修正・追記しています。 特に Casio Basic入門18 は誤記や説明不足が多くあり、多くの部分を修正・追記しています。申し訳ありません。必要に応じて、もう一度ご覧ください。



Chapter 6

◆ Chapter 6 の目標: プログラムを速くする
入力ボックスの改良と拡張

はじめに


Chapter 3 - 自由自在に入力する - で作った入力ボックス サブルーチン INPI Ver 1.0 は、0以上の正の整数の入力のみに対応しています。これを正負小数入力に対応するために、必要な処理を追加して拡張したものを作って、使っていましたが、入力応答がとても遅くて、入力時にストレスを感じているので、公開には至っていません。個人的に我慢して使っていると言う状況でした。

最初から、高速応答するように作っていれば良かったのですが、私自身も Casio Basic を手探りで調べながら、分かったことを公開しているので、拡張してみてから応答が非常に遅くて実用に耐えられないので、これをなんとか改善したいと思っていました。

そこで、遅いプログラムの高速化の具体例として、入力ボックスを高速応答させるための改良方法を紹介します。そして今回作る拡張版入力ボックスを活用して頂きたいと思い、プログラムライブラリ にも収録しました。

但し、Chapter 3 はそのまま残します。私の失敗を公開することで、これをお読みの方が同じ失敗に陥らなければ幸いです。


実際に行ったこと

さて、最終的に正負小数に対応した入力ボックス IN を高速化して、ストレスの無い入力ができるようになりました。さらに高速化したことで、機能拡張もてきました。この時の考え方や手法を、今回 Chapter 6 で紹介します。

実際に私が手元で行った改良と機能拡張の流れと、作った機能拡張版 (INPINは以下の通り;

入力ボックス機 能
INPI0との正の整数の入力に対応
INP0 と正の整数と正の小数の入力に対応
IN0 と正負整数と正負小数の入力に対応

1) INPI Ver 1.0 を拡張し、0と正の小数に対応した INP Ver 1.0 および 正負小数に対応した IN Ver 1.0 を作成

2) INPI を高速化して Ver 1.2 とした 
INPI Ver 1.2 をプログラムライブラリで公開。応答速度は許容範囲と判断したものの、まだ物足りない。

3) 同じ手法を INPIN に適用して、Ver 1.2 とした。
INP Ver 1.2IN Ver 1.2 はまだ遅いので、非公開のまま。

4) INPI Ver 1.2 をさらに見直して Ver 2.0 とした
※ 入力応答を速くするために、ロジックを変更。
INPI Ver 2.0 は十分に満足できる応答速度になった。

5) 同じ手法を INPIN に適用して、Ver 2.0 とした
※ INP Ver 2.0 と IN Ver 2.0 共に実用上許容範囲の応答性になった。

6) IN, INP, INPI Ver 2.0 のキーリピート抑制バージョンとして Ver 2.1 を作成

※ 以上は fx-5800P 専用入力ボックス

7) Ver 2.1  をグラフ関数電卓 (fx-9860GIIfx-CG20CG50)に対応にした Ver 2.1G を作成


[2015/01/09 追記]
INPI Ver 2.0 は最初のバージョン 1.0 の2倍の入力応答速度が得られ、十分満足できるレベルになっています。INP Ver 2.0 IN Ver 2.0 についても、下表のように実用上ストレスを感じない応答性が得られています。

改善した入力ボックス 2.0  の応答性能まとめ

バージョン10桁入力の所要時間1秒間のキー入力回数
INPI Ver 1.02.8秒3.5回
INPI Ver 1.22.2秒4.5回
INPI Ver 2.01.4秒7.1回
INP Ver 2.01.8秒 / 1.6秒※1)5.5回 / 6.2回※1)
IN Ver 2.01.8秒 / 1.6秒※2)5.5回 / 6.2回※2)

※1) [小数入力時] / [整数入力時] 、1.8秒 および 5.5回は、小数入力時のデータ 。
※2) [負の小数入力時] / [整数入力時] 、1.8秒および 5.5回は、負の小数入力時のデータ。


INPI のロジック見直し(Ver 2,0) の効果が顕著に表れていることが分かります。
INP Ver 2.0 と IN Ver 2.0 は結果的に応答性に大きな差がありません。

ようやく実用的な入力ボックスができたと思います。


Chapter 6 の内容

実際の検討内容通りに紹介すると、私の試行錯誤にお付き合い頂くだけで無駄が多過ぎるので、Chapter 6 では、以下の流れで進めることにします。Chapter 6 では実際に行ったプログラム高速化の手法を紹介します。

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-0
コマンドや命令の処理速度を比較する

fx-5800P は、通常使用なら単4電池で1年程度動作するとカシオのホームページに書かれています。この省電力化は、CPU の動作クロックを遅くすることに繋がり、上位機種 (fx-9860GII、fx-CG20 や fx-FD10 Pro など) よりも Casio Basic の動作速度がかなり遅く抑えられています。

そこで、INPI Ver 1.0Ver 1.2 に変更するにあたり、プログラム動作を遅くする Casio Basic のコマンドや命令を探し出し、それを極量使わないようにプログラムを変更します。

ちなみに、Casio Basic は、メモリに保存されているプログラムがリアルタイムで読み取られ、CPUを動作させる命令に翻訳されながら動作します。翻訳して動作するので、Casio Basic はインタープリター(通訳という意味)型の言語に分類されます。つまり、遅いコマンドや命令を排除することで、プログラム全体の顕著な高速化が期待できます。言い換えれば、最初から高速動作を念頭に入れてプログラムを書くべきとも言えます。Chapter 3 の入力ボックスでは、それがうまく出来ていなかったことになります。

パソコンで使うプログラムの多く、そしてカシオの上位機種 fx-9860GII などでSDKを使って作れる C 言語などのプログラムは、プログラミング言語で作ったファイルを一旦変換して実行ファイルや中間ファイルと呼ばれるものを作り、それが実行されます。実行ファイルや中間ファイルを作る時には、無駄なく高速に動作するように最適化処理が行われるので、遅いコマンドや命令単位を排除しても、Casio Basic のような顕著な高速化が期待できません。前向きに考えれば、工夫次第で顕著な高速化が可能なのが fx-5800P Casio Basic の面白いところです。

さて、プログラムを構成する主な要素には、以下のようなものがあります。
・ループ処理
・条件分岐処理
・論理演算や比較演算
・算術演算
・変数アクセス(変数の値の読み出しと書き込み)

これらの中から、特に処理速度の遅いものを探し出すために、主要なコマンドや命令の速度比較を行います。

ループ処理や条件分岐処理については、単純な制御構造について以前調べて以下にまとめているので、ご覧ください。
 ⇒ fx-5800P コマンドの処理速度

制御構造とは、プログラムを構成する命令やコマンドを実行する順序のことです。プログラムは通常上から下へ実行され、If 文や命令、Dsz/Isz命令 による条件分岐、Goto/Lbl などの無条件分岐や Do/While/For 文などのループ処理、そして Prog コマンド によるサブルーチン呼び出しがある時だけ、この原則を破ってすぐ下の処理ではなくて、別のところへジャンプします。分岐やループの処理の無いプログラムは最も単純な制御構造を持っています。分岐やループ、サブルーチン呼び出しが多くなるほど制御構造が複雑になります。

ループ処理については、Casio Basic では以下の4種類があります。
・Lbl / Goto ループ
・Do ループ
・While ループ
・For ループ

以前単純な制御構造で調べた結果は、これらのどれも大きな差はなく、上記のループの種類を変えても、速度向上には殆ど寄与しない結果が得られています。但し、複雑な制御構造を持つプログラムでは未確認です。

プログラムが長く複雑になって結果的に制御構造が複雑になるとLbl / GOTO ループは速度が極端に低下することが経験的に分かっていますので、今回はこれについても調べる予定です。

一方、条件分岐については、以下の3つ;
・条件ジャンプ命令 ⇒
・If 文
・Dsz/Isz命令

調べた結果は、以下のようになります。ここにも工夫の余地があります。

変数が0 (偽) か、そうでない (真) かによる条件分岐
If [変数] よりは、[変数]⇒ の方が2倍程度速い。

[式] が真か偽かによる条件分岐
If [式] よりは、[式]⇒ の方が、1.5 倍程度速い。

複数条件による分岐
[式/変数]⇒ を2つ以上記述するよりも、If [式/変数] Then~Else~IfEnd の方が速い。

Isz / Dsz
A+1→A よりも Isz A の方が2倍程度速い。


次に、変数アクセス、比較・論理演算、条件分岐の処理速度比較を、以下で行いました。
 ⇒ 変数アクセス、比較・論理演算、条件分岐の速度比較

その結果は、

制御変数 (インデックス) を使った配列へのアクセス(読み出しと書き込み処理)
これらには、配列変数、行列、リストが使える。
これらの読み出しや書き出しは非常に遅いが、使う場合はこれらの中で相対的に速いのが「配列変数」。そこで配列には「配列変数」を使うべき。但し、そもそも配列変数へのアクセスは遅いので、読み出しや書き込みの回数を極力少なくするべき。

比較演算や論理演算
これらは、算術演算(+、-、×、÷)よりも遙かに遅い。さらに変数同士の比較演算や論理演算処理の方が、変数と数値(即値)との処理よりも少し速い。従って、即値(数値)はできるだけ使わず、さらに比較演算や論理演算を使う回数を減らすべき。

条件分岐
条件判定に使う比較演算や論理演算が、条件分岐処理の殆どの時間を占める。さらに条件判定には、変数同士を用いる方が、変数と即値(数値)を用いるよりも少し速い。

この結果を一覧にしたのが以下の表です。

表 fx-5800P Casio Basic 処理時間の比較
処理内容処理時間 (ミリ秒)
A⇒4.1
通常変数アクセス5.4
If A6.2
If A Else7.2
比較演算(変数)9.7
A≠B⇒10.0
論理演算10.0
比較演算(数値)11.5
If A≠B (変数)12.0
if A≠0 (数値)13.7
テキスト表示 (Locate)20.4
配列変数書き込み22.0
配列変数読み出し22.7
リスト読み出し25.1
リスト書き込み25.3
行列読み出し26.9
行列書き込み28.4
※ 処理時間は基準ループからの相対差で得たもので、比較にために計算したものです。絶対値に厳密性はありません。

同様の方法で、掛け算 (乗算)、割り算 (除算)、累乗、整数部を取り出す関数 Int( ) の処理時間を調べて見ると以下のようになり、上記の調査結果と比べると、これらの演算は十分に速いと言え、とくに最優先で工夫をする必要はなさそうです。

処理処理時間 (ミリ秒)
乗算 (AxB)1.6
除算 (A÷B)1.8
累乗 (A)1.9
Int(1.23)1.5

算術計算の高速化については、計算式を簡単にして、算術演算の回数を減らすことがポイントになります。一例を挙げれば、自然数AからA+9 まで和を求めてBに代入する場合、

A+(A+1)+(A+2)+(A+3)+(A+4)+(A+5)+(A+6)+(A+7)+(A+8)+(A+9)→B

とするよりも、これを計算してしまって、

10A+45→B

とする方が、fx-5800P の Casio Basic では確実に速くなります。

以上の結果から、fx-5800P で 10ミリ秒や 20ミリ秒もかかる処理は、相対的にとても遅い処理なので、このような遅い処理をできるだけ使わないようにすれは、プログラム全体の処理が速くなることが分かります。



プログラムの高速化を試みる前に、実際に入力ボックスの応答性を比べるための評価用プログラムを作っておきましょう。

プログラム名: INBOX TEST
Norm 2
0→Z:0→S:0→T:0→U
Locate 1,1,"1:"
Locate 1,2,"2:"
Locate 1,3,"3:"

Lbl 0

-1→M
Do
Getkey→K
While K=0
K=35⇒1→M
K=36⇒2→M
K=37⇒3→M

Locate 1,4,"        "
  [スペース16個]

If M=1:Then
3→X:1→Y:10→D:2→E
Prog "INPI":Z→S
Else If M=2
Then
3→X:2→Y:10→D:2→E
Prog "INPI":Z→T
Else If M=3
Then
3→X:3→Y:10→D:2→E
Prog "INPI":Z→U
IfEnd:IfEnd
IfEnd

Locate 1,4,"Z="

Locate 3,4,Z

Goto 0


赤文字INPI の部分は、応答性を比較するための実際のプログラム名が入ります。

なお、冒頭に Norm 2 があります。これは指数表示を行う条件を決めるコマンドです。Norm 1 とすると3桁以上になると指数表示され、Norm 2 にすると10桁以上になると指数表示されます。できるだけ指数表示されないようにしたいので、Norm 2としておきます。

Norm については、以下を参照。
 ⇒ Casio Basicコマンドリファレンス - Norm



Chapter 6-1
具体的に対処する遅い処理を見つける

Chapter 3 で作った INPI のプログラムを再度掲載します。

最初に作った INPI Ver 1.0 (プログラム名: INPI10)

INPI_Ver10_sec_1 

キー入力の応答性を速くするのが目的なので、高速化対策が必要なのは、青文字で示した Lbl 0K≠47⇒Goto 0 で挟まれるループの内の処理です。入力ボックスが >>>> で表示された後、入力が完了して [EXE] キーを押すまで、このループが回ります。

ここで真っ先にに気がつくのは、赤文字で示した If 文、つまりテンキー入力部での条件判定部分です。この条件判定では、論理演算 (AndOr) が合計5個、そして比較演算子 (=) が合計7個使われています。このループ内全体では、論理演算が7回、比較演算が14回、配列変数 Z[ ] が使われています。

上記の、各コマンド・命令の処理時間の調査から、論理演算に約10ミリ秒、比較演算に約11.5ミリ秒かかることが分かっています。

遅 い 処 理赤文字のIf文ループ全体
種別時間 (ミリ秒)個数時間 (ミリ秒)個数時間 (ミリ秒)
論理演算10660
770
比較演算11.5780.514161
合  計
140.5
231
※ 時間(ミリ秒)の絶対値は、必ずしも正しくなく、比較の為の目安として使っています。

こうしてみると、この If 文の条件判定だけでも 140.5 ミリ秒ループ全体の処理時間 231 ミリ秒のかなりの割合を占めていることが分かります。

この最初の INPI のファイル名を INPI10 としてください。次に倫理演算と比較演算を減らしたプログラムを作り、Ver 1.2 とし、プログラム名を INPI12 としてください。


INPI Ver 1.2 (プログラム名: INPI12)

Ver 1.0 に対して、変更した部分を赤文字で示します。

INPI_Ver12_src_1 

INPI Ver 1.2 のコードから、Lbl 0 ~ K≠47⇒Goto 0 のループ内にある、論理演算と比較演算の個数を調べて、Ver 1.0 と比較してまとめてみます。

ループ全体の論理演算と比較演算の個数と所要時間

Ver 1.0 (INPI10)
Ver 1.2 (INPI12)
処理種別 (時間)個数時間 (ミリ秒)個数時間 (ミリ秒)
論理演算 (10ミリ秒)770330
比較演算 (11.5ミリ秒)141619103.5
合  計
231
133.5
※ 処理時間(ミリ秒)の絶対値は、必ずしも正しくなく、比較のための目安として使っています。

この表から、If 文の条件判定の倫理演算と比較演算を減らすだけで、大幅な処理時間の低減が見込めます。



では、評価用プログラム INBOX TEST を以下のように変更して、実際に入力時の応答性を確認します。

プログラム名: INBOX TEST
0→Z:0→S:0→T:0→U
Locate 1,1,"1:"
Locate 1,2,"2:"
Locate 1,3,"3:"

Lbl 0

-1→M
Do:Getkey→K
While K=0
K=35⇒1→M
K=36⇒2→M
K=37⇒3→M

Locate 1,4,"        "
  [スペース16個]

If M=1:Then
3→X:1→Y:10→D:2→E
Prog "INPI10":Z→S
Else If M=2
Then
3→X:2→Y:10→D:2→E
Prog "INPI12":Z→T
Else If M=3
Then
3→X:3→Y:10→D:2→E
Prog "INPI":Z→U
IfEnd:IfEnd
IfEnd

Locate 1,4,"Z="

Locate 3,4,Z

Goto 0



INBOX TEST を起動すると、シンプルな画面を表示

IN TEST 1 

入力すると、一番下の行に、変数Z の値が表示されます。

IN TEST 2 

上記の評価プログラム INBOX TEST では、[1] キーを押せば INPI Ver 1.0 が呼び出され、[2] キーを押せば INPI Ver 1.2 が呼び出されます。実際に入力して、Ver 1.0Ver 1.2 を比較してみてください。応答性が改善されていることは体感できると思います。


実際に測定を試みる

タイマーソフトを使って、10 桁入力する時間を測定します。INPI は、Ver 1.0Ver 1.2 ともにキーを押しっぱなしにすると、連続的に入力され、それが最も速い入力方法になります。そこで、INBOX TEST プログラムでは、青文字で示したように入力ボックスの桁数を 10 桁に設定します。

今回は、以下の Android スマホのアプリを使ってみました。iPhone 用も多くのものがあります。0.1秒の単位まで測定したいので、それより1桁多い 0.01秒まで表示のあるアプリで、評価の高いもの(=精度に問題ないだろうと言うもの)を選びました。

Android 用 Stopwatch and Timer

1) INBOX TEST を起動して、入力モードに入っておく。

2) タイマースタートと同時に、INBOX TEST で、どれかのキーを押し続ける。

3) 10 桁目が表示された瞬間に、タイマーを止める。

4) 測定を何度も繰り返して、同じタイミングで止める感覚をつかむ。

結果は以下の通り;
INPI バージョン10桁入力所用時間1秒あたりの入力回数
1.02.8秒3.5回
1.22.2秒4.5回

この測定はとても個人差があるので、比較の意味しかありませんが、Ver 1.0 に対してVer 1.2 の応答性は、約 1.3倍程度向上しています。



Ver 1.2 への修正内容

Ver 1.0 から Ver 1.2 への変更について、修正部分は少ないのですが、念のために説明します。

Lbl 0 ~ K≠47⇒Goto 0 ループ内は、テンキーが押された時の処理を行う最初の If 文と部分と [DEL] キーが押されたの処理を行う Else If 文の部分で構成されています。今回の主な修正部分は、最初の If 文 の条件判定です。

ループに入ると、先ず最初に

Do
Getkey→K
LpWhile K=0


で、キーコードを取得して変数 K に代入しておきます。

そして、この K の値によって、テンキーか [DEL] キーかに応じて、最初の Ifか、次の Else If 文へ条件分岐され、それぞれの処理を行っています。

テンキーである [1][9] 、そして [0] のキーコードは、バラバラですので、これらの判定には論理演算と比較演算を多用する必要がありました。

さて、プログラム冒頭にある初期設定の部分では、配列変数を使ってテンキーの番号とキーコードを関連づけています。

1→Z[35]:2→Z[36]
3→Z[37]:4→Z[21]
5→Z[22]:6→Z[23]
7→Z[31]:8→Z[32]
9→Z[33]:0→Z[25]


これは、得られたキーコードからテンキーの数字を導くために必要なものです。キーコードが K に代入されているので、Z[K] は押されたテンキーの番号を与えます。

Ver 1.0 では、キーコード K を使って、押されたキーがテンキーかどうかを最初の If 文で判定しています。ここでは多くの論理演算と比較演算を使っています。

ところが、もし押されたテンキーの数字 Z[K] を使って、最初の If 文 での判定を行うと以下のようになり、論理演算と比較演算を大幅に減らすことができます。これが、 Ver 1.2 での応答性改善の基本的なアイディアです。

If Z[K]≧0 And Z[K]≦9

Ver 1.2 では、このアイディアに合わせて、いくつかの部分を修正しています。


配列変数の扱い

配列変数のアクセス(読み書き)には、20ミリ秒以上の時間がかかることが分かっています(上記の処理時間比較の実験による)。
 ⇒ fx-5800P:変数アクセス、比較・論理演算、条件分岐の速度比較 (リンクを再掲)

配列変数をできるだけ使いたくないので、Z[K] を一旦通常変数、例えば I に代入しておき、

Z[K]→I

I  を If 文の判定で使えば、配列変数 Z[K] の使用を1回に限定できます。

If I≧0 And I≦9

配列変数については、以下を参照。
 ⇒ Casio Basicコマンドリファレンス - 配列変数


通常変数 I の選定

通常変数に I を用いるには理由があります。変数 I は、ループの前と後(プログラムの頭の部分と最後の部分)で、For 文の制御変数としてのみ使われています。For 文の最初で変数 I1→I と初期化されます。つまり、I  に何が代入されていても For 文の動作には全く影響がありません。つまり、I は使い捨て変数として自由に使えることから、テンキーの数を代入しておく変数として使えます。

INPI は、他のプログラムから呼び出して使うサブルーチンです。INPI で自由に多くの変数を使ってしまうと、呼び出すメインルーチンで使える変数に制限がかかります。言い換えれば、INPI では、できるだけ少ない変数を使い回しておく必要があるわけです。


入力数計算の変更

キー入力した数から、入力値(複数桁)を更新するための計算は、Ver 1.0 では以下のようにしています。

10Z+Z[K]→Z

Ver 1.2 では、Z[K] は I に置き換えて、読み出し時間が 20ミリ秒程度かかる配列変数を使わずに、

10Z+I→Z

と変更します。これによって、結果的に、Ver 1.0Ver 1.2 も ループ全体での配列変数アクセスの回数が1回に抑えられます。配列変数による処理速度の違いはありません。


Z[K]≧0 の問題

さて、If I≧0 And I≦9 の I≧0 は Z[K]≧0 と同じであり、実は、Z[K]≧0 には大きな問題があります。Z[K] 0 (ゼロ) になるのは、[0] キーが押された時だけではありません。例えば Z[10] も 0 です。

テンキーの数とキーコードを関連付けるために Z[K] を使いますが、37→DimZ では、配列変数37個の領域確保を行い、領域確保を行うと、Z[1]Z[37] までの全てが 0 で初期化されます。すると、テンキーを割り当てた10個以外の27個の Z[ ] には 0 が代入されています。

従って、 [0] キーを押した時の Z[25] 以外にも 0 になる Z[K] が多くあるので、[0] キーが押されていない時、例えば [X] 乗算キーが押された時は、キーコード K=24 なので Z[K] 0 になります。すると、[0] キーが押されていないのに、最初の If 文 の処理が行われることになり、プログラムは誤動作します。

 従って、[0] キーの判定は、キーの番号 Z[K]=0 つまり I=0 ではなくて、K=25 で判定する要があります。

以上の事情から、1つめの If 文は、以下のようにしておきます。

If (I≧1 And I≦9) Or K=25


0→Z[25] は不要

配列変数の領域確保を行ったところで、全ての配列変数は 0 で初期化されるので、配列変数が初期化された時点で Z[25] には 0 が代入されています。従って 0→Z[25] は不要です。これは、Ver 1.0 でもそうなのですが、以前は気がつきませんでした。配列変数への書き込みは 20ミリ秒程度の時間がかかります。0→Z[25] を省略しても、今回の目的である入力応答を速くする効果はありませんが、入力ボックスが呼びだされてから入力ボックスに入力できるようになるまでの時間短縮には役立ちます。

そこで、Ver 1.2 では 配列変数初期化の部分から、0→Z[25] を削除することにします。


配列変数の領域確保の問題

変数 I を判定で使うために、Z[K]→I とする部分に、もう一度着目します。配列変数の領域確保で、37→DimZ とすると、キーコードが 37 より大きい場合は、Z[K]→I でエラーが発生します。

プログラムを使っていて、テンキーと [DEL] キー以外のキーを押してしまうことは普通にあります。キーコードが 37 よりも大きくなるキーが押されると、どうなるでしょうか?

37→DimZ が実行されたときのメモリ内の様子
・・・・・・Z[1]Z[2]・・・Z[36]Z[37]・・・・・・

配列変数の領域確保とは、電卓のメモリの中で、配列変数のために使えるメモリを確保することです。37→DimZ を実行すると、メモリの中から37個分の領域を確保し、全てに 0 を代入します。

この時、例えば [RCL] キーを押すと(キーコード 41)、K41 が代入され、次に Z[41]→I が実行されると、Z[41] から値を読みだそうとします。ところが、メモリには Z[41] は確保されていないので、エラーとなるわけです。

このエラーを回避するには、十分な領域を確保しておけば良いことが分かります。fx-5800P のキーコードで一番大きい値は 87 です。取扱説明書の99ページに、キーコードの説明があり、そこで分かります。或いはキーコード取得プログラムで実際にキーコードを調べて見ると、キーの配置とキーコードには一定の規則があって、 [▶] キーが最大の 87 を返すことが分かります。
 ⇒ プログラムライブラリ - キーコード取得

配列変数の領域は、いくらでも確保できます(使えるメモリが残っている限り)。配列変数が使うメモリ領域とプログラムを格納しているメモリ領域は共用されるので、多くのプログラムを保存していると、その分使える配列変数の領域が減ります。いずれにせよ、配列変数は必要なだけ確保し、プログラム終了時には解放しておく必要があります。解放しなければ、メモリの無駄食いになり、保存できるプログラムが減ります。

そこで、Ver 1.0 では 37→DimZ としていましたが、Ver 1.2 では 87→DimZ と変更します。 



Ver 1.2 が完成しました。

さて、1つめの If (I≧1 And I≦9) Or K=25 内の処理には、以下のコードが含まれています。

C≠1 Or A≠25⇒Isz X
C=1⇒K→A


ここで、論理演算が1回、比較演算が3回使われているので、目安として44.5ミリ秒程度かかることになります。ここから、できるだけ 論理演算と比較演算を減らせば、さらに高速化できそうです。

実のところ、小数対応するために拡張すると、この部分はさらに複雑な処理になり、より多くの論理演算と比較演算が必要になっている、と言う事情もあります。

そこで、次回では、この部分の処理を見直す予定です。



つづく...

Casio Basic入門33 / 目次




応援クリックをお願いします。励みになるので...
にほんブログ村 IT技術ブログ 開発言語へ




keywords: fx-5800PCasioBasic、世界時間換算, プログラミング入門プログラム関数電卓

リンク集 | ブログ内マップ




続きを読む

関連記事

テーマ : プログラム関数電卓
ジャンル : コンピュータ

楽屋裏 - fx-5800Pで換算プログラム

楽 屋 裏
e-Gadget

更新: 2015/01/23

有用な電卓プログラムの代表格が換算プログラムではないかと思っています。

・年齢換算
・和暦・西暦換算
・割り勘計算
・海外時間の換算
・各種通貨換算
・消費税計算
・様々な技術的な単位の換算


いくらでも、ありそうですね...

これらは、パソコンをいちいち起動しないで、出先やフィールドで電卓をサッと出して、パッと使えると便利なことが多いものです。



貧弱な標準入力命令:?

換算するパラメータが全て1画面に表示されていて、1つを変更すると、他が同時に変更される...そんな一覧性の高い変換プログラムは、fx-5800Pでは意外に作りづらいものです。

その最大のネックが、入力命令が貧弱なことにあります。CasioBasic標準の入力命令だと、要らぬ所に入力結果を表示したり、勝手にスクロールしたり、入力したい位置に入力できず...

標準の入力命令は、画面構成を破壊するので、一覧性の高い換算プログラムを作れません。



ついに公開

換算プログラムを作るために、fx-5800P用「入力ボックス」プログラム(サブルーチン)を、長らく暖めていました。

とても便利なので、いよいよCasioBasic入門で公開することにしました。もっと良い名前がありそうですが、暫定的に「入力ボックス」と命名しました...(´д`)

CasioBasic入門15 から連載します。

実際に私が作った「換算プログラム」の一例を、CasioBasic入門15 で紹介しています。

Casio Basic入門15 から「入力ボックス」の作成方法を紹介していますが、ここで完成したものは動作が遅いので、より高速化して実用的改造しています(以下参照)。


優れたアルゴリズムによる高速化、と言う課題は残っているのですが、一人で暖めておくよりも、いっそ公開することで、凄いアルゴリズムがあれば是非知りたいと言う思いもあります。

[2015/1/23 追記]
入力応答の大幅な改善を行いました。以下をご参照ください。
 ⇒ Casio Basic入門32
 ⇒ fx-5800P プログラムライブラリ - 入力ボックス

併せて、fx-9860GII 用の入力ボックスも公開しています。
 ⇒ Casio Basic入門37

「入力ボックス」は、プログラムライブラリでも紹介しています;
 ⇒ 入力ボックス 2.0 / 2.1 - fx-5800P専用
 ⇒ 入力ボックス 2.1G - fx-9860Gシリーズ、fx-CG20、fx-CG50用




応援クリックをお願いします。励みになるので...
にほんブログ村 IT技術ブログ 開発言語へ




keywords: fx-5800PCasioBasic換算プログラムプログラミング入門プログラム関数電卓

リンク集 | ブログ内マップ
関連記事

テーマ : プログラム関数電卓
ジャンル : コンピュータ

Casio Basic入門37

Casio Basic入門
<目次>

誤字脱字・記載ミスや分かりにくい表現は随時追記・修正します

修正:2015/01/25


 4. CasioBasicを使ってみる(続き)

Chapter 6

前回: Casio Basic入門36 を見る


◆ Chapter 6 の目標: プログラムを速くする
入力ボックスの改良と拡張

入力ボックス2.0 にバージョンアップして、ようやくfx-5800P Casio Basic でストレスなく使えるようになりました。この汎用サブルーチンは、より画面の広い fx-9860GII ではさらに役立つので、今回は入力ボックスfx-9850GII に移植します。
今回 fx-9860GII に移植したプログラムは、以下のモデルでも動作します。
- fx-9860GIII
- fx-9750GIII
- fx-CG10 / fx-CG20
- fx-CG50




Chapter6-7
fx-5800P から fx-9860GII へ移植する

高速動作する fx-9860GII では、キーリピート対策が必要です。そこで、この対策を行った fx-5800P 用 IN Ver 2.1 のプログラム(以下)を元に移植を進めます。

[2015/01/25 修正] INPI Ver 2.0 のコードを掲載していたのを正しく IN Ver 2.0 に修正しました。

INv20src1_fx-5800P 


プログラム構造

[初期表示と変数の初期化]

[キー入力]

[後処理]


このプログラムをそのまま fx-9860GII で動作させても、以下の理由からエラーになります。

1. fx-9860GII には配列変数がない
2. fx-9860GII では、Getkey が返すキーコードが異なる

さらに、fx-9860GII の広い画面を活かすために、Locate の引数を最適なものに変更した方が良いでしょう。



fx-9860GII Casio Basic での配列

fx-5800P の配列変数 Z[ ] は fx-9860GII の Casio Basic には準備されていません。配列変数の代わりとして使えそうなのは、行列とリストです。

以前、fx-9860GII (ノーマルクロック 29MHz) で行列とリストのアクセス速度を測定しています。結果の表を再掲載します。
 ⇒ fx-9860GII への移植 - ピタゴラス数

表 行列とリストのアクセス速度
処理内容処理時間 (ミリ秒)
リスト読み出し13.2
リスト書き込み17.6
行列読み出し10.7
行列書く込み12.0

明かに行列の方がアクセス速度が速いので、配列変数の代わりに行列を採用します。

さて配列変数は、1行K列の行列と同じなので、87個の配列変数Z[K] を1行87列の行列 Z[1,K] に置き換えます。ここで、K は1~87の整数です。


この行列 Z の領域確保は、{1,87}→Dim Mat Z

そして、配列変数 Z[K] の代わりに、行列 Mat Z[1,K] で各要素にアクセスします。
例えば、数 1 を、1行72列目の要素に代入する場合は、

1→Mat Z[1,72]

とします。


異なるキーコードの対応

同じキーを押しても Getkey が返すキーコードは、fx-9860GII と fx-5800P で異なります。キーコードを知るには、取扱説明書を見るか、キーコード取得プログラムを実行してください。

取扱説明書キーコード取得プログラム
fx-5800P99 ページプログラムライブラリ - キーコード取得
fx-9860GII8-17 ページ

入力ボックス IN Ver 2.1 で使うキーコードをまとめます。

キーfx-5800Pfx-9860GII
1 (テンキー)3572
2 (テンキー)3663
3 (テンキー)3752
4 (テンキー)2173
5 (テンキー)2263
6 (テンキー)2353
7 (テンキー)3174
8 (テンキー)3264
9 (テンキー)3354
・ (小数点)2661
DEL3444
(-) (負号)5741
- (減算記号)6732
その他 [AC] 以外のキー・・・・・・
キーコード最大値8779
入力ボックスで使うキーだけを具体的に示していますが、他のキーのキーコードも異なります。



これに基づき、行列の領域確保と初期化は、以下のようになります。

なお、fx-9860GII ではキーコードの最大値が 79 になっていることに留意します。行列の領域確保はその分メモリを消費するので、必要最小限にすべきです。

{1,79}→Dim Mat Z
1→Mat Z[1,72]
2→Mat Z[1,62]
3→Mar Z[1,52]
4→Mat Z[1,73]
5→Mat Z[1,63]
6→Mat Z[1,53]
7→Mat Z[1,74]
8→Mat Z[1,64]
9→Mat Z[1,54]



初期表示と変数の初期化

fx-9860GII 用に書き直すために、キーコードの扱いを変更し、配列変数を行列に切り替えましたが、最後に Locate コマンドを使った画面表示を変更します。入力ボックス・インジーケータは、画面の右下の端に表示するのが良いので、それに併せて表示位置を変更します。なお、fx-9860GII では小文字アルファベットや特殊記号 などが文字として使えるので、それを利用します。

If E=2:Then
Locate 11,7,"<EXE>:Enter"
Else If E=1:Then

Locate 20,7,"▶E"
IfEnd:IfEnd
E→Mat Z[1,1]
For 1→I To D
Locate X+I-1,Y,">"
Next
0→C:0→F:1→E



キー入力ループ

キーコードが異なる以外は、fx-5800P のプログラムと全く同じになります。fx-5800P と異なる部分を青文字で示します。同じ Casio Basic なので移植性は高いようです。

但し、fx-9860GII は、fx-5800P に比べると処理速度が格段に速く、キーリピートが頻発して使いにくいので、以下の赤文字の部分でキーリピートを抑制しています。

 ⇒ キーリピート抑制 - 逆引き Casio Basic

Do

While Getkey
WhileEnd


Do
Getkey→K
LpWhile K=0
Mat Z[1,K]→I

If (I≧1 And I≦9) Or K=71
Then
If C<D:Then
If F:Then
Z+EI÷10^(F)→Z
Isz F
Else
10Z+EI→Z
IfEnd
Locate X+C,Y,I
Isz C
IfEnd

Else If K=61
Then
If F=0:Then
Locate X+C,Y,"."
Isz C:Isz F
IfEnd

Else If K=44
Then
If C:Then
If F:Then
F-1→F
F⇒Int(10^(F-1)Z)÷10^(F-1)→Z
Else
Int(Z÷10)→Z
IfEnd
C-1→C
C=0⇒1→E
Locate X+C,Y,">"
IfEnd

Else If K=32 Or K=41
Then
If C=0:Then
Locate X,Y,"-"
-1→E:Isz C
IfEnd
IfEnd:IfEnd
IfEnd:IfEnd

LpWhile K≠
31




行列利用の後処理

fx-5800P と殆ど変わりません。fx-5800P では 配列変数の領域解放を行っているので、配列変数の代わりに使った行列も領域解放を忘れずに行います。これをやらないと、メモリが占有されたままになって、使えるメモリが減ってしまいますので要注意です。行列の領域解放には ClrMat コマンドを使います。変更部分を青文字で示します。


For 1→I To D-C
Locate X+C+I-1,Y," "
Next
Mat Z[1,1]→E
If E=2:Then
Locate 11,7,"      "
Else If E=1:Then
Locate 20,7,"  "
IfEnd:IfEnd
ClrMat
Return


これで、IN の移植が終わりました。ここで行った変更は、INPINPI にもそのまま適用できます。

なお、移植したプログラムのプログラム名は、fx-5800P 向けと同様に、それぞれ INPIINPIN としておきます。



プログラム

fx-9860GII (及び fx-CG20) は、プログラムを入力せずに、ファイルをダウンロードして電卓に転送するだけでプログラムを利用できます。プログラムファイルのダウンロードはプログラムライブラリからできます。
 ⇒ 入力ボックス 2.1G - fx-9860GII & fx-G20専用

IN Ver 2.1G for fx-9860GII

IN Ver2.1G for fx-9860GII 


INP Ver 2.1G for fx-9850GII

INP Ver2.1G for fx-9860GII 


INPI Ver 2.1G for fx-9860GII

INPI Ver2.1G for fx-9860GII 


処理能力の高い fx-9860GII では、入力速度の問題は全くありません。



さて、処理速度の遅い fx-5800P でも実用的な応答性で動作する入力ボックス2.0 /2.1 が出来たので、次々回 Chapter 8入力ボックス2.0 /2.1 (好みの方を使って下さい)を使って、初級者向けの簡単なプログラムを作ってみます。

次回 Chapter 7 では、Casio Basic(超)入門編 として、Casio プログラム電卓で作れる、最も簡単なプログラムの作り方を紹介します。この最も簡単に作れるプログラムを出発点として、Casio Basic を駆使した高品質なプログラムへの改造方法を Chapter 8 で行います。



つづく...

Casio Basic入門38 / 目次



応援クリックをお願いします。励みになるので...
にほんブログ村 IT技術ブログ 開発言語へ




keywords: fx-5800PCasioBasic、入力ボックス, プログラミング入門プログラム関数電卓

リンク集 | ブログ内マップ



関連記事

テーマ : プログラム関数電卓
ジャンル : コンピュータ

Casio Basic入門36

Casio Basic入門
<目次>

誤字脱字・記載ミスや分かりにくい表現は随時追記・修正します

修正: 2015/01/25


 4. CasioBasicを使ってみる(続き)

Chapter 6

前回: Casio Basic入門35 を見る


◆ Chapter 6 の目標: プログラムを速くする
入力ボックスの改良と拡張

前回は、IN Ver 2.0  を作り、入力ボックス 2.0 が一通り揃いました。

本ブログの読者の方から、応答性が大幅に向上した一方で、「ちょっと油断してキーを離すのが遅れると、数字がリピートしてしまう」というご報告を頂きました。

応答速度を測定するためにキーを押しっぱなしにして測定しました。つまりキーを長押しすれば数字がリピートするのは、プログラムの仕様としてそのように作ったので、仕様通りの動作と言えます。

電卓としてのハードウェア仕様は、長押しで数字はリピートしません。PCでは一定時間を超えて長押しするとキーリピートが発生する仕様になっています。

入力ボックス 2.0 がキーリピートするものの、チャッチャとキーを押せばそれほど問題なく入力できるのは、たまたま fx-5800P の動作が遅いことが理由です。実は、このプログラムを 動作の速い高機能グラフ関数電卓 fx-9860GII に移植すると、チャッチャとキーを押してもキーリピートが頻発して、とても使いにくくなります。

つまり、fx-5800P 用としては結果オーライですが、プログラムとしては不完全と言うこともできます。もし、応答性に影響を与えずにキーリピートを防止できれば、その方が良いプログラムです。

そこで、今回はキーリピート対策を施した 入力ボックス 2.1 を作ります。



Chapter6-5
キーリピートを抑制する

キーリピートが発生する原因が分かれば、対処方法も分かります。

キー入力ループ内で、以下のようにキーコードを取得しています。

Do

Do
Getkey→K
LpWhile K=0


・・・・・

LpWhile K≠47


外側の Do ループの中に、もう一つの Do ループが入っています。


[EXE] キーで入力確定を行わない限り([AC]キーは除く)、この外側の Do ~ LpWhile K≠47 ループの中で処理が繰り返されます。

何かキーを押すと、内側の Do ~ LpWhile K=0 ループでキーコードを取得してスグにループを抜けます。もし、キーを押しっぱなしにすると、内側のループはキーコード取得後1回で通り抜け、押されたキーを出力する処理が繰り返し続くので (外側のループの働きによる)、同じキーがリピートして表示されます。これが、キーリピートの原因です。

ならば、キーリピートを抑制するために、内側の Do ループに入る前に、キーを離さないと先に進めない(つまり、キー取得処理を行う Do ループに入れない)ようなゲート(関所)を設ければ良いわけです。

そこで、内側の Do ループの上に、以下の2行を追加してみます。この2行でループになってます。

While Getkey
WhileEnd


Getkey コマンドは、キーが離されている時は 0 を返し、キーが押されていると 0 以外のキーコードを返します。この2行のプログラムは、Getkey が実行される時に何かキーが押されていると0 以外を返すので「真」となりループを継続します。Getkey が実行される時キーが離されていると、0 を返すので「偽」となり、ループを終了します。すると次へ進めます。つまり、キーを離さないと先に進めないゲートの役割を果たします。

ここで、While [ループ継続条件] の書式では、[ループ継続条件] が 「真」か「偽」かで判定します。戻り値のあるコマンド Getkey をこのような記法で使うと、戻り値が 0 ならば「偽」、0 以外なら「真」と判定されることを思い出して下さい。
 ⇒ Casio Basic コマンドリファレンス - While 文

Do

While Getkey
WhileEnd


Do
Getkey→K
LpWhile K=0


・・・・・

LpWhile K≠47


この僅か2行のコードで応答速度にどの程度影響があるかは、実際にプログラムを走らせて確認します。

キーリピート抑制機能を盛り込んだものを入力ボックス 2.1 とします。プログラムをまとめて以下に掲載します。

さて、INBOX TEST から呼び出して使ってみると、INPI 2.1 はキーリピートを抑制しても、応答性が悪くなる感覚が殆どないと思います。一方、INP 2.1IN 2.1 はどうしても応答性が悪くなるようです。

そこで、私は INPI 2.1INP 2.0IN 2.0 の組み合わせで使うことにします。ゆっくりとキー入力する方なら全て 2.1 でも良いかも知れません。好みに合わせてうまく使い分けて下さい。



キーリピート抑制機能付きの 入力ボックス 2.1INPI Ver 2.1INP Ver 2.1IN Ver 2.1 は以下のようになります。

INPI Ver 2.1

INPI Ver2.1 for fx-5800P 


INP Ver 2.1

INP Ver 2.1 for fx-5800P 


IN Ver 2.1

IN Ver 2.1 for fx-5800P 



入力ボックス2.1 は便利なので、次回は fx-9860GII への移植を行います。私には入力ボックスは不可欠なサブルーチンになっています。



つづく...

Casio Basic入門37 / 目次




応援クリックをお願いします。励みになるので...
にほんブログ村 IT技術ブログ 開発言語へ




keywords: fx-5800PCasioBasic、入力ボックス, プログラミング入門プログラム関数電卓

リンク集 | ブログ内マップ



関連記事

テーマ : プログラム関数電卓
ジャンル : コンピュータ

Casio Basic: Getkey

Casio Basic
コマンドリファレンス

Casio fx-5800P、fx-9860GII、fx-CG20、fx-CG50 で確認をとっています。Casio fx-FD10 Pro では互換性はあると考えられますが、実機で確認していないので「可能性」としてご覧ください。
2015/01/12 更新

fx-5800P / fx-9860GII / fx-CG20 / fx-CG50
Getkey

◆概 要:
押されたキーのキーコードを取得する。[AC]キー以外の全てのキーにはそれぞれ異なるキーコードが割り当てられている。

◆書 式: Getkey

◆引 数:なし

◆戻り値:
押されたキーのキーコードを返す。キーが押されていない場合は0を返す。

※ 互換性: キーコードはハードウェア(機種)に依存し、fx-5800P と グラフ関数電卓ではキーコードが異なります。一方で、グラフ関数電卓ではモデルにかかわらず共通です。

キーコードを知るには、取扱説明書を見るか、キーコード取得プログラムを実行してください。

取扱説明書キーコード取得プログラム
fx-5800P99 ページプログラムライブラリ - キーコード取得
fx-9860GII8-17 ページ
fx-CG20/508-20 ページ



Getkey を使うと[AC]キー以外のキーが押された時に、そのキーの判別ができます。

Getkeyはプログラムの実行を止めて、キー入力を待つ機能は備わっていません。Getkey はキーが押される時に実行されなければなりません。従って、キー入力を待ち受けるために、繰返し処理(ループ)の中に Getkey を記述する必要があります。

例えば、Do~LpWhile ループを使い、

Do
Getkey→K
[必要な処理]
LpWhile K=0

[押されたキーに応じた処理]


とします。このプログラム例は、Getkey0を返す時(キーが押されていない時)は繰返し処理が継続され、0以外を返す時(何かキーが押された時)は、繰り返し処理を終える動作をします。

入力命令?を使うと、入力確定のために [EXE] キーを押す必要がありますが、Getkey を使えば [EXE] キーを押さずに、リアルタイムでキー入力に反応するプログラムを作れます。

fx-5800Pでは、キーが押された瞬間から所定の時間の範囲なら、Getkey でキーコードを取得できるようです。つまり、キーが押された、まさにその瞬間に Getkey が実行されていなくても、一定の時間的猶予が与えられます。

Lbl 0
0→C
Do
Getkey→K
LpWhile K=0
Locate 1,1," "
Locate 1,1,K
Goto 0


これを実行すると、最初に表示されるキーコードKの値は、プログラム実行開始で押さなければならない [EXE] キーの47となります。つまり、[EXE] が押された瞬間ではなくて、押されてから一定時間内で(短い時間ですが...)キーコードを取得していることがわかります。

次に、上のコードの冒頭のLbl 0 の上に、次のように赤文字で示した4行のコード(1000回ループ)を追加して、一定の待ち時間がかかるようにします。

1000→A
Lbl 1
Dsz A
Goto 1

Lbl 0
0→C
Do
Getkey→K
LpWhile K=0
Locate 1,1," "
Locate 1,1,K
Goto 0


すると、プログラム実行開始で押された [EXE] のキーコードを取得できないまま、キー入力待ちになります。つまり、キーが押されてから所定時間を経過すると Getkey はキーコードを取得できないことがわかります。

非常に長いループ(多くの処理が記述されていて、ループ1回がまわるのに時間がかかるループ)の内で Getkey でキー入力を監視すると、キーの取りこぼしが起こり得ます。キー入力監視は極力短めのループを使うようにすると良いでしょう。

fx-9860GII ではプログラムの実行が速いので、上記のような現象の違いを確認できません。むしろ実行速度が速いために、プログラムによっては、キーリピートを抑制するコード(赤文字部分)が必要になります。

While Getkey
WhileEnd

Do
Getkey→K
LpWhile K=0


赤文字の2行が、キーリピート抑制の働きをします。Getkey の戻り値が 0 でない限り、つまりキーがが押されている限り、このループが継続します。そして、キーが離れたら Getkey は 0 を返すのでループを脱出して次のキーコード取得が行われます。




応援クリックをお願いします。励みになるので...
にほんブログ村 IT技術ブログ 開発言語へ





keywords: fx-5800PCasioBasicGetkeyプログラミング入門プログラム関数電卓

リンク集 | ブログ内マップ
関連記事

テーマ : プログラム関数電卓
ジャンル : コンピュータ

Casio Basic: Int / Frac

Casio Basic
コマンドリファレンス

Casio fx-5800P、fx-9860GII、fx-CG20、fx-CG50 で確認をとっています。Casio fx-FD10 Pro では互換性はあると考えられますが、実機で確認していないので「可能性」としてご覧ください。
2015/01/09 更新

fx-5800P/ fx-9860GII / fx-CG20 / fx-CG50
Int関数 / Frac関数

◆概 要: Int 関数は数値の整数部を、Frac 関数は数値の小数部を取り出す

◆書 式: Int(引数) / Frac(引数)
  • fx-9860GII や fx-CG20/50 では、( ) なしで引数を書けるが、( ) を使っても問題ない
◆引 数: 数値、変数、戻り値のある関数を指定
  • fx-9860GII や fx-CG20/50 で引数に式を使う場合は、引数の範囲を明確にするために ( ) 使用を勧める

Int や Frac は、関数電卓として内蔵されている関数機能。


Frac 関数では、内部精度15桁を超える引数を与えると、丸め誤差が発生する。Int 関数では、このような丸め誤差は発生しない。




Int や Frac の使用例

1) 整数Nを3で割った余りMを求める: Int( )関数の使用例

N-3Int(N÷3)→M

(証明)

任意の整数は3で割った余りが、必ず0、1、2のいずれかになります。
Nを3で割った時の余りをMとすると、整数Aを用いて

N = 3A+M
    ・・・(式1)


と表すことができます。従って余りMは、

M = N - 3A      ・・・(式2)

となります。ここで、Mを求めるためには、Aを知る必要があります。


そこで、(式1)を変形して、

N/3 = A + M/3      ・・・(式3) 

但し、M = 0, 1, 2 なので、M/30以上1未満になります (0 ≦ M/3 <1)。
従って、A + M/3 を小数で表すと、

A + 0.xxxxxxxxx


となります。

ここで、Aは整数なので、A+M/3 の整数部は、A になります。
これを Int( )関数を用いて表すと、

A = Int(A+M/3)

となり、(式3)から、Int( ) の引数は、N/3 に置き換えられるので、

A = Int(N÷3)      ・・・(式4)

(式4)を(式2)に代入すると、

M = N - 3Int(N÷3)

が得られます。

(証明終わり)



2) 整数Nの偶数 / 奇数の判定:Frac 関数の使用例

If Frac(N÷2)
Then "N is odd"
Else "N is even"
IfEnd


Nが奇数の場合は、2で割り切れません。このとき N÷2 は整数にならず、小数を伴う数値になります。つまり、Frac(N÷2) は0になりません。Frac(N÷2)0でないは、Frac(N÷2) が「真」です。If [条件][条件]「真」なので、Then 以下を処理し、N is odd (Nは奇数) と表示します。
Nが偶数の場合は、2で割り切れます。このとき N÷2 は整数になり、小数部はありません。つまり、Frac(N÷2) は0になります。「Frac(N÷2)0である」は、Frac(N÷2)「偽」で、「真」でありません。If [条件][条件]が「真」でないので、Else以下を処理し、N is evern (Nは偶数)と表示します。

この場合、Frac の引数である N÷2 が、少数以下14桁になることはあり得ないので、丸め誤差が発生することはありません。従って Frac を使っても問題はありません。


( ) を使わない引数指定時の注意 [fx-9860GII や fx-CG20/CG50]

1.2→A
Int A+0.8


を実行した結果は 1.8 となる。Int の引数の範囲が A+0.8 ではなくて、A  のみになっていることが分かる。

A+0.8 を引数にするには、( ) を使って、

1.2→A
Int (A+0.8)

とする必要がある。




応援クリックをお願いします。励みになるので...
にほんブログ村 IT技術ブログ 開発言語へ




keywords: fx-5800PCasioBasicInt 関数Frac 関数プログラミング入門プログラム関数電卓

リンク集 | ブログ内マップ
関連記事

テーマ : プログラム関数電卓
ジャンル : コンピュータ

Casio Basic入門35

Casio Basic入門
<目次>

誤字脱字・記載ミスや分かりにくい表現は随時追記・修正します

修正: 2015/01/26


 4. CasioBasicを使ってみる(続き)

Chapter 6

前回: Casio Basic入門34 を見る

◆ Chapter 6 の目標: プログラムを速くする
入力ボックスの改良と拡張

前回は、INP Ver 2.0  を作り、ストレスの無い実用的な応答性が得られました。そこで、今回はこれをさらに拡張して、負の整数/小数入力に対応した入力ボックスを作ります。プログラム名は IN です。


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-4
処理速度を意識して機能拡張する(2)

前回作った、INP Ver 2.0 は、0以上の小数入力まで拡張した入力ボックスです。

前回作成した INP Ver 2.0 (ファイル名: INP)

INP_Ver20_src_1 


このプログラムを拡張して、負の数値入力まで拡張した IN Ver 2.0 (プログラム名: IN)を作ります。


負数の扱い

fx-5800P には、負の数を入力するために、2つのキー [(-)][-] があり、高速化を考えた時、どちらかのキーのみを使わせることも考えられますが、プログラムの実用上、これらを限定するのは良くありません。そこで、先ずはこれら両方のキーを使えるようにプログラムを作り、満足ゆく応答性が得られない時は、どちらかに限定するようにしようと思います。

そこで先ず、負号 [(-)]  キーや減算記号 [-] キーを入力する時の処理を考えます。[(-)] のキーコードは 57、[-] のキーコードは 67 なので、Getkey コマンドの戻り値が 57 あるいは 67 の時、負号入力処理を行うようにします。

IN への拡張の元になる INP のプログラム構造を踏襲すると、IN のプログラム構造は以下のようになります。


 [初期化処理 1]

 [初期表示]

 Do


   [キーコード取得]

   [初期化処理 2]

   If (テンキーの場合):Then

    [テンキーの時の処理]

   Else If (小数点の場合):Then

    ※[小数点の時の処理]


   Else If (DELキーの場合):Then

    
[DELキーの時の処理]


   Else If (負記号の場合):Then

    [負記号の時の処理]

   IfEnd:IfEnd

   IfEnd:IfEnd

 LpWhile K≠47


 [後処理]

 Return


----------

赤文字で示したのは今回追加する処理です。さらに を付けたブロックは修正が必要と思われます。


負記号キーが押された時の処理

キーコード K が 57 あるいは 67 の時、負記号キーが押された時の処理を行います。

Else If K=57 Or K=67
Then 
 [負記号キーが押された時の処理]
IfEnd


さて、現在入力している数が正の数か負の数かを管理するための変数が必要です。今回は新しい変数を使うのではなく、既に使っている変数を使い回すことにします(理由は後で述べます)。

元になる INP のプログラムを眺めると、7つの通常変数 CDFIXYZ はキー入力ループ内で使っていますが、変数 E はループ内で使われていないので、入力値の正負管理変数として E を使うことにします。

E は入力モード・インジケータの書式を指定するために、入力ボックスを呼び出す時に使われる変数で、キー入力ループの前の初期化表示処理と、ループの後の後処理で使われています。そこで、変数 E に格納されている変数を一旦別の変数に待避させておき、キー入力ループ内で E を自由に使います。そして、ループを出た時には、待避させていた本来の値を 変数 E へ戻し、後処理を実行させます。

E から待避させる変数には、今回は配列変数を用いることにします。87個の配列変数の多くは、プログラム内で使われていません。そこで、ループの前にある入力モード・インジケータの表示が終わった直後に、E を一旦 配列変数 Z[1] に待避しておきます(E→Z[1])。

これにより、キー入力ループ内では E を好きなように使えるようになり、ループが終わった直後に、Z[1] に待避させていた値を E に戻して(Z[1]→E)、後処理で入力モード・インジケータを消去します。

正数入力時に E = 1 とし、負数入力時に E = -1 とします。入力ボックス起動時の初期設定では、1→E、つまり正数入力としておきます。このように設定すれば、入力値の更新計算の時、変数 E を有効に使えそうです。

さて、負号は必ず最上位にしか入力できない、と言う大きな条件があります。つまり、C = 0 の時のみ負号の入力が許されます。これをプログラムコードで表現すると、以下のようになります。

If C=0:Then
Locate X,Y,"-"
-1→E:Isz C
IfEnd


負号を入力したら、E を -1 にします(-1→E)。そして入力桁数が1つ増えるので、Isz C とします。これが、負記号入力時の処理です。今回追加するのは、以下となります。

Else If K=57 Or K=67
Then
If C=0:Then
Locate X,Y,"-"
-1→E:Isz C
IfEnd:IfEnd


一番最後の IfEndElse If が追加されたので、合わせて追加しています。


テンキー入力時の処理

INP Ver 2.0 のテンキー入力時の処理を、抜き出してみます。

If (I≧1 And I≦9) Or K=25
Then
If C<D:Then
If F:Then
Z+I÷10^(F)→Z
Isz F
Else
10Z+I→Z
IfEnd
Locate X+C,Y,I
Isz C
IfEnd


これを変更して、負数の入力に対応させます。

テンキー入力時の処理を考えます。表示は単にテンキーの数字が入力されるだけなので、Locate コマンドを使った表示部分は、変更の必要がなさそうです。変更が必要なのは、上で赤文字で示した入力値 Z の更新の計算でしょう。

赤文字で示した小数入力の時の処理は、以下のようになっています;

Z+I÷10^(F)→Z

ここでは、Z + (I ÷ 10F) を計算して、Z に代入しています。この処理では、入力キーの数字 I に応じて、Z が更新されるのですが、I 自体は、常に一桁の正の整数です。一方、Z は、正になったり負になったりします。Z が正なら正数の入力ということですね。

この計算を数直線上でイメージすることで、Z + E(I÷10F) で良さそうだと、思いつきました。要するに、

Z + E(I÷10F) = Z + EI÷10F 

従って、

Z+EI÷10^(F)→Z

これをスグに思いつかなくても、以下のように考えるとうまくゆきます。


シミュレーション 1

-12.3 が入力されていて、さらに 4 をキー入力する時、その直前の状態は、以下のようになっています。

C=5   (5桁が入力されている)
I=4
F=2   (小数点を含めて、小数点以下2桁)
E=-1  (負数入力モード)
Z=-12.3
・ 表示: -12.3

この状態で、4 を入力すると、-12.34 となるべきで、Z を更新する計算は以下のようになるべきです;

つまり、
-12.34 = -12.3 - 0.04 = -12.3 - (4÷100)
= -12.3 - (4÷102) = -12.3 - 4÷102   [ 除算は減算より優先される ]
= -12.3 - 4÷10F       [ F=2 だから ]
= -12.3 + (-1)x4÷10F   
= Z + EI÷10F         [ Z=-12.3、E=-1、I=4 だから ] 
= Z+EI÷10^(F)


つまり、

Z+EI÷10^(F)→Z

とすれは、良さそうです。


シミュレーション 2

今度は、正数入力モードで、シミュレーションしてみます。

12.3 が入力されていて、さらに 4 をキー入力する時、その直前の状態は以下のようになっています;

C=4   (4桁が入力されている)
I=4
F=2   (小数点を含めて、小数点以下2桁)
E=1   (正数入力モード)
Z=12.3
・ 表示: 12.3

この状態で 4 を入力すると、12.34 となるべきです。

ここで、上で考えた処理

Z+EI÷10^(F)→Z

をそのまま適用してみます。

Z + EI÷10^(F) = Z + EI÷10F
= 12.3 + (1)x4÷102 = 12.3 + 0.04 = 12.34


正しく更新されることが分かります。

INP Ver 2.0 では、

Z+I÷10^(F)→Z

だったので、I E を乗算する(以下の赤文字を追加する)だけで、修正できることが分かります。

Z+EI÷10^(F)→Z


シミュレーション 3

次に、整数入力の時は、INP Ver 2.0 では以下の処理で 入力値 Z を更新しています。

10Z+I→Z

正負の整数入力に対応するには、上と同様に

10Z+EI→Z

で良さそうです。

そこで、-123 が入力されていて、4 をキー入力する時、その直前の状態は、

C=4   (4桁が入力されている)
I=4
F=0   (小数点を含めて、小数点以下0桁)
E=1   (数入力モード)
Z=-123
・ 表示: -123

なので、この状態から -1234 としたいわけです。

-1230 -

で良いので、

-123 x 10 - 4 = 10Z -I = 10Z +(-1)I = 10Z + EI

となり、これで良いことが分かります。

以上で、入力値 Z の更新計算をどうすれば良いか決まりました。

他の部分は、キー入力した数を Locate コマンドで追加表示するだけなので、INP Ver 2.0 と同じで良いわけです。
Locate X+C,Y,I
Isz C



INP Ver 2.0 のコードに、追加する部分を赤文字で示します。

If (I≧1 And I≦9) Or K=25
Then
If C<D:Then
If F:Then
Z+EI÷10^(F)→Z
Isz F
Else
10Z+EI→Z
IfEnd
Locate X+C,Y,I
Isz C
IfEnd


意外なことに、正負管理変数 E を2カ所に追加するだけでした。


小数点キーが押された時の処理

INP Ver 2.0 での処理は、以下のものです。

Else If K=26
Then
If F=0:Then
Locate X+C,Y,"."
Isz C:Isz F
IfEnd


入力値 Z の更新処理は行っておらず、単に小数点を表示するだけです。従って、入力値が正か負かは、この処理では無関係。
つまり、この部分は何も変更する必要はありません。


DELキーが押された時の処理

INP Ver 2.0 では、以下のようになっています。

Else If K=34
Then
If C:Then
If F:Then
F-1→F
F⇒Int(10^(F-1)Z)÷10^(F-1)→Z
Else
Int(Z÷10)→Z
IfEnd
C-1→C
Locate X+C,Y,">"
IfEnd


画面表示の変更は必要なさそうです。

赤文字で示した 入力値 Z の更新をどのように変更すれば良いか、考えてみます。

小数から末尾の数字を削除する時の Z の更新は、

F-1→F
F⇒Int(10^(F-1)Z)÷10^(F-1)→Z


となっています。Z が正の小数の時は、これで良いのですが、Z が負の小数の時は、どうなるでしょうか?
実は、そのまま適用でき、変更する必要は無さそうです。


シミュレーション 4

-1.2 と表示されていて、[DEL] キーで末尾を削除する直前は、以下の状態になっています。

C=4   (4桁が入力されている)
F=2   (小数点を含めて、小数点以下2桁)
E=-1   (負数入力モード)
Z=-1.2
・ 表示: -1.2

F-1→F
が実行されると、F=1 となります。そして、次の処理は、F が 0 でないので、条件ジャンプ命令 の右が実行され、

Int(10^(1-1)Z)÷10^(1-1)→Z 

ここで、

Int(10^(F-1)Z)÷10^(F-1) = Int(100Z)÷100 = Int(Z)÷1 = -1
 

なので、
-1→Z となって、Z=-1 となります。表示は、-1. と小数点が残っています。これで正常動作が確認されました。


シミュレーション 5

-1. と表示されている時、さらに [DEL] キーを押して、末尾の小数点の削除を行うことを考えます。その直前の状態は、以下になっています。

C=3   (3桁が入力されている)
F=1   (小数点を含めて、小数点以下1桁)
E=-1   (負数入力モード)
Z=-1
・ 表示: -1.

ここで、[DEL] キーを押すと、

F-1→F

が実行されて、F=0 となり、次の処理は、F=0 なので、⇒命令の次は飛ばされて実行されません。

F⇒Int(10^(F-1)Z)÷10^(F-1)→Z

つまり、Z は変更されず、Z=-1 のままです。表示は、-1 となり、正常動作が確認されました。
F=0 となったので、整数入力モードに切り替わります。


シミュレーション 6

-1 と表示されている時、さらに [DEL] キーを押して、末尾の 1 を削除する直前の状態は以下のようになっています。

C= 2   (3桁表示されている)
F=0    (小数点を含めて、小数点以下0桁)
E=-1   (負数入力モード)
Z=-1
・ 表示: -1

ここで、整数入力モードに切り替わっているので、つまり F=0 なので、Z の更新は、

Int(Z÷10)→Z

で行われます。Int(Z÷10) を計算すると、
Int(Z÷10) = Int(-1÷10) = Int(-0.1) = 0  
となるので、Z=0 となります。Int( ) 関数は、Int(-0.1) は、-0 ではなくて、0 となります。

表示は、- となって、負号が残った状態になります。これも正常動作です。この状態で、例えば 2 を入力すると、-2 になる(上のテンキー入力時の処理を参照)ので、まだ負数入力モードのままです。


シミュレーション 7

画面に負号 - のみが表示されている時、さらに [DEL] キーを押す直前の状態は、

C=1   (1桁が入力されている)
F=0   (小数点を含めて、小数点以下0桁)
E=-1   (負数入力モード)
Z=0
・ 表示: -

と、このようになっていて、ここで [DEL] キーを押すと、

Int(Z÷10)→Z

が実行され、Int(0÷10) = Int(0) = 0 なので、0→Z となり、Z=0 のままです。
表示は、何もなくなります。実際は、>>>>>>>> と入力ボックス起動直後の状態と同じになります。

このあと、テンキー 2 を入力することを考えてみます。すると、Z=2 となるはずですが、E=-1 のままなので、Z=-2 となってしまいます。というのも、テンキー入力時の処理で、Z の更新処理として、

10Z+EI→Z

が実行されるからです。これはマズイ、バグです。

シミュレーション 7 のように、負号を削除する時は、E の値を -1 から 1 に変更しておく必要があります。負号の削除は最上位桁の削除です。最上位が削除される時は、C=1 で、処理が終わるとき、C-1→C を実行して、C は 0 になります。そして、C=0 の時 E を 1 にすることで、正数入力モードに切り替えられます。

最上位桁を削除して、表示がなにも無くなるのは、入力ボックス起動時と同じ状態で、その時は負数入力モードではなくて、正数入力モードです。従って、最上位削除の時は、削除されるものが負号だろうと、数字だろうと、小数点だろうと、なんでも良いので、E を 1 にしてしまえば良いわけです。

そこで、

C=0⇒1→E

という処理を、DEL キーが押された時の処理の一番最後に追加しておきます。それ以外は、 INP Ver 2.0 と全く同じで変更は必要ありません。

DEL キーが押された時の処理は、以下になります。

Else If K=34
Then
If C:Then
If F:Then
F-1→F
F⇒Int(10^(F-1)Z)÷10~(F-1)→Z
Else
Int(Z÷10)→Z
IfEnd
C-1→C
C=0⇒1→E
Locate X+C,Y,">"
IfEnd


結局、今回追加したのは、上の赤文字の1行のみです。



これで、INP Ver 2.0 から IN Ver 2.0 への拡張ができました。負号管理は、 変数 E を使い回すようにし、負号キーが押された時の処理を追加し、それ以外はわずか3カ所の修正のみで拡張ができました。Ver 2.0 で採用した新しいロジックのおかげで、最小限の修正で済んだわけです。

正負小数入力に対応した入力ボックス IN Ver 2.0 が完成です。ファイル名を IN とします。

IN_Ver20_src 

INP Ver 2.0 から追加修正した部分を赤文字で示しています。


IN Ver 2.0 の仕様

fx-5800P を普通の関数電卓として使う時、0123 、.123 、 -.0123 と入力すると、そのまま表示されます。そして [EXE] キーを押せば、それぞれ 123 、0.123 、-0.0123 と表示されます。これは、fx-5800P の関数電卓としての入力仕様です。

IN Ver 2.0 でも同様で、例えば 0123.123-.0123 と入力すると、そのまま表示されます。そして [EXE] キーで確定すると Z には、それぞれ 1230.123-0.0123 と格納され、メインルーチンに戻ります。

さらに、Z の更新とリアルタイム表示を互いに独立させた結果、入力ボックスのプログラム上の桁数制限が無くなりました、fx-5800P で使う場合は、Locate コマンドの制限が1行あたり16桁なので、入力ボックスの桁数は最大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

- X: 入力ボックス表示開始桁
- Y: 入力ボックス表示開始行
- D: 入力ボックス桁数
- E: 入力ボックスインジケータの選択
   E=2: 画面右下に <EXE>:ENTER と表示
   E=1: 画面右下に ▶E と表示
   E=(上記以外): インジケータを表示しない
- Z: 入力ボックスで確定した数値が代入される

入力ボックス内部では、上記5つの変数以外に、C、I、F、配列変数を用いています。以上9個の変数は、入力ボックスを呼び出すたびに格納されている数値が変更されます。メインルーチンでこれら変数を使っても問題はありませんが、これら8個の変数は入力ボックスを呼び出すたびに変更されても良い変数として使ってください。特に配列変数は、入力ボックス内で87個の領域確保を行い、メインルーチンに戻る前に領域解放を行いますので、それを念頭に置いてメインルーチンで使用してください。


キー入力回数の測定

実際に前回と同じ方法で、INBOX TEST プログラムを使って、今回も入力応答性を実際に調べてみました。

比較対象は以下の2つのバージョン:

- INP Ver 2.0
前回作成したもの

- IN Ver 2.0
今回作成したもの

なお、整数入力と小数入力では、処理を分岐させているので、整数入力と小数入力の2通りで比較測定を行います。


整数入力時の応答性

評価プログラム INBOX TEST を以下のようにします。
ここで、INP Ver 2.0 のプログラム名を INP とし、IN Ver 2.0 のプログラム名を IN とし、入力ボックスの桁数を 10→D により10桁にします。

プログラム名: INBOX TEST
Norm 2
0→Z:0→S:0→T:0→U
Locate 1,1,"1:"
Locate 1,2,"2:"
Locate 1,3,"3:"

Lbl 0

-1→M
Do
Getkey→K
While K=0
K=35⇒1→M
K=36⇒2→M
K=37⇒3→M

Locate 1,4,"        "
  [スペース16個]

If M=1:Then
3→X:1→Y:10→D:2→E
Prog "IN12":Z→S
Else If M=2
Then
3→X:2→Y:10→D:2→E
Prog "INP":Z→T
Else If M=3
Then
3→X:3→Y:10→D:2→E
Prog "IN":Z→U
IfEnd:IfEnd
IfEnd

Locate 1,4,"Z="

Locate 3,4,Z

Goto 0


比較は、メニュー2 INP Ver 2.0メニュー3 IN Ver 1.2 で行います。

測定方法は、前回と同じ方法です。
それそれの入力ボックスを開いた状態で、タイマースタートと同時にいずれかのテンキーを押しっぱなしにします。そして10桁目に表示された瞬間にタイマーを止め、時間を読み取ります。

表1 整数入力時の応答性比較
プログラムバージョン10桁入力所要時間1秒あたりの入力回数
IN Ver 1.2 整数入力3.6秒2.7回
INP Ver 2.0 整数入力1.6秒6.2回
IN Ver 2.0 整数入力1.6秒6.2回
※ この測定は、非常に個人差があるので、比較の意味しかありません。

参考までに、以前 INPI Ver 1.2 と同じロジックで作った IN Ver 1.2 の測定結果も併せて掲載しました。このバージョンは入力にストレスを感じるほど応答性が悪く、正直言って公開できるレベルではありませんでした。

IN Ver 2.0 は、INP Ver 2.0 と差が認められず、同様の応答性能であることが分かりました。

実際にかなり速く入力してもキーの取りこぼしが無く、十分満足できる応答性になっていると思います。


小数入力時の応答性

INBOX TEST では、D→12 として入力ボックスの桁数を12桁に設定します。

INP Ver 2.0メニュー2)では、0. と2桁入力しておき、タイマースタートと同時に、いずれかのテンキーを押しっぱなしにし、12桁目(押しっぱなしで10桁目)が表示されたと同時にタイマーを止め、時間を読み取ります。

IN Ver 2.0メニュー3)では、-. と2桁入力しておき、タイマースタートと同時に、いずれかのテンキーを押しっぱなしにし、12桁目(押しっぱなしで10桁目)が表示されたと同時にタイマーを止め、時間を読み取ります。

そこで、INBOX TEST を以下のように、赤文字部分を変更します。

プログラム名: INBOX TEST
Norm 2
0→Z:0→S:0→T:0→U
Locate 1,1,"1:"
Locate 1,2,"2:"
Locate 1,3,"3:"

Lbl 0

-1→M
Do
Getkey→K
While K=0
K=35⇒1→M
K=36⇒2→M
K=37⇒3→M

Locate 1,4,"        "
  [スペース16個]

If M=1:Then
3→X:1→Y:10→D:2→E
Prog "INPI":Z→S
Else If M=2
Then
3→X:2→Y:12→D:2→E
Prog "INP":Z→T
Else If M=3
Then
3→X:3→Y:12→D:2→E
Prog "IN":Z→U
IfEnd:IfEnd
IfEnd

Locate 1,4,"Z="

Locate 3,4,Z

Goto 0



表2 小数入力時の応答性比較
プログラムバージョン10桁入力所要時間1秒あたりの入力回数
INP Ver 2.0 小数入力1.8秒5.5回
IN Ver 2.0 小数入力1.8秒5.5回
※ この測定は、非常に個人差があるので、比較の意味しかありません。

IN Ver 2.0 の小数入力は、INP Ver 2.0 と差が見られず、同等の応答性能があることが分かりました。

実際に入力してみると、かなり速くキーを打っても、取りこぼしは発生せず、十分実用に耐えると思います。


Lbl/Goto ループと Do/LpWhile ループ

前回の INP Ver 2.0 と同様に、IN Ver 2.0 でのループの影響を調べて見ました。
キー入力中の処理は Do ~ LpWhile K≠47 ループ内に記述していますが、これを Lbl 0 ~ K≠47⇒Goto 0 ループに書き直して、応答速度の比較をしました。

表3 IN Ver 2.0 のループの違いによる応答性への影響比較
入力モードループ種別10桁入力所用時間1秒あたりの入力回数
整数入力
Lbl/Goto2.0秒4.7回
Do/LpWhile1.6秒6.2回
小数入力
Lbl/Goto2.3秒4.3回
Do/LpWhile1.8秒5.5回
※ この測定は、非常に個人差があるので、比較の意味しかありません。

Lbl/Goto ループを使った時の応答性低下は、整数入力で25%、分数入力で 27% なので、Do ループを用いた高速化の効果が明かに現れています。INP Ver 2.0 では Lbl/Goto による応答性低下は 15~16% だったので、IN Ver 2.0 の方がより顕著です。制御構造がより複雑になると、Do ループに対して Lbl/Goto ループの処理速度の低下がより顕著になることが分かります。



まとめ

Chpater 6 では、以下の3種類の入力ボックスの高速化を図り、十分実用に使える Ver 2.0 ができました。

1. INPI Ver 2.0
0以上の整数入力のみに対応。0以上の整数のみの入力を行わせたいプログラムで使うことを想定していて、負数や小数入力のエラーチェックが不要になります。

2. INP Ver 2.0
0以上の整数/小数入力に対応。0以上の小数を含んだ数値みの入力を行わせたいプログラムで使うことを想定していて、負数入力のエラーチェックが不要になります。

3. IN Ver 2.0
正負小数/整数の入力に対応。最も汎用性のある入力ボックス。


プログラム高速化の指針

1) 比較演算、論理演算、配列変数の使用を極力控えること

2) 条件分岐には、以下を指針とする
 - If A≠0 よりも If A が高速
 - If A=B:Then [1つの処理]:IfEnd よりも A=B⇒[1つの処理] の方が高速

3) 制御構造 が複雑な(プログラムでは、Lbl/Goto ループでなくて、Do ループや While ループを使う

4) 上記3点を実現するための最適なロジックを考える

※ 制御構造とは、プログラムを構成する命令やコマンドを実行する順序のことです。プログラムは通常上から下へ実行され、If 文や⇒命令、Dsz/Isz命令 による条件分岐、Goto/Lbl などの無条件分岐や Do/While/For 文などのループ処理、そして Prog コマンド によるサブルーチン呼び出しがある時だけ、この原則を破ってすぐ下の処理ではなくて、別のところへジャンプします。分岐やループの処理の無いプログラムは最も単純な制御構造を持っています。分岐やループ、サブルーチン呼び出しが多くなるほど制御構造が複雑になります。


今回の高速化検討では、ロジックの見直しがうまく働いたと思います。結果的にストレスのない入力応答性が得られたので、当面これで良いことにします。さらに高速化可能なロジックがあれば、将来バージョンアップするかもをしれません。

今回は、プログラムライブラリに入力ボックス2.0 として公開します。



入力ボックス 2.0 が高速化したため、チョットした問題が出てきました。次回は、その対応について紹介します。



つづく...

Casio Basic入門36 / 目次



応援クリックをお願いします。励みになるので...
にほんブログ村 IT技術ブログ 開発言語へ





keywords: fx-5800PCasioBasic、入力ボックス, プログラミング入門プログラム関数電卓

リンク集 | ブログ内マップ



関連記事

テーマ : プログラム関数電卓
ジャンル : コンピュータ

Casio Basic入門10

Casio Basic入門
<目次>

誤字脱字・記載ミスや分かりにくい表現は随時追記・修正します
最終: 2015/01/07

 4. CasioBasicを使ってみる(続き)


Chapter 2 - 初級

◆ Chapter 2 の目標: 動きのあるプログラムを作る
簡単なアクションゲームを作る

前回: Casio Basic入門9


Chapter 2-1 で羽根が回るアニメーションを作りました。

プログラム名 CH2-1
50→C
Lbl 0
If C-4Int(C÷4)≦1
Then
Locate 8,2,"X"
Else
Locate 8,2,"+"
IfEnd
Dsz C
Goto 0
Locate 8,2," "


羽根が回る時間は、カウンターCで決まり、今はCが50に固定されています。反射ゲームでは、羽根が回る時間をランダムにします。



Chapter 2-2
ランダム関数を使う

fx-5800Pには、Ran#RanInt# の2つのランダム関数が用意されています。

Ran#
0~1の間のランダムな小数を返します。
羽根を回す時間は、整数のカウンタC をランダムに設定する必要があります。そのためにはRan#が返す小数から整数を作る必要があります。例えば、2桁の整数を得るには、Int(100Ran#) とします。

RanInt#(L,H)
整数 L 以上、整数 H 以下の範囲でランダムな整数を返します。
羽根を回す時間は、整数のカウンタC をランダムに設定したいので、整数を返す RanInt#( ) 関数を用いる方がシンプルです。そこで、2~50の間の整数をランダムに得て、それをカウンタC に使うことにします。

そこで、50→C を RanInt#(2,50)→C  に置き換えます。

RanInt#(2,50)→C
Lbl 0
If C-4Int(C÷4)≦1
Then
Locate 8,2,"X"
Else
Locate 8,2,"+"
IfEnd
Dsz C
Goto 0
Locate 8,2," "



ところで、ここで 0~50 の間の整数を得ようとして、RanInt#(0,50) とすると、たまに問題が発生します。つまりバグです。

RanInt#(0,50) は、0を返すことがあります。すると、カウンタ C が0になります。

この場合、Locate 8,2,"×" に続いて Dsz C が実行されると、Cが1つ減って-1になります。ループが回るに従ってC はどんどん減ってゆき、0になることが決してありません。ループを脱出するのはC が0になる時です。つまり、いつまでたってもループが回り続けるので、ゲームになりません。

では、RanInt#(1,50) はどうでしょうか?
1を返すときは早過ぎて、表示と同時に消えてしまい、ゲームとしては鬼仕様。実際に実行すると分かります。

そこで、RanInt#(2,50) としました。

これで、アニメーションを表示し、ランダムな時間が経過すると消える部分(ゲームの前半)ができました。

プログラム名 CH2-2
RanInt#(2,50)→C
Lbl 0

If C-4Int(C÷4)≦1
Then
Locate 8,2,"X"
Else
Locate 8,2,"+"
IfEnd
Dsz C
Goto 0
Locate 8,2," "


続いてプログラムの後半部分を作ります。



Chapter 2-3
再びGetkeyDoループでカウンタ計測

プログラムの後半部分は、アニメーションが消えた後、キーを押すまでの反応時間を計測して、その結果を表示する機能です。

そのためには、何かキーが押されるまでループをまわし、ループの中でカウンタを増やしてゆく....これで反応時間を測定できます。キーが押されてループから抜けた時、カウンタの値を表示すれば、後半部分ができそうです。

今回は特定のキー、fx-5800P の [EXE] キーの隣にある [(-)] キーを押すことにして、このキーが押されたことの検出は Getkey を使います。そして、このキーが押されたらループから抜けて、その時のカウンタの数を表示するにコードを書いてゆきます。

さて、[(-)] キーのキーコードは、......Chapter 1 で作った Get Keycode プログラムで調べてみてください。DoループとGetkeyの使いこなしを、Chapter 1 で紹介しています。

或いは、プログラムライブラリ - キーコード取得 を参照してください。


先ずは、Do ループ:

Do
[処理]
LpWhile
[ループ継続条件]


ここで、ループ継続条件は、「押されたキーが [(-)] でないこと」 となります。従って、

Do
[処理]

LpWhile Getkey≠57

となります。


次に、このループ内でカウンタを増やす処理を追加します。

Chapter 2-2 で作った部分でもカウンタC を使っています。カウンタは0からスタートさせる必要があり、0→Cで初期化しておくのが常道です。

ところで、アニメーションが終わった時は、このカウンタC は必ず0(ゼロ)になります。そして、今作るループでは、カウンタを0からスタートすれば良いので、そのままカウンタCを使えば良さそうですが、今後のプログラム変更でどうなるか分かりません。

もし、Cがゼロから始まらないようなプログラム変更を行い、その時、ゼロに初期化していないことを忘れていると、バグに悩むことになります。そこで、明示的に 0→C で初期化しておきましょう。

ループがまわるたびにカウンターを1づつ増やせば良いので、Isz C を使います。

[(-)] キーを押すとループから抜けるので、ループから抜けた直後にカウンターCの値を表示します。

これで、今回の追加部分がきそうです。

プログラム名 CH2-3
RanInt#(2,50)→C
Lbl 0
If C-4Int(C÷4)≦1
Then
Locate 8,2,"×"
Else
Locate 8,2,"+"
IfEnd
Dsz C
Goto 0
Locate 8,2," "

0→C
Do
Isz C
LpWhile Getkey≠57
Locate 8,2,C


今回追加した部分は、赤文字で示しています。

実際にプログラムを走らせてみてください。ゲームの基本的動作を確認できます。



Chapter 2-4
例外処理・例外フラグ・Breakコマンド

これまでに作ったプログラムを走らせて遊んでみます。

すると、アニメーションが表示されている時から、[(-)] キーを連打し続けると、1 を表示させることができてしまいます。フライングができるわけです。これではマズイ。ゲームになりません。

フライングしたらペナルティーを与えるとゲームらしくなります。

そこで、今回はフライングしたことを検知するようにします。具体的なペナルティーについては、点数の付け方やゲーム全体のルールをに即して、あとで考えることにします。

アニメーションを表示するループを抜き出します。

Lbl 0
If C-4Int(C÷4)≦1
Then
Locate 8,2,"×"
Else
Locate 8,2,"+"
IfEnd
Dsz C
Goto 0


このループの中で、[(-)] キーが押されたら、それはフライングです。

フライングを検出したら、直ちにループから抜け出し、さらに「フライング」と表示するように、プログラムを変更しようと思います。

[(-)] キーのキーコードは、57なので、Getkeyの戻り値を監視して、57になればループを抜けるようにすれはOKですね。

このループは、カウンタC がゼロになればループから抜けるようになっています。これら2つの条件でループを抜けるように変更しなければなりません。

そこで、Lbl/Goto + Dsz ループとは別に、Doループ + Dsz を使うと、2つの条件でループを抜ける方法が、簡単に実現できるので、ループ自体を Doループに変更します。

Do
If C-4Int(C÷4)≦1
Then
Locate 8,2,"×"
Else
Locate 8,2,"+"
IfEnd
Dsz C
LpWhile Getkey≠57


Lbl 0 / Goto 0Do / LpWhile Getkey≠57 に置き換えただけですが、Cがゼロになる時とキーコード57になる時の2つの条件でループを抜けられます。とても簡単に変更できました。



では次に、ループを抜けた後、フライングがあったことをを表示する方法を考えます

本来のプログラムの流れとは別に、例外的なことが発生した時に対処することを「例外処理」と言います。今回のフライングを「例外処理」として扱うことにします。


例外処理は、本来のプログラムの流れとは別に、一括して処理するのが良い方法です。プログラムの構造を単純化し、流れを明確にするために役立ち、バグの発生を抑える効果があります。

複数の異なる例外に対しても、一カ所でまとめて処理するようにします(ゲームを完成させるためには、他の例外処理も必要になりそうです)。

そこで、例外が発生した時に、その例外の種類を「例外フラグ」という変数に記録し、例外1、例外2、例外3,といったように区別します。こうしておくと、例外を一括処理する時に、フラグの内容に応じて、場合分けをして処理を行います。

本来のルートからちょっと寄り道して例外処理を行って、それが終われば元のルートに戻る、これが例外処理です。



例外検知と例外フラグ

フライングは、ループで構成されるプログラムの流れを壊す例外事象なので、「フライングだゾ!」と表示し、さらにゲーム進行上の変数(点数など)を変更するのは、一括して 「例外処理」として扱うことにします。なお、例外が発生したことを記録しておく例外フラグの「フラグ」とは旗のことです。「例外が発生したゾ!」と旗を揚げるイメージですね。フラグの最初の概念は、0 か 1、つまり旗を下げるか挙げるかのどちらかでした。

しかし、例外フラグに整数を割り当てる方が便利です。例外が発生していない時は、フラグは0(ゼロ)、例外発生時には0以外の整数値を割り当てることで、例外の種類を区別して記録できます。

ここでは、変数 E を例外フラグとします。例外 = Exception (イクセプション )の頭文字をとって E としました。

・例外フラグ E は、例外が発生していない時はゼロと決めます。つまり最初はゼロに初期化します: 0→E

・フライングが発生した時は例外フラグ E の値を 1 とします:
1→E



すると、以下のように変更できます。

Do
If C-4Int(C÷4)≦1
Then
Locate 8,2,"×"
Else
Locate 8,2,"+"
IfEnd
Getkey=57⇒1→E
Dsz C
LpWhile E=0


※ 重要: 例外フラグ E は、プログラムの先頭部分で0に初期化しておく必要があります。

変更した部分を赤文字で示しています。Getkeyの戻り値が57の時、例外フラグ E に 1 を代入します。
ここで、If を使っても良いのでですが、より動作の速い ⇒(ジャンプ命令) を使いました。

LpWhile [ループ継続条件] の部分を、E=0 としました。例外が発生しなければ、例外フラグ E はゼロなので、この時はDoループを継続します。フライングと言う例外が発生して、例外フラグ E が 1 になると Doループを抜けます。

同時に、カウンタC がゼロになると、Dsz C のおかげで、LpWhile E=0 をジャンプして、次へ進んでループを抜けられます。


今後、フライング以外に新たな例外に対応する必要になれば、例外フラグ E に、2、3...と別の整数を割り当てます。その場合でも、Eは 0 以外ですから、ループを抜けられます。

以上をまとめます。

0→E
RanInt#(2,50)→C
Do
If C-4Int(C÷4)≦1
Then
Locate 8,2,"×"
Else
Locate 8,2,"+"
IfEnd
Getkey=57⇒1→E
Dsz C
LpWhile E=0
Locate 8,2," "



そして、キーを押すまでの反応時間を測定して表示を行う以下の処理が続きます。

0→C
Do
Isz C
LpWhile Getkey≠57
Locate 8,2,C



さて、フライングをした場合は、反応時間の計測は不要で、直ちにこのブロックを通り抜けて欲しい。

そこで、フライングした時、つまり例外フラグEが1の時に、直ちにDoループを抜けるようにします。そこで、CasioBasicに備わっているBreakコマンドを使います。Breakコマンドはループから抜けだすコマンドです。


※ CasioBasicコマンドリファレンス
      - Break



そこで、以下のようにプログラムを変更します(赤文字が変更部分)。

0→C
Do
E=1⇒Break
Isz C
LpWhile Getkey≠57
Locate 8,2,C

If E=1
Then
Locate 3,2,"FALSE START"
IfEnd

Doループの中では、Eが1 ならば Break を実行して、ループから抜けます。

Doループが終われば、例外処理を一括して行う部分です。Eが1ならは「フライング」と表示を行います。

※ フライングは和製英語なので英語としては通じません。そこで FALSE START (不正スタート)と表示することにします。


これで、フライング検知と表示を盛り込むことができました。

プログラム名 CH2-4

0→E

RanInt#(2,50)→C
Do
If C-4Int(C÷4)≦1
Then
Locate 8,2,"×"
Else
Locate 8,2,"+"
IfEnd
Getkey=57⇒1→E
Dsz C
LpWhile E=0
Locate 8,2," "

0→C
Do
E=1⇒Break
Isz C
LpWhile Getkey≠57
Locate 8,2,C

If E=1
Then
Locate 3,2,"FALSE START"
IfEnd


改行を使って機能ブロックに分けています。CasioBasicでは、このように改行を入れてもエラーになりません。

1ブロック: 初期化
2ブロック: アニメーション表示(フライング検出)
3ブロック: 反応時間計測と表示
4ブロック: 例外処理(フライング表示)



次回は、これまで作ったプログラムを繰り返すようにし、さらに得点を計算させて、ゲーム仕様に近づけてゆきます。



つづく...


CasioBasic入門11 / 目次



応援クリックをお願いします。励みになるので...
にほんブログ村 IT技術ブログ 開発言語へ




keywords: fx-5800PCasioBasicプログラミング入門プログラム関数電卓

リンク集 | ブログ内マップ
関連記事

テーマ : プログラム関数電卓
ジャンル : コンピュータ

Casio Basic入門34

Casio Basic入門
<目次>

誤字脱字・記載ミスや分かりにくい表現は随時追記・修正します

最終更新:2015/01/25


 4. CasioBasicを使ってみる(続き)

Chapter 6

前回: Casio Basic入門33 を見る


◆ Chapter 6 の目標: プログラムを速くする
入力ボックスの改良と拡張

前回は、高速化したINPI Ver 2.0  を作りました。今回はこれを拡張して、0以上の小数に対応した入力ボックスを作ります。プログラム名は INP です。


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-3
処理速度を意識して機能拡張する

前回作成した INPI Ver 2.0 (ファイル名: INPI)

INPI_Ver20d_src_1 


このプログラムを基に、小数入力(但し0以上の正の小数)ができるように拡張します。


小数点の扱い

先ずは、小数点のキー [・] を入力するところから、全ては始まります。

[・] キーが押されたことを検出するには、Getkey コマンドの戻り値が 26 かどうかを判定します。

先ず、INPI のプログラム構造を確認しておきます。

 [初期化処理]

 [初期表示]

 Do


 [キーコード取得]

 If (テンキーの場合):Then

  [テンキーの時の処理]

 Else If (DELキーの場合):Then

  
[DELキーの時の処理]


 IfEnd:IfEnd


 LpWhile K≠47


 [後処理]

 Return


これを拡張して、キーコード 26 の [・] キーが押された時の処理を追加すると、次のプログラム構造になります。


 [初期化処理 1]

 [初期表示]

 Do


 [キーコード取得]

 [初期化処理 2]

 If (テンキーの場合):Then

  [テンキーの時の処理]

 Else If (小数点の場合):Then

  [小数点の時の処理]


 Else If (DELキーの場合):Then

  
[DELキーの時の処理]


 IfEnd:IfEnd

 IfEnd

 LpWhile K≠47


 [後処理]

 Return


----------

赤文字で示したのは、今回追加する処理です。さらに  を付けたブロックは修正が必要と思われます。


小数点キーが押された時の処理

小数点以下の桁数(小数点自身を含む)を新しい変数 F で管理することにします。例えば、現在入力されている数が整数の時は F=0、小数点のみが入力されている時は F=1、小数部が1桁の時は F=2、小数部が2桁の時は F=3、小数部が5桁の時は F=6 とします。F の初期値は、0 とします。従って、[初期化処理 2] で、

0→C:0→F

としておきます。[初期化処理 2] は、キー入力ループで必要な変数の初期化を行うブロックです(ブロックと言っても1行で書けますが...)。

小数点キーが押された時の処理は、以下のようになります。[・] キーのキーコードは 26 なので、

Else If K=26
Then
If F=0:Then
Locate X+C,Y,"."
Isz C:Isz F
IfEnd


ところで、Else If  で If が1つ増えたので、LpWhile K≠47 の前に忘れずに IfEnd を1つ追加しておく必要があります。
 ⇒ Casio Basicコマンドリファレンス - If ~ Then ~ IfEnd




上のコードを1行づつ追いかけてシミュレーションしてみます。今、12 が入力されている状態で、小数点キーを押すと、上のコードが実行されます。これが実行された後は、

C=3
F=1
・ 表示は、12.

となることが確認できます。

3桁入力されているので、C=3 は正しく、小数点を含めて小数点以下の桁数 F=1 となり、画面表示は 12 の後に小数点が付加されているので、このシミュレーション結果から、この部分が正しく動作することが分かりました。

さらに、小数 [・] を入力する場合を考えます。1つの数に小数点が2つ以上あってはいけないので、小数点は1回しか入力できないようにすべきです。上のプログラムコードでは、小数点を追加する処理は、If F=0:Then ~ IfEnd の中に書かれています。1度小数点を入力すると、 F は 1 以上になるので、If F=0:Then ~ IfEnd の中を実行することはありません。これで、小数点が2つ以上入力されることを防いでいます。




テンキーが押された時の処理

INPI のプログラムの中で、テンキーが押された時に実行される部分は、以下のようになっています。

If (I≧1 And I≦9) Or K=25
Then
If C<D:Then
10Z+I→Z
Locate X+C,Y,I
Isz C
IfEnd

・・・
IfEnd

これを小数入力時に対応できるように修正してゆきます。





12. が入力されている状態で、3 をキー入力する直前の状態は、

C=3   (3桁入力されている)
F=1   (小数点を含んで小数点以下1桁)
Z=12.
・ 表示: 12.

となっていますので、ここで 3 を入力すると I=3 となります。この状態で Z の値を正しく更新するには、

12 + 0.3

となれば良いので、これを実現するためには、

Z+I÷10→Z   ・・・ (1)

であれば良さそうです。

(注意)加算 (+) や減算 (-) よりも 乗算 (X) や 除算 (÷) が優先されるので、この式は、
Z+(I÷10)→Z
と同じです。少しでもプログラムを速く動かすために、余計な処理を省きます。

さらに、12.3 が入力されている状態で 4 をキー入力する直前の状態は、

C=4   (4桁入力されている)
F=2   (小数点を含んで小数点以下2桁)
Z=12.3
・ 表示: 12.3

となっています。ここで 4 を入力すると I=4 となります。このとき Z の値を正しく更新するには、

12.3 + 0.04 

となる計算が必要で、

Z+I÷100→Z   ・・・(2)

であれば、Z が 12.34 となります。ここで、上の (1)(2) に出てくる除数 10 や 100 は、実は 10F とすれば良いだろう、と言う予測をしています。ところで、小数の桁数が増えるので、どこかで Isz F を行って F を1つ増やす(インクリメント)しないといけないのですが、これを (1) や (2) の前か後か、どちらで実行すれば良いのかが、計算をシンプルにして処理を速くするためのポイントです。結果として、Isz F(1)(2) の後で実行すれば、10100 の代わりに 10F を使えるので、これが最適だと分かります。

もし、Isz F(1)(2) の前に実行すると、10F ではなくて 10(F-1) を使う必要があり、F-1 という余計な処理が出てくるので、処理速度向上には良くないわけです。

10F をプログラムで表現するには、10^(F) となります。
具体的には、[1] [0] [x] [ALPHA] [F] [ ) ] の順にキー入力します。

そこで、入力値 Z を更新する処理は、

Z+I÷10^(F)→Z
Isz F


とすれば良いことが分かります。

整数入力の場合は、

10Z+I→Z

となり、これは既に作っています。

処理速度向上を目的にしているので、分数入力時か整数入力時かに関わらず、1つの式で処理できれば理想的です。ところが、これら2つの式を見比べても、効率的に1つの式にまとめられそうにありません。



そこで、整数入力時か小数入力時かによって、これらのいずれかを実行させるような条件分岐処理を行うことにします。整数入力時か分数入力時かは、F の値を見れば分かります。F=0 の場合は整数入力時、F=0 で無い場合は小数入力時を意味します。実は、このようになるだろうと予測して、変数 F の初期値を 0 にしたわけです。

さらに言えば、F0 かそうでないかの条件分岐には、F⇒ と言う条件ジャンプ命令か、If F:Then という記法を使え、これらは共に処理速度が速いことが分かっているので、整数入力時に F=0 とし、小数の桁数に応じて F をインクリメントすると決めたわけです。その結果、F は「小数点を含めた小数点以下の桁数」という、普通の数学とはチョット異なる定義になっています。

表示については、INPI Ver 2.0 と同様に

Locate X+C,Y,I 

とすれば良く、整数入力時と小数入力時で共通して使えます。Z の更新と表示を独立させるという、ロジック変更の効果がここに現れています(実は、予めこれを狙ってロジックを変更したのですが...)。


では、テンキー入力時の処理を、具体的に作ります。そこで、INPI Ver 2.0 のテンキー入力時の処理を以下のように修正します。

INPI Ver 2.0 のテンキー入力時の処理
If (I≧1 And I≦9) Or K=25
Then
If C<D:Then
10Z+I→Z
Locate X+C,Y,I
Isz C
IfEnd


INP Ver 2.0 のテンキー入力時の処理
If (I≧1 And I≦9) Or K=25
Then
If C<D:Then
If F:Then
Z+I÷10^(F)→Z
Isz F
Else

10Z+I→Z
IfEnd
Locate X+C,Y,I
Isz C
IfEnd


追加部分を赤文字で示しました。

赤文字の一番上の行は、If F:Then となっています。これは、If F>0:Then と同じですが、処理速度は If F:Then の方が2倍速いことが分かっているので、(予定通りに(^^;)処理速度が速くなる記法を採用します。
 ⇒ Casio Basicコマンドリファレンス - If ~ Then ~ IfEnd 
 ⇒ fx-5800P:変数アクセス、比較・論理演算、条件分岐の速度比較


DELキーが押された時の処理

INPI Ver 2.0 では、整数の最下位の数字を消せば良いので、10 で割ってから整数部を取り出すことで、Z の更新をしました。
例えば、1234 (=Z) と入力されている時、10 で割ると 123.4、 これの整数部を Int( ) 関数で取り出すので、

Int(Z÷10)→Z

としました。

小数入力時は、どうなるか?
例えば、今 12.34 (=Z) と入力されている時、最下位の 4 を消して、12.3 にしたいわけです。

Z (=12.34)10 倍して、123.4 とし、整数部を取り出して 123 にしてから、これを 10 で割ると 12.3 になります。具体的なコードで表すと、

Int(10Z)÷10→Z   ・・・(1)

となります。

12.34 と入力されていて、[DEL] キーを押す直前の状態は、

C=5   (5桁入力されている)
F=3   (小数点を含み、小数点以下3桁)
Z=12..34
・ 表示: 12.34

となっています。


シミュレーション 1

[DEL] キーで末尾を1桁減らすのですから、小数点以下の桁数も1つ減ります。そこで、先に F-1→F を実行してみます。
すると、F=2 となります。実は、(1) の2カ所に出てくる 10 は、上と同様に10F で処理できないかと予想しています。ところが、10F だと F=2 なので、100 になってしまい、ここは 10(F-1) (=10)でないとうまくゆきません。

F-1→F
Int(10^(F-1)Z)÷10^(F-1)→Z
Locate X+C,Y,">"
C-1→C


で良さそうで、確かに Z=12.3 となります。高速処理を目的にしているので、上と同様に (F-1) といった余計な計算をさせたくありません。ここで、10F を採用すると、上のテンキー入力所の処理では 10(F+1) が必要になり、両立が難しいことが分かります。どちらを取るか、の選択が必要です。

テンキーを入力時の高速化を優先させることにします。テンキー入力と [DEL] キーの頻度を考えると、テンキー入力が圧倒的に頻繁に実行される筈と考え、テンキー入力時の高速化を優先させる方が、入力時の体感も良くなると考えたわけです。


[DEL] キーによる削除処理での表示は、最下位の 4 は最上位から5桁目 (C=5) なので、4 が表示されているところに > を上書きします。

Locate X+C,Y,">"
C-1→C


で表示の更新を行ったのち、桁が1つ減るので C を1つ減らせば良いですね。


シミュレーション 2

続けて、[DEL] キーを押して、12.3 の末尾の 3 を消去するところをシミュレーションします。 
12.3 と表示されていて、[DEL] キーを押す直前には、

C=4   (4桁表示されている)
F=2   (小数点を含んで、小数点以下2桁)
Z=12.3
・ 表示: 12.3

となっています。

ここで、[DEL] キーを押した時の処理は、上と同様に、

F-1→F
Int(10^(F-1)Z)÷10^(F-1)→Z
Locate X+C,Y,">"
C-1→C


ここで、

Int(10^(F-1)Z)÷10^(F-1) = Int(1xZ)÷1 = 12 

となります。

なお、今 F=1 なので、10^(F-1) = 10(F-1) = 100 = 1 となります。何をゼロ乗しても、その結果は 1 と定義されているので、10のゼロ乗も 1 になり、これで正しいことがわかります。

さて、表示については、最上位から4桁目 (C=4) の 3 の上に > を上書きし、桁が1減るので、C-1→C としておきます。

これで、入力値 Z の更新と表示が正しく動作することがわかりました。


シミュレーション 3

続いてさらに [DEL] キーを押して、小数点を削除する際、その直前の状態は、

C=3   (小数点を含んで3桁表示されている)
F=1   (小数点を含んで、小数点以下1桁)
Z=12
・ 表示: 12.

となっています。

先ず、F-1→F が実行され、F=0 となります。小数点がなくなれば、F=0 になるのは正しい動作です。

続いて以下が実行されます。

Int(10^(F-1)Z)÷10^(F-1)→Z

F=0 なので、この計算は以下のようになります。

Int(10^(F-1)Z)÷10^(F-1) = Int(10F-1 x Z) ÷ 10F-1
= Int(10-1 X Z) ÷ 10-1 = Int(0.1 x Z) ÷ 0.1 = Int(1.2) ÷ 0.1
= 1 ÷ 0.1 = 10 
[Z=12 を代入]

となり、本来 Z = 12 となるべきところ、Z=10 となるので、これはマズイ!

今は、小数点を削除するだけで、Z の値は変化させる必要がありません。そこで、F=0 の時には、この計算を行わないようにすれば、良いので、この Z の更新処理を、

F⇒Int(10^(F-1)Z)÷10^(F-1)→Z

と変更すると、うまくゆきます。F=0 でない時はこの計算を実行し、F=0 の時は実行しません。これも、F をうまく定義したことの効用です。

F⇒ 命令の処理時間は 4.1ミリ秒、If A の処理時間は 6.2 道秒なので、F⇒ が 1.5 倍速いことが分かっているので、 命令を使っています。
 ⇒ fx-5800P:変数アクセス、比較・論理演算、条件分岐の速度比較 の 表参照

なお、F が負の数になると、上の F⇒ を使った Z の更新がおかしくなる心配があります。例えば、F = -1 の時は、上の Z の更新計算を実行することになり、計算結果がおかしくなりそうです。F を1つ減らす処理は、[DEL] キーが押された時の削除処理でしか行いません。そこで、F0 になれば、さらに1つ減らす処理を行わないようにすれば良いので、F の値の管理をきちんと行うことにします。





シミュレーション 4

さらに続いて [DEL] キーを押した時をシミュレーションします。
[DEL] キーをオス直前の状態は、以下のようになります。

C=2   (2桁表示されている)
F=0   (小数はなく、整数になっている)
Z=12
・ 表示: 12

この状態で、[DEL] キーで削除を行うと整数を削除することになるので、INPI Ver 2.0 で作った処理をそのまま適用すれば、問題ありません。小数からの削除を行うのか整数からの削除を行うのかは、F の値で条件分岐します(テンキー入力時の処理と同様です)。条件分岐した整数の末尾を1つ減らす処理で、F-1→F が含まないようにします。こうすれば、F0 より小さくなることはありません。

以上から、小数からの末尾削除については、以下のコードで良いことをシミュレーションから確認できました。

F-1→F
F⇒Int(10^(F-1)Z)÷10^(F-1)→Z
Locate X+C,Y,">"
C-1→C



整数からの削除と小数からの削除

さて、整数から最下位(末尾)の桁の削除する時と、小数からの最下位(末尾)の桁を削除する時では、Z の更新のために用いる計算が以下のように異なります。

整数からの末尾削除時は、

Int(Z÷10)→Z

小数からの末尾削除時は、

F⇒Int(10^(F-1)Z)÷10^(F-1)→Z

これらを、1つの式(処理)にまとめるのは難しそうなので、整数からの削除と、小数からの削除は、処理を分岐させることにします。

今入力されている数が整数か小数かは、F の値で判別できます。F=0 の時は整数、F>0 の時は小数なので、これを処理分岐の条件に使うことにします。


DEL キーを押した時の処理全体

さて整数のみを扱う入力ボックス INPI Ver 2.0 では、  [DEL] キーを押した時の処理は、以下です。

Else If K=34
Then
If C:Then
Int(Z÷10)→Z
C-1→C
Locate X+C,Y,">"
IfEnd


これに赤文字部分を追加して、小数対応すると、以下のようになります。

Else If K=34
Then
If C:Then
If F:Then
F-1→F
F⇒Int(10^(F-1)Z)÷10^(F-1)→Z
Else
Int(Z÷10)→Z
IfEnd
C-1→C
Locate X+C,Y,">"
IfEnd


Cつ減らす処理と、Locate コマンドによる削除の結果の表示処理は、修正なしにそのまま使えます。ロジック変更の狙い通りです。

なお上と同様に、If F>0:Then の代わりに、速度が2倍近い表記 If F:Then を使っています。 
 ⇒ fx-5800P:変数アクセス、比較・論理演算、条件分岐の速度比較 の表参照



上記以外の処理は、INPI Ver 2.0 と全く同じで問題ありません。今回作った0以上の小数入力に対応した入力ボックスを INP Ver 2.0 とし、プログラム名を INP とします。

INP_Ver20_src_1 



INP Ver 2.0 の仕様

高速化のため、Z の更新とリアルタイム表示を完全に独立させるようにロジックを変更した結果、INP Ver 2.0 は、入力仕様が Ver 1.2 と異なっています。

Ver 1.2 では、最上位に 0 や 小数点の入力をさせないようにするために余計は処理を加えていました。Ver 2.0 ではこの処理を省いています。

fx-5800P を普通の関数電卓として使う時、0123 や .123 と入力すると、そのまま表示されます。そして [EXE] キーを押せば 123 や 0.123 と表示されます。これは、fx-5800P の関数電卓としての仕様です。

INP Ver 2.0 でも同様で、例えば 0000123 や .0000123 と入力すると、そのまま表示されます。そして [EXE] キーで確定すると Z123 や 0.0000123 と格納され、メインルーチンに戻ります。

さらに、Z の更新とリアルタイム表示を独立させた結果、入力ボックスの桁数制限がプログラム上不要になりました。しかし、ハードウェアに依存して Locate コマンドによる制限は残ります。fx-5800P で使う場合は、Locate コマンドの制限が1行あたり16桁なので、入力ボックスの桁数は最大16桁に制限されます。実際の入力ボックスの桁数は、表示開始位置から右端までの桁数となりますので、最大16桁と言う言い方になります。fx-9860GII に移植して使う場合は、Locate コマンドの制限が1行あたり21桁なので、入力ボックスの桁数は最大21桁に制限されます。こうして、INP Ver 2.0 は、ソフトウェアとしての柔軟性と他機種への移植性が得られました。


使用方法

メインルーチンから以下の書式で呼び出します。

△→X:△→Y:△→D:△→E
Prog "INPI"
Z→▽


は、任意の数、但し使用機種の画面範囲内に収まるように設定する。
  参考 fx-5800P: 1≦X≦16、1≦Y≦4、X+D≦16


- X: 入力ボックス表示開始桁
- Y: 入力ボックス表示開始行
- D: 入力ボックス桁数
- E: 入力ボックスインジケータの選択
   E=2: 画面右下に <EXE>:ENTER と表示
   E=1: 画面右下に ▶E と表示
   E=(上記以外): インジケータを表示しない
- Z: 入力ボックスで確定した数値が代入される

入力ボックス内部では、上記5つの変数以外に、C、I、F、配列変数を用いています。以上9個の変数は、入力ボックスを呼び出すたびに代入されている数値が変更されます。メインルーチンでこれら変数を使っても問題はありませんが、これら9個の変数は入力ボックスを呼び出すたびに変更されても良い変数として使ってください。特に配列変数は、入力ボックス内で87個の領域確保を行い、メインルーチンに戻る前に領域解放を行いますので、それを念頭に置いてメインルーチンで使用してください。

なお、INPI に比べて、INP では、使う変数が1つ増えています(変数 F が追加)。


キー入力回数の測定

実際に前回と同じ方法で、INBOX TEST プログラムを使って、今回も入力応答性を実際に調べてみました。

比較対象は以下の2つのバージョン:

- INP Ver 1.2
INPI Ver 1.2 のロジックのまま拡張したもの。ソースコードは割愛します。

- INP Ver 2.0
今回作成した 新しいロジックの INPI Ver 2.0 を拡張したもの

なお、整数入力と小数入力では、処理を分岐させているので、整数入力と小数入力の2通りで比較測定を行います。


整数入力時の応答性

評価プログラム INBOX TEST を以下のようにします。
ここで、INP Ver 1.2 のプログラム名は INP12Ver 2.0 のプログラム名を INP とし、入力ボックスの桁数を 10→D により10桁にします。

プログラム名: INBOX TEST
Norm 2
0→Z:0→S:0→T:0→U
Locate 1,1,"1:"
Locate 1,2,"2:"
Locate 1,3,"3:"

Lbl 0

-1→M
Do
Getkey→K
While K=0
K=35⇒1→M
K=36⇒2→M
K=37⇒3→M

Locate 1,4,"        "
  [スペース16個]

If M=1:Then
3→X:1→Y:10→D:2→E
Prog "INPI":Z→S
Else If M=2
Then
3→X:2→Y:10→D:2→E
Prog "INP12":Z→T
Else If M=3
Then
3→X:3→Y:10→D:2→E
Prog "INP":Z→U
IfEnd:IfEnd
IfEnd

Locate 1,4,"Z="

Locate 3,4,Z

Goto 0


比較は、メニュー2INP Ver 1.2メニュー3INP Ver 2.0で行います。

測定方法は、前回と同じ方法です。
それそれの入力ボックスを開いた状態で、タイマースタートと同時にいずれかのテンキーを押しっぱなしにします。そして10桁目に表示された瞬間にタイマーを止め、時間を読み取ります。

表1 整数入力時の応答速度比較
プログラムバージョン10桁入力所要時間1秒あたりの入力回数
INPI Ver 1.2 整数入力2.2秒4.5回
INPI Ver 2.0 整数入力1.4秒7.1回
INP Ver 1.2 整数入力2.6秒3.8回
INP Ver 2.0 整数入力1.6秒6.2回
※ この測定は、非常に個人差があるので、比較の意味しかありません。

参考までに、INPI Ver 1.2Ver 2.0 の結果も併記しました。

表1から、以下が分かります。
INP Ver 2.0 は、Ver 1.21.6 倍の入力応答性になる。
INP Ver 2.0 は、INPI Ver 2.0 に比べて、87% 程度の入力応答性になる。

実際に色々な数を入力してみると、かなり速く入力してもキーの取りこぼしが無く、十分満足できる応答性になっていると思います。


小数入力時の応答性

INBOX TEST では、D→11 として入力ボックスの桁数を11桁に設定します。そして、入力ボックスを開き、先に小数点を入力しておきます。すると残り10桁なので、タイマースタートと同時に、いずれかのテンキーを押しっぱなしにし、10桁目が表示された瞬間にタイマーを止め、時間を読み取ります。

なお、INP Ver1.2 は桁数制限があるので10桁までしか入力できません。一方で、新しいロジック(仕様)の Ver 2.0 は1行に収まる限り桁数の制限はありません。Ver 1.2十分遅いことが明かなので、INP Ver 2.0 のみを測定することにします。

そこで、INBOX TEST を以下のように、赤文字部分を変更します。そして、メニュー2 を測定に使用します。

プログラム名: INBOX TEST
Norm 2
0→Z:0→S:0→T:0→U
Locate 1,1,"1:"
Locate 1,2,"2:"
Locate 1,3,"3:"

Lbl 0

-1→M
Do
Getkey→K
While K=0
K=35⇒1→M
K=36⇒2→M
K=37⇒3→M

Locate 1,4,"        "
  [スペース16個]

If M=1:Then
3→X:1→Y:10→D:2→E
Prog "INPI":Z→S
Else If M=2
Then
3→X:2→Y:11→D:2→E
Prog "INP":Z→T
Else If M=3
Then
3→X:3→Y:10→D:2→E
Prog "INP":Z→U
IfEnd:IfEnd
IfEnd

Locate 1,4,"Z="

Locate 3,4,Z

Goto 0



表2 小数入力時の応答速度比較
プログラムバージョン10桁入力所要時間1秒あたりの入力回数
INP Ver 2.0 整数入力1.6秒6.2回
INP Ver 2.0 小数入力1.8秒5.5回
※ この測定は、非常に個人差があるので、比較の意味しかありません。

参考までに、INP Ver 2.0 の整数入力の結果を併記します。

表2から、以下が分かります。
INP Ver 2.0 の小数入力時の応答性は、整数入力時の88%程度。

実際に色々な数を入力してみると、かなり速くキーを打っても、取りこぼしは発生せず、ストレスのない実用に十分耐える応答性が得られたと思います。


Lbl/Goto ループと Do/LpWhile ループ

INPI Ver 1.2 では、入力ボックスで入力が確定されるまでのループに Lbl/Goto ループを使っていました。一方、INPI Ver 2.0 を作る時は、Do/LpWhile ループに変更しました。プログラムが長く、複雑になると Lbl/Goto ループの処理時間が長くなることが経験的に分かっているので、このような変更を行いました。但し、INPI Ver 2.0 では、このループの変更により応答性の顕著な変化は見られません。

INP Ver 2.0 では、INPI Ver 2.0 に比べてプログラムの処理がより長く、制御構造がより複雑になったので、試しに Do/LpWhileLbl/Goto に換えて、応答性の比較測定を行ってみました。

表3 INP Ver 2.0 の Lbl/Goto と Do/LpWhile の応答性への影響比較
入力モードループ種別10桁入力所用時間1秒あたりの入力回数
整数入力
Lbl/Goto1.9秒5.2回
Do/LpWhile1.6秒6.2回
小数入力
Lbl/Goto2.1秒4.7回
Do/LpWhile1.8秒5.5回
※ この測定は、非常に個人差があるので、比較の意味しかありません。

表3から、INP Ver 2.0 では、Lbl/Goto ループによる応答性低下は、整数入力で 15%、小数入力で 16% になり、Do ループを用いた高速化の効果が明らかに現れました。

Goto 命令は ジャンプ先の Lbl を検索する際に、プログラムの一番最初から順に検索を行うことは、簡単な実験から分かっています。Goto 処理では Casio Basic 特有の特別な処理が行われていることが分かってて、制御構造が複雑になると処理速度が低下します。今回はその効果が明らかに現れています。

ある程度長くて制御構造が複雑なプログラムでは、高速化を考える際には、Lbl/Goto ループを使わず、Do ループや While ループを使うべきです。 Goto の特殊な処理の詳細は、"楽屋裏 - Dsz によるループ脱出" に書いたので、ご興味があればご覧ください。




プログラム高速化の指針

これまでのところ、Casio Basic プログラムを高速化するには、少なくとも以下の指針が有効だと言えます。

1) 比較演算、論理演算、配列変数へのアクセスを極力減らすこと

2) プログラムロジックの見直しによる、余計な処理の排除

3) ループには、Lbl/Goto ループを使わず、Do ループや While ループを用いること



今回作った INP Ver 2.0 を基に、次回から正負の小数入力ができるように拡張してゆきます。



つづく...

Casio Basic入門35 / 目次




応援クリックをお願いします。励みになるので...
にほんブログ村 IT技術ブログ 開発言語へ





keywords: fx-5800PCasioBasic、入力ボックス, プログラミング入門プログラム関数電卓

リンク集 | ブログ内マップ



関連記事

テーマ : プログラム関数電卓
ジャンル : コンピュータ

最新記事
検索フォーム
最新コメント
カテゴリ
C# (3)
Online Counter
現在の閲覧者数:
プロフィール

やす (Krtyski)

Author:やす (Krtyski)
since Oct 30, 2013


プログラム電卓は、プログラムを作って、使ってナンボ!

プログラム電卓を実際に使って気づいたこと、自作プログラム、電卓での Casio Basic, C.Basic そして Casio Python プログラミングについて書いています。

なお管理人はカシオ計算機の関係者ではありません。いつでもどこでもプログラミングができるプログラム電卓が好きな1ユーザーです。


写真: 「4駆で泥んこ遊び@オックスフォード郊外」

リンク
月別アーカイブ
Sitemap

全ての記事を表示する

ブロとも申請フォーム

この人とブロともになる

QRコード
QR