为啥这个 for 循环不处理完整的数据集?

Posted

技术标签:

【中文标题】为啥这个 for 循环不处理完整的数据集?【英文标题】:Why doesn't this for loop process the full data set?为什么这个 for 循环不处理完整的数据集? 【发布时间】:2011-03-07 23:10:38 【问题描述】:

背景

我有一个活动门票分配的电子表格。电子表格的每一行都有一个名称和分配的票数。

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

我需要更改电子表格,以便每个票的每个名称在不同的行上重复一次,如下所示:

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

我有一个宏来执行此操作,但它表现出奇怪的行为

问题

宏不会循环遍历整个数据集。单步执行代码表明,尽管故意增加了 LastRow 的值,但 For 循环仅循环指定原始值的多少倍。每次迭代结束时LastRow 的新值似乎被忽略了。

这似乎特别奇怪,因为等效的 Do While 循环可以正常工作(请参阅下面的使用 Do While 循环的工作代码)

问题

为什么会出现问题部分(上文)中描述的行为,为什么它与等效结构不一致?

For 循环宏

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

Do While 循环宏

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

【问题讨论】:

【参考方案1】:

这是 VB 设计的。 for 循环的限制只计算一次并保存在一个临时变量中,就在循环开始之前。因此,如果您更改用于计算限制的变量的值,则临时变量不受影响。虽然这有您遇到的意想不到的效果,但它的优点是限制只计算一次,因此在此计算中使用的任何方法都只输入一次,可能会更快地执行循环。

【讨论】:

正如我所怀疑的,尽管这在不同的循环结构中不一致仍然很奇怪【参考方案2】:

编译器以不同的方式解释“For”循环结构,并使用不同的汇编调用将临时变量放入 CPU 缓存中,因此每次迭代后无需返回 RAM 读取变量,它可以只需从 cpu 的缓存中获取它。这是为了提高性能而设计的,这就是为什么“For”循环通常比“While”循环快的原因。 'for' 循环的限制变量仍然存在于内存中,但它不会在每次迭代期间读取它。因此,如果您更改用于最初设置上限的变量,您的循环仍将运行到您设置的原始边界。 While 循环在每次迭代时检查其退出子句,并且不缓存变量。通常,当您有一定数量的迭代时应该使用“For”循环,而不是当您不确定需要循环多少次并且需要更多动态控制时使用 while 循环。

【讨论】:

【参考方案3】:

要继续使用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 上的行,通常最好从末尾开始并向后工作。这意味着在大多数情况下您不必调整循环索引

【讨论】:

以上是关于为啥这个 for 循环不处理完整的数据集?的主要内容,如果未能解决你的问题,请参考以下文章

如何进行 OLAP 多维数据集增量处理?

将完整的 ADO 记录集插入现有的 ACCESS 表而不使用循环

加快R中大数据的for循环处理时间

以追加到列表为结果的多处理 for 循环

OleDb:为啥我不能用我的数据源中的完整模式和信息轻松地填充我的数据集?

在 for 循环中构建 Spark sql 数据集