Excel (VBA) |
![]() ![]() |
(Windows 11 Home : Excel 2019)
ANDとOrの使い方
投稿日時: 23/07/01 13:39:43
投稿者: shimoichimabu
|
---|---|
下記の通り、1つのブックに1か月分のシートを作ろうとしています。
|
![]() |
投稿日時: 23/07/01 15:37:59
投稿者: simple
|
---|---|
1.
If Weekday(年月日, 2)< 6 And (Month(年月日)=8 And Not (N = 13 Or N = 14 Or N = 15)) _ Or (Month(年月日) = 12 And Not (N = 29 Or N = 30 Or N = 31)) _ Or (Month(年月日) = 1 And Not (N = 1 Or N = 2 Or N = 3)) Then ActiveSheet.Copy after:=ActiveSheet ActiveSheet.Name = N End Ifと解釈されます。 上記は、3つの要素を Or で結んでいますが、 1,8,12以外の月は、どの3つの要素でもTrueになり得ないことは明らかです。 (しかも、1月,12月は 日曜も対象になっていると思います。) 2. いわゆる"正記帳"的に、シートを作成するときの条件を列挙しても勿論OKですが、 "逆記帳"的に、シートを作成しない条件を列挙することもできるでしょう。 例えばこんな書き方です。 For N = 1 To 月末 年月日 = 元号 & 年次 & "年" & 月次 & "月" & N & "日" flag = True If Weekday(年月日, 2) = 7 Then flag = False Else Select Case Month(年月日) Case 8: If (N = 13 Or N = 14 Or N = 15) Then flag = False Case 12: If (N = 29 Or N = 30 Or N = 31) Then flag = False Case 1: If (N = 1 Or N = 2 Or N = 3) Then flag = False End Select End If If flag Then ActiveSheet.Copy after:=ActiveSheet ActiveSheet.Name = N End If Next なお、投稿にあたっては変数の宣言等も漏らさず書かれたほうがよいでしょう。 そのこともあり、上記は実際に動かして確認はしていません。(その前提で受け止めて下さい。) |
![]() |
投稿日時: 23/07/01 18:19:19
投稿者: WinArrow
|
---|---|
コメント
|
![]() |
投稿日時: 23/07/01 22:27:03
投稿者: simple
|
---|---|
分かりにくかったですか。
(Weekday(年月日, 2)< 6 And ( Month(年月日) = 8 And Not (N = 13 Or N = 14 Or N = 15))) OR (Month(年月日) = 12 And Not (N = 29 Or N = 30 Or N = 31)) OR (Month(年月日) = 1 And Not (N = 1 Or N = 2 Or N = 3))↑は、_ もなく、現実のコードとは違うので注意して下さい。 ■言及のあった以下の点、少し敷衍しておきます。 混乱するようなスキップしてください(情報提供としてメモしておきます)。 >最初の1つの条件で成立したとしても、全ての条件がチェックされる(VBAの特徴)ということを >覚えておいた方がよいです。つまり、処理時間に影響するということです。 Sub test() Dim a&, b&, c& a = 1 b = 2 c = 3 If a = 0 And b = 2 And c = 3 Then Debug.Print "OK" Else Debug.Print "NG" End If End Subこの例では、 最初のa = 0 はFalseなので、第二項、第三項は計算するまでもなく結果はFalseと分かります。 このように「途中で結果が分かった場合に、それ以降の処理を行わない評価の仕方」を"短絡評価"といいます。 (いくつかのOR条件で、ひとつでもTrueになれば、それ以降は計算は不要となるというケースも同じです) VBAは、このような"短絡評価"は行わず、 無駄ではあっても、すべての要素を実直に計算する方式です。 したがって、こうした処理が多数ある場合は、(無駄な評価をしているので)効率が悪いとも言えます。 (少件数であれば影響はさほどなく、分かりやすい面があるとも言えます。使い分けるのがよいでしょう。) 処理効率を優先するなら、以下のような書き方にしたほうがよいでしょう。 Sub test2() Dim a&, b&, c& a = 1 b = 2 c = 3 If a = 0 Then If b = 2 Then If c = 3 Then Debug.Print "OK" End If End If End If End Subこれであれば、aが0でないと分かった段階で、直ぐに脱出するからです。 ■(参考情報) 色々なプログラム言語がありますが、 ・自動的に"短絡評価"を行うもの ・"短絡評価"と"非短絡評価"とそれぞれの評価を別の演算子で持つもの など言語によって異なります。 VBA(VB6)は、"非短絡評価"しか備えておらず、比較的珍しい部類に属するかもしれません。 ちなみに、test1で、本当に三つの要素を実際に評価しているかどうかは、 ステップ実行してもわかりません。 しかし、下記のようなことをすると確かめることができます。 ・以下のコードで、test3を実行すると、 ・immidiateウインドウに、 bb executed cc executed が表示されることで分かります。 Sub test3() Dim a& a = 1 If a = 0 And bb() = 2 And cc() = 3 Then Debug.Print "OK" End If End Sub Function bb() Debug.Print "bb executed" bb = 2 End Function Function cc() Debug.Print "cc executed" cc = 3 End Function |
![]() |
投稿日時: 23/07/02 00:22:34
投稿者: shimoichimabu
|
---|---|
simpleさん、WinArrowさん、回答・貴重な助言有難うございます。
|
![]() |
投稿日時: 23/07/02 00:51:17
投稿者: shimoichimabu
|
---|---|
訂正です。
|
![]() |
投稿日時: 23/07/02 01:37:50
投稿者: たらのり
|
---|---|
こんばんは(おはようございます)
If 条件A And 条件B Or 条件C Or 条件D Then 次のように書いても同じ結果になると思います: If (条件A And 条件B) Or 条件C Or 条件D Then 条件Aは「土、日でないこと」、 条件Bは「8月の休暇でないこと」、 条件Cは「12月の休暇でないこと」、 条件Dは「1月の休暇でないこと」をそれぞれ意味しています。 上の文(後者)をみると、土日を除外する「条件A」は「条件B」にしか 係っていませんね。 「条件C」と「条件D」は曜日を問わず単独で成立します(土日を除外する 「条件A」が係っていない)。 8月分は正しく出力されるかもしれませんが、12月と 1月分については 土日も出力されているのではないでしょうか。 # 勘違いだったらごめんなさい |
![]() |
投稿日時: 23/07/02 01:53:53
投稿者: たらのり
|
---|---|
すみません,12月と 1月の週末が除外されていなことについては
|
![]() |
投稿日時: 23/07/02 07:44:21
投稿者: hatena
|
---|---|
コードを書くときには、可読性とメンテナンス性を考慮するというのは他の人からも指摘があります。
Const Holidays = "08/13 08/14 08/15 12/9 12/30 12/31 01/01 01/02 01/03" If Holidays Like "*" & Format(年月日, "mm/dd") & "*" Then MsgBox "休日です" End If 休日日程が変更になった場合、定数を修正するだけですみますのでメンテナンス性も高いし、可読性も高いと思います。 今回の要件なら速度は重視しなくてもいいでしょう。 休日判定は1行ですみますので、条件式もOrでまとめても可読性は落ちないと思いますので、下記のようなコードでどうでしょう。 Public Sub Sample() Const Holidays = "08/13 08/14 08/15 12/9 12/30 12/31 01/01 01/02 01/03" Dim 元号 As String, 年次 As Long, 月次 As Long 元号 = "令和" 年次 = 5 月次 = 1 Dim 月初め As Date, 月末 As Date 月初め = CDate(元号 & 年次 & "年" & 月次 & "月" & "1日") 月末 = DateSerial(Year(月初め), Month(月初め) + 1, 0) Dim 年月日 As Date For 年月日 = 月初め To 月末 If Weekday(年月日, 2) > 5 _ Or Holidays Like "*" & Format(年月日, "mm/dd") & "*" Then Else ' ActiveSheet.Copy after:=ActiveSheet ' ActiveSheet.Name = Day(年月日) Debug.Print Day(年月日) End If Next End Sub |
![]() |
投稿日時: 23/07/02 08:05:39
投稿者: WinArrow
|
---|---|
AND OR を使わない方法に拘ってみました。
Dim 月初め As Date, 月末 As Date, 年月日 As Date, flag As Boolean 月初め = 元号 & 年次 & "年" & 月次 & "月" & "1日" 月末 = WorksheetFunction.EoMonth(月初め, 0) For 年月日 = 月初め To 月末 flag = True Select Case Weekday(年月日) Case vbMonday To vbFriday Select Case Month(年月日) Case 2 To 7, 9 To 11 Case 1 Select Case Day(年月日) Case 1 To 3 flag = False End Select Case 8 Select Case Day(年月日) Case 13 To 16 flag = False End Select Case 12 Select Case Day(年月日) Case 29 To 31 flag = False End Select End Select Case Else flag = False End Select If flag = True Then ActiveSheet.Copy after:=Sheets(ActiveSheet.Parent.Sheets.Count) ActiveSheet.Name = Day(年月日) End If Next |
![]() |
投稿日時: 23/07/02 08:21:53
投稿者: MMYS
|
---|---|
私なら、下記のように作成します。
Sub Sample() Dim 年 As Integer Dim 月 As Byte Dim 月末 As Byte Dim 年月日 As Date Dim N As Byte Dim BF As Boolean 年 = 2023 月 = 7 月末 = Day(DateSerial(年, 月 + 1, 1) - 1) For N = 1 To 月末 年月日 = DateSerial(年, 月, N) BF = True BF = BF And (Weekday(年月日) <> vbSaturday) BF = BF And (Weekday(年月日) <> vbSunday) BF = BF And (年月日 <> DateSerial(年, 8, 13)) BF = BF And (年月日 <> DateSerial(年, 8, 14)) BF = BF And (年月日 <> DateSerial(年, 8, 15)) BF = BF And (年月日 <> DateSerial(年, 12, 29)) BF = BF And (年月日 <> DateSerial(年, 12, 30)) BF = BF And (年月日 <> DateSerial(年, 12, 31)) BF = BF And (年月日 <> DateSerial(年, 1, 1)) BF = BF And (年月日 <> DateSerial(年, 1, 2)) BF = BF And (年月日 <> DateSerial(年, 1, 3)) Debug.Print BF, Format(年月日, "yyyy/mm/dd(aaa)") Next End Sub 上記コードは、ANDで演算してますから、BFがFalseになると、後の判定は関係ありません。 VBAではWinArrowさん、simpleさんの指摘のとおり、"短絡評価"は行われません。処理速度は不利ですが、速度を求める内容でもないと思いますし、なりより可読性を優先すべきだと思います。 |
![]() |
投稿日時: 23/07/02 08:27:55
投稿者: MMYS
|
---|---|
ところで祝日はどうするのですか。
Sub Sample2() Dim 年 As Integer Dim 月 As Byte Dim 月末 As Byte Dim 年月日 As Date Dim N As Byte Dim cnt As Long Dim rngHoliday As Range Set rngHoliday = Worksheets("休日リスト").Range("A:A") 年 = 2023 月 = 7 月末 = Day(DateSerial(年, 月 + 1, 1) - 1) For N = 1 To 月末 年月日 = DateSerial(年, 月, N) cnt = Application.WorksheetFunction.CountIf(rngHoliday, 年月日) Debug.Print cnt, Format(年月日, "yyyy/mm/dd(aaa)") Next Set rngHoliday = Nothing End Sub 上記は、「休日リスト」シートのA列に、年間の休日を記載。 ※土曜、日曜は、オートフルで作成。祝日・夏休暇・年末年始のみ個別に作成すればよく、手間はかからない。 |
![]() |
投稿日時: 23/07/02 08:58:27
投稿者: simple
|
---|---|
何かうまくコミュニケーションがとれませんが、
|
![]() |
投稿日時: 23/07/02 17:27:36
投稿者: shimoichimabu
|
---|---|
hatenaさんの
|