クラスについて自分なりの捉え方を書いてみる
やらないこといけないことがあるけれども、絶賛現実逃避ちうなあてくしです。
こんちくわ|ω・)ノ
さて、最近クラスについての考え方についていろんなツイートや記事を見かけます。わたくしもクラスについてはそれがどんなものなのか一定の理解を得るまでに結構苦しみました。最終的にはこう捉えればいいぢゃん的なところを見出すことができたのですが、それは知る限りどこのサイトにも書かれていない気がしたので、皆さんの参考になればよいと思い記事にしてみることにしました。
というわけで、シリーズ記事は今日はお休みでーす。
さて以下に記述することは、他言語についての知識が全くない私が、VBAの勉強を進めていく中で到達したクラスの捉え方です。
オブジェクト指向の何たるかなどプログラム言語の原理原則がきちんと理解できているわけではありませんので、そもそも間違いだと指摘されることは重々承知しております。
ただVBAのクラスに関しては情報が少なく、またそれぞれが初学者にとってはかなり難解(おそらく理解には多くの基礎知識が必要)であり、それ故に初学者にとって高いハードルになっているだろうことは身をもって体験してきました。一方でクラスが使えれば大変便利なことも皆さんご承知の通りかと思いますので、私なりの捉え方がクラスを勉強を始めるとっかかりとして役立てばうれしい限りです。
でわいきます ̄▽ ̄)ノ
私がクラスについての情報を集めていた時に一番知りたかったことは、
クラスを使うと何ができるようになるの?
の1点でした。
ところが目にする情報は”クラスって何?”にフォーカスが当たったものでしたので、なかなか前に進むことができなかったわけですが、最終的に私なりに到達した答えは、
- 専用にカスタマイズしたプロパティやメソッドを詰め込んだオブジェクトを使えるようになる。
- 作ったカスタムプロパティやメソッドを使うことで、理解しやすい記述でプロシージャを書くことができる
でした。
今日の記事ではこれらを順を追って説明しようと思います。
専用にカスタマイズしたプロパティやメソッドを詰め込んだオブジェクトを使えるようになる。
VBEでコードを書いているときに、こういう表示をよく見かけますよね。
上の例で行くとRangeにはそれに紐づいてあらかじめ準備された機能として、Activateメソッド
やAddressプロパティ
などが使えますよ。ということを意味しています。
ここで例えば、以下のような表を取り扱うとします。
(なんちゃって個人情報で生成しました)
ご存じのように、VBAには上記のような個人情報に紐づいたプロパティやメソッドなどは都合よく準備されていませんよね。でもクラスを使うことで、例えば、個人情報を扱うために都合良く自作したプロパティやメソッドを詰め込んだオブジェクトが利用できる
ようになるのです
(上記ではmyObject
というお好みオブジェクトに各プロパティやメソッドが詰め込まれています)
作ったカスタムプロパティやメソッドを使うことで、理解しやすい記述でプロシージャを書くことができる
上の表中の薬師丸 明弘さんの血液型を調べて表示する
コードを書くとき、どうするでしょうか?
例えば、以下のようなコードがプロシージャに含まれることになるでしょうか。
’名前から血液型を検索して設定した文字列と組み合わせて表示する Dim Name As String: Name = "薬師丸 明弘" Dim person As Range Set person = Worksheets("個人情報").Range("A1:A5").Find(what:=Name, lookat:=xlWhole) MsgBox person.Value & " さんの血液型は " & person.Offset(0, 2).Value & "ですねー"
ここで、このコードを血液型で紹介メソッド
としてクラス(モジュール)に書いておけば、以下のようなコードであっさり書けちゃったりするのです。
myObject.血液型で紹介(“薬師丸 明弘”)
("myObject"というお好みで作ったオブジェクトに紐づけています)
これのどこがいいのかというと、何といってもコードが理解しやすくなる
ということなのですね。
もちろん、メソッドを記述する複数行に渡る複雑なコードがクラス(モジュール)に存在していることに変わりはないのですが、様々な処理が混在することになるメインプロシージャをできるだけ読みやすく(理解しやすい)ように記述する
という観点でも、このようなクラスを使う意義があるかと思います。
さらには、クラスとして好きなように作ったプロパティやメソッドのセットは何度でも利用可能ですので、ここで上げた例に則ると、上記のような個人情報の表を扱うプロシージャを作るときは何度でも利用することが可能です。
このようなことを実現するためにクラスモジュールにコードを書くとか、クラスを鋳型にしたオブジェクト(インスタンス)を生成するためにNewする
というVBA上のオキテに従ってコード書かなきゃですが、そのあたりは詳しく説明しているサイトがたくさんあるので、そちらを見ていただければと思います
おわりに
以上が、私なりに到達したクラスで何ができるの?
な答えになりなす。
もちろんクラスを使う意義としてはもっとたくさんあるのでしょうが、クラスを初めて勉強しようとする方が、この程度のレベルでクラスを捉えてさらに掘り進めていくきっかけになれば良いなと思います。
記事を書いている私自身もクラスに対する理解は全然浅いと思いますので、あんなん書いていたけど違うよね~なんてことになるカモですが、まぁご容赦くださいまし。
でわまた~ ̄▽ ̄)ノシ
鳥の飛来地をエクセルで集計したい5
Markdown記法を絶賛勉強中の管理人です。
こんちくわ|ω・)ノ
シリーズでお伝えしています和歌山県鳥類目録を集計する件です。
このPDFファイルをエクセルにコピペしたら・・
こうなったので
エクセルで集計できるように,VBAでデータを構造化しようというこの企画です。
前々回までの処理で1種1行にまとまった鳥情報を、次は以下の各項目ごとに分割してセルに収めることを目指します
(こうなっているものを)
(こうしたい)
前回の記事では1行にまとまった鳥情報の中で独自色が強い③と④の情報を利用して、文字列を下記のように加工するための作戦を立てました。そして、そのうち 1. と2. のコードをお示ししたところで力尽きてしまいました。。
実現したい処理
作戦
- 各行に含まれる鳥情報文字列の中で特定の文字が何番目にあるかを返す関数(③④特有文字がどこにあるかを調べる共通プロシージャ)(Function)
済
- 特定の文字を任意の文字に変換する(Sub)
済
- ③特有文字の横に半角スペースを挿入する(Sub)
- ④特有文字を境に鳥情報文字列を2つに分割して配列を返す関数(Function)
- 分割・配列化した文字列をそれぞれ別のセルに出力する(Sub)
と、いうわけで、残りのコードも一気に行きます。
③特有文字の横に半角スペースを挿入する(Sub)
処理のロジックは以下の通り。
- 鳥情報文字列の中の③特有文字を検索
- 見つかった
特有文字
を(半角スペース)+特有文字
に置換する
Sub InsertSpace(ByVal Target As Range) Dim Habitat As Variant Habitat = indexInfo(Target, Array("夏", "冬", "旅", "留", "迷", "帰"))(1) Call 文字列変換(Target, Habitat, " " & Habitat) End Sub
ポイントは前回の記事で作成した特有文字情報を返す indexInfo()
関数ですね。
この関数の戻り値は配列で、indexInfo(引数1,引数2)(0):位置
とindexInfo(引数1,引数2)(1):含まれる特有文字
の2つがその中身になります。
ここで変数Habitatには鳥情報の項目③に該当する特有文字が代入されますので、あとはそれをCall 文字列変換()
で半角スペース付きに変換します。
④特有文字を境に鳥情報文字列を2つに分割して配列を返す関数(Function)
処理のロジックは以下の通り
- 鳥情報文字列の中の④特有文字を検索
- 見つかった文字を境に左側の文字列を配列(0)、右側の文字列を配列(1)に代入
Private Function Devide(ByVal Target As Range) As Variant Dim rarityPosition As Variant rarityPosition = indexInfo(Target, Array("普", "少", "多", "稀"))(0) Dim tempArr(1 To 2) As Variant tempArr(1) = Left(Target.Value, rarityPosition) tempArr(2) = Mid(Target.Value, rarityPosition + 1) Devide = Arr End Function
ここでも indexInfo()
関数が活躍します。
今度は③特有文字を検索してその位置indexInfo()(0)
を変数rarityPosition
に代入して、次の元の鳥情報文字列を2つに区切る処理に活用します。
因みにMid()
関数は第3引数を省略すると、第2引数で与えた位置から残り全部の文字列を返します。
分割・配列化した文字列をそれぞれ別のセルに出力する(Sub)
いよいよ、ワークシート上の鳥情報に目的とする加工(③の横にスペース入れて、④を境に分割)を加える今回の処理のメインプロシージャです。
処理のロジックは以下の通り
- 鳥情報のあるA列セルオブジェクトを一時変数
temp
として上からループ InsertSpace()
プロシージャを呼び出して③特有文字の横に半角スペース導入Devide()
関数を使って、④特有文字を境に分割した文字列配列をtempArr
に代入temp
セルに分割した左の文字列tempArr(0)
を出力temp.Offset(0,1)
セルに分割後の右の文字列tempArr(1)
を出力
Private Sub 観察地とそれ以外を分ける() With ws修正データ Dim tempArr As Variant Dim temp As Range For Each temp In .Range("A:A").SpecialCells(xlCellTypeConstants) Call InsertSpace(temp) tempArr = Devide(temp) temp.Value = tempArr(1) temp.Offset(0, 1) = tempArr(2) Next End With End Sub
処理のポイントはこれといってありませんが、あえて挙げるとするならば、④分割後の配列を一時的にtempArr
に移している処理でしょうか。
もしかすると、その後のセル出力コマンドでtemp.Value = Devide(.Name, temp)
とすれば変数減らせるんじゃね?という疑問が湧くかもしれません。
それでも悪くはないのですが、そう書くと、セルに出力する2つのコマンドで全く同じ処理を2回実行することになりますので、そこは効率性を考えてDivide()
関数の結果を一時的な変数に移すことにしました。
それでは今回の処理の実行結果です
御覧のような感じで、全ての鳥情報について目的の処理ができました。
今回の記事はこれでおしまいですw
次回は出来上がったB列の "〇" があったりなかったりをどうにかしようと思いますー
でわまた~ ̄▽ ̄)ノシ
鳥の飛来地をエクセルで集計したい4
同僚から借りた "幽遊白書" をうっかり読破してしまい、週末のブログ更新に支障をきたした管理人です
こんちくわ|ω・)ノ
シリーズでお伝えしています和歌山県鳥類目録を集計する件です。
このPDFファイルをエクセルにコピペしたら・・
こうなったので
エクセルで集計できるように,VBAで何とかしようというこの企画です。
前回の記事までの処理で、
上のようなわけわからない状態だったものを・
番号、科名、種名、生態、備考(飛来地など)
として下記のように1種の各情報を1行にまとめることができました。
ところでバードウォッチャーではない皆さんにはさっぱり何のことやらな各行の情報ですが、下記ような意味を表しています。
このデータを解析しやすいように構造化したいわけですが、これらの情報を下の表のようにをそれぞれ別のセルに分割して整理することが次の目標になります。
それで次の作戦ですが、③と④の情報を利用して文字列を加工していきます。
実わ、これらの項目では表記の文字種に限りがあります。
記事の本題からは離れますが、参考のために列挙します。
("普”と"多"は同義で使われていましたが、記録した人の好みで表記が分かれたものと思われます)
項目ごとにユニークな表記文字があるということは以下の法則性を示すことになります
- ③の項目には "夏" "冬” "旅" "留" "迷“ "帰" のどれかしか入らない。
- ④の項目も "普" "多" ”少” "稀" のどれかしかない。
(さらに、これらの文字は⑤の項目以外では出現しない)
ですので、現時点で1行にまとまっている文字列の中から③あるいは④特有の文字列を探し出して、そこで区切りをつけやすいように加工します。
具体的には
- ③特有文字の前にスペースを差し込む
- ④特有文字の後ろを境に分割して2つのセルに分けて収納する
この処理をVBAでやってみよー。
と、いうのがやっとこさ出現した今回の記事の本題になります
この目的を達成するために次のプロシージャを作成しました。
- 各行に含まれる鳥情報文字列の中で特定の文字が何番目にあるかを返す関数(③④特有文字がどこにあるかを調べる共通プロシージャ)(Function)
- 特定の文字を任意の文字に変換する(Sub)
- ③特有文字の横に半角スペースを挿入する(Sub)
- ④特有文字を境に鳥情報文字列を2つに分割して配列を返す関数(Function)
- 分割・配列化した文字列をそれぞれ別のセルに出力する(Sub)
文字検索プロシージャで(今回の処理の肝となります)
検索対象文字列の開始位置に最も近い位置にある検索文字情報を戻り値して返すFunctionプロシージャとしました。
ここでは引数Targetに渡された各行の鳥情報文字列を対象として、引数として渡された③または④独自文字に関する情報を配列として返します。
戻り値を配列としたのは、複数ある独自文字のどれがHitするかはそれぞれの鳥情報毎に異なるため、Hit文字の位置とHit文字そのものの2つが戻り値に含まれるようにしたかったためです。
また、独自文字のいくつかは⑤観察場所を示す文字列にも含まれるため、鳥情報の開始位置から最も近い位置に含れるものが戻り値となるようにしました。
そこで、このプロシージャのロジックは、
- ↓検索対象文字(Target)と候補文字すべて含む配列(Indexes)を引数として受け取る
- ↓Targetの総文字数を配列(0)の初期値として設定
- ↓Indexsに含まれる検索文字の文字位置を検索(ループ)
- ↓Hitしたらその位置を配列(0)と比較
- ↓Hit位置<配列(0)ならば、配列(0)の値を更新し、Hitした検索文字を配列(1)に代入
- ↓(検索文字を変更してループ継続)
- ↓出来上がった配列を戻り値として設定
となりました。
コードは以下の通り
Dim temp As Variant Dim Position As Long Dim tempArr(1) As Variant tempArr(0) = Len(Target) For Each temp In Indexs Position = InStr(Target, temp) If Position <> 0 Then If Position < tempArr(0) Then tempArr(0) = Position tempArr(1) = temp End If End If Next If tempArr(0) = Len(Target) Then tempArr(1) = "" End If indexInfo = tempArr End Function
(初のシンタックスハイライト!)
処理のポイントの一つはFor Eachステートメント
です。
ループの対象を配列(Indexes)そのものとすることで、そ中身を 変数tempに 次々代入しながらループします。
InStr(Target, temp)
は、”Target” 文字列の中で "temp” に代入された文字の位置を返す関数です。いったん変数 "Position" に格納しておいて、tempArr(0)と比較しながらループを回して、最小(対象文字列の開始位置にもっとも近い位置)が最終値としてtempArr(0)に代入されるようにします
(テストコードで④特有文字の検索テスト)
テスト鳥情報にはわざと④特有文字を2つ入れてみましたが、惑わされることなく鳥情報文字列の開始位置から一番近いところにある④特有文字とその位置を返していますね。
特定の文字を任意の文字に変換するプロシージャ
これわなんてことはないただの置換なのですが、各鳥情報の表記ゆれを修正するために多用することになったため、コードの可読性を観点であえて別プロシージャとして独立させることにしました。
Private Sub 文字列変換(ByVal Target As Range, ByVal From_ As String, ByVal To_ As String) Target.Replace What:=From_, Replacement:=To_, LookAt:=xlPart, _ SearchOrder:=xlByRows, MatchCase:=False, SearchFormat:=False, _ ReplaceFormat:=False End Sub
(マクロの記録で生成したコードをちょっといぢっただけなことわ・・ヒミツです)
さて今回の本題はまだ半分しか紹介できていませんが、長くなってしまったので一旦記事を区切りますね。
でわまた~ ̄▽ ̄)ノシ
鳥の飛来地をエクセルで集計したい3
みなさんこんにちわ ❘ω・)ノ
シリーズでお伝えしています和歌山県鳥類目録を集計する件です。
<PDFファイルをエクセルにコピペしたら・・>
<こうなった>
これでは集計もへったくれもないので、PDFの表をエクセルワークシートに再現するために、
番号、科名、種名、生態、備考(飛来地など)
の情報を1行にまとめ無くてはいけません。
そこで、次のような処理をVBAで作ることにしました
1.セルの中身を左から走査して、左端から最初半角スペースまでの文字が数字となるセル(ヘッダーセル)を見つける
2.ヘッダーセルとその次のヘッダーセルのいっこ上のセルまでの情報を連結する
ヘッダーセル(数字+半角スペースがあるセル)を配列 Arr()に入れたイメージ
3.連結した文字列を新しいシートに順に並べる
(どこからどこまでのセットは目録に記載されている鳥の種類の数だけあるので、それぞれを配列に格納しながら、進めていきます)
前回は上記1.の処理について書きましたので、今回は2.と3.の処理について書こうと思います
ちなみに、処理2, 3はひとまとまりにしてヘッダーセルの数だけループするという処理にしました。
つまり、
文字列連結 → 新しいシートへの書き出し
をヘッダセルごとに行っていきます
でわ2.の処理ですが、上では簡単に書きましたが、さらに3つの処理で完成させます
2-1. 1行にまとめる範囲を特定する
2-2. 特定した範囲内の文字列を順に結合する
2-3. 2-1と2-2の処理を1. で見つけたヘッダーセルの数だけ繰り返して、結果を新しい配列に収める
コードは以下の通り
’処理2-1
2 Dim Arr2() As Variant
3 Dim tempArr As Variant
4 For Each tempArr In Arr 'ヘッダーセルオブジェクトは1. の処理で配列変数 Arr に代入済み
6 Set Arr2(j - 1) = Worksheets("初期データ").Range(Arr(j - 1), Arr(j).Offset(-1, 0))
8 Exit For
9 End If
10 j = j + 1
11 Next
2 Dim k As Long
5 Dim tempStr As String: tempStr = ""
6 ReDim Preserve Arr3(k)
7 For t = 1 To Arr2(k - 1).Cells.Count
8 tempStr = tempStr & Arr2(k - 1).Cells(t).Value
11 Worksheets("修正データ").Range("A1").Offset(k, 0) = Arr3(k)
13 Next k
まず、処理2-1部分。
処理のポイントになるのは2、5、6行目です。
2, 5行目:ループを回しながらArr2の配列要素数を増やしていくため、2行目で動的配列を宣言したのちに、5行目で要素数を指定して宣言しなおしています。ただし、宣言により配列の中身がリセットされないように Preserve キーワード付きで宣言します。
6行目:1行にまとめる範囲をRange指定して、配列 Arr2 に格納していきます(Rangeオブジェクトを配列化するため、Setステートメント使用。配列ArrはRangeオブジェクトとして作っていますので、Arr().Offsetといった表現になります。)
次に処理2-2部分。
ポイントは7-9行目です。
7-9行目:Arr2配列各要素のRange範囲内に含まれるセル数をカウンタにしたループを設定
8行目:Arr2配列各要素のRange範囲内に含まれるセルの中身を次々に結合していきます
次に処理2-3部分。
10行目:出来上がった文字列を動的に宣言したArr3に格納(Preserveキーワード付き)
処理3(さりげなく紛れ込んでいますね)
11行目:結合して出来上がった文字列をワークシート"修正データ" にA1セルを起点として書き込んでいきます。
実行結果は以下の通り。
番号に続いて種名、生態などの情報が1種1行にまとまりました。
ココまで来てやっとスタートラインに来た感じです。
まだまだすべての情報が1行に詰め込まれているので、これを項目ごとに分けて、構造化された表の完成を目指します。
でわまた~ ̄▽ ̄)ノシ
鳥の飛来地をエクセルで集計したい2
みなさんこんにちわ。
和歌山県鳥類目録をエクセルで集計してみたい管理人ですが、
目録がまさかのPDFでできていて、試しに全コピペしてみたら、
表構造が保たれていないばかりか、PDFの表の各項目をどんな法則性で複数のセルに分割したのか理解不能なブツが出来上がってやや思考停止となりました。
<コレを全コピペしたら>
<こうなりましたw>
んが、
表の先頭項目になるべき行の最初の記述に "セル内の文字列は数字+半角スペースで始まる” の規則性発見したため、VBAで何とかなるんじゃね? 感が出てきました。
でわ、これをどう料理しましょうか。
まずやらないといけないことは、PDFで見る表のすべての項目がA列に並んでしまっているので、これをPDFの表の形に近づけます。
つまり
エクセルワークシートでは
番号、科名、種名、生態、備考(飛来地など)
の情報を1行にまとめることを目指します。
ここで、数字+半角スペースの法則性が生きてきます。
これにより、A列のどの行に含まれる情報が最初に来るべき情報であるかがはっきりするとともに、どこの行のセルまでを1行にまとめるべきかもわかることになります。
というわけで、次のような処理を作っていきます
1.セルの中身を左から走査して、左端から最初半角スペースまでの文字が数字となるセル(ヘッダーセル)を見つける
2.ヘッダーセルとその次のヘッダーセルのいっこ上のセルまでの情報を連結する
3.連結した文字列を新しいシートに順に並べる
どこからどこまでのセットは目録に記載されている鳥の種類数だけあるので、それぞれを配列に格納しながら、全体の処理を進めていくことにします。
1.の処理コードはこんな感じ
0. Sub 文字列の先頭が番号なセルを配列化()
1. Dim temp As Range
2. Dim Arr() As Variant
3. Dim i As Long
4. With Worksheets("初期データ")
5. For Each temp In .Range("A1:A1153")
6. If IsNumeric(Left(temp.Value, InStr(temp, " "))) Then
7. ReDim Preserve Arr(i)
8. Set Arr(i) = temp
9 . i = i + 1
10. End If
11. Next
12. End With
13. End Sub
処理のご本尊は5-11行目になりますが、ポイントは
5行目: 走査対象データの入っている A1~A1153 セルをオブジェクトとしてVariant型変数tempへ順に入れて以降の処理を行うループ
6行目: tempの文字情報(.Value)の左から数えて半角スペースまでの文字列が数字(IsNumeric())か否かを判定
InStr(temp," ")はtempに代入された文字列を左から数えて何番目に半角スペースがあるかを返します
8行目: (6行目で数字=Trueなら) Rangeオブジェクトtempを配列に登録する(Setステートメントを使えばRangeオブジェクトであっても配列化できます)
因みに、IsNumeric()関数はString型の数字であっても、数字として認識してくれます
実行してみる
確認のために配列に登録されたRangeオブジェクトのアドレスをArr().Addressで出力していますが、元データの先頭文字とそのあとに来る半角スペースで挟まれた部分が数字となっている(赤線付けた)セルアドレスがデバッグ出力されていますね。
これで処理1はおkですね。
処理に2についてはまた後日  ̄▽ ̄)ノシ
鳥の飛来地をエクセルで集計したい1
皆様初めまして
VBA好きな私がなんとなくブログ始めてみました。
記念すべき最初の記事ですが、こんなんで初めてみたいと思います。
さて、エクセルVBAでブログを開設した管理人ですが、実わバードウォッチャーでもあります(そっち側のアカウントはありません)。
ある日、和歌山県在住の同僚に和歌山県鳥類目録(日本野鳥の会和歌山支部が過去の和歌山県の鳥の飛来をまとめたもの)を見せてもらいました。
印刷物でしたが、種名、飛来地、観察時期などが結構細かく記録されていて、大変興味深かったです。
それで、見せてくれた同僚と話をしていてふと、
これって、このリストを種名と飛来地、飛来時期をデータとして解析したら、和歌山県の探鳥地が種別に絞れて面白そう・ω・)。
しかも、リストの見た目からしてちょっとしたネ申エクセルのにほひがプンとする。
これわ、VBA案件に違いない(ΦωΦ)フフ
ってことで、鳥類目録をエクセルでまとめてみようプロジェクトスタートです。
早速、同僚からリンクをいただいて(http://bird-wakayama.server-shared.com/mokuroku2018.pdf)どんなネ申エクセルでできているのかワクワクしながら開いてみたら、
これ、pdfなのね・・
(リンク見て気が付けよ!ってツッコミはひとまず置いときましょう!)
エクセルVBAを少々嗜む管理人ではありますが、PDFファイルを取り込む知識はあいにく持ち合わせていません。
ぢゃあ、これを機会に勉強しましょう!
でもあるのですが、それはまたベつの機会に(勉強したら記事にするカモしないカモ・・)
とりあえず早くやってみたいので、必殺! ”無理からエクセル” でやってみることにしました。
プランA:開いたPDFをCtrl+Aで全コピ して、まっさらなワークシートにペーストしてみる。
ハイきた!一発思考停止w
PlanB: PDFをエクセルアプリケーション上から取り込んでみたら、PDFの表構造は案外保たれるのでわ?
なんかHTMLっぽいのがでてきて、余計に手に負えない感。。
文字化けしてるし・・・
しょうがないので思考停止モノで何とかならないかと見つめていたら
なんと!
表の先頭項目になるべき行の最初の記述に ”セルの文字列は数字+半角スペースで始まる” の規則性発見!!
これで見えた!!
でわまた~  ̄▽ ̄)ノシ