したらばTOP ■掲示板に戻る■ 全部 1-100 最新50 | |
レス数が1スレッドの最大レス数(1000件)を超えています。残念ながら投稿することができません。

おちゃめくらぶ掲示板

2461御茶目菜子:2015/09/08(火) 22:20:20
「簡易関数電卓QSP」はQSPで出来たOSもどきだった!?
先日作った簡易関数電卓QSPに拡張関数ライブラリ3、4、5を用意してみたにょ。
もちろん、どれもQSPにょ。(3のみ厳密な意味のQSPではないけど)
https://miiverse.nintendo.net/posts/AYIHAAAEAAASVZKcd5Cdyg

簡易関数電卓についてまだ読んでない人は前回書いたこちらをまず読んでおくのをオススメ
するにょ。(今見たら簡易関数電卓の本体のリストは短縮できる場所がたくさん見つかったけど
「プログラムを変更せずに機能拡張ができる」というのを強調するためあえて短縮できる箇所は
そのまま放置している)

 ◎プチコン3号で「簡易関数電卓QSP」を作ってみた
  http://6407.teacup.com/ochame/bbs/4990
 ◎簡易関数電卓QSPのプレイ(?)動画
  https://www.youtube.com/watch?v=4bCe7czTHfg
 (序盤40秒までが電卓本体のみの機能でそれ以降が拡張機能による動作)

というわけで、まずは今回の拡張関数ライブラリの紹介や動作の仕組みの解説を行って
みるにょ。
前回は簡易関数電卓を作ったいきさつなどがメインだったためライブラリの解説はかなり
端折ったけどライブラリを作ることである程度自分自身で作り慣れてきたため前回は
できなかった戻り値を文字列にしたり、戻り値を無しにしたりという関数(いずれも擬似的に
そのような動作になるようにしているだけ)も用意してみたにょ。

 ◎拡張関数ライブラリ3
 FRAC()関数・・・値を分数に変換する関数
 PS()関数・・・・高精度なPSTR$を使って計算結果を表記する関数

FRAC()を使えば分数計算が可能になるわけだけどこれは原理は単純で与えられた数値に
近い値を持つ分数を分母1から100000(=1E5)までの範囲で調べているだけにょ。
この場合は(ROUNDで丸め処理を行った場合)特定の分母に対応する分子は1つで済み
最大でも100000回ループを回せばいいのでプチコン3号(New3DS)ならば少しの時間
(最大でも0.5秒くらい)で済むにょ。
昔ポケコン(PC-E500)で同様のアルゴリズムで分数計算をさせた時には分母が最大999
でも(最大ではなく平均で)数秒かかったけどさすがにポケコンと比べて演算速度が
ワーストケースであっても1000倍弱、普通に数1000倍、A=A+1ならば何と1万倍以上速く
パソコンとスパコンくらいの速度差があるためこの単純探索方法でも全く問題はないと
感じたにょ。(ただし、旧3DSだと最大で2秒くらいかかってしまう)
ちなみに分母の最大桁数は簡単に変更できるのでさらに多くの桁数が必要(もしくは
こんなに要らない)ならば下記の方法でやってみてにょ。

このFRAC()関数は原理は単純とはいえ、この関数1つで小数を分数に変換したり、約分を
したり、分数計算をしたりと非常に使えるものになっているにょ。
ちなみに特定の分数に近い値かどうかのしきい値(許容誤差)としては与えられた数値と
特定の分数との差分の絶対値で判断すればいいのだけどその際には「1/最大分母」の2乗
よりも小さな値(つまり、1E5が分母の上限ならば1E-10よりも小さい1E-11)をしきい値に
設定する必要があるということにょ。(余裕を持たせるため1E-11ではなく1E-12をしきい値と
した)
こうしないと、平方根や円周率のような無理数でもたまたま近い分数で表記されるという
ことが起きてしまうにょ。(1E-12というしきい値を1E-10に変更してFRAC(PI())を実行
すればこの意味が分かると思う)
例えば1E6が分母の上限ならばしきい値は1E-13にすればたまたま分数で表記されるという
可能性をほぼ0にできるので分母の上限を変更したいという人はこれを元に判断すれば良い
と思うにょ。(1E4が分母の上限ならば1E-9をしきい値にする)

