滝の音

滝の音

名こそ流れてなお聞こえけれ

VBAで中学数学の問題を扱おう! その4 (多項式)×(多項式)の計算

今回は多項式同士の計算です。

今回はケースに合わせて少しずつコードを修正していくのではなく

必要だと思う機能を書き出してから

それらをひとつづつコードにしていきます。

 

新スタイル。

はたしてできるだろうか・・・

 

あ。

今回からコード全部を載せるのはやめにします。

その1から読んでいる人はそろそろ自分で考えたいかなというのと。

全部載せると分量がめちゃ多くなってしまうので。。

ぜひコメントに答えを書いてみてください~

  • 必要な機能

(a+b)(a+2b+5c)^2

について考えて見ましょう。

これを計算するためには何が必要か。

 

それぞれの多項式を認識する

それらを掛け合わせる

同類項をまとめる

 

まぁこの3つですよね。

 

それぞれについてひとことずつ。

 

それぞれの多項式を認識するには括弧と累乗記号が肝かなと。

 

それらを掛け合わせるのは掛け合わせる単項式を指定して

前に作った単項式の計算の関数を用いればいいかなと。

 

同類項をまとめるコードはすでに前回作ったのでそれを流用。

 

 

こんな感じです。

 

  • 概要デザイン

f:id:nozomi-hayashi:20180625025119g:plain

 

Private Sub CB1_Click()

 Dim siki As String
 Dim answer As String
 
 '-------------------------
 '式の認識
 '-------------------------
 siki = TB1
 
 '-------------------------
 '多項式に分解
 '-------------------------
 Dim takou() As String
 Call 多項式に分解(takou, siki)
 
 '-------------------------
 '展開
 '-------------------------
 answer = expantion(takou)
 
 '-------------------------
 '同類項をまとめる
 '-------------------------
 answer = Similar_Terms_Culculation(answer)
 
 '-------------------------
 '表記
 '-------------------------
 TB2 = answer
 
End Sub

このように概要を書くことはとてもいいような気がします。

関数間の連携をとりやすくなるというか。

それぞれはうまく機能しているけどうまくつながらないコードを修正するときって

自分の無能さをのろいながら行うので精神的によくないですし。

そういうつまらないことは起きない方がいいですよねー。

 

認識系のコードはもう何回も書いているので余裕ですねw

 

Sub 多項式に分解(trgt, kou)
 Dim char As String                 '処理用の箱
 Dim tmp As Integer                 '処理用の箱
 Dim stock As String               '処理用の箱
 Dim n As Integer                   'あて先の指定
 Dim minus As Integer               '-の処理用の箱
 Dim flag(1) As Boolean
 Dim h As String                   '累乗の処理
 
 n = 0
 
 For j = 1 To Len(trgt)
    char = Mid(trgt, j, 1)
    tmp = Asc(char) - 96
   
    Select Case tmp
        Case -56                '(の場合
            If stock <> "" Then
                ReDim Preserve kou(n)
                kou(n) = stock
                n = n + 1
                stock = ""
            End If
           
            If flag(1) = True Then
                stock = kou(n - 1)
                For k = 2 To Val(h)
                    ReDim Preserve kou(n)
                    kou(n) = stock
                    n = n + 1
                Next
                stock = ""
                h = ""
                flag(1) = False
            End If
           
            flag(0) = True
        Case -55                ')の場合
            flag(0) = False
            ReDim Preserve kou(n)
            kou(n) = stock
            stock = ""
            n = n + 1
        Case -51, -53         '-,+の場合
            stock = stock & char
        Case -2         '^の場合
            If flag(0) = True Then
                stock = stock & char
            Else
               
                flag(1) = True
            End If
        Case -48 To -39 '0~9の場合
            If flag(1) = True And n > 0 Then
                h = h & char
            Else
                stock = stock & char
            End If
        Case 1 To 26    'アルファベットの場合
            stock = stock & char
           
        Case Else
            MsgBox "考慮されていない文字が入っています"
            Stop
    End Select
 Next
 
 If flag(1) = True Then
                stock = kou(n - 1)
                For k = 2 To Val(h)
                    ReDim Preserve kou(n)
                    kou(n) = stock
                    n = n + 1
                Next
                stock = ""
            End If
 
End Sub

うーん。

まだうまいことかけない。。

 

 

select caseで一文字ずつ対応を変えて

さらにflagで前後関係に対応する

 

のが今のやり方ですが。

なんだか妙に複雑なコード。。。

もう少しシンプルにできないものか。。。

 

  • それらを掛け合わせる

