VBA 中的“与”是否相同?当第一个参数为 false 时,运算符评估第二个参数?

发布于 2024-11-28 22:48:28 字数 372 浏览 0 评论 0原文

Function Foo(thiscell As Range) As Boolean
  Foo = thiscell.hasFormula And (InStr(1, UCase(Split(thiscell.formula, Chr(40))(0)), "bar") > 0)
End Function

这个函数的存在是为了测试 ( 之前是否存在某个子字符串(在本例中为 bar)。

我遇到的问题是当传递到函数的单元格为空时, thisCell.hasFormula 为 false,但是and 后面的语句仍在计算中。这会在运行时产生下标超出范围错误。

即使第一个参数为 false,VBA 是否实际上会继续计算 And 的第二个参数?

Function Foo(thiscell As Range) As Boolean
  Foo = thiscell.hasFormula And (InStr(1, UCase(Split(thiscell.formula, Chr(40))(0)), "bar") > 0)
End Function

This function exists to test for the presence of a certain substring (bar, in this case) before the (.

The case I'm having trouble with is when the cell passed into the function is empty, the thisCell.hasFormula is false, but the statement after the and is still being evaluated. This gives me a subscript out of range error in runtime.

Does VBA actually continue evaluating the second argument to the And, even when the first was false?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(7

月棠 2024-12-05 22:48:28

您正在寻找的称为“短路评估”。

VBA没有这个功能。

您可以在此处查看可能适合您情况的方法。

此处选择的方法涉及用 Select Case 替换 If。还有一个使用嵌套 Ifs 的示例。

What you are looking for is called "short-circuit evaluation".

VBA doesn't have it.

You can see an approach that is probably adaptable to your situation here.

The approach that was chosen there involved substituting a Select Case for the If. There is also an example of using nested Ifs.

大海や 2024-12-05 22:48:28

正如DOK提到的:不,VBA没有短路评估。

从技术上讲,使用 2 个 If-then 语句而不是使用 AND 运算符更有效,但除非您多次执行此操作,否则您不会注意到节省的费用,所以选择更具可读性的东西。如果您想真正了解技术,VBA 处理多个 If-then 语句的速度也比 Select Case 语句更快。

VBA 很奇怪:)

As DOK mentioned: No, VBA does not have short-circuit evaluation.

It's technically more efficient to use 2 If-then statements instead of using the AND operator, but unless you are doing it a lot of times, you wouldn't notice the savings, so go for whatever is more readable. And if you want to get really technical, VBA handles multiple If-then statements faster than Select Case ones as well.

VBA is quirky :)

坦然微笑 2024-12-05 22:48:28

答案是肯定的,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

The answer is yes, VBA does not short circuit evaluation.

It's not just a matter of style; it makes a big difference in a situation like this:

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

...which is incorrect. More appropriately:

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

Or if you have an aversion to nested ifs:

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
万劫不复 2024-12-05 22:48:28

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

<代码>?假和空
错误

这看起来像是短路行为 - 发生了什么?当连词 (And) 的另一个参数为 False0 时,Null 不会传播 - 结果只是 False0。无论是左派还是右派争论都没关系。如果析取的另一个参数 (Or) 为 True 或非零整数(浮点值将使用 此规则)。

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

VBA does have one short-circuit-like behavior.
Normally Null propagates through expressions, eg. 3 + Null is Null, and True And Null is Null.
However:

? False And Null
False

This looks like short-circuit behavior - what's going on? Null doesn't propagate when the other argument to a conjunction (And) is False or 0 - the result is just False or 0. It doesn't matter if it is the left or right argument. The same applies if the other argument to a disjunction (Or) is True or a non-zero integer (a floating point value will be rounded to an integer using this rule).

So side-effects and errors can't be prevented in arguments to And and Or, but Null propagation can be "short-circuited". This behavior seems to be inherited from SQL.

韶华倾负 2024-12-05 22:48:28

由于答案是 Google 中排名最高的答案之一,只是在寻找类似 vba if condition not lazy 的内容,我想提供一个更简单的示例,即两个条件的问题和解决方案:AND< /code> 和更有趣的 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://stackoverflow.com/a/57521572/1915920

根据品味、复杂性和可读性,这样写可能是有意义的:

If cond1 Then
    If obj.Count > 0 Then  'AND would not short-cicuit!² https://stackoverflow.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

in-使用 Select 放置不带 GoTo 的非冗余单行:

 Select Case True:  Case cond1, obj.Count = 0:  Debug.Print "no objects!":  End Select  'OR would not short-cicuit!² https://stackoverflow.com/a/57521572/1915920

以防它应该/必须位于多行上以及其他一些内容:

 Select Case True
     Case cond1, obj.Count = 0  'OR would not short-cicuit!² https://stackoverflow.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://stackoverflow.com/a/57521572/1915920
     GoTo noObjs
 End If

解决方案 3

就地,条件(可能适合)在一行上类似to OR - 与相当多的 GoTo 用法的连接:

 If cond1 Then GoTo noObjs ElseIf obj.Count = 0 Then GoTo noObjs  'OR would not short-cicuit!² https://stackoverflow.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://stackoverflow.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://stackoverflow.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://stackoverflow.com/a/57521572/1915920
 If c1 Or c2 Then Debug.Print "no objects!"  'safe to use Or now

Since the answer is one of the top ranked in Google just looking for something like vba if condition not lazy I would like to provide a simpler example, the problem and solutions of both conditions: AND and the more interesting 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

(²: I find it better to explain other devs, why you did not choose OR if they don't know the background)


the AND case

cond1 = False
If cond1 Then Set obj = New Collection

problem:

If cond1 And obj.Count > 0 Then Debug.Print "Count > 0!"  'throws error if < cond1 = False > 
                                                          'because condition 2 is always evaluated

solution:

If cond1 Then If obj.Count > 0 Then Debug.Print "Count > 0!"  'AND would not short-cicuit!² https://stackoverflow.com/a/57521572/1915920

Depending on taste, complexity and readability it may make sense to write it this way:

If cond1 Then
    If obj.Count > 0 Then  'AND would not short-cicuit!² https://stackoverflow.com/a/57521572/1915920
        Debug.Print "Count > 0!"
    End If
End If

the OR case

 cond1 = True
 If Not cond1 Then Set obj = New Collection  'obj stays < Nothing > otherwise

problem:

 If cond1 Or obj.Count = 0 Then Debug.Print "no objects!"  'throws error if < cond1 = True >
                                                           'because condition 2 is always evaluated

solution 1:

in-place, non-redundant one-liner without GoTo using Select:

 Select Case True:  Case cond1, obj.Count = 0:  Debug.Print "no objects!":  End Select  'OR would not short-cicuit!² https://stackoverflow.com/a/57521572/1915920

in case it should/must be on multiple lines and with some else:

 Select Case True
     Case cond1, obj.Count = 0  'OR would not short-cicuit!² https://stackoverflow.com/a/57521572/1915920
         Debug.Print "no objects!"
     Case Else
         Debug.Print "object count: " & obj.Count
 End Select

solution 2:

in-place, non-redundant code with minimal GoTo usage, but more lengthy If-multi-line code:

 If cond1 Then
 noObjs:
     Debug.Print "no objects!"
 ElseIf obj.Count = 0 Then  'OR would not short-cicuit!² https://stackoverflow.com/a/57521572/1915920
     GoTo noObjs
 End If

solution 3:

in-place, conditions (may fit) on one line similar to OR-concatenation with quite some GoTo usage:

 If cond1 Then GoTo noObjs ElseIf obj.Count = 0 Then GoTo noObjs  'OR would not short-cicuit!² https://stackoverflow.com/a/57521572/1915920
 GoTo skipOnAllFalse
 noObjs:
     Debug.Print "no objects!"

 skipOnAllFalse:    'use more specific label/scenario name if possible

solution 4:

out-of-place code (Sub), avoiding GoTo, conditions (may fit) on one line, but module/class code may be more unreadable/spread/cluttered:

 Private Sub noObjs():  Debug.Print "no objects!"

 If cond1 Then noObjs ElseIf obj.Count = 0 Then noObjs  'OR would not short-cicuit!² https://stackoverflow.com/a/57521572/1915920

solution 5:

using one condition variable:

 Dim any As Boolean:  any = cond1
 If Not any Then any = obj.Count = 0  'OR would not short-cicuit!² https://stackoverflow.com/a/57521572/1915920
 If any Then Debug.Print "no objects!"

solution 6:

using multiple condition variables:

 Dim c1 As Boolean:  Dim c2 As Boolean
 c1 = cond1
 If Not c1 Then c2 = obj.Count = 0  'OR would not short-cicuit!² https://stackoverflow.com/a/57521572/1915920
 If c1 Or c2 Then Debug.Print "no objects!"  'safe to use Or now
峩卟喜欢 2024-12-05 22:48:28

我认为这是最佳实践:

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 时,您才会通过条件。

I think this is the best practice:

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

Thus you will be only passaing through conditions if and only if condition i is fullfilled.

鸩远一方 2024-12-05 22:48:28

考虑必须运行的机器代码。
最快的应该是混合代码,例如...

if sfsf then goto SkipAB

if fdf then goto goneBad

if dffdefedwf then goto MustHave

SkipAB:
如果 dsda > 4 然后 MustHave

GoneBad:
退出函数

必须具备:
ThisIS = true

'仅在程序必须运行时节省一些时间
数千次...例如搜索大驱动器的文件
或者当使用简单的布尔测试来跳过耗时的函数时
就像在封闭的工作表中查找所有工作表和名称一样
[代码]

     If Not wFF.UsingFileExtMatch Then GoTo SkipExt
                If Not wFF.OKFileEXTMatch Then GoTo BADFile

跳过扩展:
如果不是 wFF.UsingFileNameMatch 则转到 SkipFileMatch
如果不是 wFF.OKFileNameMatch 则转到 BADFile
跳过文件匹配:
如果不是 wFF.UsingDaysAgo 则转到 SkipDaysAgo
如果不是 wFF.OKDaysAgo 则转到 BADFile
跳过DaysAgo:

[/代码]

Consider the machine code that has to run.
The fastest should be along the lines of a mix of code like...

if sfsf then goto SkipAB

if fdf then goto goneBad

if dffdefedwf then goto MustHave

SkipAB:
if dsda > 4 then MustHave

GoneBad:
exit function

MustHave:
ThisIS = true

' only saves a few moments when the program has to run through it
many thousands of times ... eg file searching a large drive
or when a simple Boolean test is used to skip a time consuming function
like finding all the sheets and names in a closed worksheet
[code]

     If Not wFF.UsingFileExtMatch Then GoTo SkipExt
                If Not wFF.OKFileEXTMatch Then GoTo BADFile

SkipExt:
If Not wFF.UsingFileNameMatch Then GoTo SkipFileMatch
If Not wFF.OKFileNameMatch Then GoTo BADFile
SkipFileMatch:
If Not wFF.UsingDaysAgo Then GoTo SkipDaysAgo
If Not wFF.OKDaysAgo Then GoTo BADFile
SkipDaysAgo:

[/code]

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文