t-hom’s diary

主にVBAネタを扱っているブログ…とも言えなくなってきたこの頃。

VBA ありそうで無かった、参照設定とCreateObjectの対応表を作った

表題のとおり。。。なんだけど、作ったといってもまだたったの6点のみ。

  • ファイルシステムオブジェクト
  • ディクショナリーオブジェクト
  • WSHシェルオブジェクト
  • WMIオブジェクト
  • InternetExplorerオブジェクト
  • 正規表現オブジェクト

しかしこれがまた曲者で、毎回調べてるとなかなか骨が折れる。
頭がMicrosoftだったか、Windowsだったか忘れたり、次にScriptだったかScriptingだったかScriptletだったか忘れたり、そもそもインターネットのサンプルがCreateObjectばっかりで参照設定名やオブジェクト名がわからなかったりする。

特にややこしいのがWSHシェル。CreateObjectではWScript.Shellなのでてっきり参照設定した場合のオブジェクト名もShellかなと思いきや、WshShellなのだ。しかもライブラリ名はIWshRuntimeLibraryなんていう長ったらしいもの。

それで、対応リストはこちら。私の(一応の)メインサイトである。
参照設定とCreateObjectの対応リスト - You.Activate

まぁちょっとまだ作りかけ感あるし、もう少し掲載するオブジェクトも増やすつもりだけど、一旦私がよく忘れるやつは出そろったので公開。

ちなみにここに書いているProgIDという用語はCOMの書籍で初めて知ったもの。
たぶんProgramIDの略だろうけど、CreateObjectに渡している文字列は実はProgIDというらしい。

最近COMの仕組みをもう少し詳しく理解したくて、この3冊を買った。すべて豊田孝さんという方が書いた書籍。

Visual BasicプラグラマのためのCOM入門 (DeV Selection)

Visual BasicプラグラマのためのCOM入門 (DeV Selection)

まだちゃんと読んでないけど、COM入門の方は多分VBC++で似たようなことやってるので片方で良かったかな。COMアーキテクチャと~はパラパラっと読んだけど難しくてまだまだ消化しれきる気がしない。

(解決済)VBAからPowerShellのパラメーター付きコマンドが実行できずにハマった話

先日以下の記事を書いたが、ひとつ問題が発覚した。

thom.hateblo.jp

パラメーター付きのコマンドがうまく実行されないのだ。
実行時エラーで、「ファイルが見つかりません」と出てしまう。つまり、コマンドが失敗してテンポラリーファイルが作成されてないということ。

さんざん悩んでようやく原因が判明したので詳細を解説しようと思う。

どうやら、ひとつめのコマンドにパラメーターをつける場合、パラメーターごとシングルクォートで括る必要があるようだ。

Sub Sample1()
    Debug.Print SystemAccessor.GetPSCommandResult( _
        "'dir c:\work\'") 'dirはPowerShellではGet-ChildItemのエイリアス
End Sub

ひとつめのと書いたのは、パイプで複数つなぐ場合にふたつめ以降はシングルクォートが要らないから。

Sub Sample2()
    Debug.Print SystemAccessor.GetPSCommandResult( _
        "'dir c:\work\' | select name") 'dirはPowerShellではGet-ChildItemのエイリアス
End Sub

なんでこんな変な仕様になってるのか。

まず私が作ったモジュールではコマンドは最終的に以下のように展開されてWindows Script Hostに渡る。

powershell -ExecutionPolicy RemoteSigned -Command Invoke-Expression "'dir c:\work\' | select name | Out-File -filePath C:\Users\thom\AppData\Local\Temp\rad47E4A.tmp -encoding Default"

ここで以下の部分に注目
Invoke-Expression "'dir c:\work\' | select name

もしシングルクォートがなかったらこうなる。
Invoke-Expression "dir c:\work\ | select name
このとき、Invoke-Expressionは、dirc:\work\を両方自分に渡されたパラメーターとして処理してしまい、エラーになるのだ。

しかしパイプを挟んで次のコマンドはシングルクォートで囲むとうまくいかない。統一感がなくてややこしいけど、単一コマンドならシングルクォートで囲む、パイプ処理なら最初のコマンドだけシングルクォートで囲むということをしないといけない。

そのうちVBA側で加工するように改良したいけどちょっとエスケープ関連の処理が面倒だ。ということでこの挙動は既知の問題、暫定回避策は最初のコマンドをシングルクォートで括る。として一旦対応を保留することにした。