確かにプチコン3号の有効桁数は16桁弱あるので分母の桁数に関係なく1E-15くらいをしきい
値に設定してもいいのだけど分子が分母と比べて極端に大きい場合は有効桁数の問題によって
本来は分数で表記できるはずのものができなくなってしまうためしきい値は必要最小限に
止めておいた方が変換可能な数値の範囲が広がるにょ。
このFRAC()関数では分子は2147483647(=2の31乗-1)まで対応しているので帯分数で表した
時に整数部分が2147までは少なくとも分数に変換が可能にょ。(この簡易関数電卓は帯分数
には対応しておらず仮分数のみの対応だけど)

ちなみにFRAC()関数から最小公倍数や最大公約数を求めることは可能にょ。
12と18の最小公倍数を求めたい場合にはFRAC(1/12+1/18)とすれば5/36となり、最小公倍数が
36であることが分かるにょ。
144と192の最大公約数を求めたい場合にはFRAC(144/192)とすれば3/4となるため144/3もしくは
192/4を求めれば最大公約数が48であることが分かるにょ。
まぁ通分や約分には最小公倍数や最大公約数が必要だから通分や約分の結果から逆算もできる
という小学生レベルの算数知識で理解できると思うにょ。
もっとも、最小公倍数や最大公約数を求める機会が多い場合や100000を超える値を求めたい場合
などはLCM()関数やGCM()関数を自作すれば良いだけにょ。

PS()関数を用意しなくても簡易関数電卓本体部分のリストでSTR$をPSTR$に書き換えれば済む
だけのこととはいえPSTR$は無駄に高精度表示をしてしまい場合によっては計算結果が見づらく
なってしまうので普段はSTR$を使い必要な場合のみPSTR$が使えるようにこの関数を用意した
というわけにょ。
FRAC()関数で分数が見つからなかった場合に高精度で小数を表示したいという場合にはPS()と
FRAC()をPS(FRAC(SQR(2)))のように併用して使用できるようになっているにょ。
併用する場合はFRAC(PS())ではダメでPS(FRAC())とする必要があるにょ。(PS()関数は
FRAC()命令によって分数表記となった場合、つまり、戻り値がない設定になった場合のみ
無効化しているため)


 ◎拡張関数ライブラリ4
 TMR()関数・・・タイマー機能
 STW()関数・・・ストップウォッチ機能

TMR()は何の変哲もない1秒単位のカウントダウンタイマーにょ。
引数には小数が設定できるためTMR(9.5)で9.5秒タイマーになるにょ。
タイマー機能は初心者でも簡単に作れるのだけどその際に私が気になってしまうのでは0秒の
扱いにょ。
変数Aに残りフレーム数が入っている場合に?FLOOR(A/60)とやってしまいがちだけどこれだと
59フレーム以下では0と表示されてしまうにょ。つまり、実際に終了するのは「0」が表示
されてから約1秒後にょ。(デジタルだとこれで違和感を感じない人もいるかもしれないけど
アナログ時計において秒針が12時の方向(0秒の位置)にあってそれから1秒へと動き出す
瞬間に本当の0秒を示すというのは明らかに不自然)
個人的には0が表示されたらすぐに終了して欲しいのでスタート時にはAに59を加算することで
0が表示された瞬間に表示するようにしているにょ。(最初に59を足すことで「59以下に
なったら終了する」という条件に変えてこれによってFLOOR(A/60)が0になった瞬間に終了が
可能になる)
これは私が以前作った「ラーメンタイマーQSP」でも同じにょ。

STW()は何の変哲もない100分の1秒ストップウォッチにょ。(正確には1フレーム単位で計測
するストップウォッチ)
唯一のアピールポイントとしては開始の基点時間を引数で設定できるという点にょ。
これはSTW(5)とすれば5秒からスタートするストップウォッチになるということだけどこれに
よって一旦停止した際のロスタイムを自由に設定できるにょ。
ストップウォッチ専用プログラムならば一旦停止した状態で待ってそこから再計測開始を
行えばいいだけのこととはいえ、これはあくまで電卓プログラムであり、ストップウォッチ
停止中に他のことを行うことも考慮する必要があるにょ。
そのため引数で続きを計測可能にしたにょ。
ストップウォッチを再起動したら前回からの引き継ぎ時間からの計測という方法もできたけど
それだとその時間が消えた場合(エラーが出て電卓を再起動することになったら変数が
初期化されてしまう)はどうするのかとか、ゼロにするための別の関数が必要になるため
リストが長くなるなどを考えると引数で起点設定を行うのがベストに感じたにょ。

