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

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

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

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

Q&A

解決済

5回答

825閲覧

一般的なIF文?どれがお好みですか。または更なる結果セットの良い工夫方法はありますか?

King_of_Flies

総合スコア382

VBA

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

0グッド

0クリップ

投稿2017/09/28 05:00

編集2017/09/29 06:29

タイトルの質問だけでは意図する意味が分からないと思うので
例を書きます。
①のプログラム

vba

1If A > B And C > D Then 2 Call setMsg("ありがとう") 3ElseIf A > B And C < D Then 4 Call setMsg("ありがとう") 5ElseIf A < B And C > D Then 6 Call setMsg("ありがとう") 7ElseIf A < B And C < D Then 8 Call setMsg("thank you") 9End If

例えばこんな分岐があったとして、
書き換える方法はいくらでもあると思うのですが、

書き換え1
②のプログラム

vba

1If A > B Then 2 Call setMsg("ありがとう") 3ElseIf C > D Then 4 Call setMsg("ありがとう") 5Else 6 Call setMsg("thank you") 7End If

書き換え2
③のプログラム

vba

1If (A > B And C > D) Or (A > B And C < D) Or (A < B And C > D) Then 2 Call setMsg("ありがとう") 3Else 4 Call setMsg("thank you") 5End If

①は分岐パターンを4つすべてIfで描いたことで、
分岐の論理はわかりやすいですが、結果セットが同じものが複数できていて、
冗長的に思えますよね。

②は分岐パターンが多少工夫され、
パターンは減りましたが、結果セットは同じ組み合わせがあるし、
パターン網羅されているかどうかは、①に比べてわかりにくくなった気がしますよね?

③は確かに分岐パターンが二分化されていて、結果セットはそれぞれのパターンのみに集約されていますが、
If分の構成が長く見ずらい印象を持ちますよね。

こうなった場合、皆様はどんな工夫を凝らして、
スマートなコードを書いていますか。

教えてください。

回答をいただいたので例文追加で、説明を求めます。

vba

