2021年05月01日

10万円ではじめるプログラミング講座 8

全ての国民に10万円が行きわたると言う事で、様々な事情の
方がいらっしゃるかと思いますが、それが出来る人に向けて、
Excelを活用したプログラミング講座を執筆する事にしました。
「ゲームをやった時間を記録して集計出来るプログラム」を
自分で作れるようになる事を目標にしています。

前回8月9日の更新から随分時間が経ってしまいました。当時、
AI顔認識カメラと赤外線カメラの増加速度が頂点に向かって
上昇し続けており、更新作業を中断せざるを得ませんでした。

状況の変化が多少あった事と、GW期間中に巣籠りをしている
皆様に少しでも暇つぶしを提供出来ればと思い、続きを更新
する事に決めました。

前回の作業内容を忘れてしまったと言う方は、復習を兼ねて、
前回の作業を最初からやり直してみると良いかも知れません。
講座は、前回の作業内容の続きから始まります。

前回つくったプログラムが何だったか、お分かり頂けました
でしょうか。誰もが知っている“あれ”です。ところでもし、
ご自身が“あれ”をお持ちでしたら、“あれ”を取り出して、
前回つくったプログラムと、比べてみてください。

何か違いは見当たりますか。

前回つくったプログラムは、計算して答えを出した後、その
答えを使って、続きの計算をする事が出来ない事に気付くの
ではないかと思います。

例えば、"1" "+" "1" "="と入力して 2 が表示された後に"+"
"1" "="を入力して 3 を計算させる事が出来ません。改めて
"2" "+" "1" "="と入力しなければならないのです。

また、いつも使い慣れている“あれ”ならば"1" "+" "2" "="
と入力して 3 が表示された後に"="を押せば、"3+2="の結果
を計算させる事が出来るのに、前回つくったプログラムには
その機能がありません。

機種によって違いがある様ですが、“あれ”には"1" "+" "2"
"="と入力して 3 が表示された後に"4" "="を押せば、"4+2="
の結果を計算させる機能があるのに、これもありません。

前回つくったプログラムを開きます。ファイル名は
「den-dentaku.xlsm / den-dentaku.xls」として
保存したと思います。

ファイルを開くと、まっさらなExcelブックに「セキュリティ
の警告 マクロが無効にされました。」と言う表示(表現は
バージョンによって違うかも知れません)が出現している事
が確認出来ると思います。忘れずマクロを有効に設定します。

プログラムの改造を始める前に、前回つくった内容を消して
しまう事のない様、ファイル名を変えておきましょう。

ツールバー等にある「名前を付けて保存」をクリックします。

ファイル名は「den-dentaku2.xlsm / den-dentaku2.xls」等
が良いでしょう。

ファイルを開いた状態で、「Alt+F11」を押します。
「Microsoft Visual Basic」の画面が出て来ると思います。
そのまま「F7」を押して、「コード」画面を開きます。

書き換えが必要なのは、ExecuteCalc関数とその関連部分です。

enmEnzanHoの定義を書き換えます。

「コード」の先頭にカーソルを持っていき(コンボボックス
から (General)(Declarations) を選ぶ操作でも良い)、以下
のコードを探します。

Enum enmEnzanHo
enzNoTouch = 0 'NoTouch 何もしない
enzN1eqN1opN2 = 1 'Nyuryoku1 = Nyuryoku1 (+-*/) Nyuryoku2
enzKeqN1opN2 = 2 'Kekka = Nyuryoku1 (+-*/) Nyuryoku2
End Enum

これを次の様に書き換えます。

Enum enmEnzanHo
enzNoTouch = 0 'NoTouch 何もしない
enzN1eqN1opN2 = 1 'Nyuryoku1 = Nyuryoku1 (+-*/) Nyuryoku2
enzKeqN1opN2 = 2 'Kekka = Nyuryoku1 (+-*/) Nyuryoku2
enzKeqKopN2 = 3 'Kekka = Kekka (+-*/) Nyuryoku2
enzN1eqK = 4 'Nyuryoku1 = Kekka
End Enum