余談だけどリスト中の1.667という数字は1フレーム当たり100分の1.667秒ずつタイムが増えて
いくという意味となっているにょ。
普通に考えれば「1秒=60フレーム」なので100/60と書くところだけどそうではなく1.667と
しているのは単にリスト短縮のためだけではなく1.667の方が100/60よりも正確な時間を
指し示すからにょ。
私が計測した範囲ではプチコン3号における実測値は1秒=59.835フレームだったのでこれから
逆算すると1.6712083・・・となり、100/60(=1.66666・・・)より、1.667の方がこれに
近いことが分かるにょ。(さらに除算が省ける分だけ高速になるけどそのメリットはこの
プログラムでは享受できない)
(※「なぜ、リスト中で「1.67」ではなく「1.667」としているのかは以前計測していた
59.835という数字を間違って覚えていたから)

タイマーやストップウォッチ機能が電卓に必要なのかというとこれは計算問題などの問題集を
解く際に制限時間を設定して解くという場合に便利だし、解くのにかかった時間を計測する
のにも便利にょ。
それを言ったらゲーム「CAVE QSP」の方が不必要に思えるけどこれは計算に疲れたときの
息抜きに使うと良いにょ。(それを言ったら何でもありになってしまうけど)


 ◎拡張関数ライブラリ5
 KEYS()関数・・・ファンクションキーのデータをセーブする関数
 KEYL()関数・・・ファンクションキーのデータをロードする関数
 KEYC()関数・・・セーブデータの内容をチェックする関数
 FN()関数・・・・任意のファンクションキーに書き込む関数

この簡易関数電卓をよく使うようにとなると頻繁に使用する計算式などを毎回入力し直す
のが面倒に感じることにょ。
したがって、これらのファイル操作用の関数を用意したというわけにょ。
F2〜F5の4つのファンクションキーのデータをまとめて保存するわけだけどこれは「←
(矢印)」を区切りコードにしてからロード時に分割ができるようにしているにょ。
CSVならばコロンを使うけどコロンは関数で使用するから区切りとしては不適切で改行
コードもKEYC()で区切りが確認し辛いということで矢印を区切りコードとしたにょ。
改行コードはリスト中で表記するならばCHR$を使うことになり、それだとQSPに収まらない
というのも理由にょ。

KEYC()を用意したのはファイルをいきなりロードしたら欲しいデータでは無かったという
問題があるだけではなくリスト短縮のためエラーチェックをしておらず、この簡易関数
電卓で使えない形式のデータ(上記のように矢印を区切りとした4つのデータ以外)だったら
エラーで電卓プログラムをRUNしなおすことになるのでその確認が必須に感じたにょ。
ちなみに保存するのがF1〜F5ではなくF2〜F5なのはF1は計算式を入力したらすぐに上書き
されるため保存する意味がないためにょ。(F1はシステムで常時使用されるためそこに
必要なデータを入れてもそれを活用することはできない)

FN()は任意のファンクションキーに書き込むことが可能になるわけだけどこれは良く使う
関数を電卓を終了して KEY 5,"SIN(〜以下略〜" のように書き込むのは使い勝手という
点においてはイマイチに感じるためにょ。
前回入力した計算式はF1に入っているのでそれを使えば電卓を終了することなくF5へと
書き込みそれをセーブまでできるにょ。
これは出来て当たり前のことに感じるけど「当たり前のことが当たり前のようにできる
ようになった」ということに大きな意味を持つにょ。(特にQSPなどのように文字数制限が
あるプログラムの場合はリストを書き換えて対応とかの一旦プログラムを終了しないと
設定ができない場合も多いため)

