【问题标题】:how to not enter if statement inside a loop if it have been executed如果已执行,如何不在循环内输入 if 语句
【发布时间】:2019-01-05 20:05:10
【问题描述】:

我有一个 for 循环,里面有 if 语句。

在我的 Excel 中,我有一个包含每个值一次的列表。一旦我发现它,我什至不希望代码检查条件,我希望它在每次执行循环时完全跳过这部分 if 语句,这可能吗?

这是我的代码和列表:

循环的第一次迭代会发现“c”是值,所以它会在里面做些什么(xc = i)

我什至不希望代码再次检查“ElseIf Cells(1, i) = "c",如下图,这可能吗?

编码为文本:

Sub test()

Dim i, xa, xb, xc As Integer

For i = 1 To 5

    If Cells(i, 1) = "a" Then
      xa = i
    ElseIf Cells(i, 1) = "b" Then
      xb = i
    ElseIf Cells(i, 1) = "c" Then
      xc = i
    End If

Next i

End Sub

【问题讨论】:

  • 请将代码提供为文本,而不是图像。
  • 如果您的列表只包含唯一值,这有什么关系?
  • @TimWilliams 效率和时间。我有 60 个 ElseIf
  • @trincot - 我编辑了原始帖子,在那里检查代码作为文本
  • 如果您想提高效率,请在进入 If 块之前将单元格值读入变量

标签: excel vba for-loop if-statement


【解决方案1】:

我对您的需求的最初解释是“如果代码再次点击 'c',就不要采取行动”。

为此,您可以按如下方式修改逻辑:

ElseIf (xc = 0) And (Cells(i, 1) = "c") Then

这样,一旦设置了xc,第一个布尔表达式就会为False,并且不会再次满足整体条件。正如@TimWilliams 所提到的,与其他具有短路选项的语言不同,VBA 仍将评估第二个布尔表达式。 @Gene 的回答描述了一种解决方法。通常,为了获得更好的性能,您会先评估简单的条件,然后再求助于代价高昂的条件。

补充说明

在 VBA 中,您必须为每个变量指定一个类型。在您的Dim 行中,只有 xc 是 Integer,而其他变量是 Variants。

一个不合格的Cells() 调用在当前活动的工作表上运行,这可能不是预期的。建议:使用工作表的 CodeName 限定 Cells()。 CodeName 是您在工作表的(Name) 属性下看到或指定的内容,如从Visual Basic 编辑器中看到的那样。例如,如果(Name)Sheet1,则使用Sheet1.Cells()。这仅在代码与Sheet1 位于同一工作簿中时才有效。如果代码在工作表本身后面,您甚至可以使用Me.Cells()

当像您的代码那样处理单元格值时,VBA (默默地)很好并且理解,在 Range 类的众多属性中,Value 是您感兴趣的。这是更好的做法,但是,要显式声明目标属性,例如在Sheet1.Cells(i, j).Value 中。

编辑

知道值将是不同的并且大约有 60 个,我建议您只需使用字典,如下所示,一次性获取每个值的行,而无需级联 Ifs:

Option Explicit

Sub test()
    Dim i As Integer
    Dim dict As Object 'Scripting.Dictionary

    Set dict = CreateObject("Scripting.Dictionary")

    For i = 1 To 5
        dict(Cells(i, 1).Value) = i
    Next

    Debug.Print dict("a") '4
    Debug.Print dict("b") '2
    Debug.Print dict("c") '1
    'Etc.
End Sub

【讨论】:

  • 您对附加说明的看法是完全正确的,这只是为了示例
  • 关于:ElseIf (xc = 0) 和 (Cells(i, 1) = "c"),它仍然会检查它。我的列表具有唯一值,因此它将与我的解决方案相同。我什至不希望它检查布尔值。我的意思是,你写道: ElseIf (TRUE/FALSE) And (TRUE/FALSE) 然后我想完全忽略它,并将这个 ElseIf 发送到代码中不再被触及的部分。你觉得有可能吗?
  • @TimWilliams - 我认为 OP 对非唯一值的第一个位置感兴趣。现在我们也知道其中大约有 60 个,显然还有更好的方法来解决这个问题。
  • @Excelosaurus 我尝试了您的代码并且工作正常,并且 100% 回答了这个问题。不错:) .. 对我来说是第一次看到 scripting.Dictionary...
  • @Excelosaurus - 对于您的第一个代码示例,值得注意的是,VBA 不会“短路”使用 And-ed 的布尔测试 - 所有语句都被评估,然后是 And被申请;被应用。所以即使 xc=0,仍然会检查单元格的值。
【解决方案2】:

如果我理解你的问题,你可以试试这个代码:

Sub test()
Dim i, xa, xb, xc As Integer
Dim a, b, c As Boolean

a = False 
b = False
c = False