さて、ではもう少し実践的なサンプルを。
たとえば「x200」というホストから稼働中のサービスを取得するにはこのように書く。

Sub Sample3()
    Debug.Print SystemAccessor.GetPSCommandResult( _
        "'Get-Service -ComputerName x200'|Where-Object{$_.Status -eq 'Running'}")
End Sub

短縮版のPowerShellコマンドでもOK。

Sub Sample4()
    Debug.Print SystemAccessor.GetPSCommandResult( _
        "'gsv -c x200'|?{$_.status -like 'r*'}")
End Sub

もう少し複雑な処理サンプル。

Sub Sample5()
    Debug.Print SystemAccessor.GetPSCommandResult( _
        "'Get-Service -ComputerName x200'" & _
            "|Where-Object{$_.Status -eq 'Running'}" & _
            "|Select-Object -f 10")
End Sub


当初はなぜエラーになるかわからずだいぶハマった。
teratailで質問しようかと思い、試したことや挙動を情報整理していたのだが、そこでコマンドプロンプトに直接展開後のコマンドを張り付けることを思い立ち、以下を張り付けたところ、
powershell -ExecutionPolicy RemoteSigned -Command Invoke-Expression "dir c:\work\ | select name"

Invoke-Expression : 引数 'c:\work\' を受け入れる位置指定パラメーターが見つかりません。というエラーが発生した。
f:id:t-hom:20170202033409p:plain

なぜにInvoke-Expressionが引数'c:\work\'を取ろうとしているのか。。ということで原因に気付いた次第。

基本的にVBAからWSH経由で実行させるPowerShellコマンドは、コマンドプロンプトで直接実行させることもできるので、VBAでうまくいかないときはコマンドプロンプトでやってみると良いと思う。

以上。

VBAから手軽にDOSコマンドやPowerShellを実行して結果を取得するモジュールを作成

システムの運用をやっていると、VBAからDOSコマンドとかPowerShellを実行したくなるケースがある。
WScript.ShellのExecメソッドなら標準出力が取得できるのだが、一瞬DOS窓が開いてしまうのがちょっと格好悪い。

非表示でやりたいと思ったら、Runコマンドでコマンド結果をリダイレクトでファイルに書き込み、それを読み込むという面倒なことをしないといけない。

ああ面倒くさい。。

ということで、楽にそういうことができるようにモジュールで包んで抽象化した。
さて、普段なら作り方から説明するんだけれど、今回はコードが長いのでまず使う方のコードを紹介しよう。

使い方

たとえばipconfig /allの結果を取得したいとする。

記述するコードは、

なんと、これだけ!(いぇい!)

Sub Sample1()
    Debug.Print SystemAccessor.GetCommandResult("ipconfig /all")
End Sub

パイプも、ばっちりOK。

Sub Sample2()
    Debug.Print SystemAccessor.GetCommandResult("ipconfig /all | find ""IPv4""")
End Sub

ダブルクォーテーションのエスケープがちょっとめんどい。

Sub Sample3()
    Debug.Print SystemAccessor.GetCommandResult("dir ""c:\program files""")
End Sub

PowerShellコマンドはGetPSCommandResultと書く。

Sub Sample4()
    Debug.Print SystemAccessor.GetPSCommandResult("service")
End Sub

オブジェクトパイプもばっちり

Sub Sample5()
    Debug.Print SystemAccessor.GetPSCommandResult("service|?{$_.status -eq 'Running'}")
End Sub

※ただしPowerShellの場合はコマンド全体をダブルクォートで囲ってInvoke-Expressionに渡しているので、指定するコマンド内にダブルクォートが使えない。代わりにシングルクォートを使う。

その他、結果を1行ごとに分けて配列として返すメソッドとテキストストリームとして返すメソッドを用意している。
また、FileSystemObjectとWScript.ShellはそれぞれSharedFSO、SharedWshShellというプロパティで外部公開しているので、以下のように変数も作らずにいきなり使用することができる。(AriawaseのIOモジュールからアイデアをいただきました。)

f:id:t-hom:20170131011949p:plain

ただし、以下を参照設定しておかないと入力候補は出ない。

2017/2/2 追記

このモジュールからPowerShellコマンドを使う際に一つ問題が発覚。
ひとつめのコマンドにパラメーターをつける場合、パラメーターごとシングルクォートで括る必要があるようだ。