ちなみにF1〜F5に対応している(引数として有効なのは1〜5の範囲)けどF1は上記のように
すぐに上書きされてしまうためそこに書き込んでもほとんど意味はないにょ。(2つの
ファンクションキーのデータを入れ替えたいときの作業用エリアとしては使えるかも)
F2に関しては引数を設定しない関数を使用時のみ上書きをされずに済むにょ。
F3、F4に関してはB、Xボタンを押して上書きを行わない限りは普段の計算や関数を実行時
には上書きはされないにょ。(自作関数で書き込んだ場合を除く)
F5はファイルのロードもしくはFN()関数で書き込まない限りはこの簡易関数電卓の動作中に
上書きされることはないにょ。

ちなみにプチコンmkIIでは最大256文字までだったファンクションキーへの記録だけど
プチコン3号では128文字までとなっているにょ。
ただし、これは正確な書き方ではなくプチコン3号では256文字までファンクションキーに
入るけど128文字を超えた場合にはファンクションキーを押しても最後の128文字分しか
取得できないというだけにょ。(OUT命令などを使って取得すれば128文字超の記録が可能な
ことが確認できる)
ちなみにLINPUT命令では256文字までしか入力できないもののこれはプチコンmkIIの32文字と
比べたら8倍も向上しているにょ。(INPUTではなくLINPUTを使用しているのは「INPUTだと
?が出てしまうのが嫌」というだけではなく関数を入力するのに「,」の入力が必要になる
ためで、それに加えてINPUTだと空入力のため前回入力した値が保持されてしまうため空入力
処理を行うためには変数を初期化する必要がありそれだと長くなってしまいQSPに収まらない
というのが理由)


ファイル操作関係の関数の用意が完了したことによってこれでこの簡易関数電卓の拡張
関数ライブラリも一段落した感じにょ。
元々QSPの「簡易関数電卓」をQSPであるライブラリで機能拡張していくという今まで誰も
やったことがない(というか、普通の人はやろうとはしない)試みを行ってみたのだけど
個人的にはQSPがたくさん作れて結構楽しめたにょ。
長いリストで多機能、高機能は当たり前だし、見た目をすごくするのも長いリストならば
可能とはいえ私の場合はそういうものを作ろうとした場合には作る途中で飽きてしまい
未完成に終わるという可能性が高いためこのように小さいリストの集合体の方が向いている
感じにょ。(普通に機能を1つずつ追加して作るならばリスト短縮の必要性はあまりないけど
QSP単位で作るならばそのリスト短縮の必要性が大きくそれがプログラミングの楽しみに
している私にとっては恩恵が大きい)
そういう面ではプチコン3号の自作関数があってこそ可能になっていることだし、自作関数が
使える仕様となっているこの「簡易関数電卓QSP」だからこそという感じにょ。
まぁ「QSPでもここまで出来る!」というのをアピールするためとうのもあるにょ(笑)

本体QSPに加えてQSPが6本+PSTR$が現状のフルセットとなっているわけだけどいつの間にか
ファイルサイズは12KBにまで達しているにょ。
プログラムリストだけだとせいぜい2KB少々なのでマニュアルが10KB近くあるという計算に
なるにょ。(全部合わせても2画面程度なので比較的コンパクトな部類)
10KBのテキストなんてPCで入力すれば大したことはない(夏コミで頒布したプチコン3号本も
30KBのテキストを数時間で書いたわけだし)けどこれはすべてプチコン3号で入力した
日本語(漢字かな混じり文章)なのでコーディングよりも遙かに多くの時間がマニュアルの
文字入力に費やされているにょ。
私はmkIIがQRコードに対応して以来、公開しているすべてのプログラムにおいてリストの
末尾に簡易マニュアルを付けているのだけどほとんどが数100文字程度だったので当然ながら
ここまで大きなサイズのマニュアルを用意したのは初のことにょ。(まぁこの簡易関数電卓
QSPの処理の解説についてのテキストは60KB以上書いているけどこれはプチコン講座で言えば
2回分くらのテキスト量になるためQSPの解説としては異例のこと)


さて、締めに予定していたファイル操作関数もできてこれでこのQSPによる拡張関数
ライブラリの作成も一段落ついた感じにょ。
私自身は気が向いたらまた機能追加の拡張関数ライブラリを作ると思うけど今の所は
予定はないにょ。
したがって、「こんな機能が欲しい」という人は自分で作ってみるのもいいかもしれないにょ。
(いるかどうかも怪しいけど)そういう人のために機能拡張を行うプログラムを作る際の
注意点について少し書いておくにょ。

