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

おちゃめくらぶ掲示板

2460御茶目菜子:2015/09/06(日) 01:57:56
プチコン3号で「簡易関数電卓QSP」を作ってみた
プチコン3号のQSP(WIDTH 16における1画面プログラム)で「簡易関数電卓QSP」を作って
みたにょ。
https://miiverse.nintendo.net/posts/AYIHAAAEAABEVRTqq1Rhmw

電卓といえば初心者向けのプログラミング用の題材として作られることが多いにょ。
個人的には初心者向けの題材はゲームであれば非リアルタイムのゲームならば「数当て
ゲーム」「じゃんけんゲーム」などがベターでリアルタイムのゲームならば「100m走ゲーム」
「避けゲー」などがベターと考えているにょ。
これは「作るためのハードルが低い」「他のゲームを作る場合に応用が利く」」というのが
理由にょ。

「100m走ゲーム」ならば移動は1方向だけので簡単だし、ボタン入力も1つのみを使用する
ならばBUTTON(2)で簡単に連射判定を実現が可能にょ。
つまり、ミニマム構成だと非常にシンプルということにょ。
そして、「交互にボタンを押す」ならばフラグ管理の方法を覚えられるし、背景をスクロール
させるならばBGの基本的な使用方法を覚えられるにょ。
様々な命令や技術というのは作りたいというものがあって初めて意味があるものになりそれに
よって自然に覚えることができるにょ。(使わない技術や命令は覚えようとしてもなかなか
覚えることができない)

避けゲーも「100m走ゲーム」と同じくミニマム構成だと非常にシンプルにょ。
キャラをどのように動かすかの自由度が高いため100m走ゲームよりはハードルが高くなるけど
逆に言えばゲームとして様々なバリエーションが可能になるにょ。
避ける障害物を増やしてやれば「弾幕ゲーム」みたいなものに発展させることもできるにょ。
また避けゲーの逆バージョンである「キャッチゲーム」においては上から落ちてくるものを
キャッチするだけではなく反射させることで「壁打ちピンポン」になるしさらに発展させると
ブロック崩しになるにょ。
2人対戦型にすればPONのようなテニスゲームへと発展も可能にょ。

「ヘビゲーム」は初心者向けの題材としてはやや高めになるにょ。
それは、必要最小限のルールが多い(「餌を食べたら体が伸びる」「自分の体に当たったら
ゲームオーバー」「来た方向には戻れない」など)というのもあるけど「餌を食べたら体が
伸びる」を作るためには配列変数が必要になるし、効率よく表示するにはリングバッファに
ついて覚える必要があるにょ。(プチコン3号は高速なのでリングバッファを使わずとも力業で
60fpsを維持できるので問題ないけど)
言葉で一言でルールを説明できるくらいが初心者には簡単に理解が可能にょ。
これはプログラミングというのは細分化してやりコンピュータに対して具体的な指示を与える
必要があるためにょ。

◎プログラムが作れるようになるためには
 http://ochameclub.web.fc2.com/petitcom3/lecture/program.htm

ヘビゲームの場合は「餌を食べたら〜、壁に当たったら〜」と一言では無く二言、三言の説明が
必要になり、そのため条件判定の機会も多くプログラミングを始めたばかりの初心者にとっては
負担が大きいにょ。
もっとも、一言でルールが説明できるような上記のゲームが作れるようになってからヘビ
ゲームを作るならば何ら問題はないし、「ヘビゲームが作りたい」と強く思っている初心者
ならばプログラミングを初めてすぐであってもヘビゲームを作るのは問題ないにょ。
上記の「プログラムが作れるようになるためには」で書いているように「完成させる」という
ことが重要なのだけどそれ以前に「作りたいものがある」というのが重要になってくるからね。

