Casio Basic入門21

Casio Basic入門
<目次>

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

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

Chapter 4

◆ Chapter 4 の目標: 換算プログラムを作る


まず最初に、前回の サブルーチン YRC で、必要なコードを書き忘れていましたので、前回の記事を修正しました。
修正箇所は、以下にも明記します。申し訳ありません。


これまでに作成した「西暦・和暦換算プログラム」のプログラムコードを、以下にまとめます。


プログラム名: CH4-3

0→A:0→B:0→G

Locate 1,1,"0:AD"
Locate 11,1,"<AC>
"
Locate 1,2,"1:M"
Locate 10,2,"2:T"
Locate 1,3,"3:S"
Locate 10,3,"4:H"
Locate 1,4,"5:At"
Locate 10,4,"6:yσn"
  ([FUNCTION] [7] [2] [6])
Locate 14,4," "

While 1

-1→N
Do
Getkey→K
LpWhile K=0
K=25⇒0→N
K=35⇒1→N
K=36⇒2→N
K=36⇒3→N
K=21⇒4→N
K=22⇒5→N
K=23⇒6→N


If N=0:Then
5→X:1→Y:4→D:1→E
Prog "INPI":Z→A
Else If N=1
Then
4→X:2→Y:4→D:1→E
Prog "INPI":Z→M
Else If N=2
Then
13→X:2→Y:4→D:1→E
Prog "INPI":Z→T
Else If N=3
Then
4→X:3→Y:4→D:1→E
Prog "INPI":Z→S
Else If N=4
Then
13→X:3→Y:4→D:1→E
Prog "INPI":Z→H
Else If N=5
Then
If A:Then
5→X:4→Y:4→D:1→E
Prog "INPI":Z→B
IfEnd
Else If N=6
Then
If A:Then
12→X:4→Y:3→D:1→E
Prog "INPI":Z→G
IfEnd
IfEnd:IfEnd
IfEnd:IfEnd
IfEnd:IfEnd
IfEnd

If N=0:Then
Prog "YRC"
Else If N=1
Then
M+1867→A
M=0⇒Isz A
Prog "YRC"
Else If N=2
Then
T+1911→A
T=0⇒Isz A
Prog "YRC"
Else If N=3
Then
S+1925→A
S=0⇒Isz A
Prog "YRC"
Else If N=4
Then
H+1988→A
H=0⇒Isz A
Prog "YRC"
Else If N=5
Then
If A:Then
Prog "YRC"
IfEnd
Else If N=6
Then
If A:Then
Prog "YRC"
IfEnd
IfEnd:IfEnd
IfEnd:IfEnd
IfEnd:IfEnd
IfEnd

WhileEnd



プログラムの構造

[初期設定]

[表示の初期化]

While 1

    [メニュー番号取得]

    [入力処理]

    [計算と出力処理]

WhileEnd


サブルーチン: YRC (修正あり)

If N=1:Then
A-1867→M
Else If N=2
Then
A-1911→T
Else If N=3
Then
A-1925→S
Else If N=4
Then
A-1988→H
Else If N=5
Then
B-A→G
Else If N=6
Then
A+G→B
IfEnd:IfEnd
IfEnd:IfEnd
IfEnd:IfEnd

A-1867→M
A-1911→T
A-1925→S
A-1988→H
Locate 5,1,"     "
   (スペース5個)
A>0⇒Locate 5,1,A
Locate 4,2,"      "
  (スペース6個)
M>0⇒Locate 4,2,M
Locate 13,2,"    "
  (スペース4個)
T>0⇒Locate 13,2,T
Locate 4,3,"      "
  (スペース6個)
S>0⇒Locate 4,3,S
Locate 13,3,"    "
  (スペース4個)
H>0⇒Locate 13,3,H


B-A→G
   [追加修正したコード]
If G>999 Or G<-99:Then

Locate 12,4," yσn"
  (スペース1個)
Locate 14,4,"  "    (スペース2個)
Return
IfEnd

If B Or G:Then
Locate 5,4,"     "
  (スペース5個)
Locate 5,4,B
Locate 12,4,"   "
  (スペース3個)
Locate 12,4,G
If Abs(G):Then
13+Int(log(Abs(G)))→D
Else 13→D:IfEnd
G<0⇒D+1→D
Locate D,4,"yσn"
Locate D+1,4,"  "
  (スペース3個)
IfEnd


上記の赤色で示した部分が前回抜けていました。この1行の下で変数Gに基づいた処理をしているのに、この部分が抜けていては、おかしなことになります。


さて、今回は、上記のプログラムに各年号の始まりと終わりの年月日を表示する機能を組み込みます。



Chapter 4-4
キー長押しによるメニュー選択


これまでに作成したプログラムの実行画面は、既に画面一杯になっており、新しい機能を呼び出すためのメニュー番号を表示させる場所がありません。

YrConv9 

今回追加する機能は、明治、大正、昭和、平成 それぞれの始まりと終わりの年月日を表示するものです。

明治年を入力するための項目1は、[1] キーを押して入力します。そこで、[1]キーを長押しした時に明治の始まりと終わりの年月日を表示するようにできれば、新たなメニュー番号を増やす必要がありません。


そこで今回は、キー長押しによるメニュー選択機能を追加します。


キー長押しについては、CasioBasic入門6 (キーコードを調べるプログラムを作る) で長押しを適切に検出する方法を紹介していますので、今回は応用編です。


キー長押しでは、[1][2][3][4] キーを押した時、それぞれに対応して、明治、大正、昭和、平成の始まりと終わりの年月日(平成は始まりだけ)を表示させるのですが、その表示内容は、、以下のようなものにします。


明治の始まりと終わり

大正 

大正の始まりと終わり

明治 

昭和の始まりと終わり
昭和

平成の始まり
平成 


いずれの画面でも、右下に ►E の表示をしています。これは、[EXE}キーで元に戻るこを示す表示です。

これらの表示は、サブルーチン YRD を呼び出して行うことにします。

※ サブルーチン名の YRD の末尾の D は、Display (表示) の D を意味しています。


ところで、この表示から [EXE] キーを押して、元に戻るときには、以下のような表示内容を再描画する必要があります。

YrConv9 


ところで、プログラム起動時は、以下の表示を行います。
        
YearConv_1 

こでは、[表示の初期化] ブロックで記述していますが、この表示も再描画する必要がありますので、 サブルーチン YRD で行うように変更することにします。

この表示を実行したのち、既に作っているサブルーチン YRC を実行すると、1つ上のような表示になります。

従って、[表示の初期化] ブロックは、

Prog "YRD"
Prog "YRC"


に置き換えることができます。


それでは、このような動作をするように、メインルーチンとサブルーチン YRD を作ってゆきます。



つづく...


CasioBasic入門22 / 目次




応援クリックをお願いします。励みになるので...

人気ブログランキングへ


FC2ブログランキングへ



カシオプログラム関数電卓 FX-5800P-N カシオプログラム関数電卓 FX-5800P-N
(2006/09/22)
CASIO(カシオ)

商品詳細を見る



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

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

関連記事

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

Casio Basic入門20

Casio Basic入門
<目次>

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

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

Chapter 4

◆ Chapter 4 の目標: 換算プログラムを作る

前回: Casio Basic入門19


前回は、以下のプログラムを作り、サブルーチンの有効性を実感できたと思います。


サブルーチン: YRC

If N=1:Then
A-1867→M
Else If N=2
Then
A-1911→T
Else If N=3
Then
A-1925→S
Else If N=4
Then
A-1988→H
IfEnd:IfEnd
IfEnd:IfEnd

A-1867→M
A-1911→T
A-1925→S
A-1988→H
Locate 5,1,"     "
   (スペース5個)
A>0⇒Locate 5,1,A
Locate 4,2,"      "
  (スペース6個)
M>0⇒Locate 4,2,M
Locate 13,2,"    "
  (スペース4個)
T>0⇒Locate 13,2,T
Locate 4,3,"      "
  (スペース6個)
S>0⇒Locate 4,3,S
Locate 13,3,"    "
  (スペース4個)
H>0⇒Locate 13,3,H