といっても、難しいことは特に無くて自作関数を作ってもらうだけでいいにょ。
そこで注意すべき点は対応しているのが戻り値が数値1つの自作関数のみということにょ。
戻り値が複数あるものや戻り値が無いものは作れないのかというと工夫次第でいくらでも
何とかなるにょ。
例えば戻り値が無いものは「RETURN 戻り値」を「L=0:RETURN 0」とすればいいにょ。
これは戻り値0の関数になるわけだけどL=0とすることでシステム上は戻り値がない関数
と見なされるにょ。(だからRETURN 0でなくてもRETURN 任意の数でもいい)
戻り値が無い場合はRVAL関数による計算が終わってもシステム画面で「=」+戻り値の
表示は行わないにょ。(つまり、戻り値がないのと同じ状態になる)

《 戻り値の無い関数の作成例 》
今日の日付や曜日を表示するTODAY()関数

COMMON DEF TODAY()
 VAR Y,M,D,W
 DTREAD OUT??Y,M,D,W
 ?"今日は";Y";"年";M;"月";D;"日(";"日月火水木金土"[W];"曜日)です"
 L=0
RETURN 0
END

※簡易関数電卓ではRVAL関数経由で入力された関数を評価している関係で別スロットから
 自作関数を読み出すことになるためDEFではなくCOMMON DEFとする必要がある。

ただし、戻り値がない関数、戻り値が複数の関数、関数無いでPRINTによる画面表示を
行っている関数は計算式入力画面で関数名などを入力する場合に複数をまとめて入力
すると正常動作をしない恐れがあるにょ。(もちろん、1つの関数のみを入力すれば
全く問題ない)
これは、主に表示関係の問題があるためにょ。
実は戻り値の有無を示すグローバル変数Lは正しくは計算式の戻り値を表示する際の
X座標を示しているにょ。(より正しく言えば、入力した計算式の文字数)
戻り値がない時に分数のFRAC()関数やタイマーのTMR()関数などは「=」の場所に整合性を
持たせるためLOCATE L MOD 25,CSRYという座標指定をしているにょ。
しかし、戻り値がない関数でL=0としてしまうと本来「=」を表示すべきだった座標が
失われてしまいLの値を元にLOCATEで座標している別の自作関数では表示の整合性が
取れなくなってしまうわけにょ。

したがって、そのような問題がない関数に関しては複数同時に使うことができるにょ。
TODAY()+KEYC("ABC") のようにして、今日の日付と曜日を表示してさらにファイル名
ABCの内容を表示ということも可能にょ。
ただし、プチコン3号では計算式は後ろから評価される(スタックがらPOPしている関係上
と思われる)ため上記のような記述の場合は最初に「ファイル名ABCの内容を表示」、
そして、「今日の日付と曜日を表示」となるにょ。

ちなみに拡張用の自作関数を作る場合にはデフォルトで使えるグローバル変数としては
上記の戻り値の有無(および、「=」の表示位置)を示した変数Lとどのファンクション
キーに結果を書き込むのかを示した変数Kが使えるのでこの2つの変数をうまく活用すると
「この電卓上で動いている」という感じがする自作関数が作れるにょ。

あと拡張関数ライブラリ3の中にあるANS命令を使えば本来「=」を表示するべき場所に
任意の文字を表示が可能になるにょ。

《 ANS命令を使用した関数の作成例 》
あなたの運勢を表示するUNSEI()関数

COMMON DEF UNSEI()
 ANS MID$("大吉中吉小吉吉 凶 大凶",RND(6)*2,2)?
RETURN 0
END

