Как сократить цикл блоков цикла For в один

У меня есть серия блоков кода, содержащих For Loops здесь, и я хотел бы уменьшить этот код, чтобы он мог работать одинаково, но до тех пор, пока один из этих блоков кода, а не 12 блоков в длину. Как вы можете видеть, каждый блок представляет собой группу из 6, задача, которую я имею здесь, заключается в сокращении кода, сохраняя мои переменные в группах по 6. В этой программе значения генерируются в двух столбцах и идут в последовательном порядке. Например:

Когда m равно от 1 до 6, значение p должно быть равным 1 для всех шести значений

. Когда m равно от 7 до 12, значение p должно быть равным 2 для все шесть значений

Когда m равно от 13 до 18, значение p должно быть равно 3 для всех шести значений

и т. д. ....

For m = 1 To 6 'Riser
        For p = 1 To 1 'Car
            If Not IsEmpty(ws.Range("Riser" & m)) And Not IsEmpty(ws.Range("Car_" & p)) Then
                ws.Range("C1").Offset(m).Value = p
                Exit For
            End If
        Next p
    Next m

For m = 7 To 12 'Riser
    For p = 2 To 2 'Car
        If Not IsEmpty(ws.Range("Riser" & m)) And Not IsEmpty(ws.Range("Car_" & p)) Then
            ws.Range("C1").Offset(m).Value = p
            Exit For
        End If
    Next p
Next m

For m = 13 To 18 'Riser
    For p = 3 To 3 'Car
        If Not IsEmpty(ws.Range("Riser" & m)) And Not IsEmpty(ws.Range("Car_" & p)) Then
            ws.Range("C1").Offset(m).Value = p
            Exit For
        End If
    Next p
Next m

For m = 19 To 24 'Riser
    For p = 4 To 4 'Car
        If Not IsEmpty(ws.Range("Riser" & m)) And Not IsEmpty(ws.Range("Car_" & p)) Then
            ws.Range("C1").Offset(m).Value = p
            Exit For
        End If
    Next p
Next m

For m = 25 To 30 'Riser
    For p = 5 To 5 'Car
        If Not IsEmpty(ws.Range("Riser" & m)) And Not IsEmpty(ws.Range("Car_" & p)) Then
            ws.Range("C1").Offset(m).Value = p
            Exit For
        End If
    Next p
Next m

For m = 31 To 36 'Riser
    For p = 6 To 6 'Car
        If Not IsEmpty(ws.Range("Riser" & m)) And Not IsEmpty(ws.Range("Car_" & p)) Then
            ws.Range("C1").Offset(m).Value = p
            Exit For
        End If
    Next p
Next m

For m = 37 To 42 'Riser
    For p = 7 To 7 'Car
        If Not IsEmpty(ws.Range("Riser" & m)) And Not IsEmpty(ws.Range("Car_" & p)) Then
            ws.Range("C1").Offset(m).Value = p
            Exit For
        End If
    Next p
Next m

For m = 43 To 48 'Riser
    For p = 8 To 8 'Car
        If Not IsEmpty(ws.Range("Riser" & m)) And Not IsEmpty(ws.Range("Car_" & p)) Then
            ws.Range("C1").Offset(m).Value = p
            Exit For
        End If
    Next p
Next m

For m = 49 To 54 'Riser
    For p = 9 To 9 'Car
        If Not IsEmpty(ws.Range("Riser" & m)) And Not IsEmpty(ws.Range("Car_" & p)) Then
            ws.Range("C1").Offset(m).Value = p
            Exit For
        End If
    Next p
Next m

For m = 55 To 60 'Riser
    For p = 10 To 10 'Car
        If Not IsEmpty(ws.Range("Riser" & m)) And Not IsEmpty(ws.Range("Car_" & p)) Then
            ws.Range("C1").Offset(m).Value = p
            Exit For
        End If
    Next p
Next m

For m = 61 To 66 'Riser
    For p = 11 To 11 'Car
        If Not IsEmpty(ws.Range("Riser" & m)) And Not IsEmpty(ws.Range("Car_" & p)) Then
            ws.Range("C1").Offset(m).Value = p
            Exit For
        End If
    Next p
Next m

For m = 67 To 72 'Riser
    For p = 12 To 12 'Car
        If Not IsEmpty(ws.Range("Riser" & m)) And Not IsEmpty(ws.Range("Car_" & p)) Then
            ws.Range("C1").Offset(m).Value = p
            Exit For
        End If
    Next p
Next m

Есть ли способ увеличить эти значения m и p, чтобы они поднялись до 78, сохраняя эти группы по шесть для каждого блока?

1
задан 13 August 2018 в 15:42

3 ответа

, следуя логике кода OP, я бы сделал следующее:

