VBAでパフォーマンスの良いライフゲームをつくろう その2 関数への分割とユーザーフォーム
- はじめにの前に
この記事は以前に「見たまま」編集であげたものを「Markdown」に変更したものです。
載せているコードの見易さのために移しました。
- はじめに
この記事はとっくにあげていたつもりが
あげわすれていました。
あんまり本稿とは関係のない、リアルタイムとのずれの話を。
ノートパソコンを買ってからは記事をワードに書いておいて時期を見計らって投稿しているのですが
記事をストックしておくと当然ながら「今の自分」とのずれが生じますよね。
特に今回のようなバージョンを更新していくコーナーではめちゃくちゃ違和感があります。
明らかな「過去の自分」を発表しているような変な感覚。
現在の自分はこの投稿のコードを書いた時よりは進化しているのでほんとに変な感じです。
この投稿ではバージョンを1から3にあげるのですが
リアルタイムではもっと更新されています。
フリーザじゃないけどこのコードはあと5段階強くなります。。。
違和感。
でもそれって何かを発信するときには少なからず感じることなのかもしれませんね。
起きてから発信するまでには時差があるので。
今まではブログをその場で書いたらその場で投稿していたのであまりその違和感は感じませんでしたが
今後はそういった感覚にも慣れないとなぁ。
例えば集団で何かをしようとか、人に何かをさせようと思ったらこの違和感をより感じるでしょうしね。
(実はさいきん、プログラミング初心者にjavaを教えて、自分の書いてほしいコードを書いてくれるマンを育てようとしているのですが、それはまた別のコーナーで。たぶん次のコーナーで書きます)
- 関数への分割
可読性をあげるためにlife_game_Ver2の各フェイズを関数に書き換えました。
些細なことですが、何度もコードを書き換えていく場合は細かく関数に分けておくことが吉だと思います。
Sub life_game_Ver3() Dim kigou As String Dim f_size As Integer Dim field As Range Dim gene As Integer Dim time_watch As Single Dim stock() As String '--------------------------------- 'geneは世代数を記録するためのセル '--------------------------------- gene = Cells(1, 26 * 3 + 2).Value '------------------------------ '生きているセルには■を入れます '------------------------------ kigou = "■" '----------------------------------------------------- 'fieldはセルの生死判定をする領域 'f_size * f_size のセルを扱います '----------------------------------------------------- f_size = 70 Set field = Range(Cells(2, 2), Cells(2, 2).Offset(f_size - 1, f_size - 1)) '------------------------------------- '調査フェイズの計測 '------------------------------------- time_watch = Timer stock() = cell_jdg(field, kigou) Debug.Print "調査終了" & Timer - time_watch '-------------------------------------- '更新フェイズの計測 '-------------------------------------- time_watch = Timer Call continue_jdg(stock) Call cell_dsc(stock, kigou) Debug.Print "更新終了" & Timer - time_watch '------------------------- '世代数を更新する '------------------------- Cells(1, 26 * 3 + 2).Value = gene + 1 '--------------------------------- 'このプロシージャを頭から繰り返す '--------------------------------- Application.OnTime Now() + TimeValue("0:00:02"), "life_game_Ver3" End Sub
続いてそれぞれの関数です。
まずはcell_jdg。
これは各セルの次世代の状態を判定する関数です。
内容はVer2と同じです。
Function cell_jdg(field, kigou) As String() Dim trgt As Range Dim p1 As Variant Dim p2 As Variant Dim cnt As Integer Dim stock() As String Dim num As Integer num = 0 ReDim stock(num) '------------------------------------------------ 'field内の各セルに対して '自身と周囲の計9マスの生きているセルを数えて 'それによって生死を判定します '生きている場合はそのセルのアドレスをstockに保存 '------------------------------------------------ For Each p1 In field Set trgt = Range(p1.Offset(-1, -1), p1.Offset(1, 1)) cnt = 0 For Each p2 In trgt If p2 = kigou Then cnt = cnt + 1 End If Next p2 Select Case p1.Value Case kigou If cnt = 3 Or cnt = 4 Then ReDim Preserve stock(num) stock(num) = p1.Address num = num + 1 End If Case Else If cnt = 3 Then ReDim Preserve stock(num) stock(num) = p1.Address num = num + 1 End If End Select Next p1 cell_jdg = stock End Function
次はcontinue_jdg。
繰り返し処理を行うかを決めます。
Function continue_jdg(stock) '------------------------------- 'stockがない場合 '全滅なのでstop '------------------------------- If stock(0) = "" Then Stop End If '------------------------------- 'ループを終わらせたい場合 'cells(1,52)に何かを書き込めばstop '------------------------------- If Cells(1, 26 * 3) <> "" Then Stop End If End Function
最後がcell_dsc。
次世代に生存するセルを記述します。
Function cell_dsc(stock, kigou) Dim p1 As Variant '------------------------------ 'いったんシートをまっさらに '------------------------------ Cells.ClearContents '------------------------- '生存セルに■を書き込む '------------------------- For Each p1 In stock Range(p1) = kigou Next p1 End Function
はい。
これで関数への分割が終わりました。
ただこれだけでVer3に更新というのも味気がないので
もう一つ追加というか整理をします。
- ユーザーフォームによる変数の指定
今使っているコードは「initialization」と「life_game_verN」のふたつです。
現状ではf_sizeを変更するためにはそれぞれのコード内のf_sizeを書き換えないといけないので面倒くさいです。
ほかにも何かの変数の中身を変えたい場合に二度手間になってしまうのは情けない。
なのでそれらをまとめて管理するコードを書きます。
見やすさを重視したいのでユーザーフォームを作りましょう。
ユーザーフォームは「中学数学」のコーナーでだいぶ慣れた気がします。
ユーザーフォームの名前は「mother」にします。
自分なりのコードネーム?をいろいろ使うのにあこがれています笑
ユーザーフォームの見た目はこんな感じ。
今回は水色系にしてみました。
せっかくなのでf_sizeだけでなく
kigouとreturn_timeもここで指定します。
return_timeはOnTimeに入れる値です。
ここで何秒おきに繰り返すかも指定します。
続いてユーザーフォームのコードを。
Private Sub CmdB1_Click() Dim f_size As Integer Dim kigou As String Dim return_time As String Dim field As Range '----------------------------- 'フォームの数値の読み込み '----------------------------- f_size = Val(TB1) return_time = TB2 kigou = TB3 '---------------------------- 'fieldの設定 '---------------------------- Set field = Range(Cells(2, 2), Cells(2, 2).Offset(f_size - 1, f_size - 1)) '---------------------------- '実行コードの決定 '---------------------------- Select Case True Case OB1 Call initialization(f_size, kigou, field) Case OB2 Call life_game_Ver3(f_size, return_time, kigou) End Select End Sub
当然ですがこれによって「initialization」と「life_gae_VerN」の中身も少し変わります。
Sub initialization(f_size, kigou, field) Dim p1 As Variant Cells.ClearContents '----------------------------------------------------- 'fieldのセルひとつひとつに '一定の確率で■を入れます '----------------------------------------------------- For Each p1 In field If Rnd() < 0.5 Then Range(p1.Address) = kigou End If Next p1 End Sub
life_game_VerNのほうは。。。
Sub life_game_Ver3(f_size, return_time, kigou) Dim field As Range Dim gene As Integer Dim time_watch As Single Dim stock() As String '--------------------------------- 'fieldの設定 '--------------------------------- Set field = Range(Cells(2, 2), Cells(2, 2).Offset(f_size - 1, f_size - 1)) '--------------------------------- 'geneは世代数を記録するためのセル '--------------------------------- gene = Cells(1, 26 * 3 + 2).Value '------------------------------------- '調査フェイズの計測 '------------------------------------- time_watch = Timer stock() = cell_jdg(field, kigou) Debug.Print "調査終了" & Timer - time_watch '-------------------------------------- '更新フェイズの計測 '-------------------------------------- time_watch = Timer Call continue_jdg(stock) Call cell_dsc(stock, kigou) Debug.Print "更新終了" & Timer - time_watch '------------------------- '世代数を更新する '------------------------- Cells(1, 26 * 3 + 2).Value = gene + 1 '--------------------------------- 'このプロシージャを頭から繰り返す '--------------------------------- Application.OnTime Now() + TimeValue(return_time), _ "'life_game_Ver3 """ & f_size & """, """ & return_time & """ , """ & kigou & """'" End Sub
こんな感じです。
これでユーザーフォームでいくつかの変数を扱えます。
各プロシージャで定義する必要がなくなったのでコードの見た目もすっきりしました。
Ver3はここまで。
本当はVer1でこのくらいのレベルまで行ってもいいはずなのですが。
二度もバージョンアップしたのにまだ体裁の面しかいじれていない。。
まだまだ修行不足ですね。
- まとめ
実はこの記事を書く前にVer2の機能面を変更したVer3とVer4を書いたのですが
速度を測る際にあちこちのコード内の数字をいじって走らせることにストレスを感じたのでこのVer3をはさむことにしました。
なので実はVer4とVer5もすでにできているのですが
記事に書くのは次回になりそうです。。
もうVer6とVer7での変更点も決めているのに。。
これはプログラミングうんぬんよりは計画性の問題な気がします。
もう少しまともになろう。