Sub Sample()
    Debug.Print SystemAccessor.GetPSCommandResult( _
        "'Get-Service -ComputerName x200'" & _
            "|Where-Object{$_.Status -eq 'Running'}" & _
            "|Select-Object -f 10")
End Sub

詳細はこちら
thom.hateblo.jp

作り方

まず、標準モジュールを作成し、モジュール名を「SystemAccessor」とする。
次に以下のコードをコピーして貼り付けるだけ。

参照設定しなくても動作するけど、参照設定する場合は#Const REF = False#Const REF = Trueに書き換える。

#Const REF = False
'If REF then require references below _
- Microsoft Scripting Runtime _
- Windows Script Host Object Model

'If Not REF Then Requir Nothing

#If REF Then
Private fso_ As Scripting.FileSystemObject
Private shell_ As IWshRuntimeLibrary.WshShell
#Else
Private fso_ As Object
Private shell_ As Object
#End If

#If REF Then
Property Get SharedFSO() As Scripting.FileSystemObject
#Else
Property Get SharedFSO() As Object
#End If
    If fso_ Is Nothing Then Set fso_ = CreateObject("Scripting.FileSystemObject")
    Set SharedFSO = fso_
End Property

#If REF Then
Property Get SharedWshShell() As IWshRuntimeLibrary.WshShell
#Else
Property Get SharedWshShell() As Object
#End If
    If shell_ Is Nothing Then Set shell_ = CreateObject("WScript.Shell")
    Set SharedWshShell = shell_
End Property

Function GetTempFilePath(Optional create_file As Boolean = False) As String
    Dim ret As String
    ret = Environ$("temp") & "\" & SharedFSO.GetTempName
    If create_file Then
        Call SharedFSO.CreateTextFile(ret)
    End If
    GetTempFilePath = ret
End Function

#If REF Then
Function GetCommandResultAsTextStream(command_string, Optional temp_path) As Scripting.TextStream
#Else
Function GetCommandResultAsTextStream(command_string, Optional temp_path) As Object
#End If
    Dim tempPath As String
    If IsMissing(temp_path) Then
        tempPath = GetTempFilePath
    Else
        tempPath = temp_path
    End If
#If Not REF Then
    Const WshHide = 0
    Const ForReading = 1
#End If
    Call SharedWshShell.Run("cmd.exe /c " & command_string & " > " & tempPath, WshHide, True)
    Set GetCommandResultAsTextStream = SharedFSO.OpenTextFile(tempPath, ForReading)
End Function

Function GetCommandResult(command_string) As String
    Dim ret As String
#If REF Then
    Dim ts As Scripting.TextStream
#Else
    Dim ts As Object
#End If
    Dim tempPath As String: tempPath = GetTempFilePath
    Set ts = GetCommandResultAsTextStream(command_string, tempPath)
    If ts.AtEndOfStream Then
        ret = ""
    Else
        ret = ts.ReadAll
    End If
    ts.Close
    Call SharedFSO.DeleteFile(tempPath, True)
    GetCommandResult = ret
End Function

Function GetCommandResultAsArray(command_string) As String()
    Dim ret() As String
    ret = Split(GetCommandResult(command_string), vbNewLine)
    GetCommandResultAsArray = ret
End Function

#If REF Then
Function GetPSCommandResultAsTextStream(command_string, Optional temp_path) As Scripting.TextStream
#Else
Function GetPSCommandResultAsTextStream(command_string, Optional temp_path) As Object
#End If
    Dim tempPath As String
    If IsMissing(temp_path) Then
        tempPath = GetTempFilePath
    Else
        tempPath = temp_path
    End If
#If Not REF Then
    Const WshHide = 0
    Const ForReading = 1
#End If
    Call SharedWshShell.Run("powershell -ExecutionPolicy RemoteSigned -Command Invoke-Expression """ & command_string & " | Out-File -filePath " & tempPath & " -encoding Default""", WshHide, True)
    Set GetPSCommandResultAsTextStream = SharedFSO.OpenTextFile(tempPath, ForReading)
End Function

Function GetPSCommandResult(command_string) As String
    Dim ret As String
#If REF Then
    Dim ts As Scripting.TextStream
#Else
    Dim ts As Object