プログラム名: CH4-2

0→A

Locate 1,1,"0:AD"
Locate 11,1,"<AC>
"
Locate 1,2,"1:M"
Locate 10,2,"2:T"
Locate 1,3,"3:S"
Locate 10,3,"4:H"

While 1

-1→N
Do
Getkey→K
LpWhile K=0
K=25⇒0→N
K=35⇒1→N
K=36⇒2→N
K=36⇒3→N
K=21⇒4→N


If N=0:Then
5→X:1→Y:4→D:1→E
Prog "INPI":Z→A
Else If N=1
Then
4→X:2→Y:4→D:1→E
Prog "INPI":Z→M
Else If N=2
Then
13→X:2→Y:4→D:1→E
Prog "INPI":Z→T
Else If N=3
Then
4→X:3→Y:4→D:1→E
Prog "INPI":Z→S
Else If N=4
Then
13→X:3→Y:4→D:1→E
Prog "INPI":Z→H
IfEnd:IfEnd
IfEnd:IfEnd
IfEnd

If N=0:Then
Prog "YRC"
Else If N=1
Then
M+1867→A
M=0⇒Isz A
Prog "YRC"
Else If N=2
Then
T+1911→A
T=0⇒Isz A
Prog "YRC"
Else If N=3
Then
S+1925→A
S=0⇒Isz A
Prog "YRC"
Else If N=4
Then
H+1988→A
H=0⇒Isz A
Prog "YRC"
IfEnd:IfEnd
IfEnd:IfEnd
IfEnd

WhileEnd




Chapter 4-3

今回は、オマケ機能を追加してゆきます。

項目5:
 5:At
 この表記の後に4桁の入力ボックスで入力させます。
 ここには、任意の西暦年を入力します。
 ここで入力した値は、変数B に格納します。

項目6:
 6:  y
 この 6: と y の間に3桁の入力ボックスで、年齢を入力させます。
 y は歳(years)の略です。ここで入力した値は、変数Gに格納します。

項目0の西暦年 (変数A) が0 (ゼロ) の時は、項目5と項目6の入力ができないようにします。

西暦年(変数A)が0でない時は、その値に基づいて、BからGを、またはGからBを計算して表示します。

Bが入力された時は、

B-A→G

により、西暦Bの時の年齢Gを計算します。

Gが入力された時は、

A+G→B

により、G歳の時の西暦年Bを計算します。


具体的に、各ブロックにコードを実装してみましょう。



[初期設定] ブロック


0→A:0→B:0→G

(追加部分を赤文字で示した)

BとGも 0 (ゼロ)で初期化しておきます。



[表示の初期化]

Locate 1,1,"0:AD"
Locate 11,1,"<AC>"
Locate 1,2,"1:M"
Locate 10,2,"2:T"
Locate 1,3,"3:S"
Locate 10,3,"4:H"
Locate 1,4,"5:At"
Locate 10,4,"6:yσn"
  ([FUNCTION] [7] [2] [6])
Locate 14,4," "


(追加部分を赤文字で示した)

特に説明は不要と思います。小文字の y を表示する時の裏技を思い出してください。


[メニュー番号取得]

-1→N
Do
Getkey→K
LpWhile K=0
K=25⇒0→N
K=35⇒1→N
K=36⇒2→N
K=36⇒3→N
K=21⇒4→N
K=22⇒5→N
K=23⇒6→N


(追加部分を赤文字で示した)

項目 5 と 6 を押した時、それぞれのメニュー番号を変数 N に代入しています。


[入力処理]

If N=0:Then
5→X:1→Y:4→D:1→E
Prog "INPI":Z→A
Else If N=1
Then
4→X:2→Y:4→D:1→E
Prog "INPI":Z→M
Else If N=2
Then
13→X:2→Y:4→D:1→E
Prog "INPI":Z→T
Else If N=3
Then
4→X:3→Y:4→D:1→E
Prog "INPI":Z→S
Else If N=4
Then
13→X:3→Y:4→D:1→E
Prog "INPI":Z→H
Else If N=5
Then
If A:Then
5→X:4→Y:4→D:1→E
Prog "INPI":Z→B
IfEnd
Else If N=6
Then
If A:Then
12→X:4→Y:4→D:1→E
Prog "INPI":Z→G
IfEnd
IfEnd:IfEnd

IfEnd:IfEnd
IfEnd:IfEnd
IfEnd


(追加部分を赤文字で示した)

項目5 と 6 の入力ボックスを用いた入力処理は、既に作った部分と同様です。追加部分に関する特別な処理はありせん。


[計算と出力処理]

If N=0:Then
Prog "YRC"
Else If N=1
Then
M+1867→A
M=0⇒Isz A
Prog "YRC"
Else If N=2
Then
T+1911→A
T=0⇒Isz A
Prog "YRC"
Else If N=3
Then
S+1925→A
S=0⇒Isz A
Prog "YRC"
Else If N=4
Then
H+1988→A
H=0⇒Isz A
Prog "YRC"
Else If N=5
Then
If A:Then
Prog "YRC"
IfEnd
Else If N=6
Then
If A:Then
Prog "YRC"
IfEnd
IfEnd:IfEnd

IfEnd:IfEnd
IfEnd:IfEnd
IfEnd


(追加部分を赤文字で示した)


項目 5 と 6 に関する追加部分で、特別なものはなく、サブルーチン YRC を呼び出しているだけです。
具体的な細かい処理は、サブルーチン YRC が受け持ちます。


まずは、プログラムコードを一気に紹介します。サブルーチン YRC は、全ての表示を書き換えます。そして書き換える項目に、項目5と6を追加しています。


サブルーチン: YRC

If N=1:Then
A-1867→M
Else If N=2
Then
A-1911→T
Else If N=3
Then
A-1925→S
Else If N=4
Then
A-1988→H
Else If N=5
Then
B-A→G
Else If N=6
Then
A+G→B
IfEnd:IfEnd
IfEnd:IfEnd
IfEnd:IfEnd

A-1867→M
A-1911→T
A-1925→S
A-1988→H
Locate 5,1,"     "
   (スペース5個)
A>0⇒Locate 5,1,A
Locate 4,2,"      "
  (スペース6個)
M>0⇒Locate 4,2,M
Locate 13,2,"    "
  (スペース4個)
T>0⇒Locate 13,2,T
Locate 4,3,"      "
  (スペース6個)
S>0⇒Locate 4,3,S
Locate 13,3,"    "
  (スペース4個)
H>0⇒Locate 13,3,H


B-A→G
   [2014/04/25 追加修正]
If G>999 Or G<-99:Then

Locate 12,4," yσn"
  (スペース1個)
Locate 14,4,"  "
    (スペース2個)
Return

IfEnd

If B Or G:Then
Locate 5,4,"     "
  (スペース5個)
Locate 5,4,B
Locate 12,4,"   "
  (スペース3個)
Locate 12,4,G
If Abs(G):Then
13+Int(log(Abs(G)))→D
Else 13→D:IfEnd
G<0⇒D+1→D

Locate D,4,"yσn"
Locate D+1,4,"  "
  (スペース3個)
IfEnd

(追加部分を赤文字、青文字、紫文字で示した)

サブルーチン YRC では、変数A、B、G に基づき、必要な計算を行って表示しています。


青文字の部分(エラー処理)
変数G(年齢)が大き過ぎ、あるいは小さ過ぎの場合は、3桁を超える表示になるので、正常な表示を行えません。これをエラーを考え、この部分では年齢の数値を表示させずに、空白を表示することにします。

項目6の年齢表示は、画面配置の都合上、3桁以内で表示を行います。 B-A により算出される年齢 G が3桁を超えるかどうかを判定します。年齢 G は、B-A により算出されるので、負の数になることもあり得ます。この場合は負記号 ”-”も1桁になります。

そこで、年齢G が3桁を超える条件は、以下のようになります。
・正の整数だと999が3桁の最大なので、これを超える場合として G>999、
・負の整数だと、-99が3桁の最大なので、これを下回る場合として G<-99


そして年齢Gが3桁を超える場合は、項目6(年齢表示)には空白を表示し、"y"を表示します。