enzKeqN1opN2 と enzN1eqK が新たに書き加えられた定義です。

ExecuteCalc関数を書き換えます。

以下に、前回の講座でも示したExecuteCalc関数の骨格を示し
ます。コードは同じですが、コメントが若干異なります。

Private Function ExecuteCalc(intEnzanshi As Integer) As Boolean

Dim EnzanHo As enmEnzanHo

If cmJoutai <= cmAllClear Then '☆1
ExecuteCalc = False
Exit Function
End If

If strNyuryoku = "" Then
ExecuteCalc = False
Exit Function
End If

'strEnzanshi 数値の前に押した演算子を記憶(+-*/)
'intEnzanshi 今回の呼出で押した演算子キー(+-*/=)

'Joutai strEnz intEnz EnzHo
'cmN1 "" "=" Exit 1◆ △ ×無効操作
'cmN1 "" "+-*/" NoTouch  1◆ ▽ ○通常操作
'cmN1 "+-*/" "=" K =N1opN2 1◇ △ △=の後AC押さずに数値入力、続いて= ☆8
'cmN1 "+-*/" "+-*/" NoTouch 1◇ ▽ △=の後AC押さずに数値入力、続いて+-*/
'cmN2 "" 2◆   ―有り得ない
'cmN2 "+-*/" "=" K =N1opN2 2◇ △ ○通常操作
'cmN2 "+-*/" "+-*/" N1=N1opN2 2◇ ▽ ○通常操作
'cmK "" 3◆   ―有り得ない
'cmK "+-*/" "=" K =K opN2 3◇ △ △=の後すぐに= ☆8
'cmK "+-*/" "+-*/" N1=K 3◇ ▽ △=の後すぐに+-*/ ☆8

ExecuteCalc = True '☆2
strError = ""
EnzanHo = enzNoTouch

Select Case cmJoutai
Case cmNyuryoku1 '演算子の左の数字を入力
'☆3
Case cmNyuryoku2 '演算子の右の数字を入力
'☆4
Case cmKekka '計算結果が表示されている
'☆5
End Select

Select Case EnzanHo
Case enzNoTouch 'NoTouch 何もしない

Case enzN1eqN1opN2, enzKeqN1opN2
'☆6

End Select

If ExecuteCalc Then '☆7
Select Case intEnzanshi
Case Asc("+"), Asc("-"), Asc("*"), Asc("/") '+-*/
strEnzanshi = Chr(intEnzanshi) '数値の前に押した演算子を記憶(+-*/)
Case Asc("="), vbKeyReturn '=

End Select
End If
End Function

☆8が書き換えられたコメントです。

ExecuteCalc 関数内、☆1〜☆7について説明します。

☆1、☆2、☆7は、前回の講座で示した解説と同じです。

☆3には、以下のコードを書き加えます。

If strEnzanshi = "" Then
'1◆

If intEnzanshi = Asc("=") Then
'1◆ △ ×無効操作
ExecuteCalc = False
Exit Function
End If
Else
'1◇
If intEnzanshi = Asc("=") Then
'1◇ △ =の後AC押さずに数値入力、続いて=
EnzanHo = enzKeqN1opN2
Else
'1◇ ▽ =の後AC押さずに数値入力
'EnzanHo = enzNoTouch
'数値入力をして"+-*/"押した
'⇒AC押した事にして動作継続
End If
End If

dblNyuryoku1 = ConvertNyuryoku()

太字が今回追加したコードです。

cmAllClearからcmNyuryoku1に遷移した場合、strEnzanshiは
空("")です。cmKekkaから遷移した場合は、strEnzanshiには
演算子("+-*/")が代入されています。前回の講座では、二者
の場合分けをコードに反映しませんでしたが、今回は、後者
について処理するコードを追加しました。

