Почему этот цикл for не обрабатывает полный набор данных? - PullRequest
4 голосов
/ 24 июня 2010

Фон

У меня есть таблица распределения билетов на событие. В каждой строке таблицы указаны имя и количество выделенных билетов.

Электронная таблица http://s3.amazonaws.com/twitpic/photos/full/120237739.png?AWSAccessKeyId=0ZRYP5X5F6FSMBCCSE82&Expires=1277404609&Signature=pGRx%2Fxcm3InEY2PyKd3k09hC7Xo%3D

Мне нужно изменить электронную таблицу, чтобы каждое имя дублировалось один раз для каждого тикета в отдельных строках, например:

Таблица после изменений http://s3.amazonaws.com/twitpic/photos/full/120238390.png?AWSAccessKeyId=0ZRYP5X5F6FSMBCCSE82&Expires=1277404546&Signature=xrUAdzyIJWKGnrge%2FCD4EudiyX8%3D

У меня есть макрос для этого, однако он демонстрирует странное поведение

Проблема

Макрос не перебирает весь набор данных. Пошаговое выполнение кода показывает, что, несмотря на намеренное увеличение значения LastRow, цикл For выполняет циклы только столько раз, сколько указано исходное значение. Новое значение LastRow в конце каждой итерации, похоже, не учитывается.

Это выглядит особенно странно, поскольку эквивалентный цикл Do While работает нормально (рабочий код, использующий цикл Do While, см. Ниже)

Вопрос

Почему происходит поведение, описанное в проблемном разделе (выше), и почему оно несовместимо с эквивалентными структурами?

Макрос для цикла Loop

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

Макрос цикла "пока во время"

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

Ответы [ 3 ]

8 голосов
/ 25 июня 2010

Компилятор по-разному интерпретирует конструкцию цикла «For» и использует разные вызовы сборки для помещения временной переменной в кэш ЦП, поэтому после каждой итерации не нужно возвращаться в ОЗУ для чтения переменной, он можетпросто возьмите его из кеша процессора.Это сделано для увеличения производительности, поэтому циклы «For», как правило, работают быстрее, чем «while».Переменная предела для цикла for все еще находится в памяти, но не читает ее во время каждой итерации.Поэтому, если вы измените переменную, используемую для первоначальной установки верхней границы, ваш цикл все равно будет работать с той исходной границей, на которую вы его установили.Циклы while проверяют свое предложение выхода на каждой итерации и не кэшируют переменную.Обычно циклы «For» следует использовать, когда у вас есть заданное количество итераций, в отличие от цикла while, когда вы не уверены, сколько раз вам потребуется цикл, и вам нужно больше динамического управления.

3 голосов
/ 12 июля 2010

Чтобы продолжать использовать цикл For...Next, вы можете сделать:

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

Каждый раз, когда вы хотите вставить или удалить строки в Worksheet из цикла, обычно лучше начинать с конца и работать в обратном направлении. Это означает, что вам не нужно настраивать индекс цикла в большинстве случаев

1 голос
/ 24 июня 2010

Это по замыслу в VB. Предел цикла for вычисляется только один раз и сохраняется во временной переменной непосредственно перед началом цикла. Таким образом, если вы измените значение переменных, из которых рассчитывается предел, переменная temp не будет затронута. Хотя это имеет неожиданный эффект, с которым вы столкнулись, плюс в том, что предел рассчитывается только один раз, поэтому любые методы, используемые в этом вычислении, вводятся только один раз, что потенциально может сделать цикл быстрее.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...