#End If
    Dim tempPath As String: tempPath = GetTempFilePath
    Set ts = GetPSCommandResultAsTextStream(command_string, tempPath)
    If ts.AtEndOfStream Then
        ret = ""
    Else
        ret = ts.ReadAll
    End If
    ts.Close
    Call SharedFSO.DeleteFile(tempPath, True)
    GetPSCommandResult = ret
End Function

Function GetPSCommandResultAsArray(command_string) As String()
    Dim ret() As String
    ret = Split(GetPSCommandResult(command_string), vbNewLine)
    GetPSCommandResultAsArray = ret
End Function

科学のモデルとはフィクションにおける「設定」のようなもの

今回はこちらの記事を受けて、私が学生の頃、先生にどう説明してほしかったのかという話を。
chemiphys.hateblo.jp

こういうのって個人差が大きいので、クラス全員がまんべんなく理解できるような解は無いんだろうけど。

実は私、ここ半年くらいの間に原子構造について本気で色々と調べたことがある。
きっかけは以下の記事で、コンピューターの動作を説明しているうちに、どうせなら電気が流れる仕組みにまで遡ってみようと思ったわけだ。
thom.hateblo.jp

まずANDやORなどの論理回路の中身を調べ始めると半導体の話になる。トランジスタである。
じゃあトランジスタってなんぞやと調べていくと、こういう動画に行き着いた。
www.youtube.com

英語だけれど図解が秀逸なので動画だけ見てもなんとなく言ってることがわかると思う。
英語圏の方々は本当に恵まれている。

こういうことを考えているうちに、原子の自由電子とかどうなっているか気になる。そこでよく説明に使われるのが冒頭の記事で紹介されているようなボーアモデルという図。

学生の頃私はこのモデルを事実と混同しており、いくら考えてもわからなかった。
だってもし図の通りなら原子というのはスカスカである。そんなものでこの世界が構成されていたら、コップに入れた水だってスリ抜けてこぼれるだろう。かといって原子核をぎちぎちに敷き詰めたらもはや電子が回るスキマなんてない。何か原子と原子の間を保つような、たとえば磁力のようなものがあるのだろうか。それとも膜でもあるのだろうか。

私の場合、イメージできないってことはなかった。
だってイメージもなにも、それはすでに黒板に書いてあるし、教科書にも図は載っている。

だからたぶん分からないといっている生徒は、

  • 聞いてない
  • イメージしすぎる

のどちらかじゃないかと思っている。(素人考えだけれど)

さて、モデルというのは厳密さを犠牲にして本質だけを抜き出したものである。「そのように考えると色々説明がつく」というものに過ぎず、事実とは異なることが多い。いわゆるマンガなどのフィクションでいう「設定」というやつだ。

人間は空を飛べないし、体が伸びたり火を出したりといったことはできないけれど、そんな事実は置いといて、マンガの世界では一旦「できる」という設定にしてしまう。そうしないとストーリーが先に進まない。

乱暴にいうと科学におけるモデルも似たようなものかなと。これはあくまで「設定」なので現実世界の物理法則は一旦忘れて、そういうものだと受け入れるしかない。

電子雲というモデルもあるようだ。動画で見たらよくわかった。
www.youtube.com
超高速で動き回ってるので残像が雲のように見えるってことかな。要はドラゴンボールの「残像拳」をイメージすると良いかと。
ま、これも所詮モデルにすぎない。

じゃあモデルじゃなくて実際のところはどうなってるの?と色々調べてみたんだけど、無理だということが分かった。
今の技術力では、原子1つがやっと映像化されたところだ。
www.youtube.com

科学の世界はモデルでしか説明できないことが多いのだ。
私が学生の頃にモデルと事実の違いについてきちんと説明を受けたいたらもう少し興味が持てたかもしれない。

VBA マクロ、プロシージャ、メソッドなどの用語について正確に理解する

初心者向けの解説によくマクロとVBAの違いが取り上げられる。
一応違いについて説明した後、同じようなものなのであまり気にしなくて良いと締めくくるケースも多いけど、私はやはり違いを意識したほうが良いと考える。

初心者は同じようなものと言ってくれたほうが嬉しいだろう。単純に「VBA=マクロ」と覚えれば良いのだから。

しかしいざネットで調べものをしようと思った際に、そこに書かれているVBAあるいはマクロという言葉がその文章において何を指しているのか理解できなければ、筆者の意図を正確に理解することはできず、誤解のもとになるかもしれない。