では、電卓は本当に初心者向けの題材と言えるのかを考えてみるにょ。
電卓がプログラミング初心者向けの題材に使われることが多いのはWindowsであればVBとかを
使えばボタン配置も簡単というのが理由に考えている人も居そうにょ。
その点、プチコン3号で作るならばボタンの自作から入ると意外にハードルが高くなるにょ。
とはいえ、ボタンを作る(画面上に表示してどのボタンが押されたかどうかの判定まで行う)
ことができればあとは簡単かというとそういうわけでもないにょ。
しかし、サンプルプログラム(SYSフォルダ)にあるEX2CALC(けいさんCOMPUTER ニコチャン)
のように数字を2つ入力してその間にある演算子を入力ようなタイプにすれば大幅にハードルが
下がるにょ。
まずは、そういった超シンプルなものから始めるといいにょ。
極端に言えばこんな感じで2つの数を入れて2つの合計値を表示するということから初めて
問題はないにょ。
電卓を作ること(計算すること)に興味があればこれが最初の第一歩となるにょ。

 INPUT A
 INPUT B
 C=A+B
 ?A;"+";B;"=";C

EX2CALCのような2つの数字とその間の演算子を選択するだけの超初心者向けの計算機
プログラムは演算子が1つしか計算できないため実用性という面では極めて厳しいけど
これは「=」を入力するまで処理を繰り返すように改造するだけでその問題は一応解決
できるにょ。
その場合に新たに発生する問題としては演算優先順位にょ。
それは四則演算において加法、減法よりも乗法、除法の方が演算優先順位が高いため例えば
2+3*4を計算する場合において頭から順に計算すると20になるけど優先度を加味して計算すると
14になるにょ。

これを簡単に解決するために古くから用いられている手法が逆ポーランド記法による記述にょ。
逆ポーランド記法では「2+3*4」は「2 3 4 * +」と表記されるにょ。
これはスタックにPUSHとPOPを行うだけで簡単に処理できるし、プチコン3号には標準で配列に
PUSH、POPを行うことが可能であるためまさに初心者がプチコン3号で演算子やスタックに
ついて学ぶには「演算優先順位を加味した電卓」はもってこいの題材と言えそうにょ。
逆ポーランド記法に関しては詳しく書くと長くなるためネット上で検索してみてにょ。

数字、演算子という順番で入力していくのならばそれほど難しくはないけど数式を入力して
それを元に演算処理していく場合には構文解析が必要になってくるにょ。
といってもこれは整数による四則演算のみをサポートするならば頭から順に1文字ずつ読み
出していって数字か演算子かで判別すれば良いだけなので簡単にょ。
これが実数(浮動小数点)をサポートする場合にはハードルが少し高まるにょ。
1.23456×10のマイナス10乗は「1.23456e-10」と表記されるけれどその際は小数点は1度だけ
登場、「e」もしくは「E」も一度だけ登場でその前に数字が付いていたら数字扱いになり
「e」もしくは「E」の後にある「-」や「+」は演算子ではなく指数の符号という感じで
きちんと例外処理を行う必要があるからね。

