質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
VBA

VBAはオブジェクト指向プログラミング言語のひとつで、マクロを作成によりExcelなどのOffice業務を自動化することができます。

Q&A

解決済

4回答

7605閲覧

見やすい モジュール,プロシージャ の書き方

kamikazelight

総合スコア305

VBA

VBAはオブジェクト指向プログラミング言語のひとつで、マクロを作成によりExcelなどのOffice業務を自動化することができます。

1グッド

2クリップ

投稿2018/07/19 05:35

編集2018/07/19 06:48

見やすい モジュール,プロシージャ の書き方

今まで人に自分のコードを見てもらったことが無かったので
書き方がその時の気分によって変わっていました。

見やすくするために自分なりに条件を決めてみました。
悪いところ等の指摘をお願い致します。

決めてみた条件

  • 変数の宣言を強制Option Explicitを入れ一行空ける

  • 先ず有れば条件付きコンパイルを書く

  • PrivateよりもPublicの方が上」の様にスコープが広いものほど上に書く

  • 定数より変数を上に書く

  • 宣言は同じ型が隣り合うように、またカウンタ変数など役割が同じものは隣り合うようにする。

  • 宣言の後は一行空ける

  • プロシージャの前後は3行空ける

  • プロシージャ内の最初の行に処理の簡易説明、その次に 返値と引数の補足説明

  • 宣言は インデントは1

  • 処理は インデントは2

  • ループ等の始まりと終わりがセットになっているものは その中に書く処理はインデントを一つ増やす

  • 但し配列処理などの多重ループはあえてインデントをそろえる

  • 処理のグループはインデント数が同じ文章と隣り合う場合は一行空ける

条件に沿って過去に書いたコードを手直ししてみました。

VBA

1Option Explicit 2 3'配布する時には以下のフラグをFalseにする 4#Const Coding = True 5#If Coding Then 6 Private FSO As FileSystemObject 7 Private TextStream_ As TextStream 8#Else 9 Private FSO As Object 10 Private TextStream_ As Object 11#End If 12#If VBA7 And Win64 Then '64ビット版 13 Private Declare PtrSafe Function QueryPerformanceFrequency Lib "kernel32" (frequency As Double) As Long 14 Private Declare PtrSafe Function QueryPerformanceCounter Lib "kernel32" (procTime As Double) As Long 15#Else '32ビット版 16 private Declare Function QueryPerformanceFrequency Lib "kernel32" (frequency As Double) As Long 17 private Declare Function QueryPerformanceCounter Lib "kernel32" (procTime As Double) As Long 18#End If 19 20Private PNo As Long 21Private Indent As Long 22Private StartTime As Double 23Private ErrorFlag As Boolean 24Private FIlename As String 25Private Const IndentWide As Long = 2 26 27 28 29Function Redims(ByVal Ar As Variant, Optional ByVal Vertical As Long = -1, Optional ByVal Horizontal As Long = -1) As Variant 30'二次元配列のリサイズを行う 31'返値:二次元配列 32' Base は 0 33'引数1: Ar = 二次元配列 34' Base は 0 or 1 35'引数2: Vertical = 縦の要素数 36' Base 0基準 37'引数3: Horizontal = 縦の要素数 38' Base 0基準 39 40 Dim min As Long 41 Dim VC As Long 42 Dim HC As Long 43 Dim VCF As Boolean 44 Dim X As Long 45 Dim Y As Long 46 Dim Tray As Variant 47 48On Error GoTo Err 49 '元の配列サイズ 50 min = LBound(Ar) 51 VC = UBound(Ar) 52 HC = UBound(Ar, 2) 53 54 '変更後の配列サイズが指定されていなければ保持 55 If Horizontal = -1 Then 56 Horizontal = HC - min 57 End If 58 If Vertical = -1 Then 59 Vertical = VC - min 60 End If 61 62 '配列の移し替え 63 ReDim Tray(Vertical, Horizontal) 64 Do While (Y <= Vertical And Y + min <= VC) 65 Do While (X <= Horizontal And X + min <= HC) 66 Tray(Y, X) = Ar(Y + min, X + min) 67 X = X + 1 68 Loop 69 X = 0 70 Y = Y + 1 71 Loop 72 73 '返値の指定 74 Redims = Tray 75Err: 76End Function 77 78 79 80Function Extract(ByVal Ar As Variant, ByVal Index As Long, Optional ByVal Vertical As Boolean) As Variant 81'二次元配列から任意の一行(列)を一次元配列で抜き出す 82'返値:一次元配列 83' Base は 0 84'引数1: Ar = 二次元配列 85' Base は 0 or 1 86'引数2: Index = 抜き出す行又は列 87' Ar の Base 基準 88'引数3: Vertical = 抜き出す方向 89' True:縦方向 90' False:横方向 91 92 Dim Ans As Variant 93 Dim Str As String 94 95 With WorksheetFunction 96 '列を書き出す場合は配列の縦横を反転 97 If Vertical Then 98 Ar = .Transpose(Ar) 99 End If 100 101 '指定された行をタブ区切りの文字列として格納 102 Str = Join(.Index(Ar, Index, 0), vbTab) 103 End With 104 105 'タブ区切りの文字列を一次配列に変換 106 Ans = Split(Str, vbTab) 107 108 '返値の指定 109 Extract = Ans 110End Function

