VBA: cómo omitir condicionalmente una iteración de bucle for

101

Tengo un bucle for sobre una matriz. Lo que quiero hacer es probar una determinada condición en el ciclo y saltar a la siguiente iteración si es verdadera:

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

Sé que puedo hacer:

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

pero quiero poder registrar el último valor de i en la variable PrevCouponIndex.

¿Algunas ideas?

Gracias

Richard H
fuente
3
Dijiste: "Sé que puedo hacer If (Schedule(i, 1) < ReferenceDate) Then Continue For" : ¿Estás seguro de eso? Continueno es una palabra clave de VBA.
mwolfe02
@ mwolfe02 - no, no estoy seguro, pero vi ejemplos en alguna parte (¿cpearson?)
Richard H
puede haber sido un ejemplo de VB.NET
Tipo anónimo

Respuestas:

31

¿No podrías hacer algo tan simple como esto?

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
fuente
4
De hecho, es exactamente lo que he hecho :) Pero aún me molesta, tengo que envolver cosas en la pieza Else. Gracias
Richard H
4
+1 @RichardH bueno, tienes que usar un IFpara la prueba, así que esto no es tan caro en cuanto al código. Sin embargo, debe asegurarse de que el resultado más común Schedule(i, 1)sea ​​menor que ReferenceDatepara evitar ejecutar Elsemás a menudo de lo necesario. De lo contrario, utilice (ReferenceDate>=Schedule(i, 1)). (si la prueba es 50/50, entonces no es necesario optimizar)
brettdj
Es posible que se ensucie un poco con numerosos ifs anidados ... si, por ejemplo, necesita verificar algunos resultados de Application.Match dentro de cada iteración para no encontrar una coincidencia antes de usar los resultados. Pero que así sea, ¡hay cosas peores en la vida!
JeopardyTempest
183

VBA no tiene una Continuepalabra clave equivalente ni ninguna otra para saltar inmediatamente a la siguiente iteración del ciclo. Sugeriría un uso juicioso de Gotocomo solución, especialmente si esto es solo un ejemplo artificial y su código real es más complicado:

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

Sin embargo, si eso es realmente todo su código, @Brian es absolutamente correcto. Simplemente ponga una Elsecláusula en su Ifdeclaración y termine con ella.

mwolfe02
fuente
18
Gracias, ese es un buen consejo con respecto al GoTo (VBA: te devuelve a 1964)
Richard H
3
@George: Se puede abusar de GoTo (por eso califiqué mi declaración; ver juicioso ), pero no es intrínsecamente malvado. En serio, sin embargo, es imposible escribir un VBA robusto sin la instrucción Goto simplemente porque la necesita para el manejo de errores (es decir, On Error Goto).
mwolfe02
3
@George: Lo que recomiendo aquí es una solución para otra limitación del idioma (sin Continuedeclaración). Se puede argumentar que el uso de Continueen otros idiomas debe evitarse y, por lo tanto, también debe evitarse aquí. De alguna manera, el enlace que publicaste hace mi punto. El enlace es a la GoTodeclaración en VB.Net. VB.Net tiene un manejo de errores estructurado y declaraciones Continue For/ Continue Do. Realmente no hay necesidad de GoToen VB.Net; Sospecho que se dejó en su lugar en gran parte para admitir una conversión más fácil del código VBA / VB6 existente.
mwolfe02
4
@George GoTotiene la ventaja de reducir el anidamiento. Omitir una iteración de bucle sin agregar un nivel de sangría es, en mi opinión, uno de los pocos usos legítimos de GoToen VBA / VB6. Especialmente si extrae el cuerpo del bucle en su propio procedimiento .
Mathieu Guindon
4
@George He visto anidamientos que no rompen el código , pero destrozan el cerebro ;)
Mathieu Guindon
35

Puede usar una especie de continueusando un anidado 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
Excepción no controlada
fuente
1
interesante ... mejor que usar goto!
ozmike
Esto es increíble
Kubie
1
Esta debería ser la respuesta
Stian Ulriksen
Muy elegante y agradable
Alexis Sánchez Tello
5
¡Inteligente! Sin embargo, odiaría ser el tipo que se encuentra con eso sin comentarios. lol
Caltor
14

Continue For no es válido en VBA o VB6.

Desde esta página de MSDN , parece que se ha introducido en VB.Net en VS 2005./Net 2.

Como han dicho los demás, en realidad no hay otra opción que usar Gotoo un Else.

Jon Egerton
fuente
2

Hola, también estoy enfrentando este problema y lo resuelvo usando el siguiente código de ejemplo

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
fuente
No estoy seguro de por qué se votó en contra y la siguiente respuesta tiene más de 100 votos a favor, ¡y son la misma respuesta!
rryanp
4
Probablemente porque esta respuesta fue escrita 5 años después de esa respuesta, y es exactamente el mismo concepto. ¿Por qué debería recibir votos a favor?
Tyler StandishMan
-2

Tal vez intente ponerlo todo al final si y use otro para omitir el código, esto hará que no pueda usar 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
fuente