ANS命令を使えば「UNSEI()=大吉」のように関数と等号が結ばれた表示になるにょ。(ANS
命令の最後に「?」が付いているのは改行のためで、これによってUNSEI()+UNSEI()のように
複数個入力した際にその個数に応じた結果を出してくれる)
ただし、これも戻り値無しの設定で使用しているため他の関数を同時に実行させようとすると
問題が発生する可能性があるにょ。
TODAY()+UNSEI()ならば両方正しく表示されるけどUNSEI()+TODAY()ならば正しく表示を行う
ことができないにょ。
これは式を評価する順番から言って後のTODAY()が先に実行され「=」の表示場所である
Lの値がクリアされてしまいUNSEI()関数の中にあるANS命令が正常に機能していないためにょ。
戻り値無しの2つの自作関数を一度に実行したい場合は表示場所が左端でいいものを最初に
記述するとうまく行くにょ。(ANS命令を使用した時点で戻り値無しの関数と見なされる
ということ)
表示位置が左端でいいものならばどんな種類の関数をいくつ組み合わせても表示の問題は
発生しないにょ。(といっても、LINPUTで入力できる256文字が上限だけど)

ちなみにANS命令を使わなくても上記のUNSEI()関数は次のように記述できるにょ。

《 戻り値を文字列にした関数の作成例 》
COMMON DEF UNSEI()
 PRGEDIT 2,1
 PRGSET MID$("大吉中吉小吉吉 凶 大凶",RND(6)*2,2)?
RETURN 0
END

これは擬似的に関数の戻り値を文字列にしてみただけにょ。(システム上はスロット2の
1行目に書かれたものを計算結果として処理しているため)
ただし、計算結果を強引に書き換えているだけであり、この方法が使えるのは1回の入力
あたり1つのみなので複数の関数を同時に実行させることはできないにょ。(上記のように
UNSEI()+UNSEI()のように複数同時に入力しても1つしか結果を表示してくれない)
ちなみに「戻り値がない設定で使用できるような仕様になっている」というのは最初から
考えてたわけではなく計算をしていくと画面が詰まって見にくくなってしまう場面が発生
したため空入力で改行ができるようにしたことによる恩恵にょ。
改行の際に前回入力した計算式や結果が""(null)で上書きされるのは嫌だし、結果を示す
等号のみ表示されるのも嫌(これは文字数0を掛ければ解決可能)なのでIFで判定を行ったら
結果としてそれが「L=0」として空入力と見なせるようにすれば戻り値無しを擬似的に表現
できると後から思いついたにょ。(IFの判定をRVALによる式の評価の前後2つ置いている
理由については最後に書いている)

こんな感じで機能拡張は簡単にできてしまうので興味がある人は自分でこの簡易関数電卓の
機能拡張を行い自分好みのものにしてみてにょ。


やはり、「OSもどき」を作る人は「自分好みを実現するため」という人も多いのでは
ないかと思われるにょ。
これはシンプルなツールの寄せ集めであり、例えるならば幕の内弁当のような感じのもので
高性能ツール(豪華なディナー)とは異なる別の楽しみ方があるにょ。(まぁOSもどきを
作っている人も様々で私もマルチタスクやマルチウィンドゥを実現するにはどうするかを
考えるのが一番の楽しみだったわけなのでみんながシンプルなツールの寄せ集めとして
捉えているわけではないと思うけど)
それを考えるとこの簡易関数電卓は一種のOSもどきと言えなくもないにょ。(コマンド
ラインで各種アプリやコマンドを実行できるわけだし)

プチコン3号でいくつも作られている他のOSもどきと違うのは電卓(計算機能)をベースと
して作られている点だけにょ。
「自分が作ったOSもどきに電卓を組み込みたい(電卓機能を持たせたい)」という人は
結構いるけど「自分が作った電卓をOSもどきにしたい」という人はいないと思うにょ。
この簡易関数電卓を「OSもどき」と呼ぶには語弊があると考える人もいるかもしれないけど
「これがOSもどきだ!」というちゃんとした定義もないわけなので個人的にはそれほど
問題ないと思うにょ。(「OS」と「OSもどき」は完全に別物だし、「OSの定義」そのものも
広義の場合と狭義の場合では異なるわけだし「OSもどきの定義」なんて個人によって
バラバラなので統一見解を求めるのは無理がある)


《おまけ》

◎自作RVAL関数を使ってボタン操作による電卓を作る方法

すでに画面にボタンを配置して数値や演算子が入力できる状態にあるとするにょ。

