計算プリントを自動生成する その7_1 引き算を考える(Int_Subtracitonの実装)
はじめに
良いか悪いかはよくわかりませんが、個人的には1つのシステムに実装されている関数は、少ないほうが格好良いと思っています。
といっても実利はあまりないのですが。
モチベーションが下がらないという個人的な利はありますが。。
えっと。
というわけで引き算は新たに関数を増やさずに作ってみようと思います。
と思ったのですがちょっと内容が濃いので今回も2回に分けます。
今回はどうやって関数を増やすことなく引き算を実装するのかと
「Int_Subtraction」関数の実装をします。
え、関数増やしてるんじゃん。
って突っ込まないでください笑
引き算の実装の仕方
例えば1ケタの引き算を実装したい場合。
5-3=2
のような。
Num(1)を1~9
Num(2)を1~num(1)
のように設定することもできます。
がこれでは新たに「Int_Subtraction」と「Int_Subtraciton_Num_Select」の2つを作らなければならない。
さらにlevelの反映という、正直ダルい部分を考える手間があります。
これらの面倒を回避するために「Int_Addition」を少し書き換えることにしました。
自動でやってみる
先ほど書いた通り、「Int_Addition」を書き換えます。
一気に書き換えるとわかりにくいので、まずは便宜的に「Int_Subtraction」関数を書きます。
Function Int_Subtraction(level) As String '---------------------------------- '整数の引き算の計算式を生成する '---------------------------------- '---------------------------------- '扱う変数の定義 '---------------------------------- Dim num() As Integer '指定する数値 Dim ans As String 'strに戻す値 '---------------------------------- 'numの値の決定 '---------------------------------- num() = Int_Addition_Num_Select(level) '---------------------------------- '引き算用の処理 '---------------------------------- Dim tmp As Integer '繰り返しの制御変数 tmp = num(1) num(1) = num(3) num(3) = tmp '---------------------------------- '計算式を文字列にする '---------------------------------- ans = "a-b=c" ans = Replace(ans, "a", num(1)) ans = Replace(ans, "b", num(2)) ans = Replace(ans, "c", num(3)) '---------------------------------- '文字列を戻す '---------------------------------- Int_Subtraction = ans End Function
「Int_Addition」とだいたい同じ機能になっていることが分かりますか?
実際「Int_Addition_Num_Select」関数で数値の選択を行っています。
変わった部分は2つ。
引き算用の処理の追加。
Ansの値が「a+b=c」から「a-b=c」へ変更。
3+5=8
の3と5を入れ替えて+を-に変えると…
8-5=3
となります。
これを自動で行うのが、今回追加された処理です。
まとめ
関数を増やさないことが格好いい!
とか言ったのに関数を増やしちゃいました笑
このままでは恥ずかしいので次回に関数を一つ減らします。
具体的に言うと、「Int_Addition」と「Int_Subtraction」を統合して「Int_AddSub」関数を実装します。
計算プリントを自動生成する その6 いろいろな掛け算
はじめに
今回はいろいろな掛け算を実装します。
今回は簡単かなーと思っていたのですが、いざやってみると思いがけない問題が。。
難度の設定
まずは難度を設定します。
どうせ次に変数levelと対応をとるので、初めからその形式で。
level | 内容 |
---|---|
1 | 九九の掛け算 |
2 | 1ケタ×2ケタ |
3 | 2ケタ×2ケタ、答えが百の位まで |
4 | 2ケタ×2ケタ、答えが千の位まで |
5 | 2ケタ×2ケタ、制約なし |
んー。
もしかしたらもっと良い難度設定があるかもしれません。
自動化してみる
傾斜のかけ方
前回の九九の回に傾斜を掛けました。
その時はnumに1が出てきた場合はもう一度数字を選びなおして
出現頻度を偏らせました。
この偏らせ方がlevelによってそれぞれ異なることが作っているうちにわかりました。
たとえば2ケタの数字で10,20,30などが出てくると
これって実質1ケタの掛け算と一緒になってしまいます。
前回は「Int_Multiplication」関数で傾斜を実装していましたが
今回はその一つ下の層の「Int_Multiplication_Num_Select」関数に移します。
傾斜を新しい関数で実装しようかとも考えましたが、傾斜と数値の選択は密接な関係があるので同じ関数内で書くことにしました。
「Int_Multiplication_Num_Select」
まずは前回書いた内容で、この関数内に傾斜を設けてみます。
Function Int_Multiplication_Num_Select(level) As Integer() '------------------- '変数の定義 '------------------- Dim num() As Integer '計算式に入れる数値 Dim N() As Integer 'num1を作るための変数 Dim M() As Integer 'num2を作るための変数 Dim i As Integer '繰り返しの制御変数 Dim flag As Boolean '傾斜のための変数 '------------------- 'numの数を決定 '------------------- ReDim num(1 To 3) '------------------- '傾斜の準備 '------------------- flag = False ReSelect: '--------------------- 'num1,2の値の決定 '--------------------- num(1) = Rnd_Num(1, 9) num(2) = Rnd_Num(1, 9) '--------------------- '傾斜の設置 '--------------------- If flag = False Then If num(1) = 1 Or num(2) = 1 Then flag = True GoTo ReSelect End If End If '--------------------- 'num3の値の決定 '--------------------- num(3) = num(1) * num(2) Int_Multiplication_Num_Select = num End Function
FlagとReSelectの部分で一度だけ繰り返しを行っています。
続いて先ほど決めたlevelを反映さえます。
Function Int_Multiplication_Num_Select(level) As Integer() '------------------- '変数の定義 '------------------- Dim num() As Integer '計算式に入れる数値 Dim N() As Integer 'num1を作るための変数 Dim M() As Integer 'num2を作るための変数 Dim i As Integer '繰り返しの制御変数 Dim flag As Boolean '傾斜のための変数 Dim adj As Integer '数字選択補助の変数 '------------------- 'numの数を決定 '------------------- ReDim num(1 To 3) '------------------- '傾斜の準備 '------------------- flag = False ReSelect: '--------------------- 'numの値の決定 '--------------------- Select Case level Case Is = 1 '1_1 num(1) = Rnd_Num(1, 9) num(2) = Rnd_Num(1, 9) Case Is = 2 '2_1 num(1) = Rnd_Num(10, 99) num(2) = Rnd_Num(1, 9) Case Is = 3 '2_2百の位まで num(1) = Rnd_Num(10, 99) adj = 999 \ num(1) num(2) = Rnd_Num(1, adj) Case Is = 4 '2_2千の位まで num(1) = Rnd_Num(10, 99) adj = 999 \ num(1) num(2) = Rnd_Num(adj + 1, 99) Case Is = 5 '2_2制約なし num(1) = Rnd_Num(1, 99) num(2) = Rnd_Num(1, 99) End Select '--------------------- '傾斜の設置 '--------------------- If flag = False Then flag = True Select Case level Case Is = 1, 2 If num(1) = 1 Or num(2) = 1 Then GoTo ReSelect Case Is = 3, 4, 5 If num(1) Mod 10 = 0 Or num(2) Mod 10 = 0 Then GoTo ReSelect End Select End If '--------------------- 'numの入れ替え '--------------------- If Rnd() < 0.5 Then tmp = num(1) num(1) = num(2) num(2) = tmp End If '--------------------- 'num3の値の決定 '--------------------- num(3) = num(1) * num(2) Int_Multiplication_Num_Select = num End Function
んー。ちょっとコードがだらだらしてますかね。
Numの入れ替えという機能を追加しました。
たまにnum(1)とnum(2)を入れ替えることで問題の見た目の偏りを和らげています。
まとめ
傾斜の設定が意外と難しいな、という回でした。
前回は気づきませんでしたが、傾斜の設定のやり方で問題の難度を調節できそうですね。
たとえば傾斜と乱数を組み合わせることで「どの程度の割合でReSelectをするか」を決めることができます。
ここまで考え始めると疲れちゃうので今は保留しますが。。
計算プリントを自動生成する その5 九九の掛け算
はじめに
今までは足し算を扱ってきました。
そのコードをちょっと変えるだけで掛け算にできます。
手順
- 1桁の数字を2つ決める
- 2つの数字を掛け合わせる
- 計算式をつくる
自動でやってみる
「Int_Multiplication」関数を作ります。
前回までにつくった「Int_Addition」に倣って、のちのち必要になりそうな機能も加えておきます。
Int_Multiplication
Function Int_Multiplication(level) As String '---------------------------------- '1ケタの二つの数を定義する 'それらの和を求める '計算式を文字列にして戻す '---------------------------------- '---------------------------------- '扱う変数の定義 '---------------------------------- Dim num() As Integer '指定する数値 Dim ans As String 'strに戻す値 '---------------------------------- 'numの値の決定 '---------------------------------- num() = Int_Multiplication_Num_Select(level) '--------------------- '配分の傾斜 '--------------------- If num(1) = 1 Or num(2) = 1 Then num() = Int_Multiplication_Num_Select(level) End If '---------------------------------- '計算式を文字列にする '---------------------------------- ans = "a×b=c" ans = Replace(ans, "a", num(1)) ans = Replace(ans, "b", num(2)) ans = Replace(ans, "c", num(3)) '---------------------------------- '文字列を戻す '---------------------------------- Int_Multiplication = ans End Function
ひとつ特徴が配分の傾斜のところ。
これを入れないと選択された数字の約20%が1になってしまいます。
掛け算ではあまり1は出てほしくないのですよね。
この傾斜によって1の出る確率は(たぶん)4%にまで下がっています。
Int_Multiplication_Num_Select
Function Int_Multiplication_Num_Select(level) As Integer() '------------------- '変数の定義 '------------------- Dim num() As Integer '計算式に入れる数値 Dim N() As Integer 'num1を作るための変数 Dim M() As Integer 'num2を作るための変数 Dim i As Integer '繰り返しの制御変数 '------------------- 'numの数を決定 '------------------- ReDim num(1 To 3) '--------------------- 'numの値の決定 '--------------------- num(1) = Rnd_Num(1, 9) num(2) = Rnd_Num(1, 9) num(3) = num(1) * num(2) Int_Multiplication_Num_Select = num End Function
こちらはInt_Addition_Num_SelectのN,M導入前とほぼ一緒。
まとめ
足し算をすでに扱ったおかげで今回はかなり楽に作れました。
つぎは掛け算の難度を設定します。
今回と次回は息抜きの回ですね。
計算プリントを自動生成する その4_2 いろいろな足し算(本番)
はじめに
前回、その4_1で関数を変更、追加しました。
今回はいよいよ「いろいろな足し算」を実装します。
足し算の種類
- 繰り上がりなしの1ケタどうしの足し算
- 繰り上がりありの1ケタどうしの足し算
- 制約なしの1ケタどうしの足し算
- 繰り上がりなしの2ケタ+1ケタ
- 繰り上がりありの2ケタ+1ケタ
- 繰り上がりなしの2ケタ+2ケタ
- 1の位が繰り上がる2ケタ+2ケタ
1と3はすでに作っているので、今回作るのはそれ以外です。
自動化してみる
levelと難度の対応
コードを書く前に変数「level」と足し算の種類を対応付けましょう。
level | 足し算の種類 |
---|---|
1 | 繰り上がりなし、1ケタ+1ケタ |
2 | 繰り上がりあり、1ケタ+1ケタ |
3 | 制約なし、2ケタ+1ケタ |
4 | 繰り上がりなし、2ケタ+1ケタ |
5 | 繰り上がりあり、2ケタ+1ケタ |
6 | 繰り上がりなし、2ケタ+2ケタ |
7 | 1の位繰り上がり、2ケタ+2ケタ |
Int_Addition_Num_Selectを書く
前回のコードを書き換えます。
具体的にはSelect Caseを追加してlevelによるnumの設定の変更を実装します。
Function Int_Addition_Num_Select(level) As Integer() '------------------- '変数の定義 '------------------- Dim num() As Integer '計算式に入れる数値 Dim N() As Integer 'num1を作るための変数 Dim M() As Integer 'num2を作るための変数 Dim i As Integer '繰り返しの制御変数 '------------------- 'numの数を決定 '------------------- ReDim num(1 To 3) ReDim N(1 To 2) ReDim M(1 To 2) '------------------- 'N,Mの数値を決定 '------------------- Select Case level Case Is = 1 '1_1繰り上がりなし N(1) = Rnd_Num(1, 8) M(1) = Rnd_Num(1, 9 - N(1)) Case Is = 2 '1_1繰り上がりあり N(1) = Rnd_Num(1, 9) M(1) = Rnd_Num(10 - N(1), 9) Case Is = 3 '1_1制約なし N(1) = Rnd_Num(1, 9) M(2) = Rnd_Num(1, 9) Case Is = 4 '2_1繰り上がりなし N(1) = Rnd_Num(0, 8) N(2) = Rnd_Num(1, 9) M(1) = Rnd_Num(1, 9 - N(1)) Case Is = 5 '2_1繰り上がりあり N(1) = Rnd_Num(1, 9) N(2) = Rnd_Num(1, 8) M(1) = Rnd_Num(10 - N(1), 9) Case Is = 6 '2_2繰り上がりなし N(1) = Rnd_Num(0, 8) N(2) = Rnd_Num(1, 8) M(1) = Rnd_Num(0, 9 - N(1)) M(2) = Rnd_Num(1, 9 - N(2)) Case Is = 7 '2_2繰り上がり1 N(1) = Rnd_Num(1, 9) N(2) = Rnd_Num(1, 7) M(1) = Rnd_Num(1, 9 - N(1)) M(2) = Rnd_Num(1, 9 - N(2)) End Select '--------------------- 'numの値の決定 '--------------------- For i = 1 To 2 num(1) = num(1) + N(i) * 10 ^ (i - 1) num(2) = num(2) + M(i) * 10 ^ (i - 1) Next num(3) = num(1) + num(2) Int_Addition_Num_Select = num End Function
ふぅ。
この部分を書くのはちょっと疲れました。
試行錯誤した結果、変数N,Mを導入することにしました。
まとめ
「Int_Addition_Num_Select」はもっとシンプルな書き方があるかもしれません。
昔に「Nケタ+Mケタでkの位を繰り上がりさせる計算式」を書いたことがあるのですが
もしかしたらそちらを使ったほうが良かったのか。。
ちょっと課題は残りましたが保留で。
とにかくこれで足し算は終わりです。
次回は掛け算をやりましょう。
計算プリントを自動生成する その4_1 いろいろな足し算(準備編)
はじめに
前回は繰り上がりなしの1ケタの足し算でした。
今回はもうちょっと種類を増やしてみましょう。
……
と思ったのですが「計算の難度」を導入するためには関数をいろいろ書き換える必要があるので、今回は準備をしましょう。
準備すること
- 変数numを配列にする
- Numの値を決める部分を関数化する
- 難度を設定するために変数を1つ足す
この3つ。
3を実現するために2を
2を実現するために1を準備します。
変数numを配列にする
今まではnum1,num2というように変数を複数個使っていましたが
今回からnum(1 to 3)というように変えます。
関数Int_Addition_Ver1をnumの部分を書き換えてVer1_1にしましょう。
関数Int_Addition_Ver1
Function Int_Addition_Ver1() As String '---------------------------------- '1ケタの二つの数を定義する 'それらの和を求める '計算式を文字列にして戻す '---------------------------------- '---------------------------------- '扱う変数の定義 '---------------------------------- Dim num1 As Integer '指定する数値 Dim num2 As Integer '指定する数値 Dim num3 As Integer '計算結果 Dim ans As String 'strに戻す値 '---------------------------------- 'num1とnum2の定義 '---------------------------------- num1 = Rnd_Num(1, 9) num2 = Rnd_Num(1, 9) '---------------------------------- 'num3を求める '---------------------------------- num3 = num1 + num2 '---------------------------------- '計算式を文字列にする '---------------------------------- ans = "a+b=c" ans = Replace(ans, "a", num1) ans = Replace(ans, "b", num2) ans = Replace(ans, "c", num3) '---------------------------------- '文字列を戻す '---------------------------------- Int_Addition_Ver1 = ans End Function
を 関数Int_Addition_Ver1_1
Function Int_Addition_Ver1_1() As String '---------------------------------- '1ケタの二つの数を定義する 'それらの和を求める '計算式を文字列にして戻す '---------------------------------- '---------------------------------- '扱う変数の定義 '---------------------------------- Dim num(1 To 3) As Integer '指定する数値 Dim ans As String 'strに戻す値 '---------------------------------- 'num(1)とnum(2)の定義 '---------------------------------- num(1) = Rnd_Num(1, 9) num(2) = Rnd_Num(1, 9) '---------------------------------- 'num(3)を求める '---------------------------------- num(3) = num(1) + num(2) '---------------------------------- '計算式を文字列にする '---------------------------------- ans = "a+b=c" ans = Replace(ans, "a", num(1)) ans = Replace(ans, "b", num(2)) ans = Replace(ans, "c", num(3)) '---------------------------------- '文字列を戻す '---------------------------------- Int_Addition_Ver1_1 = ans End Function
内容は特に変わっていません。
Numの値を決める部分を関数化する
最終的には条件分岐を使ってnumの値を設定する仕様にするつもりです。
その時にnumの値を設定する部分を関数化しておくとコードが読みやすいかなと思い、ここで関数化しておきます。
現在数式を作っている関数を「Int_Addition」と名付けているので
この関数の数字を選ぶ部分を「Int_Addition_Num_Select」とします。
Function Int_Addition_Num_Select() As Integer() '変数の定義 Dim num() As Integer 'numの数を決定 ReDim num(1 To 3) 'numの数値を決定 num(1) = Rnd_Num(1, 9) num(2) = Rnd_Num(1, 9) num(3) = num(1) + num(2) Int_Addition_Num_Select = num End Function
ここでも本質は変わらず。
難度を設定するために変数を1つ足す
これは「ひながた」プロシージャの部分に足します。
そしてInt_Additionなどの計算式をつくる関数に引数として渡します。
仮で「ひながた2」という名前で書きます。
Sub ひながた2() '------------------------------ '0. 扱う変数の定義 '------------------------------ Dim N As Integer '問題数を決める変数 Dim i As Integer '繰り返しの制御変数 Dim str As String '計算式の文字列 Dim level As Integer '難度の設定 '------------------------------ '1. 問題数の指定 '------------------------------ N = 10 For i = 1 To N '------------------------------ '2. 問題を指定して式を求める '------------------------------ level = 1 str = Int_Addition_Ver1_3(level) '------------------------------ '3. 出力 '------------------------------ Call Insert_Equation(20, 30 * i, str) Next End Sub
これでひながたから難度をコントロールできるようになりました。
まとめ
今回はいくつかの関数の名前をいろいろ変えてしまってややこしくなったので
それらの関数の名前を定義しなおします。
まずは「ひながた」プロシージャ。
Sub ひながた() '------------------------------ '0. 扱う変数の定義 '------------------------------ Dim N As Integer '問題数を決める変数 Dim i As Integer '繰り返しの制御変数 Dim str As String '計算式の文字列 Dim level As Integer '難度の設定 '------------------------------ '1. 問題数の指定 '------------------------------ N = 10 For i = 1 To N '------------------------------ '2. 問題を指定して式を求める '------------------------------ level = 1 str = Int_Addition(level) '------------------------------ '3. 出力 '------------------------------ Call Insert_Equation(20, 30 * i, str) Next End Sub
次に「Int_Addition」関数。
Function Int_Addition(level) As String '---------------------------------- '足し算 '計算式を文字列にして戻す '---------------------------------- '---------------------------------- '扱う変数の定義 '---------------------------------- Dim num() As Integer '指定する数値 Dim ans As String 'strに戻す値 '---------------------------------- 'numの値の決定 '---------------------------------- num() = Int_Addition_Num_Select(level) '---------------------------------- '計算式を文字列にする '---------------------------------- ans = "a+b=c" ans = Replace(ans, "a", num(1)) ans = Replace(ans, "b", num(2)) ans = Replace(ans, "c", num(3)) '---------------------------------- '文字列を戻す '---------------------------------- Int_Addition_nnd = ans End Function
あ。
「Int_Addition_Num_Select」も変数levelに対応した仕様にしましょう。
Function Int_Addition_Num_Select(level) As Integer() '変数の定義 Dim num() As Integer 'numの数を決定 ReDim num(1 To 3) 'numの数値を決定 num(1) = Rnd_Num(1, 9) num(2) = Rnd_Num(1, 9) num(3) = num(1) + num(2) Int_Addition_Num_Select = num End Function
現状ではlevelがいくつであろうがnumの値は変わりませんが。。。
この条件分岐の部分をつくるのが次回です!
計算プリントを自動生成する その3 1桁繰り上がりなしの足し算
はじめに
今回は問題の難度を設定します。
簡単な問題、難しい問題って設定したいですよね。
手順
- 数字をひとつ決める
- それと足し合わせても繰り上がらない数字をひとつ決める
- 足し合わせた数字もとめる
- 計算式をつくる
今回は数字の選び方がちょっと難しい。
手動でやってみる
手順 | 具体的な数値 |
---|---|
数字をひとつ決める | 4 |
繰り上がらない数字をひとつ決める | 5 |
足し合わせた数を求める | 9 |
計算式を作る | 4+5=9 |
自動でやってみる
前回の関数「Int_Addition_Ver1」をちょっと書き換えます。
前回はこんな感じ。
Function Int_Addition_Ver2() As String '---------------------------------- '1ケタの二つの数を定義する 'それらの和を求める '計算式を文字列にして戻す '---------------------------------- '---------------------------------- '扱う変数の定義 '---------------------------------- Dim num1 As Integer '指定する数値 Dim num2 As Integer '指定する数値 Dim num3 As Integer '計算結果 Dim ans As String 'strに戻す値 '---------------------------------- 'num1とnum2の定義 '---------------------------------- num1 = Rnd_Num(1, 8) num2 = Rnd_Num(1, 9 - num1) '---------------------------------- 'num3を求める '---------------------------------- num3 = num1 + num2 '---------------------------------- '計算式を文字列にする '---------------------------------- ans = "a+b=c" ans = Replace(ans, "a", num1) ans = Replace(ans, "b", num2) ans = Replace(ans, "c", num3) '---------------------------------- '文字列を戻す '---------------------------------- Int_Addition_Ver2 = ans End Function
Num1とnum2の選び方が肝です。
まとめ
繰り上がらない数の選び方、わかりましたか?
これが難なくわかった方はセンス◎です!
次回いろんな足し算です。
足し算は次でいったん終わりかな。
計算プリントを自動生成する その2 足し算を20問つくろう
はじめに
第2回は「足し算を20問つくろう」です。
第1回で書いたことを前提としているので
まだ読まれていない方はこちらからーーー
手順
- 足し算の計算式を文字列で一つ作る
- それを表示する
- 1,2を繰り返す
1は前回行ったので2と3が今回のテーマです。
今回は「手動でやってみる」の段は省略します。
自動でやってみる
書く順番が前後しますが、まず手順3を書いて
次に手順2について説明します。
Sub ひながた() '------------------------------ '0. 扱う変数の定義 '------------------------------ Dim N As Integer '問題数を決める変数 Dim i As Integer '繰り返しの制御変数 Dim str As String '計算式の文字列 '------------------------------ '1. 問題数の指定 '------------------------------ N = 20 For i = 1 To N '------------------------------ '2. 問題を指定して式を求める '------------------------------ str = Int_Addition_Ver1 '------------------------------ '3. 出力 '------------------------------ Call Insert_Equation(20, 30 * i, str) Next End Sub
Int_Addition_Ver1
こちらは前回説明しています。
taki-no-oto.hatenablog.com
Insert_Equation
計算式の文字列を出力する関数です。
手順の2のやつです。
Function Insert_Equation(left, top, str) '------------------------------ '計算式をシートに挿入 '------------------------------ ActiveSheet.Shapes.AddLabel(msoTextOrientationHorizontal, left, top, 0, 0).Select Application.CommandBars.ExecuteMso "InsertBuildingBlocksEquationsGallery" Selection.Text = str Selection.Font.Size = 13 Selection.ShapeRange.TextFrame2.WordWrap = msoFalse Application.CommandBars.ExecuteMso "EquationProfessional" End Function
意味は難しいので、これは呪文として扱います。
Left,topの引数で画面の座標を指定
Strの引数で画面に出力したい文字列を指定します。
何を言っているかわからなくて大丈夫です。
とにかく「画面に計算式を表示する」関数と思ってください。
まとめ
これで計算プリントが作れます。
次は「繰り上がりのない1桁どうしの足し算」の自動生成を行います。