当第一个参数为假时,VBA“与”运算符是不是评估第二个参数?
Posted
技术标签:
【中文标题】当第一个参数为假时,VBA“与”运算符是不是评估第二个参数?【英文标题】:Does the VBA "And" operator evaluate the second argument when the first is false?当第一个参数为假时,VBA“与”运算符是否评估第二个参数? 【发布时间】:2011-10-24 08:01:50 【问题描述】:Function Foo(thiscell As Range) As Boolean
Foo = thiscell.hasFormula And (InStr(1, UCase(Split(thiscell.formula, Chr(40))(0)), "bar") > 0)
End Function
这个函数的存在是为了测试在 (.
我遇到问题的情况是,当传递给函数的单元格为空时,thisCell.hasFormula 为假,但 and 之后的语句仍在评估中。这在运行时给了我一个下标超出范围的错误。
VBA 是否真的继续评估 And 的第二个参数,即使第一个参数为假?
【问题讨论】:
请注意,VBA 的And
运算符不会短路,因为它是 按位 运算符而不是 逻辑 运算符。见:***.com/questions/8042744/…
@jtolle 不正确 - 如果它的参数是布尔值,它将返回一个布尔值,因此它支持按位和逻辑运算。 (当然你可能会争辩说,逻辑是使用 1 位整数的按位的一种特殊情况,但关键是如果微软选择这样做,他们可能会支持短路)
@Hugh,很有趣。一直以来,我一直假设“And”只是一个按位运算符,尽管它模拟了逻辑运算,因为“True = -1”和“False = 0”。但是你是对的,如果传递给它的两个表达式都是布尔值,'And' is 是一个逻辑运算符。如果一个或两个操作数都是数字,则仅按位计算。但我想它不能短路,因为无论如何都必须评估两个表达式,以确保一个或两个不是数字而不是布尔值。所以我认为“按位”仍然不会导致这里发生短路。
@jtolle VBA 支持的另一件事是强类型。使用变体是可选的。如果在编译时已知逻辑运算符的参数是布尔值,那么是的,它仍然可以支持短路。即使是整数,如果 Or
的左参数是“全 1”(&HFFFFFFFF
或 -1&
),也可以跳过右参数,如果左参数为 0,And
也是如此。
@Hugh,这当然是真的。不过,这可能有点令人困惑。我不知道其他语言中有任何短路位运算符。此外,VBA 可能试图保持与旧版本 BASIC 的向后兼容性。最好添加新的运算符,就像 MS 最终为 VB.NET 所做的那样。 (VBA 的 BASIC 血统也出现在其他地方,例如我的最爱之一:***.com/questions/1070863/hidden-features-of-vba/…)
【参考方案1】:
由于答案是谷歌中排名最高的答案之一,只是在寻找类似vba if condition not lazy
的东西,我想提供一个更简单的例子,这两种情况的问题和解决方案:AND
和更有趣的OR
。 ..
Dim cond1 As Boolean 'some 1st condition that may be True or False
Dim obj As Collection 'just some sample object that may or may not be instantiated
(²:我觉得最好解释一下其他开发者,如果他们不知道背景,为什么你不选择OR
)
AND
案例
cond1 = False
If cond1 Then Set obj = New Collection
问题:
If cond1 And obj.Count > 0 Then Debug.Print "Count > 0!" 'throws error if < cond1 = False >
'because condition 2 is always evaluated
解决方案:
If cond1 Then If obj.Count > 0 Then Debug.Print "Count > 0!" 'AND would not short-cicuit!² https://***.com/a/57521572/1915920
根据品味、复杂性和可读性,以这种方式编写它可能是有意义的:
If cond1 Then
If obj.Count > 0 Then 'AND would not short-cicuit!² https://***.com/a/57521572/1915920
Debug.Print "Count > 0!"
End If
End If
OR
案例
cond1 = True
If Not cond1 Then Set obj = New Collection 'obj stays < Nothing > otherwise
问题:
If cond1 Or obj.Count = 0 Then Debug.Print "no objects!" 'throws error if < cond1 = True >
'because condition 2 is always evaluated
解决方案 1:
使用Select
的不带GoTo
的就地、非冗余单线:
Select Case True: Case cond1, obj.Count = 0: Debug.Print "no objects!": End Select 'OR would not short-cicuit!² https://***.com/a/57521572/1915920
如果它应该/必须在多行和其他一些:
Select Case True
Case cond1, obj.Count = 0 'OR would not short-cicuit!² https://***.com/a/57521572/1915920
Debug.Print "no objects!"
Case Else
Debug.Print "object count: " & obj.Count
End Select
解决方案 2:
就地、非冗余代码,GoTo
使用最少,但If
更冗长-多行代码:
If cond1 Then
noObjs:
Debug.Print "no objects!"
ElseIf obj.Count = 0 Then 'OR would not short-cicuit!² https://***.com/a/57521572/1915920
GoTo noObjs
End If
解决方案 3:
就地,在一行上的条件(可能适合)类似于OR
-concatenation 与相当多的GoTo
用法:
If cond1 Then GoTo noObjs ElseIf obj.Count = 0 Then GoTo noObjs 'OR would not short-cicuit!² https://***.com/a/57521572/1915920
GoTo skipOnAllFalse
noObjs:
Debug.Print "no objects!"
skipOnAllFalse: 'use more specific label/scenario name if possible
解决方案 4:
异地代码 (Sub
),避免 GoTo
,条件(可能适合)一行,但模块/类代码可能更不可读/传播/混乱:
Private Sub noObjs(): Debug.Print "no objects!"
If cond1 Then noObjs ElseIf obj.Count = 0 Then noObjs 'OR would not short-cicuit!² https://***.com/a/57521572/1915920
解决方案 5:
使用一个条件变量:
Dim any As Boolean: any = cond1
If Not any Then any = obj.Count = 0 'OR would not short-cicuit!² https://***.com/a/57521572/1915920
If any Then Debug.Print "no objects!"
解决方案 6:
使用多个条件变量:
Dim c1 As Boolean: Dim c2 As Boolean
c1 = cond1
If Not c1 Then c2 = obj.Count = 0 'OR would not short-cicuit!² https://***.com/a/57521572/1915920
If c1 Or c2 Then Debug.Print "no objects!" 'safe to use Or now
【讨论】:
【参考方案2】:作为DOK mentioned:不,VBA 没有短路评估。
从技术上讲,使用 2 个 If-then
语句而不是使用 AND
运算符在技术上更有效,但除非您多次这样做,否则您不会注意到节省的空间,因此请选择更具可读性的语句。如果你想获得真正的技术,VBA 处理多个If-then
语句的速度也比Select Case
更快。
VBA 很古怪 :)
【讨论】:
【参考方案3】:我认为这是最好的做法:
sub my conditions()
If Condition1=constraint1 then
if Condition2=constraint2 then
if condition3=constraint3 then
...
....
end if
end if
end if
else
end if
....
end if
end sub
因此,当且仅当条件 i 满足时,您才会通过条件。
【讨论】:
5 年前,OP 询问条件语句中的and
关键字是否在第一个条件为假时“短路”。你在回答这个问题吗?
@ppovoski 是的,我真的是【参考方案4】:
考虑必须运行的机器代码。 最快的应该是混合代码的行,比如......
如果 sfsf 然后转到 SkipAB
如果 fdf 则转到 gotogoneBad
如果 dffdefedwf 然后转到 MustHave
跳过AB: 如果 dsda > 4 则必须有
变坏了: 退出函数
必备: 这是=真
' 仅在程序必须运行时节省一些时间 数千次...例如文件搜索大驱动器 或者当一个简单的布尔测试用于跳过一个耗时的函数时 就像在封闭的工作表中查找所有工作表和名称 [代码]
If Not wFF.UsingFileExtMatch Then GoTo SkipExt
If Not wFF.OKFileEXTMatch Then GoTo BADFile
跳过分机: If Not wFF.UsingFileNameMatch Then GoTo SkipFileMatch If Not wFF.OKFileNameMatch Then GoTo BADFile 跳过文件匹配: If Not wFF.UsingDaysAgo Then GoTo SkipDaysAgo If Not wFF.OKDaysAgo Then GoTo BADFile SkipDaysAgo:
[/代码]
【讨论】:
我认为这只是将嵌套的If
s 替换为GoTo
s 的其他答案的一个版本。因为我会考虑在编程风格不好这样简单的情况下使用GoTo
s,所以这个答案有点超出范围。【参考方案5】:
VBA 确实有一种类似短路的行为。
通常Null
通过表达式传播,例如。 3 + Null
是 Null
,True And Null
是 Null
。
然而:
? False And Null
False
这看起来像是短路行为 - 发生了什么?当连词 (And
) 的另一个参数是 False
或 0
时,Null
不会传播 - 结果只是 False
或 0
。不管是左参数还是右参数。如果析取 (Or
) 的另一个参数是 True
或非零整数(使用 this rule 将浮点值四舍五入为整数),这同样适用。
因此,And
和 Or
的参数中无法防止副作用和错误,但 Null
传播可以“短路”。这种行为似乎继承自SQL。
【讨论】:
嗯,我迷路了,你能添加一个例子来说明它如何适用于A And B
@Enissay 我的两个示例使用And
。也许你能更好地解释一下你对什么感到困惑?
这与短路行为无关。对两个表达式求值,第一个返回False
,第二个返回True
,这里没有短路。然后 VBA 做了一些奇怪的转换并且不一致地返回 False
或 Null
【参考方案6】:
答案是肯定的,VBA 不会短路求值。
这不仅仅是风格问题;在这样的情况下会有很大的不同:
If i <= UBound(Arr, 1) And j <= UBound(Arr, 2) And Arr(i, 1) <= UBound(Arr2, 1) Then
Arr2(Arr(i, 1), j) = Arr(i, j)
End If
...这是不正确的。更恰当地说:
If i <= UBound(Arr, 1) And j <= UBound(Arr, 2) Then
If Arr(i, 1) <= UBound(Arr2, 1) Then
Arr2(Arr(i, 1), j) = Arr(i, j)
End If
End If
或者如果你讨厌嵌套的 if:
If i > UBound(Arr, 1) Or j > UBound(Arr, 2) Then
' Do Nothing
ElseIf Arr(i, 1) > UBound(Arr2, 1) Then
' Do Nothing
Else
Arr2(Arr(i, 1), j) = Arr(i, j)
End If
【讨论】:
【参考方案7】:您要查找的内容称为“短路评估”。
VBA 没有。
您可以看到一种可能适合您的情况的方法here。
在那里选择的方法是用Select Case
代替If
。还有一个使用嵌套Ifs
的例子。
【讨论】:
当我使用 VBA 时,我开始觉得我在尝试用蜡笔画蒙娜丽莎。 确实如此。太糟糕了,您不能改用 C# :(以上是关于当第一个参数为假时,VBA“与”运算符是不是评估第二个参数?的主要内容,如果未能解决你的问题,请参考以下文章