具体的な処理の内容は置いておいて
見やすさ はどうでしょうか?
ご教授をお願い致します。

蛇足 見やすいコードが書きたい

最近 作る内容が複雑になってきて過去に作ったコードを再利用出来る様に
したいと思い始めました。
現状 コードがぐちゃぐちゃで探すよりも書き直した方が早いという感じで
再利用が出来ません。
書籍を買って読んだりしていますが
知識が増える度に選択肢の多さに迷い
手が止まります。
最終目標は判断基準の確立と
コードの使いまわしが出来るようにすることです。
宜しくお願い致します。

hihijiji👍を押しています

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

m.ts10806

2018/07/19 05:47

(後で見やすいコードの書き方が分からない_2) は不要かと思います。タイトルは要件のみを記載してください。
m.ts10806

2018/07/19 06:28

質問は編集できるので指摘があった場合は適宜対応してください。
kamikazelight

2018/07/19 06:49

すみませんでした。タイトルも修正できることを初めて知りました。気を付けます。
guest

回答4

0

ベストアンサー

いい質問だね。

折角だから、自分のマイルールを少しばかり。

・Option Private Module を Option Explicit の下に追記する
※ユーザ側から関数を隠す

・関数の説明
中でも外でもどちらでもメリデメがあると思います。

中に書く場合は、VBAでコードの説明を抜き出す時に便利
(モジュール内の最初の関数の説明を抜き出すのが面倒)
また、1関数だけ表示する「プロシージャの表示」にしても説明が見れる
(外に書いた場合は説明が見えない)

 ただ、往々にして、説明が更新されずに処理が変わる、という事が起こりやすいので、
説明を書くよりも、コードを分かりやすく書く方が重要だと思います。
分かりやすく書くという事は、関数名、変数名、処理の流れ等を意識するという事ですね。

・変数の宣言位置
最初は関数の頭に書くように教えられたけど、
正直、コーディングしにくい。。。
しかも、他の言語では直前に宣言する事が主流だと聞く。
なので、自分も必要になった時に、近場の分かりやすい位置に書くようにしてます。

・繰り返し
どうしても Do-Loop じゃなきゃって時意外は、For-Next がメインかな。
ていうか、インクリメントを自分で書くという事は、バグの可能性が増えるということで。。。
そして、Do-Loop は無限ループの可能性があるから、敬遠する気持ちが必要かと。。。

あと、ループx2の時にインデント揃えるのは微妙な気がする。。。

・ネーミングセンス
これからビシバシ鍛えた方がいいと思うよ。
Ar なんていう変数名、何だと思えばいいのか。。。
共通関数ならなおさら。

他にも2文字を多用してるけど、なぞなぞになっちゃうのでは? 

Vertical って変数名で Boolean型 にしてるところは、
自分は Direction って変数名で xlRowCol列挙体 を使うかな~
というのも、エクセルではシートの行・列操作が多いから、
必然的に配列も1,2次元配列で十分なんだよね。
だから、配列の次元数は、基本的に2次元以内という前提で共通関数を作ってるよ。