同じように処理をしていけばSINやCOSといった関数の処理も可能になるにょ。
この場合はSIN(30/180*3.14)+COS(60/180*3.14)という数式はカッコ内の計算処理を先に
実行してSINが関数であるということを構文解析で行い(「S」「I」「N」「(」の順番に
なっていれば「)」の間を計算してそれを元にしたSIN関数の値を求めるようにしていけば
いいにょ。
こうやって、SIN、COSだけではなく様々な関数に対応していき計算式の演算結果を返す
関数を作ってやれば電卓っぽいものはすぐにできるにょ。
要するにEVAL関数のような自作関数を作るということにょ。


EVAL関数とは文字列を式と見なしてその処理を行う関数にょ。
プチコン3号でそれを行うためにはすでに書いているように「構文解析」「逆ポーランド
記法への置き換え」「関数や演算子の処理」というものが必要になってくるにょ。
この場合はサポートする関数や演算子が増えれば増えるほどプログラムリストが長くなる
という問題があるにょ。
定数リテラルに対応しようと思ったら例えば構文解析によって「#RED」という文字列が
現れたらそれを-524288という数字に変換すれば良いだけとはいえすべての定数リテラルを
サポートするにはプログラムリストはそれなりに長くなるにょ。

しかし、プチコン3号ならば構文解析は一切不要でEVAL関数のようなものを作る方法は
あるにょ。
それは別スロットでその式を実際に実行してその値を取得するという方法にょ。

DEF RVAL(E$)
 VAR R$
 PRGEDIT 1,1
 PRGSET "@RVAL L$="STR$("+E$+")":PRGEDIT 2,1:PRGSET L$:RETURN
 USE 1
 GOSEUB "1:@RVAL"
 PRGEDIT 2,1
 R$=PRGGET$()
RETRUN R$
END

このRVAL関数を使えば計算式にどのような関数、演算子、定数リテラルが含まれていても
プチコン3号で実行できるものならば構文解析をせずにその値を得ることができるにょ。
ソフトや処理系によってEVAL関数がどのようなものかは異なるけどポケコン(PC-E500
シリーズ)におけるEVAL関数だと計算式に変数が含まれていてもそれを使った値を取得可能に
なっているにょ。
しかし、このRVAL関数は変数には対応していないにょ。
したがって、定数専用のEVALみたいなものなので「RVAL」とネーミングしたにょ。

変数が使えないのはプチコン3号では異なるスロットのプログラムを実行可能とはいえ
変数はスロット間で共有しているわけではないためにょ。
これはスロット0のグローバル変数とスロット1のグローバル変数が異なるという意味にょ。
したがって、スロット間の変数の値をやり取りする別の関数が必要になってくるにょ。
しかし、それを作るには構文解析を行い変数名を取得する必要があるにょ。
構文解析不要がこのRVAL関数の最大のメリットなのに変数名の取得のためだけに構文解析を
行えばそのメリットを失うことになってしまうにょ。
ただし、実行した結果のやり取りを行う必要があり、それはスロット2に値を書き込みそれを
取得することで対応しているにょ。


このように定数のみの対応とはいえその効果は絶大にょ。
とはいえ、すごく便利に思えるこの関数だけど実際に何に使えるのかとなると具体的には
なかなか思い浮かばず電卓を作るのが超簡単になるという程度にょ。
それならば、電卓を作るのが一番と思い作ったのが冒頭の「簡易関数電卓QSP」にょ。
この簡易関数電卓はRVAL関数をさらにリスト短縮するため関数化をやめてプログラムの
メインにそのまま組み込んでいるにょ。
とはいえ、実際に「計算式を入力したらその結果を表示する」というだけではダイレクト
モードで計算できるためわざわざ作るメリットはあまりないにょ。
とはいえ、UIに拘ったらとてもQSPには収まらないにょ。
したがって、ダイレクトモードにはないメリットをQSPでできる範囲内で実装する必要が
あったにょ。

 ◎「簡易関数電卓QSP」のアピールポイント
 (1)見やすい画面
 (2)ファンクションキーへの記録機能
 (3)自作関数による拡張

(1)ダイレクトモードはプログラム不要で計算できるためお手軽とはいえ決して画面的には
見やすいとは言い難いにょ。
そのため見やすくするため「1+1=2」のような画面表記にしたにょ。
計算式の下の行に計算結果を表示するというのが関数電卓のスタンダードとなっているけど
それよりもこのように等号を横に並べた1つの式の方が見やすく感じたにょ。
それだけではなくダイレクトモードだといちいちPRINTや?を使う必要があったり1回計算
するごとにOKの文字が出てきてしまう(DEFAULTプロジェクトから変更している場合には
そのプロジェクト名まで出てしまう)ということで余分なものを入力したり、表示したり
というため見やすさというメリットはそれなりに大きいのではないかと思われるにょ。
さらにAボタンの空打ちでスクロールもできるのでダイレクトモードよりは見やすさという
面では確実に上にょ。

それとダイレクトモードで計算した時には絶対値が5e-9よりも小さい数は0扱いになったり、
絶対値が大きな数は末尾に0が羅列されて非常に見にくくなるにょ。
もっともこれは表示の際に文字列化してやれば改善できるとはいえ、そうなると計算する
度に?STR$(1+1)のようにする必要があるため何も考えずに絶対値が小さな数や大きな数が
見やすく表示できるというのは大きなメリットだと思うにょ。

(2)ダイレクトモードで計算する場合にはA=123のように変数に代入できるし、計算式の
一部を変更する場合にも履歴から簡単に変更が可能になるにょ。
変数には対応できなくても履歴から簡単に変更という機能に相当するものは何とかして
付ける必要があったにょ。
それがファンクションキーによるメモリー機能にょ。
これは、前回の計算式のファンクションキーに入っているのでそれを入力して修正すれば
計算式の一部の変更は容易にできるにょ。
もしも、この機能が無かったらプチコン3号で計算できない計算式を入力した際に最初から
入力し直しになってしまうけどこの機能のお陰で一部を修正するだけで済むにょ。
また計算結果においては前回のものだけではなく任意の2回分を記録できるためそれを
使えば一般的な電卓のメモリー機能以上のことができるにょ。

(3)ダイレクトモードでは自作関数は動作しないにょ。
そのため、便利な自作関数を作っても結局使えるのはデフォルトの関数のみにょ。
しかし、この簡易関数電卓は任意の自作関数(戻り値が数値1つのもの)を実行が可能に
なっているにょ。
下記の拡張関数ライブラリなどを見てもらえば分かるようにそのメリットは極めて大きい
と思うにょ。
ただし、これをアピールしすぎると本体が「QSP」というメリットが薄れてしまう両刃の
剣となってしまうのが唯一のネックにょ。(リストが長くなっていいのならば機能を
増やすことなんて簡単にできることなのでQSPだからこの簡易関数電卓がすごいといっても
拡張機能によってリストが長くなるならば相対的にそのすごさが薄れてくるため)


さて、拡張性の話が出たところでまずは拡張関数ライブラリ1、拡張関数ライブラリ2を
見てみるにょ。
https://miiverse.nintendo.net/replies/AYIHAAAEAABEVRTqq3d9EQ
https://miiverse.nintendo.net/posts/AYIHAAAEAACHVRTq5Yy6bQ
この拡張関数ライブラリ1では6つの自作関数、拡張関数ライブラリ2では4つの自作関数が
使えるようになっているにょ。
ちなみに拡張関数ライブラリは両方ともQSPとなっているにょ。

 ◎拡張関数ライブラリ1
 SIND()関数・・・「度」で計算できるSIN関数
 COSD()関数・・・「度」で計算できるCOS関数
 TAND()関数・・・「度」で計算できるTAN関数
 FACT()関数・・・階乗を計算できる関数
 P()関数・・・・・順列を計算できる関数
 C()関数・・・・・組み合わせを計算できる関数

わざわざ、「度」で計算できる関数というものを作ったのはこの簡易関数電卓ではsin 60°を
計算しようと思ったらSIN(RAD(60))と入力する必要があるためにょ。
さすがに毎回RADを入力するのは面倒なので自作関数にしてみたにょ。

 ◎拡張関数ライブラリ2
 TAXIN()関数 ・・税込価格を計算できる関数
 TAXOUT()関数・・税抜価格を計算できる関数
 HEX()関数 ・・・16進数に変換表示する関数
 BIN()関数 ・・・2進数に変換表示する関数

HEX()とBIN()は「計算」ではなく「変換表示」と記しているのはその通りの仕様となって
いるためにょ。
例えば123を16進数にしたい場合にはHEX(123)と入力するのだけどこの簡易関数電卓の仕様上
自作関数の戻り値を文字列にすることはできないにょ。
そのため別枠に変換結果である&H7Cを表示しているにょ。
これは数値にも文字列にも対応するにはリストが長くなるというのも理由だけど元々RVAL
関数が戻り値が文字列になるのは想定していなかったためにょ。
別枠表示のため上記のアピールポイントで書いている「計算式と計算結果が等号で結ばれる」
ということによって得られる見やすさは失われてしまっているけど工夫次第では当初考えて
いた仕様以上のことはできるというのは分かったにょ。(QSPに収めるために計算式と計算
結果を等号で結ぶという見やすさを優先したシステムを諦めていたら実現はできなかった)


それをさらに発展させてゲームを実行する自作関数CAVE()を作ってみたにょ。
https://miiverse.nintendo.net/replies/AYIHAAAEAABEVRTqwjqo7A

計算式入力画面でCAVE()とすれば実行されこの関数の戻り値はスコアであるためゲーム
オーバーになるとスコアが表示されるにょ。
これはおちゃめくらぶではおなじみ(?)になっている横スクロールよっぱらいゲーム
「CAVE」(初出はプチコンmkIIの1/2画面プログラム版)のQSP版であり、この簡易関数
電卓上でのみ想定された動作を行うことができるにょ。(簡易関数電卓以外で実行するため
には別途PRINTかDIALOGによるスコア表示が必要になるけどこれはDEFを外せば容易にQSPに
収めることは可能)

これを実行してみての通りこの簡易関数電卓上でゲームさえも動かすことが可能なことが
分かるにょ。
それはリストを変更してこのゲームが動くようになったというのではなくリストを全く
書き換えずに対応できるというのが大きな意味を持つにょ。
リストを書き換えていいのならばIF A$="CAVE()" THEN A=CAVE()としてやればいいだけだし
QSPでないならば構文解析をしてやればデフォルトの関数にない関数を使用している場合は
CALLを使って呼び出すだけで簡単に実現できることなので「QSPでそれができている」
というのが大きな意味を持つというだけにょ。

というわけで「簡易」関数電卓というわけで大してすごくない電卓なわけだけど「QSP」
だからこそという面が非常に大きいにょ。
リストサイズは数10倍になっていいのならばRVAL関数を使わなくてもこれと同等機能のものは
力業で作れるわけだしね。
これは夏コミで頒布した「プチコン3号 QSP完全マニュアル」でも書いていることだけど
「妥協をしない」ということがQSPの完成度を高める最大の原動力となるためにょ。


とはいえ、やはり電卓として考えるならば計算精度が気になるかもしれないにょ。
これはSTR$の変換精度に依存しているためにょ。
整数演算ならば-2147483648〜+2147483647の範囲までならば正確に得ることができるとはいえ
計算結果が小数になった場合には小数部を含めて6桁を超えた場合には6桁に丸められてしまうし
-999999〜+999999の範囲外になった場合も指数表記となり仮数部の時は6桁に丸められて
しまうにょ。
小数にならなくても関数を使用したりした場合において-999999〜+999999の範囲外の整数に
なった場合も指数表記となり仮数部は6桁になってしまうにょ。
そのため計算精度は100円均一の電卓(一般的には8桁であり、有効桁数8桁の計算が可能)
より下になってしまうにょ。
とはいえ、STR$を使った場合であっても絶対値が10の8乗を超えるような大きな数や絶対値が
小さな数も計算できるし、様々な関数や演算子に対応しているとというメリットはあるため
100円均一の電卓に劣るというわけではないけどね。

それに関してはSTR$ではなく自作関数のPSTR$を使えば解決可能にょ。
https://miiverse.nintendo.net/posts/AYIHAAAEAABEVRTp-ZVMIg
PSTR$はプチコン3号において数値を誤差ゼロで文字列に変換できる自作関数だけどこれを
使えば有効桁数は52bit(15.6桁)になるため並の関数電卓を超えるものになるにょ。
とはいえ、丸めて表示されるべき部分も正確に表示してしまうため0.1という値を表示した
場合にも内部で循環小数で表記されている影響が出てしまうという問題があるにょ。
これはこれでプチコン3号で実行した値の検算に使えるため便利と言えなくもないにょ。
(日経ソフトウェア10月号p.74のA21の0.1を20回足したら2.0000000000000004になるという
のもこの簡易関数電卓で再現が可能)


《 続き 》

◎「簡易関数電卓QSP」はQSPで出来たOSもどきだった!?
  http://6407.teacup.com/ochame/bbs/4991




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