コロ子勘違いしていた インスタンスの生成
こんにちは。
派犬事務員のコロ子です。
クラスのインスタンスの生成でちょっと気になる事がある。
ループをしながらDictionaryオブジェクト(Collection オブジェクトでも)のアイテムにクラス型のオブジェクトを追加する場合、ループの中でインスタンスを生成しているコードをよく見かける。
【例】
クラスモジュール:Class1
Public data As Long
'標準モジュール
Sub Test1() Dim dic As Dictionary Set dic = New Dictionary Dim i As Long For i = 1 To 3 Dim dicitem As Class1 Set dicitem = New Class1 Dim dickey As String dickey = "キー" & i dicitem.data = i * 2 dic.Add dickey, dicitem Next i Dim j As Variant For Each j In dic.Keys Debug.Print dic.Item(j).data Next j End Sub
イミディエイトウインドウ
2 4 6
結果は正しいけど、なんか納得できない。
なんでループの中でインスタンスを生成しているのだろう?
次のコードTest2、Test3を見ると
Sub Test2() Dim dic As Dictionary Set dic = New Dictionary Dim dicitem As Long Dim i As Long For i = 1 To 3 Dim dickey As String dickey = "キー" & i dicitem = i * 2 dic.Add dickey, dicitem Next i Dim j As Variant For Each j In dic.Keys Debug.Print dic.Item(j) Next j End Sub
イミディエイトウインドウ
2 4 6
test2で、DictionaryオブジェクトのアイテムにLong型の変数を入れると値渡ししているように思える。
Sub Test3() Dim dic As Dictionary Set dic = New Dictionary '先にインスタンスを生成する Dim dicitem As Class1 Set dicitem = New Class1 Dim i As Long For i = 1 To 3 Dim dickey As String dickey = "キー" & i dicitem.data = i * 2 dic.Add dickey, dicitem Next i Dim j As Variant For Each j In dic.Keys Debug.Print dic.Item(j).data Next j End Sub
イミディエイトウインドウ
6 6 6
でもtest3では参照渡ししているように思える。
ということはtest1では同じ変数名で何度もインスタンスを生成しているということなの??
混乱。
仕方ないのでノンプロ研で質問してみたところ、コロ子、インスタンスの生成について分かっていなかったことが判明。
【分かった事】
①オブジェクト型の変数には参照先が格納される。
なのでTest3ではDictionaryオブジェクトのキー1~3のアイテムには同じインスタンスが格納されている。
②New クラス名でいくつでもインスタンスを生成できる。
Dim 変数名 As クラス名
Set 変数名 = New クラス名
はセットで1変数1インスタンスだと思っていた。
「New クラス名でインスタンスを生成できる」は耳にタコができるほど聞いていたのに・・・。
Dim 変数名 As クラス名 でクラス型の変数を宣言
Set 変数名 = New クラス名 で生成したインスタンスを変数に格納するという意味だった!
Dim A As Class1 Set A = New Class1 Set A = New Class1
と書いたら、2行目のAと3行目のAには別のインスタンスが格納されている。
変数宣言はループ中に書いたら1度しか処理されないのと同じで、
Set 変数名 = New クラス名も1度しか処理されないのかと思っていた!
ちなみに次のように書くとクラス型変数の宣言が不要。
'--省略-- Dim dicKey As String For i = 1 To 3 dicKey = "キー" & i dic.Add dicKey, New Class1 dic.Item(dicKey).data = i * 2 Next i '--省略--
そーゆーことなのね!!
ということは、前回の記事で書いたコードはクラス型のオブジェクトを配列にしたりして、普通に分かっている人からしたら意味不明なんだろうな・・・。
クラスモジュール、シートモジュールは前回のまま
標準モジュールのみ書き直し。
Public Sub Aggregate() Dim list As ListObject Set list = Sheet1.ListObjects(1) Dim ran As ListRow Dim dic As Dictionary Set dic = New Dictionary Dim i As Long 'データ読み込み For Each ran In list.ListRows Dim dickye As String dickye = Sheet1.Key(ran) If dic.Exists(dickye) Then 'すでにkeyが存在している時は、 Call dic.Item(dickye).Add(ran) Else 'keyが存在しない(初めて)時は、 'Dicにクラス(RawData)型のオブジェクトを格納 dic.Add dickye, New RawData Call dic.Item(dickye).Init(ran) i = i + 1 End If Next ran Call Sheet2.Del 'データ書き出し i = 2 Dim j As Variant For Each j In dic.Keys Sheet2.Cells(i, cnNo).Value = dic.Item(j).No Sheet2.Cells(i, cnLot).Value = dic.Item(j).Lot Sheet2.Cells(i, cnData1).Value = dic.Item(j).data1 Sheet2.Cells(i, cnData2).Value = dic.Item(j).data2 Sheet2.Cells(i, cnData3).Value = dic.Item(j).Data3 i = i + 1 Next j Call Sheet2.MakeStyle Sheet2.Activate End Sub
インスタンスの生成、完全に理解した!(←こーゆーのが一番怪しい)
久々のVBA ノンプロ研中級講座の内容を活かす?
こんにちは。
派犬事務員のコロ子です。
実は去年12月にノンプロ研中級講座が終わってから、全くVBAを書いていなかった。
4月から部署を異動するので、ルーチン業務から外れ毎日雑用の日々。
最近はPython(GASにも)に気を取られてVBAはもうしばらくいいかなー、気分になっていた。
そんなダラダラした気分のところに上司から「マクロ作って」の依頼がきた。
VBAもういいや、な気分だったのに、話を聞いたらノンプロ研中級講座の内容がめちゃめちゃ使えそうだったから、少し気分が乗ってきた。
しかし、3ヶ月VBAをやってなかっただけなのに、関数の外にコード書いたり、変数を宣言するのを忘れたり、もうVBA忘れちゃったのかものひどい有り様だった。
ゆっくり良く考えてかっこいいコードが書きたかったけど、「急ぎ」だったので、とりあえず急いで作った。もっといい方法がないか今日考えたけど、時間があるからといっていいアイデア浮かぶわけでもない・・・。
でもノンプロ研中級講座の内容を盛り込めたと思う。
【内容】
下記のようなデータを番号、Lotが同じもので集計する。
データ1、データ2、データ3は各々最大値と最小値を除いて平均する。
表をテーブルにする。
表の中にカーソルを置いて「Ctrl+T」を押すとテーブルになる!
色が付いてオシャレになっただけじゃない。
テーブルにすると表をオブジェクトとして扱えるから便利。
考え方
①表の「番号」と「Lot」を繋げた文字列をキーにする。
【例】
2行目の場合は「BBBabc」がキー
②表の1行分のデータを格納するクラスを用意する。
③表を1行づつ読み込んで、Dictionaryオブジェクトに格納する
Key:①で作成したキー
Item:②のクラスを配列にして、配列のインデックス番号
Dictionaryオブジェクトに格納する際、Keyがすでに存在していたら、そのインデックス番号のクラスのデータを集計する。
コード
クラスモジュール RawDataクラス
Private no_ As String Private lot_ As String Private data1_ As Long Private data2_ As Long Private data3_ As Long Private max1_ As Long Private min1_ As Long Private max2_ As Long Private min2_ As Long Private max3_ As Long Private min3_ As Long Private cnt_ As Long '*初回の値をセット Public Sub Init(ByVal list As ListRow) no_ = list.Range(cnNo) lot_ = list.Range(cnLot) data1_ = list.Range(cnData1) max1_ = data1_ min1_ = data1_ data2_ = list.Range(cnData2) max2_ = data2_ min2_ = data2_ data3_ = list.Range(cnData3) max3_ = data3_ min3_ = data3_ cnt_ = 1 End Sub '*加算 Public Sub Add(ByVal list As ListRow) data1_ = data1_ + list.Range(cnData1) Call MaxMin(max1_, min1_, list.Range(cnData1)) data2_ = data2_ + list.Range(cnData2) Call MaxMin(max2_, min2_, list.Range(cnData2)) data3_ = data3_ + list.Range(cnData3) Call MaxMin(max3_, min3_, list.Range(cnData3)) cnt_ = cnt_ + 1 End Sub '最大値と最小値を探す '(max, minはByRefにして変数の中身を置き換える) Public Sub MaxMin(ByRef max As Long, ByRef min As Long, ByVal Data As Long) If Data > max Then max = Data ElseIf Data < min Then min = Data End If End Sub '番号 Public Property Get No() As String No = no_ End Property 'ロット Public Property Get Lot() As String Lot = lot_ End Property 'データ1 Public Property Get Data1() As Long Data1 = WorksheetFunction.Round((data1_ - max1_ - min1_) / (cnt_ - 2), 0) End Property 'データ2 Public Property Get Data2() As Long Data2 = WorksheetFunction.Round((data2_ - max2_ - min2_) / (cnt_ - 2), 0) End Property 'データ3 Public Property Get Data3() As Long Data3 = WorksheetFunction.Round((data3_ - max3_ - min3_) / (cnt_ - 2), 0) End Property
Sheet1モジュール
Public Property Get Key(list As ListRow) As String Key = list.Range(cnNo) & list.Range(cnLot) End Property
Sheet2モジュール
Public Sub Del() Me.UsedRange.Offset(1, 0).Delete End Sub Public Sub MakeStyle() '罫線 Me.Range("A2").CurrentRegion.Borders.LineStyle = xlContinuous 'ソート Me.Range("A2").CurrentRegion.Sort Key1:=Range("A1"), _ Order1:=xlAscending, _ Key2:=Range("B1"), _ Order2:=xlAscending, _ Header:=xlYes End Sub
標準モジュール
参照設定:Microsoft Scripting Runtimeを忘れずに。
Public Enum ColumnName cnNo = 1 cnLot cnData1 cnData2 cnData3 End Enum Public Sub Aggregate() Dim list As ListObject Set list = Sheet1.ListObjects(1) Dim ran As ListRow Dim Dic As Dictionary Set Dic = New Dictionary 'RawDataクラスは動的配列にする Dim Data() As RawData Dim i As Long 'データ読み込み For Each ran In list.ListRows Dim kye As String kye = Sheet1.Key(ran) If Dic.Exists(kye) Then 'すでにkeyが存在している時は、配列のインデックス番号のデータを加算 Call Data(Dic.Item(kye)).Add(ran) Else 'keyが存在しない(初めて)時は、Dicにデータを格納 ReDim Preserve Data(i) Set Data(i) = New RawData Dic.Add kye, i Call Data(i).Init(ran) i = i + 1 End If Next ran Call Sheet2.Del 'データ書き出し For i = 2 To UBound(Data) + 2 Sheet2.Cells(i, cnNo).Value = Data(i - 2).No Sheet2.Cells(i, cnLot).Value = Data(i - 2).Lot Sheet2.Cells(i, cnData1).Value = Data(i - 2).Data1 Sheet2.Cells(i, cnData2).Value = Data(i - 2).Data2 Sheet2.Cells(i, cnData3).Value = Data(i - 2).Data3 Next i Call Sheet2.MakeStyle Sheet2.Activate End Sub
集計結果
こんな感じで集計。
①シートモジュールを使う
②配列
③クラス
④テーブル
などなど、中級講座の内容。
今だから正直に言うけど、テーブルについては知らなかったし、クラスもちょっと自信なかった。TAじゃなくて受講するレベルだった疑惑があるけど、テーブルもクラスも使って作ったから許して~。
職場でプログラミング部を作成
こんにちは。
派犬事務員のコロ子です。
最近Googleの凄さがなんとなく分かってきた。(今更)
無料のGoogleアカウントを作成すれば、Excelみたいのとか、Wordみたいのとかその他いろいろタダで使えるし、ネット上のエリアに15GBまで無料で保存できる。
しかも、他のPCとかスマホとかからもアクセスできて、他の人とも共有できたり、凄く便利じゃない!?(今更)
パッケージ版Microsoft office派の会社で働いていると、なかなかこの感覚が分からない。
また、ノンプロ研でチャットツールのSlackを使っていて、これもすごく便利。会社で使っているところもけっこうある。コロ子が会社の人に、こういうツールあるんだよ、と口頭で説明しても、みんな???ってなるし、「うちの会社、そういうのダメだから」と一蹴されてしまう。
そうはいっても世の中便利なもの沢山ある。知らないのはもったいない。
会社はクラウド不可、アプリのダウンロード不可だけど、プライベートで遊びでやる分には構わないよね?
部活みたいな感じにして、いろいろ便利なITツールを使ったり、プログラミングをしたりしてみんなで一緒に勉強していくのはどうだろうか?
これから会社も変わっていかなければならないだろうから、若い人たちが(若くなくても)こういうのに慣れて、便利だから導入しましょう、みたいな働きかけができればいいし、「自分で仕事に最適な便利ツールを探して、使いこなす力」というもの必要なのではないかと思う。
アプリなどのダウンロードも、ちゃんと申請して情シスの許可が降りればできるらしい。どのような物をどのように使って、どのような効果があるかをちゃんと説明できる力も必要。
そんな訳でプログラミング部を作ってみた。
IT部にしようか迷ったけど
コロ子=VBA
みたいなイメージはできていると思うので、プログラミング部の方が馴染むかな。
それにIT語れるほど詳しくないし・・・。
業務関係なく「ゆる~くプログラミングして遊ぼう」を全面に押し出し、やってみよう。
部員を募集
部員募集の張り紙をしてみたところ、8人集まった。
すごい!優秀な人ばっかり!!
Slackでのオンライン部活。
嬉しいことにSlack経験者ゼロ。それだけでも部活作った甲斐ある。
仕事は関係ないので、自宅のパソコンで、オンラインでの部活動。
自宅のパソコンだとExcel(Office)が入ってない人もいるから、VBAよりGASやPythonがメインかな。
部活を作って早速だけど、コロ子休みを取ってちょっと出かけてくる。
コロ子が帰ってくるまでに課題を出しておいたけど、みんなやってくれるかな。
(今回はパソコンを持って行かないので、ちょっと心配)
提出期限は来週だけど、まだ誰も出してくれてない。
でも、誰もやってくれなくても大丈夫。
ノンプロ研で学んだスーパードライがある。
(スーパードライ:周りとの温度差があっても気にしないでクールに乗り切る術。ダメだったらやり方を変えればいいだけ。)
Pythonでゲームを写経
こんにちは。
派犬事務員のコロ子です。
Pythonでゲーム
そろそろ何かそれっぽい物を作ってみたい。
基礎編、難しいところはすっ飛ばしてゲームコードを写経してみた。
洞窟の中をロケットが進むゲーム。
洞窟は色の付いた画面に、黒い長方形をループしながら位置と形を変えて追加していく。新しい長方形を追加したら最初の長方形を消す。
そうすることによって画面が動いているように見える。
全然分かんないけど、早くゲームで遊びたいから、取り急ぎ写経。
よし!できた!
ゲームスタート!
え!!!マジ!絶対ムリ!!
コードをよーく見てみると、間違ってる。
偶然できたバグが無理ゲー過ぎてビックリ!
実際にはこんな感じでどんどん洞窟が狭くなる。まっすぐ飛ぶのが結構難しい。
今回覚えた事
Pythonの配列?(リストとかタプルとか)はインデックス番号を-1とすると、最後の要素が取れる。
test = [1,2,3,4,5] print(test[1]) print(test[-1])
↓
2 5
便利!
なんかいっぱいコード書いたけど、はっきり理解できたのはこれくらい。
バグの原因はループのインデントが間違っていた。
ループを抜けるにはインデントを戻さないといけないけど、本だとインデントの位置が分かりにくくて、まだちょっと慣れない。
【Python】 for文に変数が複数!!
こんにちは。
派犬事務員のコロ子です。
先日、ノンプロ研のVBA中級講座とPython初級講座合同の卒業LT大会に行ってきた。「こんな事できるようになたよ!」と講座で学んだ事を発表する会。
どうやらPython初級講座を受けるとWebサイトのスクレイピングとかできるようになるらしい。
「初級講座なのにそんな事できるようになっちゃうの!?コロ子もやりたーい!!」
とLTを聞きながらテンション上がって、次回講座の仮予約を申し込んでみた。
VBA, GAS, Pythonで最少催行人数に最も早く達したコースのみ開講なので、みんな急いてPythonに申し込みするのだ!
↓
docs.google.com
でも一つ注意は、Python初級講座に全くの初心者はいないらしいから講座開設までに少しでも先に進めなければ・・・。
お正月休みから全く進んでない・・・。
今日の驚き
そんな訳で細々とPythonをやっていたら「for文に変数が複数」をいうコードに遭遇。
①for文に変数が2つ
for i, j in [(1, 2), (3, 4), (5, 6)]: print(i, j)
えっ!!!こんなのアリ!?
変数2個、どうやってループするんだろう?
実行結果
1 2 3 4 5 6
変数「i」と「j」に要素が一度に代入される。
凄い!驚き!VBAしか知らなかったから、最初なかなか受け入れられなかった。
ちなみにfor文に変数が一つ、これはなんとなく馴染みがあるので受け入れ可能。
for i in [(1, 2), (3, 4), (5, 6)]: print(i)
(実行結果)↓
(1, 2) (3, 4) (5, 6)
VBAでいうところのFor each文に似ている。
ちょっと違うけどVBAならこんな感じ。
Dim i As Variant Dim Arr() As Variant Arr = Array(1, 2, 3) For Each i In Arr Debug.Print i Next i
(実行結果)↓
1 2 3
配列から1つづつ要素を取り出してるところが似ている。
ママ犬も迷走
こんにちは。
派犬事務員のコロ子です。
今日、ママ犬がコロ子の犬小屋に来た。
年末に「年が明けたら、アンタの小屋に行くから」と予告され「えーー!!来なくていいよ、何しにくるの??」と恐る恐る待つところにママ犬が来た。
(昔からママ犬は超怖い)
ママ犬「あんた、仕事してるの?」
コロ子「してます・・・」
ママ犬「何?バイト?パート?」
コロ子「派犬・・・」
ママ犬「それはいくつまで働けるの?」
コロ子「無期雇用契約しなかったから、いくつまでとかは特にない・・・」
ママ犬「そう。私もう年だし、そんなに長く生きられないと思うのよ」
いやー、それはない!自分勝手で気ままな、こういうタイプが一番長生きする。ババ犬もジジ犬も長生きだったし、結構な健康オタクだ。
ジジ犬なんて「オレはもう年だからそろそろ死ぬ」とかいいつつ、それから20年以上生きた。
ママ犬「それで死ぬ前に持っているお金全部使おうと思っているの」
終活ってやつ?お金余ってるの?
イヤイヤ、それはない。パパ犬がパチンコで全部使ってる。
昔からそれでさんざん喧嘩してた。
ママ犬「あんた、この先生きて行くのに、何か勉強して資格とか取った方がいいんじゃない?もし勉強するなら援助しようと思っているの」
えっ!マジで!!この年から親に援助してもらって取る資格って何?
コロ子「えっ!?アメリカに留学してMBAを取るとかそういうヤツ?」
ママ犬「そんなにはないわよ!!!」
ほら!やっぱりないじゃん。そもそもお金の問題以前に無理だし。
コロ子「じゃ、普通に大学とか大学院とか?でももう受験勉強とか嫌だし、卒業しても新卒枠で就職できるとも思えないんだけど」
ママ犬「そんなにもないわよ!」
コロ子「じゃぁ何?いくら?」
別に普通に現金くれてもいいんだけどーーー。
でも、ママ犬にお金貰ったらいろいろ面倒臭そう。
子供の頃はお小遣いの内訳を全部言わされていたし。
何億とかなら話は別だけど、それほどの額でもないのにこの先も絶対的な従属関係を取られるのはウンザリだ。
ママ犬「とにかく、何か勉強するなら考えてもいいと思っただけよ。じゃぁ。」
と1円も置かずに帰って行った。
ノンプロ研で勉強しているので大丈夫です。
Python始めました
明けましておめでとうございます。
派犬事務員のコロ子です。
本年もよろしくお願いいたします。
今年の目標
去年のノンプロ研BT大会で「来年は他の言語に挑戦する」とか言ってしまったので、取り急ぎPythonに挑戦。
どこにも行かなくてヒマだからPythonじゃなくて、Pythonの為にどこにも行かないんだから!(強がってみる)
以前もちょこっとPythonに手を出して途中で挫折したので、今回は「ゲームを作りながら楽しく学べるPythonプログラミング」というので再挑戦。ゲームの方が楽しそう、という安易な考えでチョイス。
今日もハマった
pygameモジュールをインポートしてRectクラスとやらを使うところで今日はハマった。
import pygame r= Rect(10,20,30,40)
エラーになるーーー。
この書き方ではダメらしい。
import pygame r= pygame.Rect(10,20,30,40)
とするか、
from pygame.locals import Rect r= Rect(10,20,30,40)
にすればOKだった。
import モジュール名
import pygameとするとpygameに定義されている全てのクラスや関数が使えるらしい。余計なものまでインポートされても、全部あるから用は足りる。でも使用する場合は必ず
pygame.Rect(10,20,30,40)のように、頭にモジュール名を付けなければいけない。
from モジュール名 import 関数/クラス/定数
from pygame.locals import Rect
だと、Rectクラスだけインポートされて
Rect(10,20,30,40)と書けばOK。
お正月早々、一日これにハマった。