1Sub CellSetter() 2 For i = 6 To rowsCount 3 '作業着手予定日が空ならFor文から抜ける 4 If Cells(i, 2).Value = "" Then 5 Exit For 6 End If 7 'B6セルから末端までのデータを一時的に格納する。 8 cellDateB = Cells(i, 2).Value 9 'C6セルから末端までのデータを一時的に格納する。 10 cellDateC = Cells(i, 3).Value 11 'D6セルから末端までのデータを一時的に格納する。 12 cellDataD = Cells(i, 4).Value 13 '① 14 If cellDateC < cellDateFrom Then 15 Call Selecter(i, cellDataD, C_START, C_END, C_START, C_END_LATE, C_START_LATE, C_END_LATE) 16 '② 17 ElseIf cellDateB < cellDateFrom And cellDateC = cellDateTo Then 18 Call Selecter(i, cellDataD, C_START, C_END, C_START, C_END_LATE, C_START_LATE, C_END_LATE) 19 '③ 20 ElseIf cellDateB < cellDateFrom And cellDateFrom < cellDateC And cellDateC < cellDateTo Then 21 Call Selecter(i, cellDataD, C_START, C_END, C_START, C_END_LATE, C_START_LATE, C_END_LATE) 22 '④ 23 ElseIf cellDateFrom = cellDateB And cellDateFrom < cellDateC And cellDateC < cellDateTo Then 24 Call Selecter(i, cellDataD, C_START, C_END, C_START, C_END_LATE, C_START_LATE, C_END_LATE) 25 '⑤ 26 ElseIf cellDateFrom < cellDateB And cellDateB < cellDateTo And cellDateFrom < cellDateC And cellDateC < cellDateTo Then 27 Call Selecter(i, cellDataD, C_START, C_END, C_START, C_END_LATE, C_START_LATE, C_END_LATE) 28 '⑥ 29 ElseIf cellDateFrom < cellDateB And cellDateB < cellDateTo And cellDateC = cellDateTo Then 30 Call Selecter(i, cellDataD, C_START, C_END, C_START, C_END_LATE, C_START_LATE, C_END_LATE) 31 '⑦ 32 ElseIf cellDateFrom < cellDateB And cellDateB < cellDateTo And cellDateTo < cellDateC Then 33 Call Selecter(i, cellDataD, C_START, C_END_FIRST, C_START, C_END_EMP, C_START_LATE, C_END_EMP) 34 '⑧ 35 ElseIf cellDateB = cellDateTo And cellDateTo < cellDateC Then 36 Call Selecter(i, cellDataD, C_START, C_END_FIRST, C_START, C_END_EMP, C_START_LATE, C_END_EMP) 37 '⑨ 38 ElseIf cellDateTo < cellDateB Then 39 Call Selecter(i, cellDataD, C_START_FIRST, C_END_FIRST, C_START_FIRST, C_END_EMP, C_START_EMP, C_END_EMP) 40 '⑩ 41 ElseIf cellDateB < cellDateFrom And cellDateFrom = cellDateC Then 42 Call Selecter(i, cellDataD, C_START, C_END, C_START, C_END_LATE, C_START_LATE, C_END_LATE) 43 '⑪ 44 ElseIf cellDateB = cellDateFrom And cellDateC = cellDateTo Then 45 Call Selecter(i, cellDataD, C_START, C_END, C_START, C_END_LATE, C_START_LATE, C_END_LATE) 46 '⑫ 47 ElseIf cellDateB = cellDateFrom And cellDateTo < cellDateC Then 48 Call Selecter(i, cellDataD, C_START, C_END_FIRST, C_START, C_END_EMP, C_START_LATE, C_END_EMP) 49 '⑬ 50 ElseIf cellDateB < cellDateFrom And cellDateTo < cellDateC Then 51 Call Selecter(i, cellDataD, C_START, C_END_FIRST, C_START, C_END_EMP, C_START_LATE, C_END_EMP) 52 '⑭ 53 ElseIf cellDateB = cellDateFrom And cellDateC = cellDateFrom Then 54 Call Selecter(i, cellDataD, C_START, C_END, C_START, C_END_LATE, C_START_LATE, C_END_LATE) 55 '⑮ 56 ElseIf cellDateB = cellDateTo And cellDateC = cellDateTo Then 57 Call Selecter(i, cellDataD, C_START, C_END, C_START, C_END_LATE, C_START_LATE, C_END_LATE) 58 End If 59 Next i 60 End Sub

このプログラムで分岐結果が少ないのは⑨の一個ですが、

vba

1If cellDateTo < cellDateB Then 2 '処理 3ElseIf

とした後のElseIfの式はどうかきますか?

BA後のコメントに対してさらに改修したコード

vba