言葉の正確な理解はコミュニケーションの基本である。

今回はマクロとVBAの違いの話がメインではないが、一応違いについても書いておく。

マクロとVBAの違い

マクロはもともと「マクロインストラクション」の略で、和訳すると「大きな命令」。
つまりExcelの命令群をたくさん集めてひとつの大きな命令として集約したものである。
対してVBAはそのマクロを書くための言語のこと。日本語とか英語とかと同じ、VBA語。

マクロとソースコードの違い

VBAなどのプログラミング言語で書かれたものは、ソースコードあるいは省略してソース、またはコードともいう。
じゃあソースコード=マクロなの?っていうとそれも違う。

マクロは実際に実行できる機能としての命令群を指すのに対し、ソースコードは単に「プログラミング言語で書かれたもの」を指している。
ソースコードはマクロの原材料になるものである。実行できようができまいがそんなことは関係なく、書きかけでも断片でもソースコードと呼ぶ。

マクロとプロシージャの違い

さて、ここからが重要。
VBAを学習していくとプロシージャという呼び方が登場する。新しい横文字が登場するたびにウンザリする方もいるだろう。ひょっとして隠れた挫折地点になっているということもあるかもしれない。

ここに適当に書いたソースコードがある。
マクロはいくつある?

Sub Sample()
    MsgBox "Hello!"
End Sub

Sub Sample2()
    Call Sample
    MsgBox "1 + 2 = " & Add(1, 2)
    MsgBox "GoodBye!"
End Sub

Function Add(a, b) As String
    Add = a + b
End Function

3つと答えた方は不正解。
マクロは実行単位で見た言い方なので、単体で実行できないAddはマクロではない。

Sampleはマクロだ。開始から終了まで一直線で実にシンプル。
f:id:t-hom:20170129085354p:plain

Sample2はもう少し複雑で、実行の流れを図示するとこうなる。
f:id:t-hom:20170129085516p:plain

SampleはHello!と表示するマクロである。
Sample2はまずHello!と表示し、次に1+2の計算結果を表示し、最後にGoodBye!と表示するマクロである。

このように、一連の実行単位で見た呼び方が、「マクロ」なのである。

一方プロシージャという呼び方はもっと単純で、単にSubからEnd Sub、FunctionからEnd Functionというかたまりを指す。
f:id:t-hom:20170129083945p:plain

プロシージャとは手続きという意味の英語である。
マクロとして単体で実行できるものもあれば、単体では実行できないものもある。

プロシージャとメソッドの違い

プロシージャが単に個々の手続きを指すのに対し、メソッドはプロシージャの所属元を意識した言い方である。

それぞれ、Module1のSampleメソッド、Module1のSample2メソッド、Module1のAddメソッドであると言える。
f:id:t-hom:20170129090430p:plain

メソッドとは「手法・方法」という意味の英語であるが、ここではModule1が持つ「技(ワザ)」と考えたほうがしっくりくる。

ちなみにSubプロシージャやFunctionプロシージャはメソッドであるが、Propertyプロシージャはふつうメソッドとは呼ばない。

※標準モジュールでメソッドという言い方は聞きなれないかもしれないが、私は標準モジュールもオブジェクトであると考えているのでメソッドと呼ぶ場合もある。とりわけModule1.Sampleなどとドット付きで呼び出す場合はメソッドという概念を持ち出したほうが解説が楽だ。
thom.hateblo.jp

フィールドとモジュールレベル変数、Public変数

VBAのモジュールはまず先頭に宣言セクションがあり、そのあとにプロシージャを記述する領域となる。
f:id:t-hom:20170129092641p:plain
※宣言セクションに何も書かずにプロシージャから始めることもできる。

この宣言セクションでDimまたはPrivateで宣言した変数はモジュールレベル変数と呼ばれ、モジュール内のどこからでも参照、代入ができる。

モジュールレベル変数はMicrosoftが用いている用語で、一般的なオブジェクト指向の用語ではプライベートフィールドと呼ぶ。

宣言セクションでPublicを用いて宣言した変数はPublic変数と呼ばれる。こちらは一般的なオブジェクト指向の用語ではパブリックフィールドと呼ぶ。

宣言セクションで宣言される変数はPublic、Privateいずれにしてもフィールドと呼ばれる。

