Powerpoint VBAを使おう!

Powerpoint VBAやExcelのVBAで遊んでいます。AdobeのScriptにも興味が少し。。

化学反応分解 (3回目)

正規表現はある程度に切り上げて,とりあえず処理できる形を作ってみました。

プログラマーではないので行き当たりばったりでやってますが,なんとなく動いているようです。

Microsoft VBScripting Runtime と Microsoft VBScript Regular Expressions 5.5への参照設定を前提としています。

Sub 化学反応式分解()

    Dim Reg As RegExp, Matches As MatchCollection, SMatches As SubMatches
    Set Reg = New RegExp
    Dim 化学反応式 As String
    化学反応式 = "4NaHCO3→2Na2CO3+2H2O+2CO2"
    With Reg
        .Global = True
        .Pattern = "([\+→])|([A-Z][a-z]?)|([0-9]*)"
        Set Matches = .Execute("+" & 化学反応式 & "+")
    End With
        
    Dim i, j, Col As New Collection
    
    For i = 0 To Matches.Count - 3
        Col.Add Matches(i).Value
        If IsNumeric(Matches(i)) = False And IsNumeric(Matches(i + 1)) = False Then Col.Add "1"
    Next
    
    Dim 反応物 As New Dictionary, 生成物 As New Dictionary, flg矢印 As Boolean, 係数 As Long, Col係数 As New Collection
    For i = 1 To Col.Count Step 2
        Select Case Col(i)
            Case Is = "+"
                係数 = CLng(Col(i + 1))
                Col係数.Add 係数
            Case Is = "→"
                係数 = CLng(Col(i + 1))
                Col係数.Add 係数
                flg矢印 = True
            Case Else
                If flg矢印 = True Then
                    生成物(Col(i)) = 生成物(Col(i)) + CLng(Col(i + 1)) * 係数
                Else
                    反応物(Col(i)) = 反応物(Col(i)) + CLng(Col(i + 1)) * 係数
                End If
        End Select
    Next
    
    Dim 総数判定 As Boolean
    
    '反応物と生成物の係数の比較
    総数判定 = True
    For i = 0 To 反応物.Count - 1
        'Debug.Print 反応物.Keys()(i) & " " & 反応物(反応物.Keys()(i)) & "," & 生成物(反応物.Keys()(i))
        If 反応物(反応物.Keys()(i)) <> 生成物(反応物.Keys()(i)) Then 総数判定 = False
    Next
    '公約数があるかどうか確認
    Dim flg As Boolean, 公約数判定 As Boolean, k, dic公約数 As New Dictionary
    公約数判定 = True
    
    For i = 1 To Col係数.Count
        If Col係数(i) <> 1 Then
            For j = Col係数(i) To 2 Step -1
                If Col係数(i) Mod j = 0 Then
                    flg = True
                    For Each k In Col係数
                        If k Mod j <> 0 Then flg = False
                    Next
                    If flg = True Then
                        公約数判定 = False
                        dic公約数(j) = 1
                    End If
                End If
            Next
                
        End If
    Next
    
    Dim 公約数列挙 As String
    For i = 0 To dic公約数.Count - 1
        公約数列挙 = 公約数列挙 & "," & dic公約数.Keys()(i)
    Next
    If 公約数判定 = False Then 公約数列挙 = Mid(公約数列挙, 2)
    MsgBox "総数判定は" & 総数判定 & ",公約数は" & Switch(公約数判定 = False, 公約数列挙, 公約数判定 = True, "ありません。")
    If 公約数判定 = True And 総数判定 = True Then MsgBox "この係数は妥当です" Else MsgBox "この係数は不適です"
    Stop
End Sub


これを実行すると,
f:id:chemiphys:20181004184857p:plain

こんな感じで何か言ってくるんですが,何をやっているのかというと,


化学反応式 = "4NaHCO3→2Na2CO3+2H2O+2CO2"

化学反応式のところに書いている化学反応式が妥当かどうかを判断させています。

まず,正規表現でばらっばらにして,
係数や添え字が1の時は省略されたりするので,等間隔で扱えるように整えます。

元はこれで,
   4NaHCO3→2Na2CO3+2H2O+2CO2
内部ではこんな感じ
  +4Na1H1C1O3→2Na2C1O3+2H2O1+2C1O2
まず1文字目に+を入れて,+か→があると物質のスタートだという合図。
係数は今は入ってますが,係数がなければ1をつけます。
各元素の直後の1が省略されててもつけるようにすることで,作業をしやすくしました。

文字数は決まっていないのでコレクションオブジェクトに放り込んでいます。

その後,Dictionaryオブジェクトを利用することで,各元素の数値を格納する。

キーに元素記号を使っているので,各元素ごとの合計の比較を容易にしています。

また,係数に公約数が含まれるかどうかを今回はきちんと処理をしました。

たぶんこれで化学反応式を与えるだけで,係数や各元素の数を把握し,その係数が適切かどうかを判断できていると思います。

作ろうとしているやつは,化学反応式の導入部分で使用する予定のもののため,括弧を含む面倒なやつは今回は想定していません。

けっこうごちゃごちゃいろんなことをやっている割には正規表現とDirectoryオブジェクトのおかげで短くできているんじゃないかなぁと思います。

とりあえず一歩進んだ ( ´ー`)フゥー...