・関数名
色々なんだよね。まぁ現場に合わせるのが原則だけど。
ただ、自分ルールでは、共通関数は fArray_Trans とか、
頭にf、その後にArray とか String とか Range とかメインとなる名称、
で、その後ろに機能を付けていくようにしてます。

非常に多くの共通関数を作っていくうちに、こうなったんだけど、
理由としては、自作関数だと見て分かりやすい事と、
f〇〇でインテリセンスを出せば、名前を憶えてなくてもリストから見て探せる事。

※Array用の共通関数は、それ用の1モジュールにまとめてたりします(M_Array モジュール等)

※本当はクラス化とかした方がいいのかもだけど、VBAの世界でクラス化を進め過ぎると、
ついてこれない人がいるかもだし、移植しやすいので、今はこれで良しとしてます。

※要望としては既存のオブジェクトの機能を上書きしたいんだけど。。。VBAではできないから残念。。。

投稿2018/07/19 07:32

ExcelVBAer

総合スコア1175

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

kamikazelight

2018/07/19 09:25 編集

Option Private Module 初めて知りました。 クラスにするかプライベートにしないと 隠せないと思っていました。 必ず入れるようにします。 xlRowColは調べて始めて知りましたが 自動補完が利かないので 忘れた時にどんな値を指定していいのかわからなくなりそうですね... また 宜しくお願いします
ExcelVBAer

2018/07/20 00:25

自動補完って、インテリセンスのことかな? Excelなら、xlRows か xlColumns を選べるはずなんだけど。。。
kamikazelight

2018/07/20 04:24

出てこないですね Sub test() Dim RowCol As XlRowCol RowCol. End Sub VBEに入力してピリオドまで打った状態でCtrl+Space 押しても 候補が出てきません。 ないとは思いますが環境の違いでしょうか? こちらの環境 Excel 2016 64bit
ExcelVBAer

2018/07/20 04:59

えと、、、列挙体を使ったことがなかったのかな? 自分で Enum を作っても同じ事になるんだけど、 この場合だと、「RowCol = 」の状態で候補が出てくるはず。 どう伝えるのが正しいか分からないけど、 Boolean型 は True/False が候補に出てくるように、 XlRowCol型 は xlRows/xlColumns が候補になる、という感じ。
kamikazelight

2018/07/20 07:07

ありがとうございます... 列挙体は一年以上前に「へーこんな機能があるんだー」っと使ったきりで 完全に忘れてました。 思い出しました...
guest

0

前回も書きましたが、書いてる人が見やすければそれでよしです。
但し、条件のうち以下の4つはあまり一般的ではないと思われます。

プロシージャの前後は3行空ける
プロシージャ内の最初の行に処理の簡易説明、その次に 返値と引数の補足説明

これは2つセットで考えますが、関数の説明は一般的に外に置きます。
そうすることで関数ごとの区切りがわかりやすくなります。
またそうすることで「3行空ける」などの縛りも必要なくなります。
関数の説明文もなるべく簡潔に書いたほうが良いでしょう。

処理は インデントは2

無用なインデントは避けるべきです。
基本的に関数内は変数宣言以降処理しかないはずですから(途中で変数宣言をする場合を除く)そうすると無駄にインデントが空いた状態になります。
プログラムは右へ行くほど見づらくなりますので、必要のないインデントは行わないべきです。

但し配列処理などの多重ループはあえてインデントをそろえる

ループの開始と終わりが簡単に認識できるようにするためにもこれは行わないべきです。

VBAの場合、自動的にコード整形が行われるので、それにそぐわない書き方をすると生産が落ちる可能性があります。
自動でここから書きなさいとインデントしてくれているのに、更にインデントを加えたり、また逆に減らしたりといったことです。

というのを踏まえて前回同様私なりに修正したコードを貼っておきます。
(関数の説明は実際にそろっているのですが、この解答欄ではずれてしまいます)

VBA