フィールドもまた、所属を意識した呼び方である。
Module1のプライベートフィールド、Module1のパブリックフィールドという言い方をする。
f:id:t-hom:20170129094021p:plain

プロパティについて

プロパティというのはこれまたややこしい奴なのだが、以下の記事で詳しく書いたので割愛する。
thom.hateblo.jp

メンバーについて

メソッド、フィールド、プロパティなど、そのオブジェクトに所属するものを総称して「メンバー」と呼ぶ。
f:id:t-hom:20170129093430p:plain

Javaなどでは「メンバ」とのばし棒が入らない呼び方が一般的だが、VBAではオブジェクトブラウザー上で「メンバー」の表記を見かけたのでこちらの表記とする。

関数について

VBAにおいて関数とは値を返すプロシージャのうち、汎用的に使用できるものを指す。

なんともふわっとした定義なのだが、MicrosoftがNowを関数と呼ぶ以上、このように定義するしかなかった。
thom.hateblo.jp

VBAの組み込み関数、ワークシート関数も関数である。
ここでいう汎用的にというのは特定のオブジェクトと結びつきが無いという意味である。

通常はFunctionプロシージャを用いて作られるが、Propertyプロシージャでも作ることができる。また、Functionで作られているものでもオブジェクトモジュールに書かれて固有のオブジェクト操作に用いるものはメソッドと呼び関数とは区別する。

C言語出身の方はVBAのSubプロシージャを関数と呼ぶケースがあるが、これは間違い。
Cの関数はmainでさえ必ず値を返すので手続き(プロシージャ)=関数であるが、VBAではSubは値を返さないので関数ではない。

おわりに

プログラミング初心者はネットで調べものをしても書かれている言葉がちんぷんかんぷんでちっとも理解できないということも多いと思う。今回は特に紛らわしい用語についてその違いを説明したかった。

ネットの記事では恰好つけるためにわざと小難しい言葉が選ばれているわけではない。
同じプロシージャでも、マクロと呼ぶか、コードと呼ぶか、プロシージャと呼ぶか、メソッドと呼ぶか、メンバーと呼ぶかでそれぞれ視点が違う。
この微妙な違いを感じ取れるようになったら、マニュアルやネットの記事を見てもより筆者の言いたいことが理解できるようになる。

やはり言葉の正確な理解はコミュニケーションの基本である。
用語は大事なのでしっかりおさえておこう。

九九、FizzBuzz、じゃんけんの3つをプログラミングの「永字八法」に認定する

漢字の「永」の字には、書に必要な以下の技法8種が全て含まれている。

  • 横画
  • 縦画
  • はね
  • 右上がりの横画
  • 左はらい
  • 短い左はらい
  • 右はらい

このことを永字八法(えいじはっぽう)というらしい。

カッコイイ!

つまり書の練習に「永」という漢字をチョイスすれば、必要な技法を効率よく身に付けることができるというわけだ。

さて、書の解説はこれくらいにしてプログラミングの話に移ろう。

はじめてプログラミングをする方は、まず書籍のコードを見ながらそのままそっくりタイプすることから入る。
これを俗に「写経」という。
thom.hateblo.jp

ではどんなコードを写経したら良いだろうか。
まずは「Hello, world!」から。これは大体どんなブログラミング言語でも共通だ。
書でも最初は漢字の「一」を書くと決まっている。

VBAではこう書く。

Sub Main()
    MsgBox "Hello, world!"
End Sub

そのあとは、文字列の結合、計算、変数、制御構文などの単純なテーマを学習していく。

では一通り基礎をやったら次はどうしようか。
書の「永字八法」に相当するような題材はないだろうか。
つまり、プログラミングに必要な技法が色々と含まれていながらそこまで複雑にならない初級の題材にふさわしいコード

今回3つピックアップしたのでそれぞれ紹介しようと思う。

九九

言わずと知れた九九。小学校で習うアレをプログラムでExcelシート上に書いてみようというコードがこちら。

Sub KuKu()
    Dim i As Integer
    Dim j As Integer
    For i = 1 To 9
        For j = 1 To 9
            ThisWorkbook.Sheets(1).Cells(i, j) = i * j
        Next
    Next
End Sub

制御構造として順次・反復が含まれ変数、オブジェクトと一通り扱う良い題材だ。
For文のネストを扱っているのも良い。

FizzBuzz