cmKekkaからcmNyuryoku1に遷移した場合、演算子の右の数字
は、dblNyuryoku2に記憶されている状態です。この状態で"="
を押す操作があった場合は、dblNyuryoku1に入力した数値を
記憶して、dblKekkaにdblNyuryoku1と「dblNyuryoku2に記憶
されている数値」を使って演算した結果を代入します。一方、
演算子キー("+-*/")を押す操作があった場合はdblNyuryoku1
に入力した数値を記憶して処理を続行可能にします。

☆4には、以下のコードを書き加えます。

dblNyuryoku2 = ConvertNyuryoku()
If strEnzanshi = "" Then
'2◆

Else
'2◇

If intEnzanshi = Asc("=") Then
'2◇ △ ○通常操作
EnzanHo = enzKeqN1opN2
Else
'2◇ ▽ ○通常操作
EnzanHo = enzN1eqN1opN2
End If
End If

太字が今回追加したコードです。

ここでstrEnzanshiについて、確認して頂きたい事があります。

strEnzanshiを選択状態にしてから、「Ctrl+F」キーを押して
みてください。「検索」ウインドウを表示されると思います。
このウインドウを使って、コード内でstrEnzanshiが使われて
いる場所を確認する事が出来ます。

strEnzanshiに値を代入するコードは、ふたつしか無いと言う
事が分かるのではないかと思います。ひとつは、ExecuteCalc
関数の末端部、そしてもうひとつは、ProcessKeyPress関数の
"A", "a"キーを押したときに実行されるコードだと思います。

この事から、strEnzanshiが空("")となるのは、cmAllClearに
遷移したときに限られる事が分かります。

続いてcmNyuryoku2について、確認して頂きたい事があります。

cmNyuryoku2を選択状態にしてから、「Ctrl+F」キーを押して
みてください。「検索」ウインドウを表示されると思います。

cmJoutaiにcmNyuryoku2を代入するコードは、ひとつしか無い
と言う事が分かるのではないかと思います。

これらの事から、cmNyuryoku2に遷移する前にはExecuteCalc
関数がintEnzanshiに"+-*/"のうちどれかが代入された状態で
実行されており、しかも必ず処理が成功している事が分かり
ます。この事からcmNyuryoku2に遷移したとき、strEnzanshi
には、必ず演算子("+-*/")が代入されている事が分かります。

前回の講座では、分量が多かった事もあり、strEnzanshiが空
("")となる事は有り得ないとして、strEnzanshiの場合分けを
コードに反映しませんでしたが、今回追加しました。

例えば、将来の改造で前提が崩れる事は十分有り得る事です。

☆5には、以下のコードを書き加えます。

If strEnzanshi = "" Then
'3◆

Else
'3◇
If intEnzanshi = Asc("=") Then
'3◇ △ =の後すぐに=
EnzanHo = enzKeqKopN2
Else
'3◇ ▽ =の後すぐに+-*/
EnzanHo = enzN1eqK
End If
End If


☆6には、以下のコードを書き加えます。

If strEnzanshi = "/" Then
If dblNyuryoku2 = 0 Then
ExecuteCalc = False
strError = "0で除算できません"
Exit Function
End If
End If

Select Case strEnzanshi
Case "" '◆
'EnzanHo = enzNoTouch となり、ここのコードは実行されない

Case "+" '◇+ △▽
Select Case EnzanHo
Case enzN1eqN1opN2 'Nyuryoku1 = Nyuryoku1 (+-*/) Nyuryoku2
dblNyuryoku1 = dblNyuryoku1 + dblNyuryoku2
Case enzKeqN1opN2 'Kekka = Nyuryoku1 (+-*/) Nyuryoku2
dblKekka = dblNyuryoku1 + dblNyuryoku2
Case enzKeqKopN2 'Kekka = Kekka (+-*/) Nyuryoku2
dblKekka = dblKekka + dblNyuryoku2

End Select