For i = 1 To 5

    If Cells(i, 1) = "a" And a <> True Then

        xa = i
        a = True
    ElseIf Cells(i, 1) = "b" And b <> True Then

        xb = i
        b = True
    ElseIf Cells(i, 1) = "c" And c <> True Then

        xc = 1
        c = True

    End If

Next i
End Sub

布尔变量设置为真,例如仅当单元格(i,1)="a" 和下一个 "a" 值被跳过...

希望对你有帮助

【讨论】:

  • 我什至不希望它检查 ElseIf。我的意思是,你写道: ElseIf (TRUE/FALSE) And (TRUE/FALSE) 然后我想完全忽略它,并将这个 ElseIf 发送到代码中不再被触及的部分。你觉得有可能吗?
【解决方案3】:

我只是想“修改”费迪南多的代码,这样它就更“可读”了,我想。此版本与费迪南多或 Excelosaurus 之间的主要(实质性)区别在于,一旦检测到值,甚至不测试单元格。请记住,问题是:我不希望代码再次检查“ElseIf Cells(1, i) = "c"...所以,这个版本就是这样做的。

Sub test()
Dim i As Integer, xa As Integer, xb As Integer, xc As Integer
Dim aFound As Boolean, bFound As Boolean, cFound As Boolean
Dim r As Range

For i = 1 To 5
    Set r = Cells(i, 1)
    If Not aFound Then
        If r = "a" Then xa = i: aFound = True
    ElseIf Not bFound Then
        If r = "b" Then xb = i: bFound = True
    ElseIf Not cFound Then
        If r = "c" Then xc = i: cFound = True
    End If
Next i
End Sub

【讨论】:

  • 你是对的,但让我改写一下:“我不希望代码甚至检查布尔语句”(不管它看起来如何),我基本上是在寻找解决方案将这部分代码标记为其余代码的忽略。我不能那样做,这就是我在这里问的原因
  • @Assaf;太好了,在这种情况下,任何建议的解决方案都应该适合您。编码愉快!
  • @Assaf - 我很难理解您的需求。对我来说,您似乎想要每个值的行位置,因此是我最新的编辑。当然,每当循环运行时,整个字典都会被重建。您想跳过哪些内容以及为什么?
  • 假设 i=3,“c”和“b”已经被“找到”。现在的值为“0”,循环仍将每次检查“ElseIf Cells(i, 1) = “b”。效率不高。当我有一个运行 40 次的 for 循环并且在我喜欢的情况下60 否则如果需要时间。我的意思是,只有一分钟,但我想了解更多关于未来项目的信息
  • @Assaf;您可能已经注意到,如果已经找到“b”,上面的代码不会每次都检查“ElseIf Cells(i, 1) = "b"。我还进行了一些小修改以使其运行效率更高。跨度>
【解决方案4】:

我不喜欢 60 个 ElseIfs 的想法。请检查下面的代码。为了对其进行测试,请创建一个名为“TestSheet”的工作表并将 A1:A5 输入到单元格 H2:H6。

Sub TestSpike()
    ' 06 Jan 2019

    Dim Rng As Range
    Dim Items As Variant
    Dim Spike As String
    Dim Tmp As String
    Dim i As Integer
    Dim R As Long

    Items = Split("c|b|0|a|1", "|")
    With Worksheets("TestSheet").Columns("H")
        For R = 2 To 6
            Tmp = CStr(.Cells(R).Value)
            If InStr(1, Spike, Tmp, vbTextCompare) = 0 Then
                Spike = Spike & "|" & Tmp

                On Error Resume Next
                i = Application.WorksheetFunction.Match(Tmp, Items, 0)
                If Err Then
                    MsgBox Tmp & " wasn't found in Array"
                Else
                    MsgBox "i = " & i & " = Item " & Tmp
                End If
            End If
        Next R
    End With
End Sub

代码有一个“尖峰”。首先根据 Spike 检查每个项目。如果发现,则不进行进一步的测试。否则,它会被添加到 Spike。 新项目在添加到 Spike 后,将根据包含 60 个元素的数组“Items”进行检查,由 Chr(124) 分隔,因此,Split("c|b|0|a|1", "|" )。我使用工作表函数 MATCH 来查找数组中的项目。结果是索引号(或错误,如果未找到)。您可以在 Select Case 语句中使用此索引号来处理与其他项不同的每个项目,这与您现在在 If 语句返回 True 时处理它的方式基本相同。 您可能会发现对这种设置有用的一个想法是使用 Match 函数中的索引从另一个数组返回一个值。例如,另一个数组可能包含函数名称,并且您使用 Application.Run 为每个项目调用不同的函数。这比检查 60 多个 Select Case 语句的运行速度要快得多。

【讨论】:

    猜你喜欢
    • 2019-05-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-12-24
    • 2016-10-31
    • 1970-01-01
    相关资源
    最近更新 更多