一つ目のコードによってtakouの配列に多項式が含まれています。

それらを掛け合わせるのが次のコード。

takouの配列の数によらないコードを書くためにいろいろ考えたのですが。

 

takouから式をひとつ取り出して

それらを掛け合わせる

そしてまた次の式を取り出して

 

という方式にしようかなと思います。

takouの式をいっぺんに処理するには

forの数をtakouの数に合わせないといけなくて

そういったコードの書き方がよくわからないので

今回はひとつずつ式で行こうと思います。

 

展開コードの概要。

Sub 展開(takou, keeper)

 Dim adder() As String
 Dim trgt As String
 Dim tmp As String
 
 trgt = kou(0)
 Call 単項式に分解(trgt, keeper)
 
 For i = 1 To UBound(kou)
    trgt = kou(i)
    Call 単項式に分解(trgt, adder)
    Call 掛け合わせ(keeper, adder)
 Next
 
End Sub

 

 

 

「単項式に分解」で多項式を単項式に分解

「掛け合わせ」で展開後の各項を個別にkeeperに格納しています

 

関数とsubプロシージャはクイズに。

 

  • 同類項をまとめる

これは前回に書いたから流用~

 

と。

思ったら。

信じられないくらい互換性が悪い。。。

 

いや。

流用することはできなくはなかったのですが

おんなじ意味を持つ処理を繰り返し行うことになってしまい

気持ち悪すぎるので書き直しました。

 

前回書いた「同類項をまとめる」はマジで無駄だったなぁ。。。

 

多項式を単項式に分解して同時に係数と文字の間にカンマを入れる

同類項をまとめると同時に特定の係数に対応する

多項式に復元する

 

この「同時」というところが肝。

ひとつの関数やプロシージャに「同時」が増えるほどに汎用性は薄れます。

それぞれの関数に入力するための形式は限られてしまいます。

 

今回どのようなことが起きたかというと。

 

展開をしてそれぞれの項を単項式として配列に格納

同類項にまとめるために形式を整える(せっかく分けた単項式を多項式に復元する)

それを単項式に分解しながら特別な処理をする

同類項をまとめながら係数の処理をする

またそれを多項式に復元する

 

というおばかなやり方。

 

これを結構がんばって修正しました。

 

単項式に分解する

特別な処理をする

同類項につつ係数の処理をする

多項式に復元する

 

3つ目の処理は分解するとむしろ面倒になりそうなので。

あとでまた後悔するのだろうか。。。

だとしたら「同類項をまとめる」コードを書いた当時はこれを"よかれ"と思って書いたわけで

それがあだになるとわかるということは

成長の証なのかも?

まず概要

 Dim co() As String
 Call 加工処理(keeper, co)
 Call 同類項をまとめる(keeper, co)
 answer = line_restoration3(keeper)

 

それぞれの関数やsubプロシージャはクイズで。

 

  • まとめ

試行錯誤したあとの概要はこんな感じ。

Private Sub CB1_Click()

 Dim siki As String
 Dim answer As String
 '-------------------------
 '式の認識
 '-------------------------
 siki = TB1
 
 '-------------------------
 '多項式に分解
 '-------------------------
 Dim takou() As String
 Call 多項式に分解(siki, takou)
 
 '-------------------------
 '展開
 '-------------------------
 Dim keeper() As String
 Call 展開(takou, keeper)
 
 '-------------------------
 '同類項をまとめる
 '-------------------------
 Dim co() As String
 Call 加工処理(keeper, co)
 Call 同類項をまとめる(keeper, co)
 answer = line_restoration3(keeper)
 '-------------------------
 '表記
 '-------------------------
 TB2 = answer
End Sub

 

んー。

書きながら成長しているから仕方がないのですが。

それぞれの処理の深さが一致していないですね。。。

原因は最初の概要の考え方がぬるいこと。

f:id:nozomi-hayashi:20180625091833g:plain

はて。

あっているのかわからん。。。

ちょっと式を簡単にしましょう。

f:id:nozomi-hayashi:20180625091927g:plain

うん。

あってますね。

そろそろユーザーフォームに出力するのも限界ですねー。

 

  • 思ったこと

ひとつの関数で複数の事を行おうとするとろくなことにならない。

流用できないので結局細かく再分解することになる。

これに実感したことが一番の収穫です。

知ってはいたはずなのですがなぜか実践できていませんでした。

 

  • 次回の予定

ユーザーフォームへの出力に限界を感じているので

次回はシートに見やすく出力するコードを書きましょう。