エラーの場合は、以下に続く項目5と6の表示を行わずに、サブルーチンを終了させて、メインルーチンへ戻るようにします。そこで、Returnコマンドを用います。

 ⇒ CasioBasicコマンドリファレンス
  - Return


赤色の部分
ここでは、項目5と項目6の計算結果を表示します。

但しプログラムを起動した時は、このおまけ機能はまだ使っていません。使っていないのに表示をするのはおかしい。
そこで、変数 G と B のいずれかが初期値(0)でない時にのみ、表示を行うことにし、具体的には以下のIf 文を使っています。

If B Or G Then
[項目5と6の表示]
IfEnd



紫色の部分
文字"y" の表示位置は年齢Gの桁数に応じて変化しますので、この部分で文字"y"を表示する桁を調べています。

この処理が面倒なら、最初から文字"y"を表示させないようにしても構いません。但し、fx-5800Pでプログラムを作る際には、表示変数の桁数が分かると便利なことがあるので、その方法を紹介します。

fx-5800P 搭載のCasioBasicには、文字列処理のコマンドがありません。そこで数学的に変数Gの桁数を調べる必要があります。

正の整数Gの桁数は 1 + Int(log(G)) で求まります。

Gの表示開始桁は 12 です。"y" の表示桁を D とすると、以下の式から D を計算します。