1 --セット条件に当てはまる引数をSetCellに渡す。 2 --pRow = 対象行数 3 --pClumn = 対象列数 4 --pDuaringPattern = 期間パターン 5 --pProgressSituation = 進捗状況 6 Sub SetCells(pClumn As Integer) 7 8 For pRow = 6 To rowsCount 9 10 --対象セルのデータを格納する。 11 pCellDate = Cells(pRow, pClumn).Value 12 --対象セルのデータの存在チェック 13 If Cells(pRow, pClumn).Value = "" Then 14 Exit For 15 End If 16 17 --期間パターンを格納する変数。 18 Dim pDuringPattern As Integer 19 --期間パターンセット 20 If pCellDate < cellDateFrom Then 21 pDuringPattern = 0 22 ElseIf pCellDate = cellDateFrom Then 23 pDuringPattern = 1 24 ElseIf cellDateFrom < pCellDate And pCellDate < cellDateTo Then 25 pDuringPattern = 2 26 ElseIf pCellDate = cellDateTo Then 27 pDuringPattern = 3 28 ElseIf cellDateTo < pCellDate Then 29 pDuringPattern = 4 30 End If 31 32 --進捗状況を格納する変数 33 Dim pProgressSituation As Integer 34 --進捗状況セット 35 cellDataD = Cells(pRow, 4).Value 36 Select Case cellDataD 37 Case 100 38 pProgressSituation = 0 39 Case 0 40 pProgressSituation = 1 41 Case 1 To 99 42 pProgressSituation = 2 43 End Select 44 45 --セルに文字をセットする処理 46 Call SetCell(pRow, pClumn, pDuringPattern, pProgressSituation) 47 48 Next pRow 49 50 End Sub 51 52 53 --対象セルへの文字列セット処理。 54 --pString = セットする文字列 55 --pClumn = 2:着手状態 3:完了状況 56 --pProgressSituation = 0:進捗状況100% 1:進捗状況0% 2:進捗状況199% 57 --pDuringPattern = 期間のパターン 4:報告期間未満 58 Sub SetCell(pRow As Variant, pClumn As Integer, pDuringPattern As Integer, pProgressSituation As Integer) 59 60 --対象セルにセットする文字を格納する変数 61 Dim pString As String 62 63 If pClumn = 2 Then 64 If pDuringPattern = 4 Then 65 Select Case pProgressSituation 66 Case 0, 2 67 pString = C_START_FIRST 68 Case 1 69 pString = C_START_EMP 70 End Select 71 Else 72 Select Case pProgressSituation 73 Case 0, 2 74 pString = C_START 75 Case 1 76 pString = C_START_LATE 77 End Select 78 End If 79 Else 80 If pDuringPattern = 4 Then 81 Select Case pProgressSituation 82 Case 0 83 pString = C_END_FIRST 84 Case 1, 2 85 pString = C_END_EMP 86 End Select 87 Else 88 Select Case pProgressSituation 89 Case 0 90 pString = C_END 91 Case 1, 2 92 pString = C_END_LATE 93 End Select 94 End If 95 End If 96 97 --対象セルへの文字セット 98 Cells(pRow, pClumn + 3).Value = pString 99 End Sub 100

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

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

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

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

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

ozwk

2017/09/28 05:09 編集

A=BやC=Dが抜けてますけどどうします?
King_of_Flies

2017/09/28 05:13

例で作った台なので本来お聞きしたかったコードを追加しました。変数が長いので見づらいかもしれませんが、追記したコードで考えた場合どう描くのがよいでしょうか
ozwk

2017/09/28 05:18

すべての条件は排他的ですか?つまり、(Else)IFの順番入れ替えても動作は変わりませんか?
King_of_Flies

2017/09/28 05:20

全く変わりません。全網羅をIfで分岐させています。
King_of_Flies

2017/09/28 05:21

例えば②と⑤を入れ替え、⑫と⑦を入れ替え、⑮と①を入れ替えても結果は変わりません。
King_of_Flies

2017/09/28 07:41

皆様たくさんの回答ありがとうございました。BAは悩んだところがありますが、どうか許して下さい。
guest

回答5

0

ベストアンサー

cellDateFrom,To,B,CをそれぞれF(rom),T(o),S(tart),E(nd)
If文の結果は三種類なのでSelX,Y,Zとしてます。

おそらく前提としてF<=T,S<=Eなんだろうなと思いつつ
Sと区間F-Tの関係
Eと区間F-Tの関係でif文をソートします。

VBA

1If E < F Then 2 SelX() 3ElseIf S < F And F = E Then 4 SelX() 5ElseIf S < F And F < E And E < T Then 6 SelX() 7ElseIf S < F And E = T Then 8 SelX() 9ElseIf S < F And T < E Then 10 SelY() 11 12ElseIf S = F And E = F Then 13 SelX() 14ElseIf S = F And F < E And E < T Then 15 SelX() 16ElseIf S = F And E = T Then 17 SelX() 18ElseIf S = F And T < E Then 19 SelY() 20 21ElseIf F < S And S < T And E = T Then 22 SelX() 23ElseIf F < S And S < T And F < E And E < T Then 24 SelX() 25ElseIf F < S And S < T And T < E Then 26 SelY() 27 28ElseIf S = T And E = T Then 29 SelX() 30ElseIf S = T And T < E Then 31 SelY() 32 33ElseIf T < S Then 34 SelZ()