1Option Explicit 2 3'配布する時には以下のフラグをFalseにする 4#Const Coding = True 5 6#If Coding Then 7 Private FSO As FileSystemObject 8 Private TextStream_ As TextStream 9#Else 10 Private FSO As Object 11 Private TextStream_ As Object 12#End If 13#If VBA7 And Win64 Then '64ビット版 14 Private Declare PtrSafe Function QueryPerformanceFrequency Lib "kernel32" (frequency As Double) As Long 15 Private Declare PtrSafe Function QueryPerformanceCounter Lib "kernel32" (procTime As Double) As Long 16#Else '32ビット版 17 private Declare Function QueryPerformanceFrequency Lib "kernel32" (frequency As Double) As Long 18 private Declare Function QueryPerformanceCounter Lib "kernel32" (procTime As Double) As Long 19#End If 20 21Private PNo As Long 22Private Indent As Long 23Private StartTime As Double 24Private ErrorFlag As Boolean 25Private FIlename As String 26Private Const IndentWide As Long = 2 27 28'二次元配列のリサイズを行う 29' 返値 : 二次元配列 Base は 0 30' Ar : 二次元配列 Base は 0 or 1 31' Vertical : 縦の要素数 Base 0基準 32' Horizontal : 縦の要素数 Base 0基準 33Function Redims(ByVal Ar As Variant, Optional ByVal Vertical As Long = -1, Optional ByVal Horizontal As Long = -1) As Variant 34 35 Dim min As Long 36 Dim VC As Long 37 Dim HC As Long 38 Dim VCF As Boolean 39 Dim X As Long 40 Dim Y As Long 41 Dim Tray As Variant 42 43 On Error GoTo Err 44 45 '元の配列サイズ 46 min = LBound(Ar) 47 VC = UBound(Ar) 48 HC = UBound(Ar, 2) 49 50 '変更後の配列サイズが指定されていなければ保持 51 If Horizontal = -1 Then 52 Horizontal = HC - min 53 End If 54 If Vertical = -1 Then 55 Vertical = VC - min 56 End If 57 58 '配列の移し替え 59 ReDim Tray(Vertical, Horizontal) 60 Do While (Y <= Vertical And Y + min <= VC) 61 Do While (X <= Horizontal And X + min <= HC) 62 Tray(Y, X) = Ar(Y + min, X + min) 63 X = X + 1 64 Loop 65 X = 0 66 Y = Y + 1 67 Loop 68 69 '返値の指定 70 Redims = Tray 71Err: 72End Function 73 74'二次元配列から任意の一行(列)を一次元配列で抜き出す 75' 返値 : 一次元配列 Base は 0 76' Ar : 二次元配列 Base は 0 or 1 77' Index : 抜き出す行又は列 Ar の Base 基準 78' Vertical: 抜き出す方向 True=縦方向 False=横方向 79Function Extract(ByVal Ar As Variant, ByVal Index As Long, Optional ByVal Vertical As Boolean) As Variant 80 81 Dim Ans As Variant 82 Dim Str As String 83 84 With WorksheetFunction 85 '列を書き出す場合は配列の縦横を反転 86 If Vertical Then 87 Ar = .Transpose(Ar) 88 End If 89 90 '指定された行をタブ区切りの文字列として格納 91 Str = Join(.Index(Ar, Index, 0), vbTab) 92 End With 93 94 'タブ区切りの文字列を一次配列に変換 95 Ans = Split(Str, vbTab) 96 97 '返値の指定 98 Extract = Ans 99End Function 100

投稿2018/07/19 06:44

ttyp03

総合スコア16998

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

kamikazelight

2018/07/19 07:01

On Errorのインデントですが もしも 後ろで On Error Goto 0 等を 入れる場合はエラー処理対象の始まりと 終わりの境目が見にくくなる気がしますが、 気にしない感じでしょうか それともOn Errorインデントを減らしたり、後の処理のインデントを増やしたりするのでしょうか Do Whileですが もし、三次配列ループ内でIfを使うと かなり階層が深くなりますが そういう場合は気にしない感じでしょうか? それとも、別関数として書き出したりするのでしょうか。 教えて下さい。
ttyp03

2018/07/19 07:15