13+Int(log(Abs(G))→D

関数 Abs( ) は、 ( ) 内の数の絶対値を求めます。

対数関数 log( ) の ( ) 内は正の数でなければエラーになります。これは対数関数の数学的性質です。

そこで、まず年齢変数Gが0(ゼロ)でない時、つまり

If Abs(G) Then

の時に、Gを Abs( ) 関数で一旦正の数にして、対数関数 log( ) を使い、Gの桁数から "y" の表示桁を求めます。

そして、Else により G が 0 (ゼロ)の時の "y"の表示桁を 13→D で決定しています。

最後に、G が負の数の時は、マイナス記号が頭に追加されるので、"y" の表示桁数D を1つ増やしています。


※ 変数 G の桁数の求め方について

変数の桁数を求めて、それに関する表示桁を求める方法については、こちら で説明していますので、参考にしてください。


今回作成したプログラムコードを以下にまとめます。

プログラム名: CH4-3

0→A:0→B:0→G

Locate 1,1,"0:AD"
Locate 11,1,"<AC>
"
Locate 1,2,"1:M"
Locate 10,2,"2:T"
Locate 1,3,"3:S"
Locate 10,3,"4:H"
Locate 1,4,"5:At"
Locate 10,4,"6:yσn"
  ([FUNCTION] [7] [2] [6])
Locate 14,4," "

While 1

-1→N
Do
Getkey→K
LpWhile K=0
K=25⇒0→N
K=35⇒1→N
K=36⇒2→N
K=36⇒3→N
K=21⇒4→N
K=22⇒5→N
K=23⇒6→N


If N=0:Then
5→X:1→Y:4→D:1→E
Prog "INPI":Z→A
Else If N=1
Then
4→X:2→Y:4→D:1→E
Prog "INPI":Z→M
Else If N=2
Then
13→X:2→Y:4→D:1→E
Prog "INPI":Z→T
Else If N=3
Then
4→X:3→Y:4→D:1→E
Prog "INPI":Z→S
Else If N=4
Then
13→X:3→Y:4→D:1→E
Prog "INPI":Z→H
Else If N=5
Then
If A:Then
5→X:4→Y:4→D:1→E
Prog "INPI":Z→B
IfEnd
Else If N=6
Then
If A:Then
12→X:4→Y:3→D:1→E
Prog "INPI":Z→G
IfEnd
IfEnd:IfEnd
IfEnd:IfEnd
IfEnd:IfEnd
IfEnd

If N=0:Then
Prog "YRC"
Else If N=1
Then
M+1867→A
M=0⇒Isz A
Prog "YRC"
Else If N=2
Then
T+1911→A
T=0⇒Isz A
Prog "YRC"
Else If N=3
Then
S+1925→A
S=0⇒Isz A
Prog "YRC"
Else If N=4
Then
H+1988→A
H=0⇒Isz A
Prog "YRC"
Else If N=5
Then
If A:Then
Prog "YRC"
IfEnd
Else If N=6
Then
If A:Then
Prog "YRC"
IfEnd
IfEnd:IfEnd
IfEnd:IfEnd
IfEnd:IfEnd
IfEnd

WhileEnd



プログラムの構造

[初期設定]

[表示の初期化]

While 1

    [メニュー番号取得]

    [入力処理]

    [計算と出力処理]

WhileEnd




16桁・4行の限られた画面の中に、7つの項目を押し込んで全ての換算値を1画面に表示することができました。

このプログラムを使っていて、ふと思ったことがあります。

和暦は、きりの良い1月から変わるわけではなくて、天皇崩御により年号が変わるのだから、例えば昭和は、何年何月から何年何月までだろうか?

Googleなどで調べれば良いだけですが、今回のプログラムに各年号の始まりと終わりの年月日を表示する機能を組み込めないだろうか?

表示のための画面は、サブルーチンを利用してやればよく、表示が終われば、元の画面に戻すことはま、まぁできるでしょう。
それよりも、問題は呼び出すための番号を表示する場所がもう無いことです。

実は、うまい手があることに気がつきました。それを、次回紹介しようと思います。



つづく...


CasioBasic入門21 / 目次




応援クリックをお願いします。励みになるので...

人気ブログランキングへ


FC2ブログランキングへ



カシオプログラム関数電卓 FX-5800P-N カシオプログラム関数電卓 FX-5800P-N
(2006/09/22)
CASIO(カシオ)

商品詳細を見る



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

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

関連記事

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

Casio Basic入門19

Casio Basic入門
<目次>

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

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

前回: Casio Basic入門18 - Chapter 3


Chapter 4
はじめに

◆ Chapter 4 の目標: 換算プログラムを作る


Chapter 3
では、自由自在に入力を行うための 入力ボックスINPI (0以上の整数用)を作りました。INPI のプログラムや使い方は、プログラムライブラリ - 入力ボックス にまとめてあります。

そこで、Chapter 4 では、入力ボックスを利用して、換算プログラムを作ってみます。

[2015/01/17 追記]
Chapter 3 で作った入力ボックス INPIChapter 6 で大幅に応答性を向上させて INPI 2.1 にバージョンアップしています。プログラムライブラリ- 入力ボックス では、バージョンアップした INPI 2.1 のプログラムを掲載しています。本 Chapter では、バージョンアップした INPI を使うことをお勧めします。



Chapter 4-0
和暦・西暦換算プログラム

正の整数と0を入力できる「入力ボックス」を利用した換算プログラムとして、和暦・西暦換算プログラムを作ろうと思います。

変換の対象は、

・西暦
・明治
・大正
・昭和
・平成

です。どれかを入力すれば残りの年号で換算された結果が表示されます。

これだけではツマラナイので、オマケ機能を搭載します。

別の入力欄に西暦年を入力すると、その年での年齢を表示し、年齢を入力すると、その年齢になる西暦何年を表示する...そんな換算機能をオマケとして追加します。

実際の完成画面を見てみましょう。

YrConv2 

0:AD は西暦年を示します。この画像では、>>>> と表示されていて、4桁の入力範囲を示しています。
つまり、入力モードになっていて、右下に確定を促す  Eマーク が表示されています。
今この状態では 「入力ボックス」プログラム INPI が走っています。

なお、
1:M は明治、2:T は大正、
3:S は昭和、4:H は平成です。


YrConv3 

西暦に 1990 と入力して、[EXE] キーを押して入力を確定すると...この画像のような表示になります。

明治123年、大正79年、昭和65年、平成2年、と表示されます。平成2年以外の和暦年は実際に存在しない年号です。明治のままだったら123年と言うことです。

[4] を押すと、4:H に入力ボックスが現れ、そこに平成年を入力できます。すると、他の項目が連動して変化します。

どれでも好きな項目のメニュー番号を押して入力すれば、他の項目が連動して換算した年を表示します。


オマケ機能は、5:At6: y  で、これらに入力しても 項目0~4への影響はありません。つまりオマケ機能は別の変換機能と言えます。

YrConv4 

[5] を押すと、5:At が入力モードになります。

YrConv5 

5:At に 2020 と入力して確定すると、6: y6:30y に変化しました。
平成2年生まれの人は、At 2020 つまり、2020年では30歳、と教えてくれています。

ここで、項目6 に別の値を入力してみます。

YrConv6  

項目6が入力モードになっています。

ここで、40 を入力します。

YrConv7 

つまり、平成2年生まれの人が40歳の時は、西暦何年か?を調べることになり、

5:At2030

と表示されています。平成2年生まれ(西暦1990年生まれの人が40歳になるのは西暦2030年だと分かります。


この人が、高校卒業、つまり18歳の時は、西暦何年か?


項目6に18を入力すると、

YrConv8 


それは、2008年だと分かります。

YrConv9 


オマケ機能は、このように過去や将来の西暦年や年齢が簡単に分かるもので、履歴書を書いたり個人の歴史を振り返るのに使えると思います。


今回は、このような換算プログラムを作ってみます。



Chapter 4-1
最初にプログラム構造を考える

CasioBasic入門 をここまで進んでくれば、プログラムを作る前に全体の構造を決めることの大切さ がお分かりでしょう。そこで、今回も最初にプログラム全体の構造を考えることにします。

「入力ボックス」の動作確認用のメインプログラム CH3-1M を一度経験していれば、同様の構造で良いことが分かります。

プログラムの構造

[初期設定]    (変数の初期設定)

[初期化処理]    (表示の初期化)

While 1

 [メニュー番号取得]    (押されたキーからメニュー番号を得る)

 [入力処理]    (入力ボックス INPI を使う)

 [計算と出力処理]    (換算値の計算とその表示)

WhileEnd


次に、このプログラムで使う予約変数、つまりサブルーチンで変更されては困る変数を決めます。入力ボックスで使っている変数とかち合わないようにします。入力ボックスで使っている変数は こちら を参照のこと。

メインルーチンの予約変数
A: 西暦年
M: 明治年
T: 大正年
S: 昭和年
H: 平成年
N: メニュー番号

それぞれ、分かりやすいアルファベットを使っています。但しメニュー番号は、メニューのM を使いたいところですが、明治年で M を使うので、メニュー番号には N を使うことにします。

INPI で使っている変数と重複しないこと!


使い捨て変数については、プログラムを作りながら決めてゆけば良いので、ここでは決めません。但しサブルーチンで変更されても良いように、各ブロック内で使い切ることを意識してゆきます。

「使い切る」とは、ブロック内では変更されては困るが、ブロックの処理が終われば、その変数はどのように変更されても、プログラムの動作に一切影響が無い....ように使う、という意味です。


換算計算の概略

換算プログラムで一番大切なのが換算計算です。おおまかな方針を決めます。

そのために必要なものは、各和暦の元年(1年)の西暦年です。Googleなどで検索すれば分かりますね。

・明治元年: 1868年
・大正元年: 1912年
・昭和元年: 1926年
・平成元年: 1989年


西暦年 A を入力したとき
・明治年: A-1867→M
・大正年: A-1911→T
・昭和年: A-1925→S
・平成年: A-1988→H

と、各和暦年を計算できます。

試しに1868年はどうなるか?
明治元年(1年)なので、1867を引き算すれば良いですね。
各和暦の元年に相当する西暦年よりも1だけ小さい値を引き算すれば良いことが確認できました。


明治年 を入力したとき
・西暦年: M+1867→A

ここで、西暦年がAが得られるので、各和暦年は西暦年から換算するようにします。
・大正年: A-1911→T
・昭和年: A-1925→S
・平成年: A-1988→H

和暦を西暦年から換算するようにすれば、同じ計算で済むのでバグが入りにくいわけです。
さらに他の和暦への換算も、同じ計算の繰り返しになるので、計算をサブルーチンで行うことでプログラムの効率化が出来ます。


以下、同様にして...

大正年 を入力したとき
・西暦年: T+1911→A
・明治年: A-1867→M
・昭和年: A-1925→S
・平成年: A-1988→H


昭和年 を入力したとき
・西暦年: S+1925→A
・明治年: A-1867→M
・大正年: A-1911→T
・平成年: A-1988→H


平成年 を入力したとき
・西暦年: H+1988→A
・明治年: A-1867→M
・大正年: A-1911→T
・昭和年: A-1925→S

これらは、まず最初に西暦年を求め、次に西暦年から各和暦年を計算すると言う順序です。従ってプログラムも、この順序で計算するようにします。

まずは、おまけ機能なしで、西暦と和暦の換算を実装してゆきます。


プログラムの構造

[初期設定]    (変数の初期設定)

[表示の初期化] 

While 1

 [メニュー番号取得]    (押されたキーからメニュー番号を得る)

 [入力処理]    (入力ボックス INPI を使う)

 [計算と出力処理]    (換算値の計算とその表示)

WhileEnd



Chapter 4-2
西暦・和暦換算プログラムを実装する

西暦・和暦換算プログラム名を CH4-2 としましょう。各ブロックのプログラムソースは以下のようになります。


[初期設定] ブロック


0→A

全ての計算は西暦年 A を基準に行うので、Aを 0 で初期化しておきます。


[表示の初期化]

Locate 1,1,"0:AD"
Locate 11,1,"<AC>
"
Locate 1,2,"1:M"
Locate 10,2,"2:T"
Locate 1,3,"3:S"
Locate 10,3,"4:H"


これは、特に説明が不要だと思います。


[メニュー番号取得]

-1→N
Do
Getkey→K
LpWhile K=0
K=25⇒0→N
K=35⇒1→N
K=36⇒2→N
K=36⇒3→N
K=21⇒4→N


Getkey の引数に K を使っています。入力ボックス INPI でも Getkey の引数に K を使っています。
キーコード K を得たら直ちに、メニュー番号 N に置き換えているので、INPI で K を使っても影響がありません。キーコード K は使い捨て変数として、このブロックで使い切っているので、問題はありません。

なお、メニュー番号は、変数 M としたいと頃ですが、明治年に M を使うので、M の 次のアルファベット N をメニュー番号にしました。


[入力処理]

If N=0:Then
5→X:1→Y:4→D:1→E
Prog "INPI":Z→A
Else If N=1
Then
4→X:2→Y:4→D:1→E
Prog "INPI":Z→M
Else If N=2
Then
13→X:2→Y:4→D:1→E
Prog "INPI":Z→T
Else If N=3
Then
4→X:3→Y:4→D:1→E
Prog "INPI":Z→S
Else If N=4
Then
13→X:3→Y:4→D:1→E
Prog "INPI":Z→H
IfEnd:IfEnd
IfEnd:IfEnd
IfEnd


ここでは、If ~ Else If 。。。。 IfEnd を使って、それぞれのメニュー番号に応じた入力を行っています。
入力ボックス INPI に渡す引数として、変数 X (入力開始の桁位置), Y (入力開始の行位置), D (入力桁数), E (確定モードの表示方フラグ) を設定した後 Prog "INPI" を実行。
その直後に、INPI の戻り値である Z を必要な変数に代入しています。

If や Else If ごとに1つのブロックと考えて良いわけで、それぞれのブロックで、変数X, Y, D, E, Z を使い捨て変数として使い切っているので、このように何度も使っても問題ありません。 

If および Else If で出てくる If の数と同じだけの IfEnd を最後に持ってきています。この数を間違えないことが重要です。


[計算と出力処理]

If N=0:Then
Prog "YRC"
Else If N=1
Then
M+1867→A
M=0⇒Isz A
Prog "YRC"
Else If N=2
Then
T+1911→A
T=0⇒Isz A
Prog "YRC"
Else If N=3
Then
S+1925→A
S=0⇒Isz A
Prog "YRC"
Else If N=4
Then
H+1988→A
H=0⇒Isz A
Prog "YRC"
IfEnd:IfEnd
IfEnd:IfEnd
IfEnd


ここでは、サブルーチン YRC を多用しています。なんども同じ処理を行うなら、サブルーチンにした方がプログラムソースがわかりやすくなります。

サブルーチン YRC では、項目1 (西暦年) から 項目4 (平成年) に表示する換算値を一気に表示するルーチンです。
YRC には、変数 A (西暦年) と N (メニュー番号) を引数として渡すようにします。YRC では 西暦年 A に従って、明治、大正、昭和、平成の各年を計算し、それを表示する処理をさせることにします。

プログラムの記述は、ほぼ同じことの繰り返しになっているので、記述量が多くなっていますが、とくに難しいことをしていないことはお分かりだと思います。

なお、青文字で示した部分は、無くても良いかも知れません。各和暦年は正の整数で、0 (ゼロ) はあり得ません。バージョンアップした INPI では0の入力ができるようになっています(Chapter 3 で作ったものは0の入力ができません)。そこで和暦年が絶対に 0 (ゼロ) にならないように、誤って 0 (ゼロ) と入力された時は強制的に 1 にするための処理が、青文字の部分です。

YRC には西暦年 A を渡します。YRC へ渡す西暦年 A は、和暦が 0 (ゼロ) の時に 1 だけ増やす処理をしています。こうしておくと、サブルーチン YRC での計算では和暦0年にならずに、和暦1年 (元年) と計算されます。


サブルーチン: YRC

If N=1:Then
A-1867→M
Else If N=2
Then
A-1911→T
Else If N=3
Then
A-1925→S
Else If N=4
Then
A-1988→H
IfEnd:IfEnd
IfEnd:IfEnd

A-1867→M
A-1911→T
A-1925→S
A-1988→H
Locate 5,1,"     "
   (スペース5個)
A>0⇒Locate 5,1,A
Locate 4,2,"      "
  (スペース6個)
M>0⇒Locate 4,2,M
Locate 13,2,"    "
  (スペース4個)
T>0⇒Locate 13,2,T
Locate 4,3,"      "
  (スペース6個)
S>0⇒Locate 4,3,S
Locate 13,3,"    "
  (スペース4個)
H>0⇒Locate 13,3,H


各年号の計算は、最初に予定した通りです。

赤文字で示した部分は、マイナスの年数を表示しないための処理です。
マイナスの年数は意味が無いばかりか、桁数が想定外に大きくなり、さらにマイナス記号の分も表示桁が増えるので、画面表示を乱します。これを避けるための処置が必要になります。

一気に駆け抜けてしまいましたが、特に新しいコマンドや処理を使っていないので、Casiobasic入門の最初から読んでいれば、分かると思います。


プログラム名: CH4-2

0→A

Locate 1,1,"0:AD"
Locate 11,1,"<AC>
"
Locate 1,2,"1:M"
Locate 10,2,"2:T"
Locate 1,3,"3:S"
Locate 10,3,"4:H"

While 1

-1→N
Do
Getkey→K
LpWhile K=0
K=25⇒0→N
K=35⇒1→N
K=36⇒2→N
K=36⇒3→N
K=21⇒4→N


If N=0:Then
5→X:1→Y:4→D:1→E
Prog "INPI":Z→A
Else If N=1
Then
4→X:2→Y:4→D:1→E
Prog "INPI":Z→M
Else If N=2
Then
13→X:2→Y:4→D:1→E
Prog "INPI":Z→T
Else If N=3
Then
4→X:3→Y:4→D:1→E
Prog "INPI":Z→S
Else If N=4
Then
13→X:3→Y:4→D:1→E
Prog "INPI":Z→H
IfEnd:IfEnd
IfEnd:IfEnd
IfEnd

If N=0:Then
Prog "YRC"
Else If N=1
Then
M+1867→A
M=0⇒Isz A
Prog "YRC"
Else If N=2
Then
T+1911→A
T=0⇒Isz A
Prog "YRC"
Else If N=3
Then
S+1925→A
S=0⇒Isz A
Prog "YRC"
Else If N=4
Then
H+1988→A
H=0⇒Isz A
Prog "YRC"
IfEnd:IfEnd
IfEnd:IfEnd
IfEnd

WhileEnd




プログラムの記述量が多いので、ちょっと面倒に思えますが、それでも 入力ボックス INPI やサブルーチン YRC を使うことで、記述量はかなり減りました。


次回は、おまけ機能を実装してゆきます。



つづく...


CasioBasic入門20 / 目次




応援クリックをお願いします。励みになるので...

人気ブログランキングへ


FC2ブログランキングへ



カシオプログラム関数電卓 FX-5800P-N カシオプログラム関数電卓 FX-5800P-N
(2006/09/22)
CASIO(カシオ)

商品詳細を見る



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

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

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

楽屋裏 - ループ脱出の問題(カシオの回答)

楽 屋 裏
e-Gadget


原因不明のループ脱出について、カシオからの回答が得られました。

発端は以下のコードでの異常動作です。

問題プログラム1
While 1
20→C
Do
Dsz C
LpWhile 1
WhileEnd


これは、最後のWhileEndでSyntax ERROR が発生します。


問題プログラム2
Do
20→C
Do
Dsz C
LpWhile 1
LpWhile 1

この場合は、Syntax ERORR の表示はないものの、外側の LoWhile から外側のDo へ戻りません。


カシオの回答では、

1.2重ループ構造という条件は無い。
Dsz/Isz/⇒で偽の場合に、次のコマンドをスキップするという
仕様のため、次の条件の場合にSynERRORとなる。
・条件1: DoやWhileを用いたループがあること
・条件2:ループ脱出に Dsz/Isz/⇒を利用すること
・条件3:Dsz/Isz/⇒ の次のコマンドが、LpwileかWhileEndであること


これまでのところ、Do / While ループの脱出に Dsz / Isz / ⇒ を使っても、2重ループでなければ問題発生の経験が無いので、本質はここだけにないと思われます。


2. 制御のスタック管理において、古くからあるDsz/Isz/⇒ と 後で実装された DoループやWhileループで整合性がとれないのが原因ではないかと想像される件については、その通りであって、従来のコマンドを慣れ親しんだユーザーを配慮し、従来仕様をあえて残したとのこと。


3. Goto 0:Lbl 0 による回避策について
これについては、私の質問の仕方に問題があったため、改めて質問をする予定です。


Santaro様が見つけられた Goto の謎が、項目1に対する理解を深めるものと思いますので、引き続きカシオとの対話を続けようと思います。


Santaro様によるGoto の謎については、以下のエントリーに続くコメントにあります。

楽屋裏 - 多重ループの謎 


以上、報告でした。




応援クリックをお願いします。励みになるので...

人気ブログランキングへ


FC2ブログランキングへ


keywords: fx-5800PCasioBasicDo/Whileループ脱出のエラープログラミング入門プログラム関数電卓

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

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

Casio Basic入門18

Casio Basic入門
<目次>

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

最終:
2015/01/17


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

2014年12月22日: テンキー入力判定の If 文の条件式の記述の修正、および必要なコードの追加の説明の抜けがありましたので、修正、追加しました。
2015年1月17日:DEL キーを押した時のシミュレーションで説明をプログラムに則して分かり易く変更しました



Chapter 3

◆ Chapter 3 の目標: 自由自在な入力をする

前回: Casio Basic入門17


「入力ボックス」プログラムの動作確認のためのメインプルーチン CH3M を変更しました。

プログラム名 CH3M

Locate 1,1,"0:"

While 1

-1→M
Do
Getkey→K
K=25⇒0→M
LpWhile K=0

If M=0:Then
3→X:1→Y:10→D:2→E
    (パラメータ D と E を追加)
Prog "CH3-1S"
Z→A

IfEnd

If M=0: Then
Locate 3,2,"              "
    (スペース14個)
Locate 3,2,A

IfEnd

WhileEnd


================


表示形式へ対応した「入力ボックス」プログラム CH3-1S を作りました。

プログラム名 CH3-1S

EngOff:Norm 2
0→Z:D>10⇒10→D
37→DimZ
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]
If E=2:Then
Locate 6,4,"<EXE>:ENTER"
Else If E=1
Then
Locate 15,4,"►t"
    ([FUNCTION] [7] [3] [4])
Locate 16,4,"E"
IfEnd;IfEnd

For 1→I To D
Locate X+I-1,Y,">"
Next


0→C
    (桁数カウンタの初期化)
Lbl 0


Do
Getkey→K
LpWhile K=0

If (K≧21 And K≦23) Or K=25 Or (K≧31 And K≦33) Or (K≧35 And K≦37)
Then
If C<D:Then
10Z+Z[K]→Z
Locate X,Y,Z
Isz C         [2015/01/17 修正]
IfEnd
IfEnd

K≠47⇒Goto 0

If E=2:Then
Locate 6,4,"           "
    (スペース11個)
Else If E=1
Then
Locate 15,4,"  "
    (スペース2個)
IfEnd:IfEnd

0→DimZ
Return



「入力ボックス」プログラムの構造

[初期化処理]

[キーコードからキーの数への変換]

Lbl 0

        [キーコード取得]

        [戻り値 Z の算出と表示]

K≠47⇒Goto 0

[後処理]

Return


現状では、入力はできても、打ち間違いの修正ができません。そこで、[DEL] キーを使った削除機能を追加します。



Chapter 3-2
入力ボックス - 末尾文字の削除機能を追加する

入力ミスや入力修正に対応するために、[DEL] キーを押すと末尾の1桁を削除する機能を追加します。

[DEL] キーは、キーコードが34なので、キーコードが34の時の処理を追加するにあたり、枠組み(構造)を先に作っておきます。


EngOff:Norm 2
0→Z:D>10⇒10→D
37→DimZ
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]
If E=2:Then
Locate 6,4,"<EXE>:ENTER"
Else If E=1
Then
Locate 15,4,"►t"
    ([FUNCTION] [7] [3] [4])