2+3*4/5という計算を行う場合は頭から順に処理して2+3を計算して5として5*4=20となって
しまいそれを5で割って4という答えになってまうにょ。
関数電卓以外の市販の大半の電卓はそのように処理しているもののこれは優先順位が乗算
除算の方が上なので正しくないにょ。

◎2+ まで操作

変数Z$には演算子が押されるたびにその演算子の前までの入力情報を文字列で記録して
いくにょ。
したがって、この時点でZ$の内容は"2"となるにょ。

この場合は最も最近押された演算子を別の変数に入れる必要があり例えばこれをB$に
入れるとすると「2+」のように演算子を込みでZ$に入れておいてB$=POP(Z$)とすればB$には
その演算子が入り、Z$には演算子が抜きになるというやり方もあるにょ。(もちろん、
Z$は"2"のままでPOPを使わずB$に押された演算子"+"をそのまま代入するという普通の
やり方をしてもいい)
次に数字が押されたらその数字の前にB$を入れて、B$は空にすればいいにょ。(B$に
入る演算子は常に1つ以下)
数字ではなく演算子の次に別の演算子が押された場合にはその演算子をB$に入れるだけで
問題はないにょ。(数字を押すまでは演算子の押し替えは何度でも可能)

◎2+3* まで操作

2つ目以降の演算子が入力された(もしくは等号が押された)時点でその演算子の前までを
RVALを使って求めるにょ。

 A=RVAL(Z$) Z$の内容は"2+3"でAの値は5になる→画面に5と表示

◎2+3*4/ まで操作

3つ目の演算子が入力された場合も同様にょ。

 A=RVAL(Z$) Z$の内容は"2+3+4"でAの値は14になる→画面に14と表示

◎2+3*4/5= まで操作

最後に=が入力された場合も同様だけど少し異なっているにょ。

 A=RVAL(Z$) Z$の内容は"2+3+4/5"でAの値は2.8になる→画面に2.8と表示
 そして、Z$=STR$(A$)とする。(Z$の内容は"2.8"へと変化)

2.8が表示された状態で「+」が押されたらZ$の内容は"2.8+"となり、数字が押されたら
例えば6が押された場合にはZ$="6"となるにょ。
こうすることで、等号をまたいだ計算に対応が可能になるにょ。

またカッコに対応するならば開きカッコと閉じカッコの差分を取ればいいだけにょ。
差分を示す変数がNならばカッコを開くたびにINC N、閉じるたびにDEC Nとするにょ。
閉じカッコが押されたときにNの値が0ならばRVALで計算して画面表示を行えばいいにょ。
Nの値が負数の場合はエラー表示を行う必要があるにょ。(そのまま続けて計算した場合
にはRVALでエラーを返して電卓が強制終了してしまうため)
カッコを扱う場合には他にもやらなくてはならない例外処理は多くて開きカッコの前に
演算子が入力されてない場合(この場合は乗算が省略されていると判断して乗算を挿入
するという方法もある)、開きカッコの後ろ、閉じカッコの前に演算子がある場合、
閉じカッコの後ろに演算子がある場合なども例外処理を行い該当する場合にはエラーを
返すか適切な処理を行う必要があるにょ。

せっかくお手軽に計算できるRVAL関数を使ってもカッコがあるとその例外処理をやって
おかないとプチコン側のエラーによってプログラムが停止することになるにょ。
まぁ簡易関数電卓QSPはその例外処理は全くやってないけどこれはQSPでそれをやるのは
不可能だし、計算式を入れてそれを計算という方式でるため構文解析をしなくてはならず
構文解析不要で式の値を求められるRVALのメリットが完全に失われてしまうためにょ。
しかし、計算式を入力した時点でファンクションキーへと記録されているし、余計な装飾も
なくためすぐに起動するため間違っている部分だけ修正するだけで済むので負担はほとんど
ないにょ。
これが、計算式の記録がRVALを使った計算をした後にまとめてF1に計算式、F2に計算結果を
記録するという方法をとった場合にはエラーで計算式をすべて再入力となってしまうため
一見無駄に思えるRVALの前後2回に分けて計算式が入力されているかどうかの判定を行い
「例外処理を行うことができない」ということに対する対処をしているというわけにょ。




掲示板管理者へ連絡 無料レンタル掲示板