On Errorのインデントについては私もどちらがよいと言えないですね。 私も昔は1文字目から書いてましたけど、最近は特に気にしていないです(というかまじめにVBAのコードは書いていない)。 一般的に(明確な理由もなく)ネストが3重にもなるのは何か設計上の問題があると思います。 三次元配列のような明確な理由がある場合は仕方ないでしょう。 但し最深の処理でIfしてSelectしてみたいに更なるネストを生むようであれば、関数化するなりしたほうがよいかもしれません。 但し安易に関数化すると可読性が落ちる可能性もあるので、実際のコードを見て判断するしかないです。
kamikazelight

2018/07/19 07:21

さじ加減が難しいですね... 頑張ります。
guest

0

コードを見て気になる点

・Functionの説明がFunction内に記述されている。Function定義の前に移動した方がいいと思います。

・2つのDo Whileのインデントが同じ位置なのが気になります。2つ目のDo Whileはインデントするべきでは?

・Function ExtractのDimのインデントとその後のWith以降のインデントが違和感あります。DimとWithは同じ位置がいいと思います。

・On Error GoTo Errの位置も上のDimと合わせるかな。

・'変更後の配列サイズが指定されていなければ保持の下のIF~End Ifと次のIf~End Ifの間は1行空けますね私なら

上記はあくまで私の私見なので自分が見やすいのが一番だと思います。

投稿2018/07/19 06:09

unz.hori

総合スコア1057

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

kamikazelight

2018/07/19 06:47

試しに直してみたら見やすくなりました。 ただ、 あんまりないですが Withのネストをするときは 内側のWithもDimのラインでいいでしょうか Do Whileですが 三次配列以上(三次配列はちょくちょくありますが、四次配列以降は使ったことないです。)の場合 インデント数が多くなってしまうので ・ループ等の始まりと終わりがセットになっているものは その中に書く処理はインデントを一つ増やす ・但し配列処理などの多重ループはあえてインデントをそろえる としていました。 もし、三次配列ループ内でIfを使うと かなり階層が深くなりますが そういう場合は気にしない感じでしょうか? それとも、別関数として書き出したりするのでしょうか。 教えて下さい。
unz.hori

2018/07/19 07:01

ネストする場合はインデントしますね。 階層が深くなる場合は切り出したりします。ただ、そこでしか使わない場合はそのまま記述します。 見やすくなるからといって切り出すと逆に追いかけにくくなるので。 インデントもTABの場合、エディタ上で変更できれば1TAB = 2SPACEとかで見やすくしたりします。
kamikazelight

2018/07/19 07:05

なるほど... 階層が深くなった場合は他でも使える処理なら書き出し、そうでないなら インデントの幅を減らして見通しをよくするのですね 参考になります!!
guest

0

コードを読んで気になった点。
a. 以下の変数の用途が書いていないのが気になりました。
何のための変数なのでしょうか。

vba

1Private PNo As Long 2Private Indent As Long 3Private StartTime As Double 4Private ErrorFlag As Boolean 5Private FIlename As String 6Private Const IndentWide As Long = 2

b. 以下はWinAPIの宣言ですよね。宣言をPrivateからPublicにして別モジュールにすることをお勧めします。

vba

1#If VBA7 And Win64 Then '64ビット版 2 Private Declare PtrSafe Function QueryPerformanceFrequency Lib "kernel32" (frequency As Double) As Long 3 Private Declare PtrSafe Function QueryPerformanceCounter Lib "kernel32" (procTime As Double) As Long 4#Else '32ビット版 5 private Declare Function QueryPerformanceFrequency Lib "kernel32" (frequency As Double) As Long 6 private Declare Function QueryPerformanceCounter Lib "kernel32" (procTime As Double) As Long 7#End If

インデントや見やすさは勿論大事なのですが、
調査のために3ヶ月後に処理を読んだ時に思考が遮らないような
モジュール分けやコメントも意識してみてはどうでしょうか。

投稿2018/07/19 07:19

umyu

総合スコア5846

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

kamikazelight

2018/07/19 09:30

調査のために3ヶ月後に処理を読んだ時に思考が遮らないような を 実現するために見やすくしたいと思っております WinAPIをprivateにしているのは移植の時にAPI定義のしわすれやAPI定義かぶるのを 嫌った為ですが確かに分けたほうが良さそうな気がしますね。 考え直してみます モジュール分けなども今困ってます... 別質問で出す予定でいますので またよろしくお願い致します
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問