Locate 16,4,"E"
IfEnd;IfEnd

For 1→I To D
Locate X+I-1,Y,">"
Next


0→C
    (桁数カウンタの初期化)
Lbl 0


Do
Getkey→K
LpWhile K=0

If (K≧21 And K≦23) Or K=25 Or (K≧31 And K≦33) Or (K≧35 And K≦37)
Then
If C<D:Then
10Z+Z[K]→Z
Locate X,Y,Z
Isz C
IfEnd

Else If K=34
Then
[末尾文字の削除機能のプログラムコード]
IfEnd

IfEnd

K≠47⇒Goto 0

If E=2:Then
Locate 6,4,"           "
    (スペース11個)
Else If E=1
Then
Locate 15,4,"  "
    (スペース2個)
IfEnd:IfEnd

0→DimZ
Return



ここでは、Else If と言う記法を使って、

If [条件1]: Then
[処理1]

Else If [条件2]
Then

[処理2]

IfEnd
IfEnd


このような複数条件分岐の構造を利用しています。

この記法の優れているのは、[条件1] と [条件2] の両方を判定することがなく、[条件1] が真なら [条件2] の判定を行なわないので、プログラム動作の効率が上がる点にあります。

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


では、[DEL」 キーを押した時の具体的処理を作りましょう。

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


シンプルですね。


1) 戻り値 Z の末尾(一番右)の数を消した Z の値を計算
  Int(Z÷10)→Z