For p = 1 To 12 ' loop through cars
    If Not IsEmpty(ws.Range("Car_" & p)) Then ' proceed only if current car isn't empty
        For m = (p - 1) * 6 + 1 To p * 6 'Riser ' loop through current car corresponding risers range
            If Not IsEmpty(ws.Range("Riser" & m)) Then ' if current riser isn't empty
                ws.Range("C1").Offset(m).Value = p ' mark with current car
                Exit For ' exit loop and process next car
            End If
        Next
    End If
Next
3
ответ дан 15 August 2018 в 16:59

Ваши внутренние петли for не нужны. Вы можете просто заменить for p = 1 to 1 на p = 1 и удалить соответствующий Next p.

Тем не менее, я думаю, что следующая структура может немного уменьшить дублирование кода:

For m = 1 To 72 'Riser
    Select Case m
    Case 1 to 6
        p = 1
    Case 7 to 12
        p = 2
    ' and so on...
    End Select

    If Not IsEmpty(ws.Range("Riser" & m)) And Not IsEmpty(ws.Range("Car_" & p)) Then
        ws.Range("C1").Offset(m).Value = p
        'Exit For ' You might need to replace this line with something adequate if necessary
    End If
Next m

Теперь инструкция Select Case позаботится о том, чтобы присвоить значения p, и вы можете выполнять всю работу в одном цикле. Это решение было бы предпочтительнее, если бы это правило увеличивало p за каждые шесть м, не было установлено в камне. (Легче изменить назначения таким образом.)

Теперь, если вы скажете, что увеличение p за каждые шесть м правило установлено в камне, тогда я бы рекомендовал вам использовать ответ @ Marcucciboy2 вместо этого.

Обычно я предлагаю переместить логику, чтобы присвоить значение p в зависимости от m в свою собственную функцию.

Public Sub YourSubStartsHere()
' [...]
For m = 1 To 72 'Riser
    p = GetPFromM(m)
    If Not IsEmpty(ws.Range("Riser" & m)) And Not IsEmpty(ws.Range("Car_" & p)) Then
        ws.Range("C1").Offset(m).Value = p
        'Exit For ' You might need to replace this line with something adequate if necessary
    End If
Next m
End Sub

Private Function GetPFromM(ByVal m as Long) as Long
    ' Your preferred logic to get the new p here
    ' be it Select Case
    Select Case m
    Case 1 to 6
        GetPFromM = 1
    End Select
    ' or rounding up
    GetPFromM = Application.WorksheetFunction.RoundUp(m / 6, 0)
End Function

Таким образом, было бы довольно легко быстро подключить новое правило для p, если это необходимо.

2
ответ дан 15 August 2018 в 16:59
  • 1
    Я думаю, что этот подход также немного легче читать, особенно если кто-то новичок в VBA. – Zack 13 August 2018 в 16:46
  • 2
    Проверка Not IsEmpty(ws.Range("Car_" & p)) повторяется без использования: лучше переместить ее в качестве внешней проверки перед прохождением через соответствующий цикл risers . – DisplayName 13 August 2018 в 18:15
  • 3
    @DisplayName Согласен. Но избавление от него внутри цикла требует некоторой дополнительной логики, которую я не хотел вводить в этот момент. Я думаю, что он хорошо помещен в свой ответ (а именно ваш). :) – Inarion 14 August 2018 в 18:18

Может быть более умный способ сделать это, но я бы использовал функцию modulo. По модулю возвращает остаток, когда вы делите два числа, поэтому, если вы разделите m на 6, остаток будет равен 0, если m кратно 6. Если это так, вы просто увеличиваете добавленную переменную everySix

Dim everySix As Long
everySix = 1

Dim wasFound As Boolean

For m = 1 To 78
    If Not IsEmpty(ws.Range("Car_" & everySix)) Then

        If Not IsEmpty(ws.Range("Riser" & m)) And Not wasFound Then
            ws.Range("C1").Offset(m).Value2 = everySix
            wasFound = True
        End If

        If m Mod 6 = 0 Then
            everySix = everySix + 1
            wasFound = False
        End If

    End If
Next m
4
ответ дан 15 August 2018 в 16:59
  • 1
    Хорошее решение. Я думал о Mod, но хотел сначала получить альтернативное решение Select Case. (Я бы предпочел по модулю, если он произведет правильные результаты - кажется, гораздо более плавный.) :) – Inarion 13 August 2018 в 16:02
  • 2
    это не будет следовать логике кода OP, которая, поскольку каждый не пустой автомобиль выйдет, как только встретит соответствующие стояки сначала не пустое значение. Кроме того, проверка Not IsEmpty(ws.Range("Car_" & everySix)) повторяется без использования: вам лучше перенести ее в качестве внешней проверки перед прохождением через соответствующий цикл стояков. – DisplayName 13 August 2018 в 18:16
  • 3
    @DisplayName хорошие уловы, спасибо – Marcucciboy2 13 August 2018 в 18:32

Другие вопросы по тегам:

Похожие вопросы: