VBA - как условно пропустить итерацию цикла for

101

У меня есть цикл for по массиву. Я хочу проверить наличие определенного условия в цикле и перейти к следующей итерации, если это правда:

For i = LBound(Schedule, 1) To UBound(Schedule, 1)
    If (Schedule(i, 1) < ReferenceDate) Then
        PrevCouponIndex = i
        Continue   '*** THIS LINE DOESN'T COMPILE, nor does "Next"
    End If
    DF = Application.Run("SomeFunction"....)
    PV = PV + (DF * Coupon / CouponFrequency)
Next

Я знаю, что могу:

 If (Schedule(i, 1) < ReferenceDate) Then Continue For

но я хочу иметь возможность записать последнее значение i в переменную PrevCouponIndex.

Любые идеи?

Спасибо

Ричард Х
источник
3
Вы сказали: «Я знаю, что могу If (Schedule(i, 1) < ReferenceDate) Then Continue For». Вы уверены в этом? Continueне является ключевым словом VBA.
mwolfe02
@ mwolfe02 - нет, не уверен, но где-то видел примеры (cpearson?)
Ричард Х
Возможно, это был пример VB.NET
Anonymous Type

Ответы:

31

Не могли бы вы сделать что-то простое вроде этого?

For i = LBound(Schedule, 1) To UBound(Schedule, 1)
  If (Schedule(i, 1) < ReferenceDate) Then
     PrevCouponIndex = i
  Else
     DF = Application.Run("SomeFunction"....)
     PV = PV + (DF * Coupon / CouponFrequency)
  End If
Next
Брайан
источник
4
В самом деле, это именно то, что я сделал :) Но все же меня беспокоит, что я должен оборачивать материал в кусок Else. Спасибо
Ричард Х
4
+1 @RichardH ну, вы должны использовать IFдля теста, так что это не так дорого с точки зрения кода. Тем не менее, вы должны убедиться, что наиболее частым результатом будет Schedule(i, 1)меньше, чем ReferenceDateизбегать выполнения Elseболее часто, чем необходимо. В противном случае используйте (ReferenceDate>=Schedule(i, 1)). (если тест 50/50, то в оптимизации нет необходимости)
brettdj
Просто может немного запутаться с многочисленными вложенными if ... если, например, вам нужно проверить довольно много результатов Application.Match в каждой итерации, чтобы не найти совпадения, прежде чем использовать результаты. Но как бы то ни было, в жизни есть вещи и похуже!
JeopardyTempest
183

VBA не имеет Continueили любого другого эквивалентного ключевого слова для немедленного перехода к следующей итерации цикла. Я бы посоветовал разумно использовать его Gotoв качестве обходного пути, особенно если это всего лишь надуманный пример, а ваш реальный код более сложен:

For i = LBound(Schedule, 1) To UBound(Schedule, 1)
    If (Schedule(i, 1) < ReferenceDate) Then
        PrevCouponIndex = i
        Goto NextIteration
    End If
    DF = Application.Run("SomeFunction"....)
    PV = PV + (DF * Coupon / CouponFrequency)
    '....'
    'a whole bunch of other code you are not showing us'
    '....'
    NextIteration:
Next

Однако, если это действительно весь ваш код, @Brian абсолютно прав. Просто вставьте Elseпункт в свое Ifутверждение и покончите с этим.

mwolfe02
источник
18
Спасибо, это хороший совет по поводу GoTo (VBA - возвращение в 1964)
Ричард Х
3
@George: GoTo может подвергаться злоупотреблениям (поэтому я уточнил свое утверждение; см. Разумно ), но по своей сути это не зло. А если серьезно, невозможно написать надежный VBA без оператора Goto просто потому, что он нужен для обработки ошибок (т. Е. On Error Goto).
mwolfe02
3
@George: Я рекомендую здесь обходной путь для другого ограничения языка (без Continueутверждения). Можно возразить, что Continueследует избегать использования на других языках и, следовательно, следует избегать и здесь. В некотором смысле опубликованная вами ссылка подтверждает мою точку зрения. Ссылка на GoToзаявление в VB.Net. VB.Net имеет как структурированную обработку ошибок, так и операторы Continue For/ Continue Do. В GoToVB.Net действительно нет необходимости ; Я подозреваю, что он был оставлен на месте в основном для поддержки более простого преобразования существующего кода VBA / VB6.
mwolfe02
4
@George GoToимеет преимущество сокращения вложенности. Пропуск итерации цикла без добавления уровня отступа - это, IMO, одно из немногих законных применений GoToв VBA / VB6. Особенно, если вы извлечете тело цикла в его собственную процедуру .
Mathieu Guindon
4
@George Я видел вложение, которое не нарушает код , но разрушает мозг ;)
Mathieu Guindon
35

Вы можете использовать что-то вроде continueвложенного Do ... Loop While False:

'This sample will output 1 and 3 only

Dim i As Integer

For i = 1 To 3: Do

    If i = 2 Then Exit Do 'Exit Do is the Continue

    Debug.Print i

Loop While False: Next i
Необработанное исключение
источник
1
интересно .. лучше, чем использовать goto!
ozmike
Это потрясающе
Куби
1
Это должен быть ответ
Стиан Ульриксен
Очень элегантно и красиво
Alexis Sánchez Tello
5
Умная! Я бы не хотел быть парнем, который сталкивается с этим без комментариев. lol
Caltor
14

Continue For недействителен в VBA или VB6.

На этой странице MSDN похоже, что он был введен в VB.Net в VS 2005./Net 2.

Как говорили другие, на самом деле нет другого варианта, кроме как использовать Gotoили Else.

Джон Эгертон
источник
2

Привет, я тоже столкнулся с этой проблемой, и я решаю ее, используя приведенный ниже пример кода

For j = 1 To MyTemplte.Sheets.Count

       If MyTemplte.Sheets(j).Visible = 0 Then
           GoTo DoNothing        
       End If 


'process for this for loop
DoNothing:

Next j 
Singaravelan
источник
Не уверен, почему это было отклонено, а следующий ответ получил более 100 голосов за, и это тот же ответ!
rryanp
4
Вероятно, потому что этот ответ был написан через 5 лет после этого ответа и представляет собой ту же концепцию. Почему это должно получать голоса за?
Тайлер
-2

Возможно, попробуйте поставить все это в конце, если и используйте else, чтобы пропустить код, это сделает так, что вы не сможете использовать GoTo.

                        If 6 - ((Int_height(Int_Column - 1) - 1) + Int_direction(e, 1)) = 7 Or (Int_Column - 1) + Int_direction(e, 0) = -1 Or (Int_Column - 1) + Int_direction(e, 0) = 7 Then
                Else
                    If Grid((Int_Column - 1) + Int_direction(e, 0), 6 - ((Int_height(Int_Column - 1) - 1) + Int_direction(e, 1))) = "_" Then
                        Console.ReadLine()
                    End If
                End If
richo7
источник