2) 桁数カウンタC1つ減らし、
  C-1→C

3) 削除された桁に 入力範囲を示す ">" を表示する
  Locate X+C,Y,">"


細かい動作をコードを1行づつ追いかけながらシミュレーションして確認すると...
[2015/01/17 修正] 以下のシミュレーションで カウンタCが示す桁 の説明を Locate X+C,Y,Z の X+C が示す桁の説明に変更し誤りを修正。


削除前
桁数カウンタ:C=5
Z=12345
1 2 3 4 5 > > > > >




  X+C が示す
桁数カウンタCには、現在の桁数が入っています。この "5" が削除されることになります。
X+C は次に入力する桁を示しています。この状態で、[EXE] キーが押された時は、

1) Z の更新: Int(Z÷10)→Z
桁数カウンタ:C=5、Z=1234 に更新
1 2 3 4 5 > > > > >




  X+C が示す桁
12345 の末尾の "5" を削除すると、1234 になるので、Z を更新する必要があります。
12345÷10 とすると 1234.5 になり、整数部を取り出せば 1234 になります。


2) 桁数カウンタを1つ戻す: C-1→C
桁数カウンタ:C=4、Z=1234
1 2 3 4 5 > > > > >



  X+C が示す桁
Dsz C ではなく C-1→C とする必要があります。
削除を続けて、1文字も入力されていない状態になれば、C=0 となります。ここで Dsz C を実行すると次のコマンドをスキップするので問題です。さて、この状態では、5桁目の "5" の表示が残っています。


4) 削除された桁に ">" を表示: Locate X+C,Y,">"
桁数カウンタ:C=4、Z=1234
1 2 3 4 > > > > > >



  X+C が示す桁
">"を表示する桁は、X+C で、"5" が表示されている桁です。

これで、表示が正しくなります。


なお、桁数カウンタC が0の時、つまり数字が全く入力されていない状態で、削除を行うと表示がおかしくなります。
そこで、C=0 の時には削除動作を行えないように制限をかけます。言い換えれば、C>0 つまり C が0でない時だけ、上の動作を行うように、プログラムコードを書きます。

なお、この制限をかけると、C は負の数になることが無いので、以下のように If C:Then と言う表記で問題ありません。

[削除動作]
If C:Then
Int(Z÷10)→Z
C-1→C
Locate X+C,Y,">"
IfEnd



これで、削除機能が追加されました。プログラム CH3-1S を修正します。変更部分を赤文字で示します。

プログラム名 CH3-1S

EngOff:Norm 2
0→Z:D>10⇒10→D
37→DimZ
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]
If E=2:Then
Locate 6,4,"<EXE>:ENTER"
Else If E=1
Then
Locate 15,4,"►t"
    ([FUNCTION] [7] [3] [4])
Locate 16,4,"E"
IfEnd;IfEnd

For 1→I To D
Locate X+I-1,Y,">"
Next


0→C
    (桁数カウンタの初期化)
Lbl 0


Do
Getkey→K
LpWhile K=0

If (K≧21 And K≦23) Or K=25 Or (K≧31 And K≦33) Or (K≧35 And K≦37)
Then
If C<D:Then
10Z+Z[K]→Z
Locate X,Y,Z
Isz C
IfEnd

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


IfEnd:IfEnd

K≠47⇒Goto 0

If E=2:Then
Locate 6,4,"           "
    (スペース11個)
Else If E=1
Then
Locate 15,4,"  "
    (スペース2個)
IfEnd:IfEnd

0→DimZ
Return



「入力ボックス」プログラムの構造

[初期化処理]

[キーコードからキーの数への変換]

Lbl 0

        [キーコード取得]

        [戻り値 Z の算出と表示]

K≠47⇒Goto 0

[後処理]

Return


今回の機能追加は、[戻り値 Z の算出と表示] の中なので、プログラム構造は変わりません。



それでは、プログラム CH3M を走らせて、動作確認をしてみましょう。

1つ、想定外の動作になることがあります。バグです。分かりますか?


プログラムの動作チェック

入力モードに入り、最初に 0(ゼロ)を入力した後 1、2、3 と入力してみます。表示は、0123 ではなくて、123 となります。

ここで、[EXE] キーで入力を確定すると 2行目に 123 と表示されます。戻り値 Z が123 になっていることが分かります。Z の値には問題ありません。


10Z+Z[K]→Z
Locate X,Y,Z

の動作に注目してみます(Zの初期値は0です)。

最初に 0 を入力すると、上の計算結果、Z には 0 が入ります。
次に 1 を入力すると、Z には 1 が入ります。
続いて 2 を入力すると、Z は 12。
さらに 3 を入力すると、Zは 123。
問題ありません。

ところが、Locate X,Y,Z に注目すると、
最初に 0 を入力すると Z =0 なので、0 が表示されます。
次に、1 を入力すると、Z=1 なので、1 と表示され、01 とは表示されません。
続いて 2 を入力すると、Z=12 なので、12 と表示され、
さらに 3 を入力すると、Z=123 なので、123 と表示されます。