これを見ると
T < Sだったら問答無用でSelZ()
そうではなくT < EだったらSelY()
残りがSelX()とわかります。

投稿2017/09/28 06:09

ozwk

総合スコア13521

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

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

King_of_Flies

2017/09/28 06:15

時間をわざわざ掛けていただいてありがとうございます。 Selecterを三種に分割する方法で処理分けをするのですね。 確かに変更がしやすいです。
ozwk

2017/09/28 06:29 編集

というかもともとの要件を想像するに * 期間内、もしくは前に終わった * 期間が終わる前に着手したが、終わらなかった * 期間内に開始すらしなかった の3種類を判定するだけだったのでは? 実際何がしたかったんですか?
King_of_Flies

2017/09/28 06:32

つたないこのコードから、 実装意図を読み取った慧眼恐れ入ります。 分岐結果を見ると4種ありますので、その通りですが、 要件の変更で下記追加の想定も考えられます。 今更ながら定数の説明ですが、 Const C_START_FIRST As String = "先行着手" Const C_START As String = "着手" Const C_START_LATE As String = "遅れ" Const C_START_EMP As String = "" Const C_END_FIRST As String = "先行完了" Const C_END As String = "完了" Const C_END_LATE As String = "遅れ" Const C_END_EMP As String = "" となっていますが、 Startの遅れ種類を遅れ(未着手)、遅れ(着手)に分割する可能性や、 Endの遅れ種類を遅れ(未完了)、遅れ(完了)とする可能性もあるかもしれないとのことでした。 これによってセットするパターンがだいぶ変わる可能性もあったので、 条件の分岐を書いた経緯はあります。
King_of_Flies

2017/09/28 06:36

↑で伝わりますでしょうか・・・?
ozwk

2017/09/28 06:37

だとすると そもそもStart種類の判定とEnd種類の判定は独立していますよね つまり、 StartとFrom-Toの関係性と EndとFrom-Toの関係性を別々に求めて それら結果を統合すればよかったのでは?
King_of_Flies

2017/09/28 06:42

すいません、動揺して、意味のない返答をしてしまいました。
ozwk

2017/09/28 06:48

ちなみに先行着手ならある程度の完了遅れは通常完了とみなすみたいな謎ルールが万が一生えてきても それは統合段階で例外的ケースとして盛り込めばいいだけです
King_of_Flies

2017/09/28 07:23

改修後はこんな感じになりました。というのを追記しておきます
ozwk

2017/09/28 08:02 編集

もっというと着手の判定と完了の判定に差がないので From-Toに対してある日時が前、中、後かを判定する関数作ればもっと短くなりますね SetEとSetFにほとんど違いないですもんね。 ECellSetterとFCellSetterにほとんど違いないですもんね。 SetFの処理書くときSetEをコピペしませんでした? コピペできるということは共通点だらけということで メソッドにまとめられるはずです
King_of_Flies

2017/09/28 09:20

ぐぬぬ、SetFとSetEのメソッドはまとめることができたので、コード修正した物に質問を編集しました。が、ECellSetterとFCellSetterの処理をどうまとめればよいのか、、詰まってしまいました。
guest

0

順列と組み合わせの話に近いかと思います。
結論いうと、仕様というか重みをおくのは何か?で決まると思います。

たとえば、2枚のコインの裏表の結果で処理を判定する場合、
(表・表)、(表・裏)、(裏・表)、(裏・裏)の4つの順列がありますが、
(裏・表)と(表・裏)は同じと考えれば3通りになります(組み合わせ)

で、結果セットに重きをおくならば③でよいと思いますが、パターン(ここでいう順列)が重要なら
すなおに①で書くものだと思います。

というのもパターンによって処理をあとから変更したい場合、もとのifのorを取るという
面倒なわりにろくな成果のでない作業が発生します。

もしパターンと結果セットが同じ意味合いとして動作させる仕様であれば、そもそもの分岐の組み合わせ
を網羅させる考え方が不要かと。

