当第一个参数为假时,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:

[/代码]

【讨论】:

我认为这只是将嵌套的Ifs 替换为GoTos 的其他答案的一个版本。因为我会考虑在编程风格不好这样简单的情况下使用GoTos,所以这个答案有点超出范围。【参考方案5】:

VBA 确实有一种类似短路的行为。 通常Null 通过表达式传播,例如。 3 + NullNullTrue And NullNull。 然而:

? False And NullFalse

这看起来像是短路行为 - 发生了什么?当连词 (And) 的另一个参数是 False0 时,Null 不会传播 - 结果只是 False0。不管是左参数还是右参数。如果析取 (Or) 的另一个参数是 True 或非零整数(使用 this rule 将浮点值四舍五入为整数),这同样适用。

因此,AndOr 的参数中无法防止副作用和错误,但 Null 传播可以“短路”。这种行为似乎继承自SQL。

【讨论】:

嗯,我迷路了,你能添加一个例子来说明它如何适用于A And B @Enissay 我的两个示例使用And。也许你能更好地解释一下你对什么感到困惑? 这与短路行为无关。对两个表达式求值,第一个返回False,第二个返回True,这里没有短路。然后 VBA 做了一些奇怪的转换并且不一致地返回 FalseNull【参考方案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“与”运算符是不是评估第二个参数?的主要内容,如果未能解决你的问题,请参考以下文章

c语言中if(c=a)是否相当于if(1),因为=不是判断符号?

逻辑运算符

Python中的逻辑运算符:'and'与'or'的用法

java 中 和 && 这类的符号都表示啥?

JAVA中&&和是啥意思 怎么用

C语言逻辑运算符和if语句