ここまでは、プログラムの内部仕様上も問題なく、プログラム実行上も問題ありません


もう一度入力モードにして、0、1、2、3 と順に入力してみます。
次に、[DEL]キーを1回押します。

あれっ?表示上何も変化がありません。削除動作に失敗しているようです!
このまま、[EXE] キーで確定すると、2行目には 12 と表示されます。
Z は正しく、その末尾の数字が削除され 12 になっているのに、画面表示上では削除動作に失敗しています。



プログラムのデバッグ

原因を調べるために、デバッグを行ってみます。デバッグでは、桁数カウンタ C戻り値 Z の変化を確認しようと思います。

そこで、入力ボックス CH3-1S で、

K≠47⇒Goto 0

の上に、次のデバッグ用コード、4行を追加してみます。

Locate 1,3," "        (スペース1個)
Locate 1,3,C
Locate 1,4,"                "
        (スペース16個)
Locate 1,4,Z

これで、メインプログラム CH3M を走らせてデバッグを行いましょう。

入力モードに入って、0 を入力すると、以下のような画面表示になります。

================
0:0>>>>>>>>>

1
0
================


C=1、Z=0 になっていて、これは内部動作上正常です。

次に 1 を入力すると、以下のようになります。

================
0:1>>>>>>>>>

2
1
================


C=2、Z=1 になっています。Z=1 は正常ですが、C=2 は異常ですね。
C=1 となっているべきです。従って、テンキー入力の処理のどこかで、この異常が発生してることが分かります。

続いて [DEL]キーを押してみましょう。

================
0:1>>>>>>>>>

1
0
================


C=1、Z=0 になっています。
C=1 は正常です。
Z は、1 から削除動作で0になるべきなので、これも正常動作です。


つまり、削除動作は正常に機能していると考えられます。
ここで、以下のように推測できます。

0 に続いて 1 を入力すると、桁数カウンタC の処理で問題が発生し、1 となるべきが 2 になっている。

別のケースで調べてみると、以下が分かります。

・1文字目が 0 の時、1以外のテンキーを押しても、同じ症状が確認される。
・1文字目が 0 以外の時は、この症状が発生しない。


従って、1文字目が 0 の時、次にテンキーを押した時に、桁数カウンタ C の処理に問題がある ようです。


プログラムの改善(デバッグ)

つまり、今回のバグは1文字目の 0 を入力した時、次のテンキー入力で、C を増やしてはいけないのに、増やしていることが問題です。通常は、テンキー入力時にCを1つ増やすのは正常ですが、1文字目が 0 の時は次のテンキー入力時に例外的に Isz C をスキップして実行しない例外処理が必要だと分かります。

そこで、Isz C を実行する前に、条件判定をして、Isz C を実行するかしないか...と言う理屈で例外処理を考えてみます。


2文字目のテンキー入力の直前は、C=1 になっています。
そして、1文字目 0 であるかどうかが分ければ、

「C=1」 かつ 「1文字目が 0」 の時 Isz C を実行しない。それ以外は Isz C を実行する 

といった処理をすれば良いことになります。

ここで、1文字目のキーコードを 変数 A に代入しておくとします。
[0]キーのキーコードは25です。

すると、

C=1 かつ A=25 の時 Isz C を実行せず、それ以外は Isz C を実行する

これを、論理演算コマンドを使って表現すれば、

C=1 And A=25 ならば Isz C を実行せず、それ以外は Isz C を実行する

If 文を使って表現すると、

If C=1 And A=25
Then
Else
Isz C
IfEnd


となります。

Then の後何も処理をせず、Else の後だけ処理を行うような If 文を使うのは効率が悪い気がします。プログラムの実行速度が無駄に遅くなることだけは確実です。それは避けたいと思うのです。

そこで、

Not (C=1 And A=25) ならば Isz C を実行する

としても良いので、

If Not (C=1 And A=25)
Then
Isz C
IfEnd


とスッキリします。

さらに、条件ジャンプ命令:⇒ を使えば、さらにスッキリします。

Not (C=1 And A=25)⇒Isz C


さて、ここで ド・モルガンの法則 を思い出しましょう。

Not (X And Y) = (Not X) Or (Not Y)

でした(以下のコマンドリファレンス、または Casio Basic入門16 を参照 )。

※ CasioBasicコマンドリファレンス
      - 論理演算: Or / And /Not


ド・モルガンの法則 を使うと、

Not (C=1 And A=25)

は、


C≠1 Or A≠0


となるので、

C≠1 Or A≠25⇒Isz C

と、非常にスッキリとしたコードになりました。


これまで作ってきたプログラムで、テンキー入力時の処理にある

Isz C



C≠1 And A≠25⇒Isz C

に置き換えれば良いことがわかります。


最後に、1文字目のキーコードを変数A に入れておくには、テンキー入力時の処理の最後で、C=1 の時に K→A とします。

C=1⇒K→A


テンキー入力時の処理部分の改善

If (K≧21 And K≦23) Or K=25 Or (K≧21 And K≦23) Or (K≧31 And K≦33) Or (K≧35 And K≦37)
Then
If C<D:Then
10Z+Z[K]→Z
Locate X,Y,Z
C≠1 Or A≠25⇒Isz C
C=1⇒K→A
IfEnd


(追加部分を赤文字で示した)


これで、例外処理の実装が終わり、期待通りの動作をするようになりました。

デバッグ用のコード(4行)を削除しておきましょう。



※ 局所変数 / 大域変数 と言う用語を概念として使ったが、大きな混乱と誤解を招きそうなので、以下の記述を大幅に変更した [2014/11/08]

変数の扱い方について: 使い捨て変数 と 予約変数

Casio Basicで使う変数は、全てのプログラムで共通して使えます。具体的に言えば、例えば変数 K をメインルーチンとサブルーチンの両方で使っている場合は、メインルーチンとサブルーチンのどちらかでその値を変更すると、もう一方での値も同時に変更されます。

サブルーチンを使う場合は、「変数の値がいつ・どこで変更されるのか」を よく考えてプログラムを作る必要があります。

例えば変数 K に着目してみます。変数 K は、メインルーチンと「入力ボックス」の両方でキーコードを格納する変数として利用しています。しかし変数 K が勝手に書き換えられても問題は発生していません。変数 K を明確に狭い範囲のみで使うように留意してプログラムを書いたことがポイントです。


メインルーチン CH3M では、 [メニュー番号取得]  ブロックで、Getkey→K を実行し、そこで変数 K を使っています。このブロック内では、取得したキーコードに従って、メニュー変数 M にメニュー番号を入れています。このブロックは、メニュー番号を取得する機能として完結しており、メニュー番号を取得できれば、その後で変数Kが変化しても全く影響がありません。変数 は、いわば使い捨ての変数です。

一方、メニュー番号 はメインプログラム全体で、正しい値になっていることを保証する必要があります。変数 M は、プログラムにより予約された変数です。メインルーチンで予約された変数 をサブルーチンで使うと、M の値が保証されず、プログラム動作に影響を与えます。

変数を使う時には、それが使い捨て変数なのか予約変数なのかを、意識する必要があります。


では、「入力ボックス」と共用する変数 X, Y, Z, D, E, Z に着目します。

3→X:1→Y:10→D:2→E
Prog "CH3-1S"
Z→A


変数 X, Y, D, E は、「入力ボックス」サブルーチン CH3-1S の動作を決める変数なので、直前で値を設定しています。サブルーチンが動作して、メインルーチンに戻ってくれば、変数 X, Y, D, E は用済みです。これらの値が変更されても問題がありません。変数 X, Y, D, E は使い捨て変数として使えます。

「入力ボックス」サブルーチンは、入力値を変数 Z に入れてから終了します。次に「入力ボックス」が使われると、Z の値は変わってしまうので、「入力ボックス」の直後で Z→A を実行して、入力値をメインルーチンの予約変数 A に代入する必要があります。変数 Z はメインルーチンの使い捨て変数として使うことができます。

