化学反応式(5)?
さて,そろそろこの話も終わらないと。
いろいろと勉強にはなりました。まず,VBScriptの正規表現は後読みとかそういうのが無いらしいこと。
できることだけやるしかないようですね。当たり前ですが(;´▽`A``
妥協点を考える。
わたしがやりたかったのは「化学反応式中の添え字の数字の位置を知りたい」という点です。位置だけを効率よく抜き出したい。
各元素の数等については,十分満足がいくレベルに来ていたので,下付け文字にするための位置を知りたかった。
以前,4択問題集とかを作った際,化学の教材で上付き下付きを無視するのはありえない,という持ち科目による自己制限を課して,克服すべくやっていました。
ForNext等を利用し,やれてはいたものの,そこで立ちふさがったのが,処理速度の問題でした。
あまり速度が速くないタブレットで十分使えるレベルを目指す,となったとき,もともとプログラミングでごりごり画像を扱うというソフトではないパワーポイントでは,
もっさりした動きがどうしても出たんです。それが理由で,ループを減らして実現したいというマニアックな制限下での取り組みです。
なので,偏っていることは承知の上(;´▽`A`` もうあきらめようとしていましたが,どうも私の記事をきっかけにいろいろと試してもらっていただいたようでしたので,
もうちょい自分も考えてみようと思いました。
Infoment さんありがとうございます。
さて,本題の位置決めです。VBScriptの正規表現に制限があろうと,それでも極めて優秀なのは間違いないので,自分の発想を柔軟にするべきとまず思いました。
そこでとりあえず候補になってるのが下記のコード Microsoft VBScript Regular Expression 5.5への参照設定を前提としています。
Sub test() Dim Reg As New RegExp, Matches As MatchCollection, Str_ As String Str_ = "3MnO2+4HCl→5MnCl2+2H2O+Cl20" '化学反応式はわざとめちゃくちゃです。 'うまく引掛けてるかどうかの確認のため。 With Reg .Global = True .Pattern = "[A-Za-z]([0-9]+)" Set Matches = .Execute(Str_) End With Stop End Sub
とても短いです(;´▽`A``
ローカルウィンドウではこんな感じ
本当は,数字の位置だけを一発で取り出したいわけですが,わたしの今の能力ではその前のアルファベットまで引掛けてしまいます。
そこをウンウン悩んでいたんですが,位置を知りたいだけなので,一文字だけ確実にひっかけてくるように考えれば位置はわかるじゃないか,というけっこう適当な発想をしてみました。
1文字確実にひっかけるなら1文字ずらせば位置は特定できますもんね。。
正規表現でひっかけたものは, FirstIndexのところに何文字目にあるかを情報として与えてくれます。なので,それ+1した数字のところが,添え字のスタート地点だといえます。
次にまれに二桁の添え字がある( P4O10みたいな)それを判別するには・・
( )を正規表現で使っているのでSubmatchesを利用することで解決できるのが一つ。まず私はそれを考えていました。でも,それじゃないね。
Item4のところを見るとわかるんですが,二桁の添え字をひっかけると,ちゃんとLengthのとこが3になっています。通常は2で二けた引掛けたときは3に。
きちんと規則正しく言うことを聞いてくれているので,どうもこれで必要な情報は得られている模様。
ふむふむ これはよさそう。FirstIndexは0文字目から数えているのかな。。ということは結局二つずらせばいいかなーと思い,次のコードになります。
Sub test() Dim Reg As New RegExp, Matches As MatchCollection, Str_ As String Str_ = "3MnO2+4HCl→5MnCl2+2H2O+Cl20" '化学反応式はわざとめちゃくちゃです。 'うまく引掛けてるかどうかの確認のため。 With Reg .Global = True .Pattern = "[A-Za-z]([0-9]+)" Set Matches = .Execute(Str_) End With Sheet1.Cells(1, 1) = Str_ Sheet1.Cells(1, 1).Font.Subscript = False Dim i For i = 0 To Matches.Count - 1 Sheet1.Range("a1").Characters(Matches(i).FirstIndex + 2, Matches(i).Length - 1).Font.Subscript = True Next End Sub
肝は
Sheet1.Range("a1").Characters(Matches(i).FirstIndex + 2, Matches(i).Length - 1).Font.Subscript = True
ここのところ。かなりすっきり書けた。
結果は (化学反応式は嘘ものですから!!そこの突っ込みはご遠慮します。)
これは満足できそうな結果に思えます。よさそう。。
---
本当は化学反応式を図示するやつは作っちゃったんですが,
県の教職員が自作の教材を提出してみてね。公開したやつはだめですよ。
という内容のものに,たまには出してみようと思って作ってたやつなので,残念ながらしばらく作ったもの自身をここに上げることはできません。
その会が終わった後,全部のコードを載せようと考えているところです。
画面構成等もVBAコードで書いて作ったので,コード載せるだけできちんと自分のやりたかったものは伝えれる仕上がりにはなったと思います。
3日しかかけてないので,穴だらけかもしれませんが・・素人なのでそこは許してもらえるはず。
期間が無かったので作ったものはささっと送ってしまいました。。
でも,そこで使っている下付きのコードは今回のコードではありません。
あきらかにこっちが効率いい。・・・あの時は思いつけなかった。。
でも,話のネタにしてもらったことがきっかけでもう一度考えてみることで自分なりには満足いくコードになったので,とても勉強になりました。
ありがたかったです。このコードは今後に生きるなぁ。
そうそう忘れていました。今回の内容ではあまり大差はないんですが,今回素直に言うことを聞いてくれるExcel のほうのVBAで書いています。
ただ,最後らへんのSheet1・・・ あたりを Activepresentation.Shapes("~").TextFrame.TextRange・・・とかにしていくだけでパワーポイントにも適応できます。
題名詐欺になってました(;´▽`A``そのうちちゃんと対応しないとかな。。いつか余裕があればパワーポイント仕様に・・。
追記しまくりですが,パワーポイント版
Microsoft VBScript Regular Expression 5.5への参照設定が必要です。(レイトバインディング嫌いなんです。ごめんなさい)
Sub test() Dim Reg As New RegExp, Matches As MatchCollection, Str_ As String Str_ = "3MnO2+4HCl→5MnCl2+2H2O+Cl20" '化学反応式はわざとめちゃくちゃです。 'うまく引掛けてるかどうかの確認のため。 With Reg .Global = True .Pattern = "[A-Za-z]([0-9]+)" Set Matches = .Execute(Str_) End With With ActivePresentation.Slides(1).Shapes.AddLabel(msoTextOrientationHorizontal, 0, 0, 200, 50) With .TextFrame .AutoSize = ppAutoSizeNone With .TextRange .Text = Str_ .Font.Subscript = False Dim i For i = 0 To Matches.Count - 1 .Characters(Matches(i).FirstIndex + 2, Matches(i).Length - 1).Font.Subscript = True Next End With End With End With End Sub
ブログ名前詐欺回避(ΦωΦ)
化学反応式の図示のためのコード とりあえずExcelで
うまくいかなくて悶々としてたので,ここに書きなぐって,お風呂に入って冷静に考えたら,
前回できなかったやつって,ポインタ使って丁寧にやっていくしかないか,と頭が冷えました。
パワーポイントでは VBAいじってても カーソルが見えなくなることが多々あり,うまく行ってない時はそんな細かい事でも
いらいらしますし,エラーが表示されないこともある,でちょっと付き合いにくいヤツなので,Excelでやってみた。
めんどうだったので,反応物と生成物で振り分けたりはしていません。
コードはこんな感じ
Sub test() Dim sht As Worksheet: Set sht = Sheet1 Dim rng As Range: Set rng = sht.Range("h2:r10") Dim i, j, k, l, x, y x = 1: y = 1 For i = 3 To 6 For j = 1 To sht.Cells(i, 2) If 11 - x + 1 < sht.Cells(i, 3) Then y = y + 1: x = 1 For k = 0 To UBound(Split(sht.Cells(i, 4), ",")) Step 2 For l = 1 To Split(sht.Cells(i, 4), ",")(k) rng.Cells(y, x) = Split(sht.Cells(i, 4), ",")(k + 1) x = x + 1 Next Next If x <> 11 Then x = x + 1 Next y = y + 1: x = 1 Next End Sub
ポインタを置いて丁寧にやったこと。
分子毎に毎回残りの列と分子数を比較して,入らないなら改行としたこと。
分子一個分原子を並べたら,xを一つ増やして空白を入れたこと。
違う物質になるときに改行したこと。
For Nextをがんがん回しただけで解決したようでした。
うまく行かなくなったらお風呂に行くのはいいことだ ( ´ー`)フゥー...
化学反応式 (4)
だいたい 化学反応式を分解したり,係数を数式内で変えたりと自由に動かせるものを作りました。
理由があって,画面を出すのはあまりよくないと判断し,言葉だけでの表現ですが,いくらかクラスモジュールも使いながらやれて,久々にそのあたりは満足しています。
ですが,配列でデータを用意したあと,二次元の座標に化合物の絵を描くところで思考がストップ。
考えをまとめる上でも,ちょっと書き出しに来たところです。
データとしては,下記のようなデータがあります。2次元配列に格納していて,元素個数のところは,区切りの文字列で格納。
ここまで用意出来たら簡単そうなはずなんですが,固まってしまっているんです。
上記のデータをこんな感じにしたい。
反応物も生成物も横11×縦9の〇を準備して白くしています。それを必要に応じて色を付けたり,文字をつけて元素ぽく見せるんです。
そのための元の配列を作りたいんですが,。。
できるだけたくさんの分子達を画面に収めたいので,
4, 5個の原子でできた分子なら二つ横に並べたい。 間に一つ空白。余ったところはキニシナイ。
2個,3個の原子でできた分子なら間に1つずつ空白を入れるとして,3つ横に並べたい。
1個の原子からなるものなら空白と交互がいいのかなぁ。
必要な情報をそろえているはずなのに,さっくりいかないです(;´▽`A``
今回一から化学反応式を題材にしたパワーポイントものを作ってるんですが,
正規表現をいくらかでも使うことで,以前よりずいぶんやりやすくなったとこを感じます。
でも,正規表現も ほんと 簡単なものしかわからないので,うまく引掛けられないのもあいかわらず。
MnO2+4HCl→MnCl2+2H2O+Cl2
とかで添え字だけ小さくしたいと思い,係数の数字はひっかけたくないけど アルファベットに続く数字だけを正規表現でマッチさせる。。
ということすら結局できませんでした(;´▽`A``
肯定後読みってのでできるんじゃないかなぁと思ってやってみてたんですが・・結局諦めた(;´▽`A``
先は長いです。
化学反応分解 (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
これを実行すると,
こんな感じで何か言ってくるんですが,何をやっているのかというと,
化学反応式 = "4NaHCO3→2Na2CO3+2H2O+2CO2"
化学反応式のところに書いている化学反応式が妥当かどうかを判断させています。
まず,正規表現でばらっばらにして,
係数や添え字が1の時は省略されたりするので,等間隔で扱えるように整えます。
元はこれで,
4NaHCO3→2Na2CO3+2H2O+2CO2
内部ではこんな感じ
+4Na1H1C1O3→2Na2C1O3+2H2O1+2C1O2
まず1文字目に+を入れて,+か→があると物質のスタートだという合図。
係数は今は入ってますが,係数がなければ1をつけます。
各元素の直後の1が省略されててもつけるようにすることで,作業をしやすくしました。
文字数は決まっていないのでコレクションオブジェクトに放り込んでいます。
その後,Dictionaryオブジェクトを利用することで,各元素の数値を格納する。
キーに元素記号を使っているので,各元素ごとの合計の比較を容易にしています。
また,係数に公約数が含まれるかどうかを今回はきちんと処理をしました。
たぶんこれで化学反応式を与えるだけで,係数や各元素の数を把握し,その係数が適切かどうかを判断できていると思います。
作ろうとしているやつは,化学反応式の導入部分で使用する予定のもののため,括弧を含む面倒なやつは今回は想定していません。
けっこうごちゃごちゃいろんなことをやっている割には正規表現とDirectoryオブジェクトのおかげで短くできているんじゃないかなぁと思います。
とりあえず一歩進んだ ( ´ー`)フゥー...
化学反応式分解試し中(2)
この夏頃からゲームにあんまり興味がなくなり,その代わりWeb小説にはまって読みふけるのに時間を取られています。
いつもはその誘惑に負けることが多いんですが,今日はすこし正規表現に引っ張られてやってみています。
サブマッチというのを完全に誤解していることがよーくわかったので,とにかく正規表現のパターンをいじってみる。
うまく行きません(;´▽`A``
Sub 正規表現() Dim RegObj As RegExp: Set RegObj = New RegExp Dim MatchObj As MatchCollection, 化学反応式 As String 化学反応式 = "C3H8+5O2→3CO2+4H2O" With RegObj .Global = True '複数マッチ 大事! .Pattern = "\+|→|([1-9]*)(([A-Z][a-z]?)([1-9]*))*" Set MatchObj = .Execute(化学反応式) End With Stop End Sub
こうしてみて,
きちんとばらけていってくれますね。
そして,係数と各元素と添え字部分をSubmatchesに収納できたら望みどおりなんですが,
そううまくはいってくれない。
Item1に入っているのは係数だと思うので,これは空文字列になるのは納得。たぶん想定通り
C3がどこかに行ってしまう。
他も,それぞれの物質の最後の部分しか収納してくれない。
むーん
(´・ω・)
また暇を見つけて考えよう。。
むずいなぁ
正規表現 復習中 化学反応式を分解する
ずいぶん以前にも同じことをやってて,化学反応式の係数を調整するやつを作っていました。
マクロ以外の部分に依存すると,けっこうこまごまと調整が面倒で,どうせならマクロだけで全部作れないかと,少しやり始めたところ。
各元素の原子の数は正規表現を利用したほうがいいし,問題を簡単に作れる方がいいなと思います。
今は追われてはいないので,のんびりやっていこう。。
さて,化学反応式の分解部分だけのコードがある程度形になったので載せてみる。Microsoft VBScriptRegular Expression5.5への参照設定をしている前提です。
Sub 正規表現() Dim RegObj As RegExp: Set RegObj = New RegExp Dim MatchObj As MatchCollection, 化学反応式 As String 化学反応式 = "C3H8+5O2→3CO2+4H2O" With RegObj .Global = True '複数マッチ 大事! .Pattern = "([+→])|([^+→])+" Set MatchObj = .Execute(化学反応式) End With Dim 化合物() ReDim 化合物(1 To (MatchObj.Count + 1) / 2) Dim 反応物数 As Long, i As Long, j As Long Dim 化合物分解() As MatchCollection ReDim 化合物分解(1 To (MatchObj.Count + 1) / 2) j = 1 For i = 1 To MatchObj.Count If MatchObj(i - 1).Value <> "+" Then If MatchObj(i - 1) = "→" Then 反応物数 = i / 2 Else 化合物(j) = MatchObj(i - 1) j = j + 1 End If End If Next For i = 1 To UBound(化合物) With RegObj .Pattern = "([0-9]+|[A-Z][a-z]?)" Set 化合物分解(i) = .Execute(化合物(i)) End With Next Stop End Sub
Dictionaryオブジェクトも絡めながら各元素の数をうまいこと処理したいなぁという気持ちを持ちつつまだ途中。
これを実行すると,
1.まず係数を含めた形で化合物()って配列に放り込む。
2.反応物が化合物の何番までかってのを生成物数に書き込んでおく。
3.各化合物をさらに各元素や係数に分解していく。
久々に正規表現なんか使うとほんと全くさっぱりで,しかもGlobalをTrueにしてなかったせいで,一つしか値がとれずに延々数時間考えた。
私は記憶力が無いので仕方ないんですが,それで調べ回る先が自分のブログという堂々巡り。
本当は,Submatchesというコレクションがせっかくあるんだからそれもうまく使ってスマートにやりたいんですが,今は全くだめだめです。
化学反応式 → 反応物と生成物に分ける → 各元素の数を調べる → 係数を変えれるように準備
と,やりたい先はあるんですが,自分の現状を吐き出すと,気づくことも多いのでとりあえず書き残した。
正規表現って複雑なことをとても短いコードでやってくれる。
使いこなしたいけど,さっぱりですね。トライアンドエラーでしかやれないや(;´▽`A``
Powerpoint VBA ActiveX Commandボタンをマクロで挿入する。
短いですが,少しPowerpointのVBA触ってて,知らない内容だったので書き残す。
初心者備忘録 様 の Powerpoint自動実行マクロ のところを見てて,
クラスモジュールを動かさなくてもいくつかのイベントが使える,という点でとても興味があります。
https://www.ka-net.org/blog/?p=2340
他のイベントも動いてくれた方が都合がよいので,きちんと整えようと思いつつ,他のこともあるので少しずつ。
下記のような記述がありました。
また、スライドショーの実行時やページ変更時にマクロを実行したいときに便利な「OnSlideShowPageChange」マクロですが、
ppsやppsmといったマクロが有効なスライドショー形式のファイルを実行した場合には処理が実行されません。
こういったときはスライド上に適当なActiveXコントロール(コマンド ボタン等)を追加することでマクロが実行されるようになります。
私は,スライドショーの形で利用することもけっこう多いので,ここは由々しき問題。。
そのあたりも含め,いろんなことを解決した形のPowerpointの自分用のテンプレートを準備してみようと思い始めました。
(Excelのように,最初からイベントが起動してくれれば何も問題ないのに!!)
と同時に,そういうテンプレートに相当するものをVBSで作るところまでもっていく予定。。・・・未定ですが。
ActiveXコントロールの挿入ってExcelだと マクロ記録でいとも簡単に見つかるのに,相変わらずPowerpointでは少し違って困ります。
ローカルウィンドウでボタンをActiveXのボタンを調べてたら,msoOLEControlObjectってでるんですよね。
適当にコード補完に頼って触ってたら,それっぽいのがわかりました。
Shapes.AddOLEObject Method (PowerPoint) | Microsoft Docs
ClassNameのところがよく把握できませんが,ちょうどいい例がありましたので,それに従って下記のコードにたどり着いた。( ´ー`)
Sub test() Dim sld As Slide: Set sld = Slide1 Dim CmdButton As Shape Set CmdButton = sld.Shapes.AddOLEObject(Left:=0, Top:=-100, Width:=250, Height:=50, ClassName:="Forms.CommandButton.1") CmdButton.OLEFormat.Object.Caption = "イベント発生のためのダミーボタン" End Sub
これで,とりあえずスライドショー形式でも OnSlideShowPageChange マクロが動くものが作れるかなぁ。
地味にボタンのCaptionの場所を探すのに時間がかかりました。。もっとさくさく目的のものが探せるようになりたい(;´▽`A``
ローカルウィンドウ内を検索できる機能がほしいですね。
追記
ClassNameの一覧は見つからなかったんですが,Wordの記録マクロである程度わかりそうでした。
Wordで記録したコードではこんな感じに出ます。
Sub Macro1() ' ' Macro1 Macro ' ' Selection.Range.ContentControls.Add (wdContentControlRichText) Selection.TypeParagraph ActiveWindow.View.ShowXMLMarkup = wdToggle ActiveDocument.ToggleFormsDesign Selection.InlineShapes.AddOLEControl ClassType:="Forms.CheckBox.1" Selection.TypeParagraph Selection.InlineShapes.AddOLEControl ClassType:="Forms.CommandButton.1" Selection.TypeParagraph Selection.InlineShapes.AddOLEControl ClassType:="Forms.ListBox.1" Selection.TypeParagraph End Sub
これは助かる。
コマンドプロンプトでもいくらか見れるらしい
reg query HKEY_LOCAL_MACHINE\Software\Classes | findstr .Forms | more
これで
いろいろでました 一部しかのせてません。
reg query HKEY_LOCAL_MACHINE\Software\Classes | findstr \.Application | more
Application系も見れるようですね。( ..)φメモメモ
computer-technology.hateblo.jp
コマンドプロンプトの分はこちらのページを参考にさせてもらいました。