2010-06-24 6 views
4

ContextePourquoi cette boucle ne traite-t-elle pas l'ensemble de données complet?

J'ai une feuille de calcul des allocations de tickets pour un événement. Sur chaque ligne de la feuille de calcul se trouve un nom et le nombre de tickets alloués.

The Spreadsheet http://s3.amazonaws.com/twitpic/photos/full/120237739.png?AWSAccessKeyId=0ZRYP5X5F6FSMBCCSE82&Expires=1277404609&Signature=pGRx%2Fxcm3InEY2PyKd3k09hC7Xo%3D

je dois changer la feuille de calcul afin que chaque nom est reproduit une fois par billet sur les lignes séparées, comme ceci:

The Spreadsheet after changes http://s3.amazonaws.com/twitpic/photos/full/120238390.png?AWSAccessKeyId=0ZRYP5X5F6FSMBCCSE82&Expires=1277404546&Signature=xrUAdzyIJWKGnrge%2FCD4EudiyX8%3D

J'ai une macro pour ce faire, mais il présente un comportement étrange

Le problème

La macro ne parcourt pas tout l'ensemble de données. En parcourant le code montre que, malgré délibérément augmenter la valeur de LastRow, la boucle For ne boucle que pour plusieurs fois la valeur d'origine spécifiée. La nouvelle valeur de LastRow à la fin de chaque itération semble être ignorée.

Cela semble particulièrement étrange que l'équivalent Do While fonctionne en boucle fine (Voir ci-dessous pour le code de travail en utilisant une boucle Do While)

La question

Pourquoi le comportement décrit dans la section problème (voir ci-dessus) et pourquoi est-ce incompatible avec des structures équivalentes?

La boucle For Macro

Sub InsertSurnames() 

    Dim LastRow As Long 
    Dim r As Long 
    Dim surname As String 
    Dim tickets As Integer 
    Dim surnameCol As Integer 
    Dim ticketCol As Integer 
    Dim targetCol As Integer 

    surnameCol = 1 
    ticketCol = 3 
    targetCol = 4 
    LastRow = ActiveSheet.UsedRange.Rows(ActiveSheet.UsedRange.Rows.Count).Row 

    For r = 1 To LastRow 

     surname = Cells(r, surnameCol).Value 
     tickets = Cells(r, ticketCol).Value 


     If (Not (Len(surname) = 0)) Then 

      Cells(r, targetCol).Value = surname 

      For x = 1 To tickets - 1 

       Cells(r + x, 1).EntireRow.Insert 
       Cells(r + x, targetCol).Value = surname 



      Next x 

      LastRow = LastRow + tickets - 1 

     End If 


    Next r 

End Sub 

La boucle Do While Macro

Sub InsertSurnames() 

    Dim LastRow As Long 
    Dim r As Long 
    Dim surname As String 
    Dim tickets As Integer 
    Dim surnameCol As Integer 
    Dim ticketCol As Integer 
    Dim targetCol As Integer 

    surnameCol = 1 
    ticketCol = 3 
    targetCol = 4 
    LastRow = ActiveSheet.UsedRange.Rows(ActiveSheet.UsedRange.Rows.Count).Row 
    r = 1 

    Do While r <= LastRow 

     surname = Cells(r, surnameCol).Value 
     tickets = Cells(r, ticketCol).Value 


     If (Not (Len(surname) = 0)) Then 

      Cells(r, targetCol).Value = surname 

      For x = 1 To tickets - 1 

       Cells(r + x, 1).EntireRow.Insert 
       Cells(r + x, targetCol).Value = surname 



      Next x 

      LastRow = LastRow + tickets - 1 

     End If 

     r = r + 1 
    Loop 

End Sub 

Répondre

7

Le compilateur intercepte différemment la construction de boucle «For» et utilise différents appels d'assemblage pour placer la variable temporaire dans le cache du processeur. Ainsi, après chaque itération, il n'a pas besoin de revenir en RAM pour lire la variable. peut simplement le récupérer dans le cache du processeur. C'est par conception pour augmenter la performance, c'est pourquoi les boucles 'For' sont généralement plus rapides que les boucles 'While'. La variable de limite pour la boucle 'for' vit toujours en mémoire, mais ne la lit pas à chaque itération. Ainsi, si vous modifiez la variable utilisée pour définir la limite supérieure, votre boucle continuera à courir jusqu'à la limite d'origine que vous avez définie. Tandis que les boucles vérifient sa clause de sortie à chaque itération, et ne cache pas est variable. Généralement, les boucles «For» doivent être utilisées lorsque vous avez un nombre défini d'itérations, par opposition à la boucle while, lorsque vous n'êtes pas sûr du nombre de boucles nécessaires et que vous avez besoin d'un contrôle plus dynamique.

1

C'est par la conception en VB. La limite d'une boucle for est calculée une seule fois et enregistrée dans une variable temporaire, juste avant le début de la boucle. Ainsi, si vous modifiez la valeur des variables à partir desquelles il calcule la limite, la variable temp n'est pas affectée. Tandis que ceci a l'effet inattendu que vous avez rencontré, il a le plus que la limite est seulement calculée une fois, donc toutes les méthodes utilisées dans ce calcul sont seulement entrées une fois, en faisant potentiellement la boucle plus rapidement.

+0

Comme je le soupçonnais, même si c'est encore étrange que ce n'est pas cohérent à travers différentes constructions de boucle – chrisbunney

3

Pour continuer à utiliser la boucle For...Next, vous pouvez faire:

For r = LastRow To 1 Step -1 
    surname = Cells(r, surnameCol).Value 
    tickets = Cells(r, ticketCol).Value 

    If (Not (Len(surname) = 0)) Then 
     Cells(r, targetCol).Value = surname 

     For x = 1 To tickets - 1 
      Cells(r + x, 1).EntireRow.Insert 
      Cells(r + x, targetCol).Value = surname 
     Next x 

     LastRow = LastRow + tickets - 1 
    End If 
Next r 

Chaque fois que vous souhaitez insérer ou supprimer des lignes sur une Worksheet à l'intérieur d'une boucle, il est généralement préférable de commencer à la fin et le travail en arrière. Cela signifie que vous n'avez pas besoin d'ajuster votre index de boucle dans la plupart des cas