個サ作 #5 カレンダーLv.1&2!
こんにちは。
前回は変数、代入、コメントアウトについて学習しました。
今回からいよいよカレンダーを作っていきます。
では、参りましょう(目次が厚くなっちゃってすみません)。
今回のゴール
今回はカレンダーLv.1と2を実装します。
上図が2つのマクロを実行する様子ですね。
これができるところまでやりましょう。
カレンダーLv.1
カレンダーを作成していきます。出来上がりのイメージは以下です。
まずは簡単な事前準備におつきあいください。シート名の変更とモジュールの作成をします。
まずはシート名の変更から。「Sheet1」を「カレンダー1」とします。
上図の通り、シート名をダブルクリックしてもいいですし、もうひとつショートカットキーで編集モードにたどり着く方法もあります。私はそちらの方が多用します。
[Alt] + [H] + [O] + [R]キーを押下するとそこまでいけます。実際にキーを押してみるとリボンやメニューにショートカットキーのアシストが出るので、見てみてください。
頻繁に利用するアクションはショートカットキーを覚えておくと本当に便利です。
次にモジュールの準備です。
まず、前回作った「B_いけない代入()」と「B_暗黙的型変換()」はコメントアウトしておいてください。特に深い意味はないのですが、説明のために用意しただけでプログラミング学習としての成果物ではないので無効化しておきます。
マクロダイアログの一覧に出てきてもノイズでしかないのでね。
そして、モジュールをひとつ追加します。追加からモジュール名の変更まで、下図のように実施してください。
追加したモジュールの名前は「Study2」にしましょう。
そして、次のソースコードを下図のように書いてください。
'変数宣言を強制する
Option Explicit
これでOK!
では、カレンダーレベル1で学ぶ一つ目の要素、プロパティについて解説していきます。
プロパティとは
プロパティとは、あるものの属性や性質を意味します。
「プロパティ」を説明文から理解しようとすると却ってわかりにくくなるので、例示からイメージしてください。一旦プログラミングのことは忘れましょう。
例えば「人間」。現代に生きる私たちは各自が共通していろんな属性や性質をもっています。下記に挙げてみましょう。
・名前
・年齢
・性別
・身長
・体重
・髪の色
これらは「人間」であれば必ずついてまわる属性・性質(=プロパティ)です。
では「自動車」はどうでしょうか。
・ブランド
・車体の色
・乗車許容人数
・車高
などがプロパティです。これも自動車は必ずもっていますね。
ここで少しエクセルの要素に対象を寄せてみましょう。セルはどうでしょうか。
・背景色
・文字の色
・文字の太さ
・結合されているか
・入力されている値
などがあります。これもセルであれば必ずもっています。
だいたい理解いただけましたでしょうか。私が初心者の頃、よく
https://wa3.i-3-i.info/word11066.html
こちらのページにお世話になっていたんですが、やはりここにも書いてある
この説明がしっくりきますね。これがプロパティです。
では、カレンダーレベル1の機能作成にあたってどんなプロパティをどのように使うのか、次の項で解説していきましょう。
Cells プロパティ
セルに対して何か処理を書きたい、そういうときはVBAが用意している|Cells《セルズ》プロパティを使います。
こちら↑は公式リファレンスと言われるもので、演算子やプロパティや関数には言語の提供元からの解説ページが用意されています(演算子、関数は#6で解説しますね)。
では!使わないことには始まりません。使ってみましょう。
次のソースコードを下図のように入力してください。
'プロパティと順次処理の学習
Sub C_カレンダー1()
Cells(2, 1).Value = "1日"
End Sub
さて、早速実行してみましょう。
はい、わかりますでしょうか。A2セルに「1日」という文字列が出力されました。では、ソースの解説です。
Cells(2, 1).Value = "1日"
先ほどご案内した公式リファレンスの中にこんな説明があります。
「行および列のインデックスを指定できます」とありますね。今実行したソースでは2と1を順に指定しています。説明文と合わせると、2行目でかつ1列目のセルを指定したことになります。つまり・・・
「Cells(2, 1)」は上図のようにA2セルを表しています。
Cells(2, 1).Value = "1日"
ソースコードを見てみると、「Value」の前にドットがありますね、「.」←これです。
このドットは「の」と捉えてもらって大丈夫です。もう少し丁寧に言うと「に所属する」です。
ですので、これを日本語読みすると「A2セルの」「A2セルに所属する」と言えます。
次に続くのが「Value」これもまたプロパティです(Cell自体もプロパティです)。「Value」には価値や値打ちという意味がありますが、プログラミングにおいては「値」と解釈してください。
さて、セルの値というのはそこに入力する文字や数字のことを意味します。これでいよいよこの命令文の内容が解読できますね。
Cells(2, 1).Value = "1日"
A2セルに所属する「値」のプロパティに"1日"という文字列を代入する、という処理をしています。前回習った代入の登場です。
カレンダーLv.1ではもうひとつ、順次処理についても説明する予定ですが、実はここまで学んだ内容でもう機能を完成させられます。
少~しだけ、考えてみてください。実装してもらってもOKです!
順次処理とは
順次処理とは、ひとつずつ順番に処理を行うことを意味します。
もうカレンダーLv.1を完成させてしまいます。
Cells(3, 1).Value = "2日"
Cells(4, 1).Value = "3日"
Cells(5, 1).Value = "4日"
Cells(6, 1).Value = "5日"
Cells(7, 1).Value = "6日"
Cells(8, 1).Value = "7日"
Cells(9, 1).Value = "8日"
Cells(10, 1).Value = "9日"
Cells(11, 1).Value = "10日"
Cells(12, 1).Value = "11日"
Cells(13, 1).Value = "12日"
Cells(14, 1).Value = "13日"
Cells(15, 1).Value = "14日"
Cells(16, 1).Value = "15日"
Cells(17, 1).Value = "16日"
Cells(18, 1).Value = "17日"
Cells(19, 1).Value = "18日"
Cells(20, 1).Value = "19日"
Cells(21, 1).Value = "20日"
Cells(22, 1).Value = "21日"
Cells(23, 1).Value = "22日"
Cells(24, 1).Value = "23日"
Cells(25, 1).Value = "24日"
Cells(26, 1).Value = "25日"
Cells(27, 1).Value = "26日"
Cells(28, 1).Value = "27日"
Cells(29, 1).Value = "28日"
Cells(30, 1).Value = "29日"
Cells(31, 1).Value = "30日"
Cells(32, 1).Value = "31日"
上記のソースコードを追記してください。これを実行すると・・・
はい、冒頭でお見せしたものと同じ動きをしています。
ただ、これを見て「本項のタイトルである順次処理が出ているでしょ?」
と言われてもちょっと首肯しづらいと思います。1から31まで一瞬で処理が終わりますからね。
ですので、順次処理である上からひとつずつ順番に処理を行う、が正しく行われていることをお伝えするために、開発において重要なテクニックを今からレクチャーします。言葉だけはこれまでも出てきていました。
デバッグといいます。
デバッグとは
デバッグとは、プログラムの中にあるバグ(不具合)を突き止めて、本来の動作をするように修正することを言います。
言葉の意味としてはこの通りなのですが、ITの現場においては「デバッグ」と言うと実行時に処理を任意の場所で止めて変数の中身や処理の流れを追うことを指すことが多いです。
今からデバッグをするので、私の案内に従って手を動かしてください。まず、ブレークポイントを置きます。処理を止める位置です。
上図のように任意の行の左にあるスペースで左クリックをしてください。すると赤い丸印とともに、行が赤くなります。
この状態で実行してみます(先ほど実行した分の1~31日はクリアしています)。
どうでしょう。わかりますか?「4日」までは一瞬で出力されたのですが、ブレークポイントの位置で一度処理が止まっています。
そこから先は[F8]キーを押下することで1行ずつ処理を進めることができます。今処理の流れがどこにあるか?は黄色でハイライトされている行が示しています。
シートに出力された値とソースコードの制御位置(黄色ハイライト行)がリンクしていますね。
「順次処理」に話を戻すと、上から下に向かってひとつずつ順番にハイライト行が移動しています。ちゃんと順次処理をしているといえます。
ちなみにデバッグで処理を一時中断してから残りの処理を全部一気に終えたいときは[F5]キーを押下するとそのように動きます。
これにてカレンダーLv.1は完了です。イケてない点として
・2回目実行する前に手動で値をクリアしないといけない
・月を問わず31日まで出力してしまう
・出力位置を変える場合31日分処理を書き替えないといけない
などがあります。レベル1故の質の低さ・・・この先の学習で利便性向上を図っていきます。
それではレベル2に行きましょう!ループ処理をやります!
カレンダーLv.2
レベル2に入ります。
レベル2でできることはレベル1と同じなのですが、上図のgif画像を見ていただいて少し違いがあるのわかりますか?出力する様子を追っかけていますよね。
レベル2では反復処理で実装します。
反復処理とは
反復処理とは、特定の処理を繰り返し行うことで、処理を終了する条件を満たすまで処理を続行します。
ループ処理とも言います。口語では反復処理よりもループ処理の方が言いますね。
ループ処理にはいくつか書き方があるのですが、今回はその中でも最も標準的なものを使用します。
では、書いてみましょう。
反復処理の構文
For [カウンタ変数] = [開始値] To [終了値] Step [カウンタ変数の増減値]
<ここに繰り返したい処理を書く>
Next
上記がVBAにおけるループ処理の構文です。
「Step [カウンタ変数の増減値]」は省略も可能で、その場合、カウンタ変数の1周ごとの上げ幅は1になります。毎回1アップします。
たとえば開始値が1で終了値が10なら10周するループ、ということです。
ループ処理にはForを使うもののほかにDoやWhileを使うものがあるので、これらを区別してForの場合は「Forループ」と呼んだりします。
Do~Whileループは#14のあみだくじで登場します。
では実際に使ってみましょう。
ループの動きを理解する
次のソースコードを下図のように実装してください。
'ループ処理の学習
Sub D_カレンダー2()
Dim day As Integer: day = 1
Dim i As Integer
For i = 2 To 32
System.Debug i
Next
End Sub
このソース、まだ完成形ではないのですが、ループ処理の理解を捗らせる目的もあり一度実行していただきます。エディタの実行ボタンからいきます。
ではソースを解説します。
Dim day As Integer: day = 1
まず最初のこれはdayという名前の変数を宣言・定義しています。
「day」なので、日にちを保持する変数です。
次に変数 i を宣言しています。
Dim i As Integer
変数名は丁寧につけてねと私自らが#4で言ったのに、たった一文字の「i」という名前を付けています。
これはですね、業界の慣習でループのカウンタとして使用する変数は「i」でいいんです。そして次がいよいよループです。
For i = 2 To 32
System.Debug i
Next
まず、先ほど実行したときのイミディエイトウィンドウの状態を見てみましょう。2から32までが出力されていますね。
System.Debugを使うと、その後に指定した変数の値をイミディエイトウィンドウに出力するのは#4でもお伝えした通りです。
後で説明しますが、2行目から出力したいから開始値を2にしています。
プログラム上に2と32の値はありますが、カレンダーLv.1とは違ってその間の値はひとつもありません。これはループ処理の仕組みでForから始まりNextに達したとき、変数 i の値を自動で+1してくれたんですね。
その様子は実際に目で確認することができます。
デバッグでループ処理の様子を確認
デバッグを使うと「今、変数はどのような値をもっているか?」を確認することができます。下図をご覧ください。画面右下のローカルウィンドウにご注目です。
ローカルウィンドウに処理で使用する変数が表示されるようになっています。保持している値が変わるとそれをリアルタイムに反映してくれます。すばらしい。
というわけで、明示的に書いてなくても変数 i の値はループの仕組みとして自動でカウントアップされていくことが証明できました。
この動きを踏まえてループ内にどんな処理を書く必要があるかを考えていきましょう。
日付の出力部分実装しよう
ループ処理の側はできましたので、ではカウンタ用の変数 i と日付を保持する変数dayを用いてどのように実装すればやりたい動作を実現させられるか、が残る課題です。
まず、出力セルはループのたびに1行ずれていきます。2行目に出したら次は3行目、次は4行目、その次は5行目・・・という具合ですね。
ということは、ここは変数 i を上手に使いたい。そうなると、次のようになります(さっきのSystem.Debugは消してね)。
For i = 2 To 32
Cells(i, 2).Value = day & "日"
Next
行の位置はCells(行, 列)で指定するので、行の位置に変数 i を使います。列は2列目で固定です。
そしてもうひとつ、ここで新しく学ぶ要素です。変数と固定のテキストを連結したい場合、&マークで繋ぎます。変数dayと"日"を繋いでいますね。これで「n日」のnの部分を周回ごとに変えることができます。
さて、この状態で実行すると・・・?
毎度"1日"と出力するだけですね。
では次に変数dayをカウントアップする方法を考えましょう。考えるといってもこれはもう知ってるか知らないかだけの問題なので・・・
day = day + 1
こちらです。右辺にも左辺にも変数dayがある辺り、ちょっと妙に思うかもしれません。ですが、このような書き方をします。
例えば、変数dayの値が1であったならば1+1の値を変数dayに代入するので、変数dayの値は2になります。同じく2であったならば2+1の値を代入するので、変数dayの値は3になります。以降も同じ要領です。
今の段階でループ部分のソースは
For i = 2 To 32
Cells(i, 2).Value = day & "日"
day = day + 1
Next
このようになっています。これで実行すると
ほいきた!レベル1と同じ実行結果!・・・なのですが・・・
レベル2の冒頭でもお見せしたこの完成形の動画だと日にちの書き出しをセルの選択位置が追っかけていますね。
残りこれをやってレベル2は終了です。
まず日付の印字にはあまり関係がないこの処理をどうして用意しているのか、という点なのですが、処理が行われる様子が目で追えた方が楽しくないですか。楽しいと思うんですよ、私は。
カレンダーLv.1なんて一瞬で終わってしまいましたし。ちょっとしたアニメーション、欲しいですよね。あみだくじなんてアニメーションそのものです。
アニメーションっぽくする方法
そのまま実行したのでは処理が一瞬で終わってしまう、ということで処理の様子が目で追えるように動きを持たせる処理を書きます。
この2行を変数dayのカウントアップ処理の次に書いてください。
Application.Wait [Now() + "0:00:00.05"]
Cells(i, 2).Select
1行目はwaitと書いてます。「待つ」ですね。処理の内容としては0.05秒間そこで処理を止めます。ここの数値をいじるともっと早くしたりゆっくりしたりできます。
2行目はセルを選択状態にするものです。行の指定に変数 i を置いているため、次から次へとセルを移動しているように見せることができるわけです。
これでソースはすべて書けました。全文はこのようになります。
'ループ処理の学習
Sub D_カレンダー2()
Dim day As Integer: day = 1
Dim i As Integer
For i = 2 To 32
Cells(i, 2).Value = day & "日"
day = day + 1
Application.Wait [Now() + "0:00:00.05"]
Cells(i, 2).Select
Next
End Sub
動作を確認してみましょう。
はい、できあがりです!おつかれさま!
[Ctrl] + [S]キーで保存しておいてください。上書き保存のショートカットです。
今回のふりかえり
お疲れさまでした。
今回の成果としましては
カレンダーLv.1
カレンダーLv.2
の実装が完了しました。この調子でいくと第1章はすぐに終わるのでは!?と思われるかもしれませんが、そうは問屋が卸さねえ~
また学習内容としましては
プロパティとはその対象物の性質・属性を示すもの
変数と文字列の連結は「&」を使う
順次処理は順番に処理を進めていくこと
デバッグのやりかた
ローカルウィンドウの使い方
ループ処理の書き方
を学びました。
順次処理はプログラム側のルールみたいなものだし、試験知識的なものなので、忘れてしまっても問題ありません。
他のものもプログラミングやってると自然と使ったり意識したりするものばかりです。なので重要ではあるけど、念を押しておきたい感じではないですね。
今回の補足
補足の章です!読み物だと思ってかるく眺めていってください。
PG言語にもバージョンがある
「Cells プロパティ」の項で公式リファレンスを紹介しました。
プログラミング言語には、それを提供している組織があって、言語にもともと用意されているプロパティや演算子、関数の使い方を紹介するページも提供してくれている、という話でしたね。
これに付随してお伝えしておきたいのが、プログラミング言語にもバージョンの概念がある、という話です。
世の中に流布されたプログラミング言語は最初から完璧なわけではなく、人々が使う中で使い勝手の悪さや言語そのものの不具合が顕わになることがあります。
それらは当然いつまでも放置されるわけではなく、どこかのタイミングで提供元の組織によって修正されます。ですので、プログラミング言語も共に成長していく、というわけですね。
公式リファレンスについて一言添えておくと、実際に参照することは少なめです。
あの手の資料はどうしても体系的でお堅い書き方がしてあるので、有志のプログラマたちが用意したページの方がちょっとした調べ事なんかではよく利用します。わかりやすくまとめてあります。
「開発」と「実装」の違い
今回、何度か「(処理を)実装する」という言い回しをしています。
ソースコードをエディタに入力することをコーディングといいますが、これを実装するとも言います。
システムづくりという意味では「開発する」も「実装する」も違わないのですが、「開発」はニュアンス的に開発環境を作ることや実際にソースコードを書くこと、動作確認をすることなども含まれます。広義です。
一方、「実装」はもっと局所的でソースコードを記載することを言います。
これからも「実装してみましょう」のように頻繁に使用していくので、覚えておいてください(これまでも使ってますけどね)。
変数の定義(初期化)
Dim day As Integer: day = 1
「ループの動きを理解する」の項で上記のソースが出てきました。
変数を宣言すると同時に定義する(初期値を入れてあげる)場合はこのような書き方ができます。もちろん・・・
Dim day As Integer
day = 1
このように宣言と定義で行を分けてもいいのですが、宣言時に同じ行で初期値の代入までやっておくと行数を余計に増やさなくてよく、また可読性の点でも優れているという利点があります。
Dim day As Integer: day = 1
宣言と代入の間をコロン(:)で繋ぐだけです!
変数の役割について
変数一つにつき役割は一つにしたい、という話をします。
カレンダーLv.2が
'ループ処理の学習
Sub D_カレンダー2()
Dim day As Integer: day = 1
Dim i As Integer
For i = 2 To 32
Cells(i, 2).Value = day & "日"
day = day + 1
Application.Wait [Now() + "0:00:00.05"]
Cells(i, 2).Select
Next
End Sub
このようなソースコードでしたね。実はこれ・・・
'ループ処理の学習
Sub D_カレンダー2()
Dim i As Integer
For i = 2 To 32
Cells(i, 2).Value = i - 1 & "日"
Application.Wait [Now() + "0:00:00.05"]
Cells(i, 2).Select
Next
End Sub
このように書いても同じ動作をします。何が違うかわかるでしょうか。
この↑ソースコードには変数dayがなく、日付の出力値を「 i - 1」としています。当然変数dayのカウントアップ処理もなし。
2から32までループするので、日付を出力するときに変数 i から -1すれば変数dayを用意したときと同じことができます。
ではなぜそれをしなかったか。
これはプログラミングの基本的な思想のひとつなのですが、ひとつの変数にいろんな役割を担わせるべきではないんです。
ひとつの変数に役割を集約すると変数の数は少なくて済みますが、読み解くのが複雑になります。今は短いソースコードなので、あまり困ることはありませんが、大きな処理でやると大変です。
ここまで何度か「変数名には気を付けて!」ということを言ってきました。
'ループ処理の学習
Sub D_カレンダー2()
Dim i As Integer
For i = 2 To 32
Cells(i, 2).Value = i - 1 & "日"
Application.Wait [Now() + "0:00:00.05"]
Cells(i, 2).Select
Next
End Sub
このソースコードだとその注意事項にも反しています。なぜならカウンタ変数 i が
カウンタ
出力行
日付
の3つの役割を1つで担ってしまっています。「i」という名前だけの情報からは出力行も日付も推測することはできません。
ですから、この理屈でいくと本来は以下のようなソースコードであるべきなんです。
'ループ処理の学習
Sub D_カレンダー2()
Dim day As Integer: day = 1
Dim writeRow As Integer: writeRow = 2
Dim i As Integer
For i = 1 To 31
Cells(writeRow, 2).Value = day & "日"
Application.Wait [Now() + "0:00:00.05"]
Cells(writeRow, 2).Select
day = day + 1
writeRow = writeRow + 1
Next
End Sub
いかがでしょうか。日付には日付を管理する変数をあてがい、出力行にはそれ専用の変数writeRowを、カウンタ変数はあくまでカウントアップするのみ(31周するだけ)です。
純粋なカウンタ変数である場合、2から始める必要がない(別途出力行を管理する変数がある)から素直に1から31としています。
ではどうしてこのソースコードを案内しなかったのかというと、これはこれで冗長なんですよね。いくら正しくても。くどいんです。
基本思想に従うかどうかはケースバイケースでして、自分用にちょこちょこっと簡単な機能を組むだけだったら杓子定規に決まりごとを守る必要もありません。
だいたいループ処理は内部で行う処理が変数 i の値の遷移に連動していることが多いので、「だったら変数 i を利用した方が便利だ」というシーンは往々にしてあるんです(今回もそうです)。
ですので、今回は間をとって、出力行は変数 i の値を利用、しかし日付は専用の変数dayを用意、という形で案内させていただきました。
先ほども触れましたが、上記で最後にお見せしたソースはループの開始値・終了値部分が
For i = 1 To 31
このようになっています。やはり1で始まり31で終わる方が(カレンダー処理としては)直感的なので、ソースコードとして美しいというのは否めませんね。
Forループの終了条件
「デバッグでループ処理の様子を確認」の項で変数 i の値が変わる様子を一緒に確認しました。ローカルウィンドウで変数の値が遷移する様子を追ったのでしたね。
カウンタ変数の動きについて補足です。
For i = 1 To 10
Debug.Print i
Next
Debug.Print i
上記のようなソースがあった場合、ループの最終周で変数 i が出力する値はなにでしょうか?またループを抜けた後に変数 i がもっている値はなにでしょうか。下図をご覧ください。
お分かりいただけますでしょうか。ループ最終周の変数 i の値は10、ループを抜けた後は11になっています。
上記を例にループの流れを今一度言葉で説明しますと・・・
変数 i に開始値1を代入しループ処理を開始
Debug.Print i で変数 i の値を出力
Nextに到達。変数 i の値を+1する(ここで2になる)
~To 10 のため、変数 i は10以下か?10以下だ。ループ続行!
(2~4を繰り返す)
という具合です。上記の3, 4が肝ですね。どこにも書いてないけど内部で行われていることです。
特に注意してほしいのが、上記の4です。10未満か?ではなく10以下か?を確認しています。ここを勘違いすると不具合のもとになります(私はよくやります)。
で、ループを抜けた後は変数 i の値が11になっているところから察しはついていると思いますが、いよいよループを抜けるとき
~~~前方の処理~~~
Debug.Print i で変数 i の値を出力(10を出力)
Nextに到達。変数 i の値を+1する(ここで11になる)
~To 10 のため、変数 i は10以下か?いや、10以上だ。ループ続行不可!
ループを抜ける
となります。毎回「ループを続けるかどうか?」の分岐が入るんですね。
ループを抜けた後、変数 i の値はループ時の値を持ち続けています。これも処理によっては使い道があるので、頭の片隅に置いておいてください。
インクリメント
今回、ループ中に変数 i の値が自動で+1されたり、明示的に変数dayの値を+1したりするシーンがありましたが、この今ある値に+1する処理のことをプログラミング用語ではインクリメントと言います。
これからはカウントアップではなく「インクリメント」を使うので覚えておいてください。
また、-1する場合はデクリメントと言います。
マクロ名をアルファベットで始めている理由
カレンダーを出力するマクロを着々と進めているわけですが、前回のメッセージボックスのものも含めてマクロ名の頭文字をアルファベットにしています。
実はこれ、あるメリットがあってやっています。
#3でショートカットキーでマクロを実行する方法をお伝えしましたよね。今回カレンダーLv.1でお伝えした
上図でもやっているのですが、[Alt] > [L] > [P] > [M] > [Tab]と押下した次!これ↓を見てください。
先頭のアルファベットのキーを押下したら即座にそのマクロを選択状態にできるんですよ。あとはそこで[Enter]キーを押すだけです。
便利なので、使ってみてください。
おわりに
今回はここまでです!
次回は分岐処理をやりましょう。条件分岐です。まだカレンダーをやりますが、次回はやっと30日までか?31日までか?の分岐ができるようになります。
それが終わると気分転換として演習問題に取り組みます。楽しみにしていてください。
では、使った頭の分甘いものでも食べてゆっくり休んでください。
今回もありがとうございました。
この記事が気に入ったらサポートをしてみませんか?