Case "-" '◇- △▽
Select Case EnzanHo
Case enzN1eqN1opN2 'Nyuryoku1 = Nyuryoku1 (+-*/) Nyuryoku2
dblNyuryoku1 = dblNyuryoku1 - dblNyuryoku2
Case enzKeqN1opN2 'Kekka = Nyuryoku1 (+-*/) Nyuryoku2
dblKekka = dblNyuryoku1 - dblNyuryoku2
Case enzKeqKopN2 'Kekka = Kekka (+-*/) Nyuryoku2
dblKekka = dblKekka - dblNyuryoku2

End Select

Case "*" '◇* △▽
Select Case EnzanHo
Case enzN1eqN1opN2 'Nyuryoku1 = Nyuryoku1 (+-*/) Nyuryoku2
dblNyuryoku1 = dblNyuryoku1 * dblNyuryoku2
Case enzKeqN1opN2 'Kekka = Nyuryoku1 (+-*/) Nyuryoku2
dblKekka = dblNyuryoku1 * dblNyuryoku2
Case enzKeqKopN2 'Kekka = Kekka (+-*/) Nyuryoku2
dblKekka = dblKekka * dblNyuryoku2

End Select

Case "/" '◇/ △▽
Select Case EnzanHo
Case enzN1eqN1opN2 'Nyuryoku1 = Nyuryoku1 (+-*/) Nyuryoku2
dblNyuryoku1 = dblNyuryoku1 / dblNyuryoku2
Case enzKeqN1opN2 'Kekka = Nyuryoku1 (+-*/) Nyuryoku2
dblKekka = dblNyuryoku1 / dblNyuryoku2
Case enzKeqKopN2 'Kekka = Kekka (+-*/) Nyuryoku2
dblKekka = dblKekka / dblNyuryoku2

End Select

End Select

Case enzN1eqK 'Nyuryoku1 = Kekka
dblNyuryoku1 = dblKekka


太字が今回追加したコードです。

「F5」キーを押して動作確認してみましょう。



動かない、ですね。

忘れないうちに、ここまでつくって来たプログラムを保存し
ましょう。ツールバー等にある「保存」をクリックします。

ここで一息つきましょう。ここから先は少し長くなります。



今回追加したコードが、正しく実行されているか、確かめて
みましょう。☆3で追加したコードを確認します。

If strEnzanshi = "" Then
'1◆

If intEnzanshi = Asc("=") Then
'1◆ △ ×無効操作
ExecuteCalc = False
Exit Function
End If
Else
'1◇
● If intEnzanshi = Asc("=") Then
'1◇ △ =の後AC押さずに数値入力、続いて=
EnzanHo = enzKeqN1opN2
Else
'1◇ ▽ =の後AC押さずに数値入力
'EnzanHo = enzNoTouch
'数値入力をして"+-*/"押した
'⇒AC押した事にして動作継続
End If
End If

dblNyuryoku1 = ConvertNyuryoku()

●で示した行にカーソルをもっていき「F9」キーを押します。
行全体が赤く反転された状態に変化すると思います。

「F5」キーを押します。

"1" "+" "2" "="キーを順番に押します。3 が表示されました
か。そのまま続いて"4" "="キーを順番に押します。この操作
では、"4+2="の結果が表示されれば正しく実行されていると
言えます。

突然画面が切り替わり、コード画面が表示されたのではない
かと思います。先程赤く反転された行にカーソルがあり黄色
い矢印と一部黄色く反転された範囲が確認出来ると思います。

これは、実行中のコードを一時中断して、一行一行、動作を
追う事が出来る様にする機能です。

intEnzanshiにマウスポインタを合わせてみてください。する
と、ツールチップが表示されて、「intEnzanshi = ○」等と
intEnzanshiに代入されている値が確認出来ると思います。

intEnzanshiには「今回の呼出で押した演算子キー("+-*/=")」
が代入されているはずです。ところでここで表示されるのは、
コンピュータの内部処理でキーを示すものとして付けられて
いる数値です。今回の呼出では"="キーを押しているので"="
キーを示すものとして付けられている数値が代入されている
はずです。Asc関数は、コンピュータの内部処理でキーを示す
ものとして付けられている数値を得る関数で、Asc("=")関数
が返す値は"="キーを示すものとして付けられている数値です。

