計算プリントを自動生成する その14_2 解析関数
はじめに
前回までに、「3数以上からなる計算式の問題」を作成できるようになりました。
今回はその問題の答えを求める関数を書きます。
解析関数
この関数自体は、前に凍結した「その11」の時に考えていました。
使い道はよくわからないけど便利かもしれないな~と思って。
これが当時に書けていたらもうちょっと頑張れたかもですね。
ただ、これには「Fraction」クラスを用いているので、当時の手持ちでは無理でしたが。
コード
Function Int_Calculation_Analyze(ByVal str As String) '---------------------- '整数の計算式の答えを求める '---------------------- '---------------------- '変数の定義 '---------------------- Dim num() As Fraction '数字を仕分ける変数 Dim ope() As String '演算子を仕分ける変数 Dim i As Integer '繰り返しの制御変数 Dim trgt As String '仕分けのアシスト変数 Dim stock As String '仕分けのアシスト変数 Dim cnt As Integer '仕分けのアシスト変数 Dim trgt2 As String '計算のアシスト変数 Dim ans As Fraction '計算のアシスト変数 Rtrn: '---------------------- '変数の初期化 '---------------------- cnt = 0 stock = "" trgt = "" trgt2 = "" ReDim num(cnt) ReDim ope(cnt) Set ans = New Fraction '---------------------- 'strをopeとnumに分ける '---------------------- For i = 1 To Len(str) trgt = Mid(str, i, 1) Select Case trgt Case Is = "+", "-", "×", "÷" ReDim Preserve num(cnt) ReDim Preserve ope(cnt) Set num(cnt) = New Fraction num(cnt).Con (stock) ope(cnt) = trgt stock = "" cnt = cnt + 1 Case Else stock = stock & trgt End Select Next If stock <> "" Then ReDim Preserve num(cnt) Set num(cnt) = New Fraction num(cnt).Con (stock) stock = "" End If '---------------------- '条件を満たしている場合は終了 '---------------------- If cnt = 0 Then Int_Calculation_Analyze = str Exit Function End If '---------------------- '計算を行う '---------------------- trgt2 = "" For i = 0 To UBound(ope) Select Case ope(i) Case "×" trgt2 = num(i).str & ope(i) & num(i + 1).str ans.Va = num(i).Va * num(i + 1).Va ans.Vb = num(i).Vb * num(i + 1).Vb str = Replace(str, trgt2, ans.str, , 1) GoTo Rtrn Case "÷" trgt2 = num(i).str & ope(i) & num(i + 1).str ans.Va = num(i).Va * num(i + 1).Vb ans.Vb = num(i).Vb * num(i + 1).Va str = Replace(str, trgt2, ans.str, , 1) GoTo Rtrn End Select Next For i = 0 To cnt Select Case ope(i) Case "+" trgt2 = num(i).str & ope(i) & num(i + 1).str ans.Va = num(i).Va * num(i + 1).Vb + num(i).Vb * num(i + 1).Va ans.Vb = num(i).Vb * num(i + 1).Vb str = Replace(str, trgt2, ans.str, , 1) GoTo Rtrn Case "-" trgt2 = num(i).str & ope(i) & num(i + 1).str ans.Va = num(i).Va * num(i + 1).Vb - num(i).Vb * num(i + 1).Va ans.Vb = num(i).Vb * num(i + 1).Vb str = Replace(str, trgt2, ans.str, , 1) GoTo Rtrn End Select Next '---------------- '予期せぬ処理の場合はSTOP '---------------- Stop End Function
正直あまり格好良くない関数です。
格好良い関数が思いつかなかったのでしかたない。
悲しいですが、できることをやっていくだけです。
「それでも良かれと思った道を選ぶ」って言葉を思い出します。
灼眼のシャナというライトノベルで出てくるのですが笑
クラスモジュール「Fraction」
そうそう。 今回のAnalyze関数を動かすために、Fractionにも関数を足しました。
Public Va As Integer '分子 Public Vb As Integer '分母 Private Sub Class_Initialize() Va = 0 Vb = 1 End Sub Property Get str() As String '-------------------- 'Va/Vb '-------------------- If Va = 0 Then str = "0" ElseIf Vb = 1 Then str = Va Else str = Va & "/" & Vb End If End Property Function Con(str) '------------------- '文字式から分子と分母を得る '------------------- Dim trgt As String Dim stock As String Dim i As Integer If str = "" Then Exit Function If InStr(str, "/") = 0 Then Va = Val(str) Exit Function End If For i = 1 To Len(str) trgt = Mid(str, i, 1) If trgt = "/" Then Va = Val(stock) stock = "" Else stock = stock & trgt End If Next If stock = "" Then Vb = 1 Else Vb = Val(stock) End If End Function Function Fit() '--------------------- '約分 '--------------------- Dim L As Integer '最大公約数 L = Application.WorksheetFunction.Gcd(Va, Vb) Va = Va / L Vb = Vb / L End Function
ちょっと気持ちの悪いコードになってしまいましたが、今は考えたくないので書き直しは保留。
まとめ
コードは管理不細工ですが。
これで「ランダム作成した問題の答えを求める」ことができます。
次回は「答えから問題を書き換える」について書きます。
計算プリントを自動生成する その14_1 3数の計算 (ランダム作成関数)
はじめに
リベンジです。
じつは「3数の計算」はその11で扱いました。
当時は力不足で凍結するに至りましたが
今回は何とかなりそうなので再び着手。
今回は「ランダム作成関数」について書きます。
ランダム作成関数とは
いままでとは全然違う考え方をします。
今までは数値の選び方にかなり気を使っていました。
望んだタイプの答えが出るように各項の値を設定していました。
ただそれだと、演算子が複数入るパターンの計算式をつくるのがめちゃくちゃ大変になります。
なので今回はかなり割り切りました。
適当に計算式を作成して、その答えを求める。
答えの値に不満があれば各項の値を適当に修正して、もう一度答えを求める。
というやり方。
コード
ランダムとはいえ、各項の初期値はなんとなく決められます。
なので今回は各項を正の数で設定します。
値と演算子はランダムで。
Function Int_Rnd_Str(NN) As String '------------------ '変数の定義 '------------------ Dim ope() As String '演算子 Dim num() As Integer '数値 Dim i As Integer '繰り返しの制御変数 Dim box As Variant '演算子決定のアシスト変数 Dim tmp As Integer '演算子決定のアシスト変数 Dim ans As String '戻り値 '------------------ 'opeとnumの配列数を指定 '------------------ ReDim ope(1 To NN) ReDim num(1 To NN) '------------------ 'boxの指定 '------------------ box = Array("+", "-", "×", "÷") '------------------ 'opeとnumの値を指定 '------------------ For i = 1 To NN - 1 tmp = Rnd_Num(0, 3) If tmp = 3 Then tmp = Rnd_Num(0, 3) ope(i) = box(tmp) Next For i = 1 To NN num(i) = Rnd_Num(1, 9) If num(i) = 1 Then num(i) = Rnd_Num(1, 9) Next '------------------ 'ansの作成 '------------------ For i = 1 To NN ans = ans & num(i) & ope(i) Next Int_Rnd_Str = ans End Function
引数「NN」に「項の数」という意味を持たせています。
演算子に「÷」が出た場合、数値に「1」が出た場合は一度だけ再考させています。
まとめ
これでとりあえず「3数の計算式の問題」だけは作れます。
ただし答えがいくつか手動でないと求められないし
答えが不必要に複雑になるかもしれません。
次回はこの「ランダム作成した問題の答え」を出してくれる関数を書きます。
計算プリントを自動生成する その13_2 負の数 (カッコつき)
はじめに
前回に制約ありで、負の数を扱えるようになりました。
今回はその制約を外していきます。
ただそれでもひとつ制約は残ります。
今回の制約
今回は負の数だけでなく正の数も扱えるようにします。
また掛け算と割り算も行えるようにします。
ただ計算式(正確には左辺)をカッコつきにします。
(+3)-(+2)=1
(-2)×(+5)=-10
のように。
ほんとは
3-(-2)=5
のように不必要なかっこは外したいのですが。
これが今回の制約。
自動でやってみる
Int_PM_Num_Select
本当はこれが真のInt_Num_Selectなのですが、とりあえず今回はPMをつけます。
Function Int_PM_Num_Select(version) As String() '------------------- '変数の定義 '------------------- Dim num(1 To 3) As Integer '計算式に入れる数値 Dim ans(1 To 3) As String '戻り値を入れる変数 Dim i As Integer '繰り返しの制御変数 Dim flag As Integer '傾斜のアシスト変数 Dim tmp As Integer 'versionを反映する変数 '--------------------- 'numの値の決定 '--------------------- Reselect: num(1) = Rnd_Num(-9, 9) num(2) = Rnd_Num(-9, 9) If flag = False Then If num(1) * num(2) = 0 Then flag = True GoTo Reselect End If End If '--------------------- 'num3の値の決定 '--------------------- Select Case version Case 1 num(3) = num(1) + num(2) Case 2 num(3) = num(1) - num(2) Case 3, 4 num(3) = num(1) * num(2) End Select '--------------------- '割り算の処理 '--------------------- If version = 4 Then tmp = num(1) num(1) = num(3) num(3) = tmp End If '--------------------- 'ansの値の決定 '--------------------- For i = 1 To 3 ans(i) = num(i) Next Int_PM_Num_Select = ans End Function
ちょっと統合したいけども保留して進みましょう。
もう少し景色が良くなってからのほうが良い感じの統合ができそうですし。
Int_PM_Calculation
前回の「Int_Minus_Calculation」を発展させます。
Function IntPM_Calculation(version, level) As String '---------------------------------- '整数の計算式を生成する 'version=1は足し算 'version=2は引き算 'version=3は掛け算 'version=4は割り算 '---------------------------------- '---------------------------------- '扱う変数の定義 '---------------------------------- Dim kou() As String '指定する数値 Dim ans As String 'strに戻す値 Dim tmp As String 'kouを入れ替えるための変数 '---------------------------------- 'numの値の決定 '---------------------------------- kou() = Int_Minus_Num_Select(version) '---------------------------------- '計算式を文字列にする '---------------------------------- Select Case version Case Is = 1 ans = "(+a)+(+b)=c" Case Is = 2 ans = "(+a)-(+b)=c" Case Is = 3 ans = "(+a)×(+b)=c" Case Is = 4 ans = "(+a)÷(+b)=c" End Select ans = Replace(ans, "a", kou(1)) ans = Replace(ans, "b", kou(2)) ans = Replace(ans, "c", kou(3)) ans = Replace(ans, "+-", "-") ans = Replace(ans, "+0", "0") '---------------------------------- '文字列を戻す '---------------------------------- Int_PM_Calculation = ans End Function
「ans」の値が特徴的です。
さらに、今回追加した、Replaceの2行。
これによって正の数が扱えるようになりました。
また、引き算と割り算が使えなかったのも、正の数が使えなかったことが原因なので、それも一緒に解決。
まとめ
負の数を扱うと引き算に気を使わなくてよくなります。
実は分数を利用すれば割り算も気を使わなくてよくなるのですが。
カッコを外す計算式の生成もおいしくはあるのですが、これはまた今度にしましょう。
割り切ってから保留がガンガン増えていますね笑
次回は3数のリベンジ。
計算プリントを自動生成する その13_1 負の数(負の数どうし)(足し算と掛け算)
はじめに
今回は負の数を扱います。
ただしいろいろと制約が。。
今回の制約
タイトルに制約を書いてありますが、ここで改めて確認を。
負の数どうし
正の数と負の数では、計算式の文字列を作るときに処理が異なってしまいます。
まずは負の数で鳴らしましょう。
足し算と掛け算
引き算と割り算は、項を入れ替えるところでちょっと問題が生じます。
正の数と負の数を区別なく扱えるようになれば、おそらくここも解決されるはず。
自動でやってみる
Int_Minus_Calculation
Function Int_Minus_Calculation(version, level) As String '---------------------------------- '整数の計算式を生成する 'version=1は足し算 'version=2は引き算 'version=3は掛け算 'version=4は割り算 '---------------------------------- '---------------------------------- '扱う変数の定義 '---------------------------------- Dim kou() As String '指定する数値 Dim ans As String 'strに戻す値 Dim tmp As String 'kouを入れ替えるための変数 '---------------------------------- 'numの値の決定 '---------------------------------- kou() = Int_Minus_Num_Select(version) '---------------------------------- '引き算、割り算用の処理 '---------------------------------- Select Case version Case Is = 1, 3 Case Is = 2, 4 tmp = kou(1) kou(1) = kou(3) kou(3) = tmp End Select '---------------------------------- '計算式を文字列にする '---------------------------------- Select Case version Case Is = 1 ans = "(a)+(b)=c" Case Is = 2 ans = "(a)-(b)=c" Case Is = 3 ans = "(a)×(b)=c" Case Is = 4 ans = "(a)÷(b)=c" End Select ans = Replace(ans, "a", kou(1)) ans = Replace(ans, "b", kou(2)) ans = Replace(ans, "c", kou(3)) '---------------------------------- '文字列を戻す '---------------------------------- Int_Minus_Calculation = ans End Function
「ans」の文字列の初期値が、正の数のみの時とは異なっています。
ここをもっとうまく制御できると正負どちらの数も扱えるのですが。
Int_Minus_Num_Select
Function Int_Minus_Num_Select(version) As String() '------------------- '変数の定義 '------------------- Dim num(1 To 3) As Integer '計算式に入れる数値 Dim ans(1 To 3) As String '戻り値を入れる変数 Dim i As Integer '繰り返しの制御変数 '--------------------- 'numの値の決定 '--------------------- num(1) = Rnd_Num(-9, -1) num(2) = Rnd_Num(-9, -1) '--------------------- 'num3の値の決定 '--------------------- Select Case version Case 1 num(3) = num(1) + num(2) Case 3 num(3) = num(1) * num(2) End Select '--------------------- 'ansの値の決定 '--------------------- For i = 1 To 3 ans(i) = num(i) Next Int_Minus_Num_Select = ans End Function
偶然にも?「Rnd_Num」関数が負の数に対応していたので助かりました。
まとめ
コードを見ていて思いましたが、過去に書いたコメント文がそのまま残っていたりしますね。
本当はあまりよろしくないのですが、今後できたら気を付ける、くらいにしておきます。
気が向かないことはしない!
次回は制約を一部外します。 なんだか、制約って書くたびにハンターハンターが思い浮かびます笑
計算プリントを自動生成する その12_2 分数の計算
はじめに
前回は分数クラスを作成しました。
今回はそれを使って分数の計算を実装します。
自動でやってみる
いきなり四則の計算を想定して書きます。
クラスモジュール
これは前回書いた通りです。
Fra_Calculation
Function Fra_Calculation(version, level) As String '---------------------------------- '整数の計算式を生成する 'version=1は足し算 'version=2は引き算 'version=3は掛け算 'version=4は割り算 '---------------------------------- '---------------------------------- '扱う変数の定義 '---------------------------------- Dim kou() As String '指定する数値 Dim ans As String 'strに戻す値 Dim tmp As String 'numを入れ替えるための変数 '---------------------------------- 'kouの値の決定 '---------------------------------- kou() = Fra_Num_Select(version) '---------------------------------- '計算式を文字列にする '---------------------------------- Select Case version Case Is = 1 ans = "a+b=c" Case Is = 2 ans = "a-b=c" Case Is = 3 ans = "a×b=c" Case Is = 4 ans = "a÷b=c" End Select ans = Replace(ans, "a", kou(1)) ans = Replace(ans, "b", kou(2)) ans = Replace(ans, "c", kou(3)) '---------------------------------- '文字列を戻す '---------------------------------- Fra_Calculation = ans End Function
これは「Int_Calculation」とほとんどおなじコードです。
「Num_Select」系の関数からの戻り値をString型にしたところが変更点です。
ちなみに「Fra」は「Fraction」の頭三文字です。
Fra _Num_Select
戻り値の型をStringにしたことで少し意味合いが変わりました。
Num_SelectよりもTerm_Makeのほうが近いかもしれませんね。
しばらくは名残でそのままの名前にしておきます。
Function Fra_Num_Select(version) As String() '------------------------ '変数の定義 '------------------------ Dim kou(1 To 3) As Fraction '分数 Dim swt As Integer Dim ans(1 To 3) As String '戻り値用の変数 '------------------------ 'kouの値を仮決め '------------------------ Set kou(1) = New Fraction Set kou(2) = New Fraction Set kou(3) = New Fraction kou(1).Va = Rnd_Num(1, 3) kou(1).Vb = Rnd_Num(3, 8) kou(2).Va = Rnd_Num(1, 3) kou(2).Vb = Rnd_Num(1, 3) * kou(1).Vb '------------------------- 'versionを反映してkou1,2を本決定 '------------------------- Select Case version Case Is = 1 Case Is = 2 If kou(1).Va * kou(2).Vb - kou(1).Vb * kou(2).Va < 0 Then swt = kou(1).Va kou(1).Va = kou(2).Va kou(2).Va = swt swt = kou(1).Vb kou(1).Vb = kou(2).Vb kou(2).Vb = swt End If Case Is = 3 swt = kou(2).Va kou(2).Va = kou(2).Vb kou(2).Vb = swt Case Is = 4 End Select '------------------------- 'kou3を決定 '------------------------- Select Case version Case Is = 1 kou(3).Va = kou(1).Va * kou(2).Vb + kou(1).Vb * kou(2).Va kou(3).Vb = kou(1).Vb * kou(2).Vb Case Is = 2 kou(3).Va = kou(1).Va * kou(2).Vb - kou(1).Vb * kou(2).Va kou(3).Vb = kou(1).Vb * kou(2).Vb Case Is = 3 kou(3).Va = kou(1).Va * kou(2).Va kou(3).Vb = kou(1).Vb * kou(2).Vb Case Is = 4 kou(3).Va = kou(1).Va * kou(2).Vb kou(3).Vb = kou(1).Vb * kou(2).Va End Select '------------------------------ 'kouの約分 '------------------------------ kou(1).Fit kou(2).Fit kou(3).Fit '------------------------------ '戻り値を決定 '------------------------------ ans(1) = kou(1).Str ans(2) = kou(2).Str ans(3) = kou(3).Str Fra_Num_Select = ans End Function
こちらは整数の時とはだいぶ違う仕様にしました。
とくに引き算や割り算。
計算前に値を調整するタイプに変えました。
まとめ
これらを組み合わせることでとりあえず分数の四則の計算が実装されました。
難度設定は、正直やっていて頭が痛くなるので保留します。
第12回までやってみてなんとなく気づきました。
あんまり気が乗らないものを義務感で行うとモチベーションが下がって、コードを書くこと自体がおっくうになってしまうことに。
めんどくさいなと感じたものはどんどん保留なり凍結なりして
「最初の願い」を失わいようにしたいなぁと思います。
次回は負の数。
次々回は3数の計算のリベンジ。
こんな流れで行きます。
数学プリントを自動生成する その12_1 分数 クラスモジュールをつくる
はじめに
分数の計算です。
今回はまずクラスモジュールをつくります。
分数の特徴
分数といえば。
1. 分子
2. 分母
3. 表記方法
4. 約分
5. 通分
このあたりが特徴でしょう。
クラスモジュールではこれらのうち1~4を実現させます。
5は関数のほうで
クラスモジュール
名前は「Fraction」にします。
Public Va As Integer '分子 Public Vb As Integer '分母 Private Sub Class_Initialize() Va = 0 Vb = 1 End Sub Property Get Str() As String '-------------------- 'Va/Vb '-------------------- If Va = 0 Then Str = "0" ElseIf Vb = 1 Then Str = Va Else Str = Va & "/" & Vb End If End Property Function Fit() '--------------------- '約分 '--------------------- Dim L As Integer '最大公約数 L = Application.WorksheetFunction.Gcd(Va, Vb) Va = Va / L Vb = Vb / L End Function
分数の特徴とコードの対応は以下の通り。
特徴 | コード |
---|---|
分子 | Va |
分母 | Vb |
表記方法 | Str |
約分 | Fit |
たとえば。
Fraction型の変数「num」について。
コード | 意味 |
---|---|
num.Va=3 | 分子が3 |
num.Vb=8 | 分母が8 |
num.Str | ”3/8” |
num.Fit | 約分して分子と分母の数値を更新する |
のようになります。
分子や分母の値を使いたいときはVaやVbを、文字列として扱いたいときはFormを使えるという、なかなか便利な仕様です。
まとめ
今回はクラスモジュールの説明に1回分の記事を割きました。
クラスモジュールも、プリント作成においてとても大事なツールです。
次回はこれを使って分数の計算を実装します。
計算プリントを自動生成する その11 整数3つの四則の計算(a+b×c)
はじめに
整数2つの四則の計算と、それを作るうちに生じた課題を前回までにまとめました。
今回は整数3つの四則の計算です。
今回はとりあえず「a+b×c」を実装します。
手順
- 整数を3つ選ぶ
- a+b×cの答えを求める
- 計算式をつくる
手動でやってみる
手順 | 数字 |
---|---|
整数を3つ選ぶ | 3,5,7 |
a+b×cの答えを求める | 38 |
計算式をつくる | 3+5×7=38 |
自動でやってみる
ひながた_Limited
まずは演算子を2つ用意しないといけないので、変数「version」を配列にします。
今回は「a+b×c」に限っているので、関数の語尾に_Limitedをつけます。
Sub ひながた_Limited() '------------------------------ '0. 扱う変数の定義 '------------------------------ Dim N As Integer '問題数を決める変数 Dim NN As Integer '扱う数字の個数 Dim version() As Integer '計算の種類を決める変数 Dim level As Integer '難度を決める変数 Dim i As Integer '繰り返しの制御変数 Dim str As String '計算式の文字列 '------------------------------ '1. 問題数の指定 '------------------------------ N = 10 For i = 1 To N '------------------------------ '2. 問題を指定して式を求める '------------------------------ NN = 3 ReDim version(1, NN - 1) version(1) = 1 version(2) = 3 level = 1 str = Int_Calculation_Limited(NN, version, level) '------------------------------ '3. 出力 '------------------------------ Call Insert_Equation(20, 30 * i, str) Next End Sub
Int_Calculation_Limited
Function Int_Calculation_Limited(NN, version, level) As String '---------------------------------- '扱う変数の定義 '---------------------------------- Dim num() As Integer '指定する数値 Dim ans As String 'strに戻す値 '---------------------------------- 'numの値の決定 '---------------------------------- num() = Int_Calculation_Num_Select_Limited(NN) '---------------------------------- '計算式を文字列にする '---------------------------------- ans = "num1+num2×num3=num4" For i = 1 To NN + 1 ans = Replace(ans, "num" & i, num(i)) Next '---------------------------------- '文字列を戻す '---------------------------------- Int_Calculation_Limited = ans End Function
一番の特徴は、計算式を文字列にする箇所です。
いままで「a+b=c」だった部分を「num1+num2×num3=num4」にしています。
Int_Calculation_Num_Select_Limited
Function Int_Calculation_Num_Select_Limited(NN) As Integer() '-------------------------- '変数の定義 '-------------------------- Dim num() As Integer Dim i As Integer ReDim num(1 To NN + 1) '-------------------------- 'numの決定 '-------------------------- For i = 1 To NN num(i) = Rnd_Num(1, 9) Next num(4) = num(1) + num(2) * num(3) Int_Calculation_Num_Select_Limited = num End Function
今回は限定版なのでとりあえず1ケタの数字を3つランダムに選択することにしました。
まとめ
次回以降にLimitedを外していこうと思っていたのですが
ちょっと思ったより難しそうなので「整数3つの四則の計算」はここでいったん凍結します。
これを進めるには少なくとも
「分数の計算」と「負の数の計算」が必要みたいなので
次回以降はそれらに取り組みます。