VBA - jak warunkowo pominąć iterację pętli for

101

Mam pętlę for na tablicy. To, co chcę zrobić, to przetestować określony warunek w pętli i przejść do następnej iteracji, jeśli to prawda:

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

Wiem, że potrafię:

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

ale chcę mieć możliwość zapisania ostatniej wartości i w zmiennej PrevCouponIndex.

Jakieś pomysły?

Dzięki

Richard H.
źródło
3
Powiedziałeś: „Wiem, że potrafię: If (Schedule(i, 1) < ReferenceDate) Then Continue For” Jesteś tego pewien? Continuenie jest słowem kluczowym VBA.
mwolfe02
@ mwolfe02 - nie, nie jestem pewien, ale widziałem gdzieś w przykładach (cpearson?)
Richard H,
mógł być przykładem VB.NET
typ anonimowy

Odpowiedzi:

31

Nie mógłbyś po prostu zrobić czegoś takiego?

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
Brian
źródło
4
Rzeczywiście, to jest dokładnie to, co zrobiłem :) Ale wciąż mnie to denerwuje, że muszę zawijać rzeczy w inny kawałek. Dzięki
Richard H,
4
+1 @RichardH cóż, musisz użyć IFdo testu, więc nie jest to aż tak drogie. Powinieneś jednak upewnić się, że najczęstszym wynikiem jest to, że Schedule(i, 1)jest mniej niż ReferenceDateunikanie wykonywania Elseczęściej niż to konieczne. W przeciwnym razie użyj (ReferenceDate>=Schedule(i, 1)). (jeśli test jest 50/50, to nie ma potrzeby optymalizacji)
brettdj
Po prostu może być trochę bałagan z wieloma zagnieżdżonymi ifs ... jeśli na przykład musisz sprawdzić sporo wyników Application.Match w każdej iteracji, aby nie znaleźć dopasowania przed użyciem wyników. Ale niech tak będzie, w życiu są gorsze rzeczy!
JeopardyTempest
183

VBA nie ma Continueani żadnego innego równoważnego słowa kluczowego, aby natychmiast przejść do następnej iteracji pętli. Sugerowałbym rozsądne użycie Gotojako obejścia, zwłaszcza jeśli jest to tylko wymyślony przykład, a twój prawdziwy kod jest bardziej skomplikowany:

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

Jeśli to naprawdę cały twój kod, @Brian jest absolutnie poprawny. Po prostu umieść Elseklauzulę w swoim Ifoświadczeniu i skończ z tym.

mwolfe02
źródło
18
Dzięki, to dobra wskazówka, jeśli chodzi o GoTo (VBA - przenosi cię z powrotem do 1964 r.)
Richard H,
3
@George: GoTo może być nadużywane (dlatego zakwalifikowałem moje oświadczenie; patrz rozsądne ), ale nie jest ono z natury złe. Poważnie, nie można napisać solidnego języka VBA bez instrukcji Goto tylko dlatego, że jest on potrzebny do obsługi błędów (tj On Error Goto.).
mwolfe02
3
@George: To, co tu zalecam, to obejście innego ograniczenia języka (brak Continueinstrukcji). Można argumentować, że Continuenależy unikać używania haseł w innych językach, a zatem również tutaj. W pewnym sensie link, który opublikowałeś, daje mi do zrozumienia. Link prowadzi do GoToinstrukcji w VB.Net. VB.Net ma zarówno strukturalną obsługę błędów, jak i Continue For/ Continue Doinstrukcje. Naprawdę nie ma takiej potrzeby GoTow VB.Net; Podejrzewam, że pozostawiono go w dużej mierze w celu ułatwienia konwersji istniejącego kodu VBA / VB6.
mwolfe02
4
@George GoToma tę zaletę, że ogranicza zagnieżdżanie. Pomijanie iteracji pętli bez dodawania poziomu wcięć jest, IMO, jednym z niewielu legalnych zastosowań GoTow VBA / VB6. Zwłaszcza jeśli wyodrębnisz ciało pętli do własnej procedury .
Mathieu Guindon
4
@George Widziałem zagnieżdżanie, które nie łamie kodu , ale niszczy mózg ;)
Mathieu Guindon
35

Możesz użyć pewnego rodzaju continue, używając zagnieżdżonego 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
Nieobsługiwany wyjątek
źródło
1
ciekawe .. lepsze niż użycie goto!
ozmike
To jest niesamowite
Kubie
1
To powinna być odpowiedź
Stian Ulriksen
Bardzo eleganckie i ładne
Alexis Sánchez Tello
5
Sprytny! Jednak nie chciałbym być facetem, który spotyka to bez komentarzy. lol
Caltor
14

Continue For nie działa w VBA lub VB6.

Na tej stronie MSDN wygląda na to, że została wprowadzona do VB.Net w VS 2005./Net 2.

Jak powiedzieli inni, tak naprawdę nie ma innej opcji niż użycie Goto lub Else.

Jon Egerton
źródło
2

Cześć, mam również ten problem i rozwiązuję go za pomocą poniższego przykładowego kodu

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
źródło
Nie jestem pewien, dlaczego głosowano w dół, a następna odpowiedź ma ponad 100 głosów pozytywnych i to ta sama odpowiedź!
rryanp
4
Prawdopodobnie dlatego, że ta odpowiedź została napisana 5 lat po tej odpowiedzi i jest to dokładnie ta sama koncepcja. Dlaczego powinno to otrzymać głosy za?
Tyler StandishMan
-2

Może spróbuj umieścić to wszystko na końcu, jeśli i użyj else, aby pominąć kod, co spowoduje, że nie będziesz mógł używać 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
źródło