そうなると、正しく実行されているか確かめる為にAsc("=")
関数が返す値が知りたくなりますが、マウスポインタを合わ
せても、Asc("=")を選択状態にしても、Asc("=")関数が返す
値は表示されません。

これを表示するには、「イミディエイト」ウィンドウを使い
ます。コード画面で「Ctrl+G」キーを押すと、表示されます。

「イミディエイト」ウィンドウで「print Asc("=")」と入力
し「Enter」キーを押してください。Asc("=")関数が返す値が
表示されます。「print Asc("=")」の代わりに「? Asc("=")」
や「?Asc("=")」と入力しても同じ結果が表示されます。

表示された数値はintEnzanshiと一致しましたか。

続いて「F8」キーを押します。「F8」キーを押すとコードを
一行だけ実行して再び実行を中断します。正しく実行されて
いればIf内の1行目に黄色い矢印が移動するはずです。

先程と同様にして、マウスポインタをEnzanHoとenzKeqN1opN2
に合わせるとそれぞれの値が表示されると思います。

再び「F8」キーを押します。再びマウスポインタを合わせて
それぞれの値を確認します。値は変化しましたか。

どうやら正しく実行されている様だと言う事が確認出来たら
「F5」キーを押して実行を再開します。

次に、"a" "1" "+" "2" "="キーを順番に押します。そのまま
続いて"4" "+"キーを順番に押します。この操作では、"4+"の
演算子の右の数字の入力待ち状態になれば正しく実行されて
いると言えます。

どうやら正しく実行されている様だと言う事が確認出来たら
「F5」キーを押して実行を再開します。

続いて"5" "="キーを順番に押します。この操作では、"4+5="
の結果が表示されれば正しく実行されていると言えます。

「ESC」キーを押してプログラムを一旦終了します。



☆5で追加したコードを確認します。

If strEnzanshi = "" Then
'3◆

Else
'3◇
● If intEnzanshi = Asc("=") Then
'3◇ △ =の後すぐに=
EnzanHo = enzKeqKopN2
Else
'3◇ ▽ =の後すぐに+-*/
EnzanHo = enzN1eqK
End If
End If


●で示した行にカーソルをもっていき「F9」キーを押します。
行全体が赤く反転された状態に変化すると思います。

「F5」キーを押します。

"1" "+" "2" "="キーを順番に押します。3 が表示されました
か。そのまま続いて"="キーを押します。この操作では、"3+
2="の結果が表示されれば正しく実行されていると言えます。

次に、"a" "1" "+" "2" "="キーを順番に押します。3 が表示
されましたか。そのまま続いて"+"キーを押します。この操作
では、"3+"の演算子の右の数字の入力待ち状態になれば正し
く実行されていると言えます。

続いて"4" "="キーを順番に押します。この操作では、"3+4="
の結果が表示されれば正しく実行されていると言えます。

突然画面が切り替わり、コード画面が表示されたのではない
かと思います。先程赤く反転された行にカーソルがあり黄色
い矢印と一部黄色く反転された範囲が確認出来ると思います。

ところで、何か様子がおかしくありませんか。☆3で追加した
赤●の行に黄色い矢印がついているのではないかと思います。

☆5で追加した赤●の行には一度も止まらなかったと思います。

おかしいですね。

「ESC」キーを押してプログラムを一旦終了します。



原因追究の為、ProcessKeyPressの"="キーを処理するコード
を確認します。

Case Asc("="), vbKeyReturn '=
● If ExecuteCalc(Asc("=")) Then
strNyuryoku = ""
cmJoutai = cmKekka
'★→cmK: "="押したとき、正常であればcmK

Call Senmonkakaigi(dblKekka, True)