これもプログラマーには有名なネタ。1から順に数字を数え上げていくが、3の倍数ならFizz、5の倍数ならBuzz、3と5の倍数ならFizzBuzzと発言するという英語圏の遊びである。

Sub FizzBuzz()
    Dim i As Integer
    For i = 1 To 100
        If i Mod 15 = 0 Then
            Debug.Print "FizzBuzz"
        ElseIf i Mod 5 = 0 Then
            Debug.Print "Buzz"
        ElseIf i Mod 3 = 0 Then
            Debug.Print "Fizz"
        Else
            Debug.Print i
        End If
    Next
End Sub

制御構造は順次・選択・反復と3種類すべてが含まれ、剰余算、デバッグプリントまで扱う。
これも良い素材だと言えると思う。

じゃんけん

もはや説明するまでもない。

Sub JyanKen()
    Dim you As Integer
    Dim com As Integer
    Do
        you = CInt(InputBox("じゃんけんの手を数字で入力" & vbNewLine & _
            "1:グー、2:チョキ、3:パー"))
        com = WorksheetFunction.RandBetween(1, 3)
        
        Select Case com
        Case 1
            MsgBox "相手はグーを出しました。"
        Case 2
            MsgBox "相手はチョキを出しました。"
        Case 3
            MsgBox "相手はパーを出しました。"
        End Select
        
        If you = com Then
            MsgBox "あいこです。もう一度。"
        ElseIf (you = 1 And com = 2) Or (you = 2 And com = 3) Or (you = 3 And com = 1) Then
            MsgBox "あなたの勝ちです。"
        Else
            MsgBox "あなたの負けです。"
        End If
    Loop While you = com
End Sub

こちらも制御構造は順次・選択・反復と3種類すべてが含まれ、加えて入力処理、ワークシート関数の利用、型のキャストまで含まれる。

ちょっと複雑で考えることも増えるけれど、まだまだ初級ネタとして使えそうだ。

結論

九九、FizzBuzz、じゃんけん。
いずれも基礎がたくさん詰まった良題である。
これらを写経することで、プログラミングに必要な技法を効率よく身に付けることができるのではないかと思う。

というわけで上記3点を「プログラミングの永字八法」として勝手に認定したいと思う。

ちなみに私の字はきたない。

VBAのコーディングガイドラインを作ってみた

VBAのコーディングガイドラインを作ってみた。

掲載先は私のメインサイト

VBA コーディングガイドライン - You.Activate

ガイドライン作成にあたり参考にしたのはこちらの4冊

VB.NETルールブック ?読みやすく効率的なコードの原則

VB.NETルールブック ?読みやすく効率的なコードの原則

リーダブルコード ―より良いコードを書くためのシンプルで実践的なテクニック (Theory in practice)

リーダブルコード ―より良いコードを書くためのシンプルで実践的なテクニック (Theory in practice)

良いコードを書く技術 ?読みやすく保守しやすいプログラミング作法 (WEB+DB PRESS plus)

良いコードを書く技術 ?読みやすく保守しやすいプログラミング作法 (WEB+DB PRESS plus)

もともとQiitaにもVBAガイドライン案は上がっているようだけど、少し私とは見解が違う部分があるので自分で作ることにした。

以下は別の方が作られたVBAのコーディングガイドライン
qiita.com

こちらは私のよりもはるかに広いトピックをカバーしていて非常に参考になる。
ただ私はエンドユーザーコンピューティングに限ってはマルチバイト変数の肯定派なのでその点がぶつかるのと、変数名の命名規則をもう少し掘り下げたかったので今回新たにガイドラインを作成した次第。

先に紹介した4冊でも識別子に適切な名前を付けることは非常に重要なこととして扱われており、またVB.NETルールブックではキャメル記法・パスカル記法などの使い分けについて具体的に書かれていたので、これらは是非とも盛り込みたかった。

結果的にガミガミとうるさいガイドラインになってしまった気もするけど、指針を求めてさまよっている人はこういうのあると助かるんじゃないかな。

みんなこれに従えなんていうつもりは毛頭無いけど、たたき台くらいにしていただけると嬉しい。

当ブログは、amazon.co.jpを宣伝しリンクすることによってサイトが紹介料を獲得できる手段を提供することを目的に設定されたアフィリエイト宣伝プログラムである、 Amazonアソシエイト・プログラムの参加者です。