★独学者が1年後にExcelVBAを爆発的に伸ばすための最低限の基礎知識メモ(ダイジェスト):Vol0026:ついでに、ウォッチウィンドウの「一般的に、普通に使う使い方」も少し-09。(エラー回避の基礎10)★変数宣言で「LET」を使う方法についてと「SET」との比較、および、「UsedRange」について調べる。
バックナンバー目次ページは→こちらです。
まぐまぐのページは以下です。
https://www.mag2.com/m/0001691660.html
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
■独学者が1年後にExcelVBAを爆発的に上達させるための最低限の基礎知識メモ(ダイジェスト)
Vol.0026
タイトル:ついでに、ウォッチウィンドウの「一般的に、普通に使う使い方」も少し-09。(エラー回避の基礎10)
★変数宣言で「LET」を使う方法についてと「SET」との比較、および、「UsedRange」について調べる。
バックナンバー目次とサンプル号
https://euc-access-excel-db.com/tips/ct07_se/ct075012_xls2k_vba_tips/mag2-01
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
今回も前回の続きです。
途中になってしまっていてすみませんが、
以下のプログラムの続きでした。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
' ' Sub test0002() Dim ttt As Object Dim ggg As Variant Debug.Print 100 * 40.2 Debug.Print 1 < 2 Debug.Print (1 < 2) = True Debug.Print 1 < 2 = True Debug.Print 1 > 2 Debug.Print (1 > 2) = True Debug.Print 1 > 2 = True Debug.Print 1 > 2 = False Debug.Print (1 > 2) = False Set ttt = ActiveWorkbook.Worksheets("Sheet1").Range("A1") ggg = ActiveSheet.UsedRange Stop End Sub ' ' |
で、まずは、
上記のコードの「ggg = ActiveSheet.UsedRange」の先頭に、
「Let」
という命令語句を書き足して下さい。
「Let ggg = ActiveSheet.UsedRange」
となります。
それからその次の行に、(つまり「Stop」の前に)、
「Set rrr = ActiveSheet.UsedRange」
というコードを1行、書き足してください。
あと、変数宣言の箇所(「 Dim ggg As Variant」 の次あたり)にも、
Dim rrr As Object
という宣言を増やしてください。
そして、Sheet1をアクティブにして、
A列とB列に上から順に
a,b
1,4
2,5
3,6
と値を入力してください。
で、今回は、
「ActiveSheet.UsedRange」と
「ggg」と
「rrr」の
3つを全部、ドラッグで青色選択してからウォッチウィンドウに放り込みます。
できましたら、その状態から、いつもどおり、F8キーを繰り返し押して、ステップ実行をします。
そして、
「Let ggg = ActiveSheet.UsedRange」
が黄色になった段階で止めておいてください。
で、ここから説明になります。
まず書き加えた「Let」ですが、これは次行や前行の「Set」と関連します。
本メルマガでは以前の号で何回も、
『 プログラミングはどんな言語であっても、「操作の対象は」「変数である」』
(※最終的には変数に代入する生データだけど)
と書きました。
そして、VBAの場合、その「変数」を大別すると、
「一般変数(文字系の一般データを代入・格納するための変数)」
と
「オブジェクト変数(オブジェクト式を代入・格納するための変数)」
の2つしかない、とも書きました。
今回書き足した「Let」は、そのうちの、
「一般変数(文字系の一般データを代入・格納するための変数)」
に生データを代入する時に使います。
多くの場合、省略されてしまうので、本メルマガでも書き忘れてましたが、
本当はこのように「Let」と書かないといけないのです。
※補足
ここで言っている「一般変数」は、例えば
https://excel-ubara.com/excelvba1/EXCELVBA440.html によると、
「プリミティブな型(基本データ型)の変数」という呼び方をするそうです。
「プリミティブ型」の説明は、
https://wa3.i-3-i.info/word15876.html (ぴよぴよ用語サイト) をご覧ください。
これによると、Variant型はプリミティブではない感じですが、「Let」を使います。
ExcelVBAの神サイトのひとつの以下のページもご参考にしてみてください。
『 VBA LetとSetとCallとカッコ 』
https://thom.hateblo.jp/entry/2015/09/17/123831
『 VBA 用語を正しく覚える ~ステートメントとは 』
https://thom.hateblo.jp/entry/2015/09/12/110436
(VBAエキスパート公式テキスト の説明ミス???)
それに対して、
「オブジェクト変数(オブジェクト式を代入・格納するための変数)」
に生のオブジェクト式を代入するときは、「Set」を使います。
LetとSetできれいにコントラストするので、見た目も「表のようで」きれいなので、今回、せっかくなので使ってみました。
今回のサンプルで言うと、こんな感じに目に飛び込んできます。
Set ttt = ActiveWorkbook.Worksheets("Sheet1").Range("A1")
Let ggg = ActiveSheet.UsedRange
Set rrr = ActiveSheet.UsedRange
「ああ、全部、変数に生データを代入してるところだな」とわかります。
基本、日本のExcelVBAの書籍は「省略だらけでいいかげん」とも書きましたが、ここからしてすでに「いいかげん」というわけなのです。
参考URL(他の省略について)
「Letなんか付けるの、めんどくせーじゃん」という著者も多いですが、バカなので、そいつらの言うことは聞かなくていいいです。
結局「VBA」は、「クラスモジュール」つまり、「=自分独自の自作のオブジェクト」が自由に作れるようにならないと、「本当のところ」は分かりません。
なのに、「省略」と称して、そういう「Let」のような「手抜き」を教えるのが、今のVBA教育の風潮です。
『 セル範囲のことを「Range」からいきなり書き始めること 』を、
「本番では絶対に使うな」とか、
「一番エラーの出る書き方」とか、
「何の但し書きもせずに」、
「ね?かんたんでしょ?」と、さも、それが正解であるかのように教えることもです。
それらは、「手抜き、ウソを教える」の典型です。
『 「ね?かんたんでしょ?」じゃねーわ!ウソ教えやがえって! 』ということなわけです。
「手抜き」したら、何も覚えられませんよね。
ましてや「Let」のような基本事項も説明ないなんて、「本当に読者に成長してほしいって思って本書いてるの?あんた?」っていうことなのです。
日本のVBAの市販書籍って、このような、『 「省略」と称した「手抜き」』 が多すぎて、ほんとう、あいつら(著者ども)説明のせいで、僕も、ExcelVBAを理解するのに苦労しました。
最初のうちは「信じて」ましたから。
僕が上達しはじめたのはあいつらの言うことを「信じなくなって」「ウォッチウィンドウやローカルウィンドウ」を使うようになってから、でした。
といいつつ、僕も忘れて、使ってませんでした。
反省・・・。
とはいっても、確かに、Letを使うことは非常に多いので、「省略」できると確かに手間が省けます。
悩ましいところですよね。
でも、「Let」を書くと、「冗長で無駄」と言う人も居るかもしれませんが、「見た目にきれい」です。また、「ああ、この箇所で変数の代入をしてるな」という風に、「Set」と共に分かりやすいですし。
「Let」があれば、初心者でよくやる「変数の宣言のし忘れ」も見つけやすくなります。
(僕なんていまだにそういうアホなミスをする・・・(^^))
また、変数宣言は、プロシージャの冒頭で、全部の変数を宣言することが多いですが、
場合によっては、その位置でやりたくないとき、つまり、どこか処理の途中で変数宣言をやりたいとき、そういう時なども、「Let」が付けてあると、「ああ、この箇所で変数の代入をしてるな」と、のちのちコードを見直す際にも、見つけやすくなります。
それに、
「Letを使った」ということは、
逆に言うと、
「絶対に、オブジェクト変数に代入したんじゃない!この位置では。」
「(場合によっては例えば)自分は間違ってない。」
ということも
「明確」になります。
ですから、「自分が今していること」も明確になり、原因調査や学習を深めるうえで、ものごく明確になって、それがいろんなことに「派生・波及」して、理解が進みやすいとも思います。
例えば、これまた、今回の例で言いますと、
Let ggg = ActiveSheet.UsedRange
Set rrr = ActiveSheet.UsedRange
の部分でもわかると思います。
2行のコードのそれぞれの変数には、右辺(見かけ上)としては「同じもの」を代入しているように見えます。
ですが、実際は、「Let」と「Set」の部分が異なるので、「見た目としては」似てはいるれけども、「実際に代入されるモノはまるで」「異なる」「非なるモノ」ということなのです。
最初のうちは意味がよくわからなくても、そういう説明を受ければ「たしかにLetとSetが異なるからそういうことなんだろうな・・・」と、なんとなくでも、アタリがつくと思います。
なので、初心者のうちは、特に、「オブジェクト変数へのオブジェクト式の代入」の意味がまだよくわかっていないうちは、練習として、「Let」を書くことをおススメします。
本当は「クラスモジュール」が自由自在に作れるようになれるまで、「Let」を省略しないほうがいいのかもしれません。
「クラスモジュール」が自由自在に作れる人だけが、「VBAが真に理解できている」と言っても許される人なので・・・。
ただ、その「クラスモジュール」が作れなくても、これも以前から何度も言っていますが、
「値(一般データ)、あるいはオブジェクトを返す自作関数が自由自在に作れる人」
「特に、オブジェクトを返す自作関数が自由自在に作れる人」
になれれば、
ヘルプやオブジェクトブラウザも扱えるようになり、「ある地点から」「爆発的に」「上達する」確率が「グンと」上がりますので、ご心配なく・・・・。
というわけで、長々とすみませんでしたが、以上、「Let」についての説明でした。
今回、前置きが超長いです。
本当にごめんなさい。
次に、ついでに、ですみませんが、「ActiveSheet.UsedRange」の「UsedRange」について少し注意点などをお話しておきます。
https://excel-ubara.com/excelvba4/EXCEL222.html の「UsedRange」の項の受け売りです。
このWebページによると、「UsedRange」のセル範囲には、以下のようなセルが含まれるようです。
(a)値や数式の入ったセル
(見かけ上空白セルに見えていても、If関数などで空白に見せる
数式が入っているもの。
また、非表示行や非表示列でも値や式が入って入れば、そのようなセルも。)
(b)値も数式も入っていないのに、でも、以下のセル
(c)書式を変更したセル(背景色を付けたり、とか)
(d)罫線を引いたセル
(e)行の高さを変更したセル(列幅はいいみたい。)
そのようなセルを、「飛び飛びに存在していても」、「全部含む」、「ひろ~い」セル範囲が、「UsedRangeだ」、ということになります。
なので、最終行の数値を取得するときは、「UsedRange」を使う時は注意しないとユーザーが勝手にセルの色や行の高さを変えると、プログラムが誤作動することになります。
(「Worksheet.UsedRange プロパティ」を使うと表の最終行がすごくカンタンに出るのですが)
逆に、「そんなことさせない」という仕組みができていれば、「UsedRange」でテーブル(表)の最終行を取得することは可能となります。
例えばデータベースを良く知っていて、「セルの色や高さなんかいじらん!いじるわけない!」、という人ばかりの職場なら、「割と使える」ということになります。
そうじゃなければ、「Range.CurrentRegion プロパティ」や「Range.End プロパティ」などを使うことのほうが安全だ、ということになります。
ほとんどの場合、ユーザーが画面(セル)をいじる可能性があるので、こちらの方法で処理することになると思います。
だから「Worksheet.UsedRange プロパティ」を使って、最終行を割り出すサンプルってすごく少ないのですね。(巷のほとんどが「Range.End プロパティ」を使うめんどくさいサンプルばっかりです。「Cells(Rows.Count, 1).End(xlUp).Row」みたいな。)
ただ、だからといって、いずれにましても(どの方法でやったとしても)、結局のところ、万能「ではない」ので、「ケースによって使い分けが必要=よく考えて使わないとドハマりする」ので、そこが一般的なデータベースソフトと違う、「Excelのアホなところ・面倒くさいところ」・・・だと思います。
そんなところひとつを取ってみても、
「Excelで」
「自分だけが使うプログラムを作るだけならまだしも」、
「他人も一緒にみんなで使うプログラム・ソフト」を作るとき、
「特にデータベース的なソフト」を作るとき、
の、「効率の悪さといったら」、「ハンパじゃない」、と思います。
特にユーザーフォームは、例えばAccessのフォームと比べると、すごくショボいものしかできなく、「ワークシートをフォームに見立てたほうがはるかに便利」、というくらい、「データベース的なデータ共有ソフトを作るときに限っては」「使えない」ので、本当に注意が必要ですね。
外注すれば、それがそのまま「工数の多さ=コストの高さ」、としてハネ返ってきます。
しかし、「Value2」プロパティがあるのだから、「Worksheet.UsedRange2 プロパティ」も作って、セルの高さや色がいじられていても関係なく、また、数式が入っていても関係無く、
「とにかく値が表示されたセルの対角線の矩形の範囲の最終行」
を出してくれるプロパティを作ってくれればいいのに、と思ってしまいます。
なぜそうならないか不思議です。
マイクロソフトはサボっている気がします。
以上、「UsedRange」を使う時の注意点、でした。
ではここで「F8キー」を2回押して、「Stop」の位置で止めます。
で、このとき、
「ActiveSheet.UsedRange」と
「ggg」と
「rrr」の
3つともに、「+」マークが付いています。
ここで、図にも書いてありますが、
「ActiveSheet.UsedRange」と「rrr」には、両方に「+」マークがあり、「Object/Range」と書かれた部分があるので、
「あ、これはRangeオブジェクトなんだな」
とわかります。
「+」マークを押すと、以下の図のようになって、いろんな「プロパティ」の名前が見えていますので、「あ、やっぱりこれはオブジェクトだな」と明確にわかります。
ところが、
「ggg」のところには、「+」マークがあるのにもかかわらず、
「Object/××××」
のような記述が見当たりません。
「Variant/Variant(1 to 4,1 to 2)」
となっています。
「+」マークがあるのに、Objectではないようです。
「ggg」とは、何なのでしょうか?
・・・というわけで・・・・
ごめんなさい!!
説明不足で大変申し訳ございません!
この「+」マークは、以前の号で「オブジェクトだよ」とお話させていただきました。
でも、実は、もうひとつ「別のモノ」にも、この「+」マークが付きます。
(まだほかにもあるのかな?)
それは「配列変数」です。
配列変数のことをまだ学んでない方には大変申し訳ないのですが、配列変数とは、
「複数の変数が集まって、1つの変数であるかのようになった感じのモノ」、です。
単に「配列」とだけ、呼ぶことが多いです。
配列の形態には、
1次元配列(1行だけ、あるいは、1列だけ、のようなイメージの変数)
2次元配列(タテ・ヨコのある表のようなイメージの変数)
3次元配列(ルービックキューブみたいな立体的なイメージの変数)
などがあるのですが、
今回のケースでは、2つめの「2次元配列」というかたちになります。
そしてExcelの場合、配列としての変数宣言をしなくても
「Variant型としての変数さえ用意しておけば」
「その中に、Letで、セル範囲(Rangeオブジェクト)を代入すると」
「勝手に、自動的に、2次元配列になって、そのセル範囲の値が配列変数に自動的に代入され」
「配列のサイズも自動的に決まる」
という風になっています。
この性質を利用すると、セル範囲の値を、セルとの行き来をすることなく、メモリの中で扱えるので、結果、高速に処理ができるようになります。
セルとの行き来をするRangeオブジェクト主体のプログラムの少なくとも100倍以上は、高速に処理できると思います。
また、「WorksheetFunction.Transpose メソッド」を使うと、1行コードでタテヨコの入れ替えができます。
大変に便利です。
とはいえ、今回、このような、配列を使ったちょっと難しいことをしてしまいました。
いきなり難しいことをしてしまって申し訳ありませんが、「配列」に関しては、「ふ~ん」くらいで済ませてもOKです。
ただし、Letを使った場合と、Setを使った場合で、「同じようなことをしていても、結果が異なることがある」ということだけは、覚えておいてください。
例えば、
「ttt」や「rrr」といった変数には「Set」で代入していますので、
この場合は、変数の中にオブジェクトを代入した・・・ということになります。
そのため、その場合は「配列には」「なりません」。
つまり、同じ「ActiveSheet.UsedRange」を代入しても
「Let」を先頭につけて代入する場合と、
「Set」を付けて代入する場合とでは、
「代入されるモノがまったく異なってくる」
ということになります。
そのこともご説明・・・・というか、さらっと体験だけしてほしたかった、そして、ウォッチウィンドウで実際に確認してほしかった・・・というのがありますので、そのことだけは覚えておいてください。
特にセル範囲(=Rangeオブジェクト)を変数に代入する場合は。
なお、Letは省略されるので、
Let ggg = ActiveSheet.UsedRange
は
ggg = ActiveSheet.UsedRange
と書かれることのほうが多いです。
でも、本来としては
Let ggg = ActiveSheet.UsedRange
なので、
Letを付けて書くと、
Set ggg = ActiveSheet.UsedRange
とは「別モノだ」ということが明確になるのではないでしょうか?
逆に「Let」が無いと、何か比較対象になるものがないので、ちょっと見失う=間違うこともあるかもしれません。
そのようなこともあるため、
「クラスモジュールを作れるようになるまで」とか、
「オブジェクト変数の代入について理解が深まるまで」、あるいは
「オブジェクト(特にRangeオブジェクト)を返す自作関数が自由自在に作れるようになるまでは」
「Let」を付けて代入のコードを書いた方がいいかもしれません。
ではこの「ggg」の「+」マークを展開してみましょう。
下図のようになります。
今回、ちょっと余計な説明が多すぎてしまったため、これ以降の詳細は次回に持ち越したいと思います。
毎度、わかりにくい説明ですみませんが、よろしくお願い致します。
==============================
今回は以上です。
==========================================================================
バックナンバー目次とサンプル号
----------------------------------------------------------------------
■独学者が1年後にExcelVBAを爆発的に上達させるための最低限の基礎知識メモ(ダイジェスト)
発行システム:『まぐまぐ!』 http://www.mag2.com/
配信中止はこちら https://www.mag2.com/m/0001691660.html
----------------------------------------------------------------------