ElseIf strError <> "" Then
cmJoutai = cmJoutai * -1
'★→cmE: エラーが発生したときcmE
End If
UpdateDisplay

●で示した行にカーソルをもっていき「F9」キーを押します。
行全体が赤く反転された状態に変化すると思います。

「F5」キーを押します。

"1" "+" "2" "="キーを順番に押します。

突然画面が切り替わり、コード画面が表示されたのではない
かと思います。先程赤く反転された行にカーソルがあり黄色
い矢印と一部黄色く反転された範囲が確認出来ると思います。

ここから先の処理は追究対象外なので「F5」キーを押します。

3 が表示されましたか。そのまま続いて"="キーを押します。

突然画面が切り替わり、コード画面が表示されたのではない
かと思います。先程赤く反転された行にカーソルがあり黄色
い矢印と一部黄色く反転された範囲が確認出来ると思います。

「F8」キーを押すとExecuteCalc関数内の1行目に黄色い矢印
が移動するはずです。

「F8」キーを二回押します。☆1からの4行は、問題無く実行
されるのではないかと思います。

続いて「F8」キーを押すと、そこでExecuteCalc関数が処理を
中断して終了してしまう事が分かると思います。strNyuryoku
が空("")である為です。

☆1からの4行に続く4行は、演算子キー("+-*/")や"="キーを
連続して押す操作を、無効として処理を中断する為に設けた
コードでした。例えば、演算子キー("+-*/")を押した後連続
して演算子キー("+-*/")を押すとか、演算子キー("+-*/")を
押した後数字を入力せずに"="キーを押すとかの操作をすると、
strNyuryokuが空("")である為、処理を中断する様になって
いるのです。

しかしながら、実際のところ、この4行がなくともプログラム
は異常を来す事無く動きます。strNyuryokuが空("")のとき、
ConvertNyuryoku()関数は0を返すので、その後の処理を続け
ても、0で演算する事を試みるだけだからです。

この4行が追加された経緯を言うと、実は今回の講座でつくる
プログラムの正しく動作する版が先にあって、そこから不要
不急のコードを削る事で前回の講座でつくるプログラムをつ
くった経緯があり、☆5のコードを削った結果、ExecuteCalc
関数で、演算子キー("+-*/")や"="キーを連続して押す操作に
反応する必要が無くなった為、後から追加したコードでした。

前回の講座でつくったプログラムでは、この4行が追加された
事によって、演算子キー("+-*/")を押した後連続して演算子
キー("+-*/")を押すとか、演算子キー("+-*/")を押した後数
字を入力せずに"="キーを押すとかの操作をした場合に、0で
演算する事を試みる無駄を省く効果がありました。



ここで、前回の講座で導入した、ある特定の条件下で数値が
「中抜き」されていくプログラムについて、「中抜き」され
た数値が分かる様に改造しようと思います。

ProcessKeyPressの Select Case KeyAscii と End Selectの
間のCase Asc("="), vbKeyReturnの内容を、以下の様に書き
換えます。

Case Asc("="), vbKeyReturn '=
If ExecuteCalc(Asc("=")) Then
strNyuryoku = ""
cmJoutai = cmKekka
'★→cmK: "="押したとき、正常であればcmK

Dim dblKekkaKaigimae As Double
dblKekkaKaigimae = dblKekka


Call Senmonkakaigi(dblKekka, True)

If dblKekkaKaigimae - dblKekka > 0 Then
strError = dblKekkaKaigimae - dblKekka
strError = strError + "億円中抜きされました(Press C key.)"

cmJoutai = cmError3
End If


ElseIf strError <> "" Then
cmJoutai = cmJoutai * -1
'★→cmE: エラーが発生したときcmE
End If
UpdateDisplay

太字が今回追加したコードです。

忘れないうちに、ここまでつくって来たプログラムを保存し
ましょう。ツールバー等にある「保存」をクリックします。

 
続きを読む...

posted by miraclestar at 05:00 | Comment(0) | 日記 | このブログの読者になる | 更新情報をチェックする