このように、メインルーチンとサブルーチンで共有する変数、メインルーチンの予約変数使い捨て変数意識して使い分けることが大切です。

プログラム名 CH3M

Locate 1,1,"0:"

While 1

-1→M
Do
Getkey→K
K=25⇒0→M
LpWhile K=0


If M=0:Then
3→X:1→Y:10→D:2→E
    (パラメータ D と E を追加)
Prog "CH3-1S"
Z→A

IfEnd

If M=0: Then
Locate 3,2,"              "
    (スペース14個)
Locate 3,2,A

IfEnd

WhileEnd


==========


最後に、変数 A に着目してみます。

メインルーチンでは、変数 A をメニュー番号0の項目で得た入力値を格納するための予約変数として使っています。
一方で、「入力ボックス」では、変数 A を「1文字目のキーコード」を格納する変数として使っています。

メインルーチンでの変数 A はメインルーチン全体で使うものだから、もし変更されては誤動作します。つまりメインルーチンと「入力ボックス」で変数Aを共有してはいけません。

今のところ、CH3M を実行しても誤動作しないのは、運が良いだけです。と言うのも、メニューが1つしかなく、戻り値から 変数A を取得(Z→A)した後、次の「入力ボックス」使用の前に A の値を表示しているので、問題が表面化していないだけです。

そこで、2つのプログラムで競合する変数 A の取り扱いをどうするか? です。

今回のプログラムだけでなく、一般にプログラムを作る際には 変数A を予約変数として比較的よく使うのではないか...と思います。そこでメインルーチンで変数 A を使用禁止にするのではなく、「入力ボックス」側でも変数 A を使いつつ問題が発生しないようにしてみます。その方が、「入力ボックス」の汎用性が向上すると思います。

その対策として、「入力ボックス」プログラムの初期化処理で、変数 A を一旦別の変数にコピーしておき、終了処理で、コピーから変数 A を元に戻してから、メインルーチンへ戻るようします。

ここで、配列変数に着目します。配列変数 Z[ ] は、37個確保していますが、そのうち10個しか使っていません。例えば Z[1] は未使用です。そこで、変数 A のコピーを Z[1] にとっておき、終了処理で 変数A を復元します。具体的には、「入力ボックス」の初期化処理で A→Z[1] としておき、終了処理で Z[1]→A とします。


配列変数の仕様による制限

なぜ、最初から 変数 A でなくで Z[1] を使わないか?という疑問が当然でてきます。

配列変数にはアクセス速度(読み書き速度)が非常に遅いと言う問題があり、入力モードのループの中では配列変数を極力使いたくない事情があります。

そこで、初期化処理と終了処理なら配列変数を使って多少時間がかかっても良いと考え、Z[1] に A のコピーを取っておく作戦を採用しました。こうすれば、入力ボックスで、入力を確定する間の Lbl / Goto ループで A を頻繁に使っても、処理速度への影響を最小限に抑えられます。

他の変数についても配列変数にコピーして使えば良いかと言えば、それにも制限があります。初期化処理でコピーに使う配列変数
が多くなれば、それだけ初期化処理に時間がかかります。その結果、入力モードに入って入力範囲を表示するまでの待ち時間が長くなってしまうので、限度があります。

現状の10個のテンキーのキーコード変換を配列変数で行う処理だけでも、それなりの待ち時間が発生しているので、このコピー作戦を無制限に使うのは良くありません。


これまで、幾つもの換算プログラムを実際に作った経験では、「入力プログラム」で使う変数が上記のもので、それほど困っていません。


Casio Basic プログラミングでの変数の扱い方

上で紹介した、使い捨て変数と予約変数の概念を意識すると、サブルーチンを使いこなせます。

1)メインルーチンで使う変数を2つに分けて考える
 ・予約変数: メインルーチン全体で使う変数で、勝手に変更されては困るもの
 ・使い捨て変数: メインルーチンのブロック内で一時的に使う変数で、ブロック外で変更されても気にならないもの

2)メインルーチンの予約変数には、「入力ボックス」で使っている変数と重複して使わない



[2014年12月23日 追記]

最後に、入力を確定した後に、入力範囲をしめす>>>>> を消去する必要があります。
そこで、現在の桁 C から入力範囲の最大位置までを " " (空白)で上書きすることにします。
以下のコードを、ループの終わり K≠47⇒Goto 0 の直後に追加します。

For 1→I To D-C
Locate X+C+1,Y," "
Next




これで、「入力ボックス」の完成です。プログラム名を 
INPI に変更します。INPI は、0と正の整数入力専用です。


入力ボックス(0と正の整数対応版)INPI Ver 1.0
EngOff:Norm 2
0→Z:D>10⇒10→D
37→DimZ
A→Z[1]
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]
If E=2:Then
Locate 6,4,"<EXE>:ENTER"
Else If E=1
Then
Locate 15,4,"►t"
    ([FUNCTION] [7] [3] [4])
Locate 16,4,"E"
IfEnd;IfEnd

For 1→I To D
Locate X+I-1,Y,">"
Next


0→C
    (桁数カウンタの初期化)
Lbl 0


Do
Getkey→K
LpWhile K=0

If (K≧21 And K≦23) Or K=25 Or (K≧31 And K≦33) Or (K≧35 And K≦37)
Then
If C<D:Then
10Z+Z[K]→Z
Locate X,Y,Z
C≠1 Or A≠25⇒Isz C
C=1⇒K→A

IfEnd

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

K≠47⇒Goto 0

For 1→I To D-C       (入力範囲 < を消去)
Locate X+C+I-1,Y," "  (スペース1個)
Next
If E=2:Then
Locate 6,4,"           "
    (スペース11個)
Else If E=1
Then
Locate 15,4,"  "
    (スペース2個)
IfEnd:IfEnd

Z[1]→A
0→DimZ
Return


(今回追加した部分を赤文字で示します)



「入力ボックス」プログラムの構造

[初期化処理]

[キーコードからキーの数への変換]

Lbl 0

        [キーコード取得]

        [戻り値 Z の算出と表示]

K≠47⇒Goto 0

[後処理]

Return



「入力ボックス」プログラムで使う変数

X: 入力開始位置のX座標(桁)
Y: 入力開始位置のY座標(行)
Z: 戻り値(確定後の入力値)
C: 桁数カウンタ
D: 入力範囲の桁数
E: 確定ガイドのフラグ
K: 取得するキーコード
I: For文のインデックス



私は、ここで完成したプログラムを、プログラム名 INPI として、様々な換算プログラムで利用しています。

実は、他に2つの「入力ボックス」を作って、使っています。

1) 入力ボックス INPI0以上の整数入力版INput-box for Positive Integer)
2) 入力ボックス INP0以上の小数入力対応版INput-box for Positive number)
3) 入力ボックス IN負の小数入力対応版INput-box)

例えば、0か自然数対応版 (INPI)を使えば、少数や負の数を入力できないので、メインルーチンでの余計な入力値管理(0か自然数数のみの入力をさせる管理)が不要になるメリットがあります。

0以上の小数入力対応版(INP)でも同様にメインルーチンで負の数の管理が不要になります。適切なバージョンを選択して使うメリットは、ここにあります。



[2014/12/31 追記]

INPI の高速化を行なった後、INPIN を作ります。

 ⇒ Casio Basic入門32 - プログラムを速くする
 ⇒ fx-5800P プログラムライブラリ - 入力ボックス




つづく...


CasioBasic入門19 / 目次




応援クリックをお願いします。励みになるので...

人気ブログランキングへ


FC2ブログランキングへ



カシオプログラム関数電卓 FX-5800P-N カシオプログラム関数電卓 FX-5800P-N
(2006/09/22)
CASIO(カシオ)

商品詳細を見る



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

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

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

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

やす (Krtyski)

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


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

実際に触って気づいたこと、自作プログラム、電卓プログラミングについて書いています

おもしろい・役に立つならクリックしてください。励みになります。

人気ブログランキングへ


FC2ブログランキングへ


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

リンク
月別アーカイブ
Sitemap

全ての記事を表示する

RSSリンクの表示
最新トラックバック
ブロとも申請フォーム

この人とブロともになる

QRコード
QR