条件をきちんと正規化するのが、maisumakunの回答。
そもそも条件を関数化しなさいが、szk.さんの回答(私は、こちらが好み)

で、私のいう趣旨は、そもそもパターンが重要だったら、結果が同じだからって、まとめちゃダメでしょ。っていうことです。
「ありがとう」を特定のパターンだけ「まことにありがとう」にするような変更が想定されるなら、条件をまとめないほうがよい。コードが重複するのがイヤなら、実行部分のコードを関数化しておけばよいかと。

ところで、一般的に多分岐はif文を使わないものかと・・・
ご存知かもしれませんが、select caseで複数条件はカンマでつなげればよいです。
これなら変更あって直すのは楽かなぁ。

VBA

1Select Case condition 2 Case 0,1,3,5,7 3 Call setMsg("ありがとう") 4 Case 2,4,6,8 5 Call setMsg("thank you") 6End Select

投稿2017/09/28 05:42

編集2017/09/28 06:07
momon-ga

総合スコア4820

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

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

momon-ga

2017/09/28 06:13

あ、結局conditionを判定するロジックは、あのif文書かなきゃか・・・
King_of_Flies

2017/09/28 06:20

Caseで 0,1,3,5,7という書き方は理解しているのですが、 つまり、こうなるんですよね。 Select Case condition Case cellDateC < cellDateFrom, cellDateB < cellDateFrom And cellDateC = cellDateTo, cellDateB < cellDateFrom And cellDateFrom < cellDateC And cellDateC < cellDateTo,///////// Call setMsg("ありがとう") Case cellDateFrom < cellDateB And cellDateB < cellDateTo And cellDateTo < cellDateC , cellDateB = cellDateTo And cellDateTo < cellDateC Call setMsg("thank you") End Select 適当に略してますが、これはちょっと私の心が折れそうです。
momon-ga

2017/09/28 06:33 編集

ozwkさんのコメントにありますが、最初に3パターンのどれか判定すれば、そもそも15種類の 分岐を網羅する必要ないのでは?あ、この書き方をどうするかの質問とかわらないのか・・・ぐるぐるまわってきた。
King_of_Flies

2017/09/28 06:33

15:13への返答> そうですよね。↑に記述した通りのことが起こりますよね。
King_of_Flies

2017/09/28 06:38

15:33への返答> 分岐網羅の経緯をozwskさんへの回答に追記しました。
guest

0

パターンの少ない場合を真っ先に考えれば、スッキリします。

vba

1If A < B And C < D Then 2 Call setMsg("thank you") 3Else 4 Call setMsg("ありがとう") 5End If

(余談ですが、「A=B」や「C=D」の場合の処理が、書き方によって微妙に違ってきますので、そこは要注意です)

投稿2017/09/28 05:08

maisumakun

総合スコア145183

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

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

maisumakun

2017/09/28 05:51

追記されたぐらいに条件が多くなった場合、分岐用の表を作って、それをなぞって処理する、というぐらいに、考え方を変えたほうがわかりやすくなると思います。
guest

0

スッキリした書き方で言えば下記でしょうか。

If A < B And C < D Then Call setMsg("thank you") Else Call setMsg("ありがとう") End If

但しこれだと可読性が悪い(どういった理由でその条件式なのかがわからない)ので、私ならこう書くと思います。

If A > B Then Call setMsg("ありがとう") Else If C > D Then Call setMsg("ありがとう") Else Call setMsg("thank you") End If End If

投稿2017/09/28 06:09

ttyp03

総合スコア16998

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

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

0

醜い条件分岐は関数で外に出します。
関数内ではお好きなように。

java

1if (isJP(A,B,C,D)) { 2 System.out.printf("ありがとう"); 3} else if (isEN(A,B,C,D)) { 4 System.out.printf("thank you"); 5}

あとご提示の3件ですが、
A,B,C,Dのいくつかが同じ時に
①のケースと②、③のケースの挙動が変わります。

投稿2017/09/28 05:12

szk.

総合スコア1400

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

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

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問