Kurs VBA – cz. 14 – Pętle

W poprzedniej części poznaliśmy metodę sterowania programem przy pomocy instrukcji warunkowych. Teraz do sterowania programem użyjemy konstrukcji zwanych pętlami.
Pozwalają one wykonywać ten sam fragment kodu kilka razy. Oczywiście ilością powtórzeń możemy sterować i może być inna przy każdym wykonaniu tego samego programu. Kod wewnątrz pętli może nawet wcale się nie wykonać.
Przerwaniem lub zakończeniem pętli nazywamy przejście od wykonywania kodu wewnątrz pętli do wykonywania instrukcji bezpośrednio po niej. Iteracja  jest jednorazowym wykonaniem pętli.


Do until / Do while

Ta pętla służy do wykonania fragmentu kodu pewną ilość razy. Najprostsza wersja składa się tylko z klamry Do…loop. Nie uruchamiaj tego kodu!

    Do
        Selection.Cells(1, 1) = "test"
        Selection.Offset(1, 0).Select
    Loop

 

Jest to przykład tak zwanej nieskończone pętli, która będzie omówiona później. Jej działanie nigdy nie będzie przerwane. Kolejne przykłady można już bezpiecznie testować.

Do pętli możemy dodać warunki while lub until, które mogą przerwać wykonywanie.

    Do While MsgBox("Kontynuować?", vbYesNo) = vbYes
        Selection.Cells(1, 1) = "test"
        Selection.Offset(1, 0).Select
    Loop
           
    Do
        Selection.Cells(1, 1) = "test"
        Selection.Offset(1, 0).Select
    Loop While MsgBox("Kontynuować?", vbYesNo) = vbYes

    Do Until MsgBox("Kontynuować?", vbYesNo) = vbNo
        Selection.Cells(1, 1) = "test"
        Selection.Offset(1, 0).Select
    Loop
           
    Do
        Selection.Cells(1, 1) = "test"
        Selection.Offset(1, 0).Select
    Loop Until MsgBox("Kontynuować?", vbYesNo) = vbNo

 

Kluczowe są tutaj słowa zaznaczone na zielono. While można przetłumaczyć jako „dopóki warunek jest prawdziwy” (podczas gdy) a Until jako „dopóki warunek nie jest prawdziwy” (dopóki). Widać to w powyższym przykładzie. Wszystkie cztery pętle zostaną przerwane, kiedy klikniemy „Nie”.

Słowa kluczowa until i while możemy umieścić na początku i końcu pętli. Kiedy umieścimy je na początku, pętla może nie wykonać się ani razu, czyli nie zrealizować nawet jednej iteracji. Umieszczając warunek na końcu mamy zagwarantowane, że przed jego sprawdzenie kod pętli raz się wykona.

For … Loop

Pętla for pozwala nam wykonać kod określoną ilość razy. W najprostszej postaci wygląda tak:

    Dim licznik As Integer
   
    For licznik = 1 To 10
        Selection.Cells(licznik, 1) = "test " & licznik
    Next licznik

 

Pętla ta wymaga dodatkowej zmiennej do przechowywania licznika pętli. Zmienna może mieć dowolną nazwę, musi być jednak typu liczbowego. Przed pierwszym wykonaniem przyjmuje on wartość zaznaczoną na niebiesko. Po każdym wykonaniu licznik zwiększany jest o jeden (instrukcja next). Pętla jest przerywana kiedy licznik jest większy od wartości czerwonej.

Pętlę można zmodyfikować o słowo kluczowe Next, które określa o ile ma zwiększać (lub zmniejszać) się licznik.

    For licznik = 1 To 10 Step 2
        Selection.Cells(licznik, 1) = "test " & licznik
    Next licznik

    For licznik = 1 To -10 Step -2
        Selection.Cells(licznik, 1) = "test " & licznik
    Next licznik

Dlaczego wywołanie drugiej pętli wywoła błąd?

For Each … Loop

Tą pętlę wykorzystuję najczęściej. Wykonuje ona zawarty w sobie kod dla każdego elementu określonej kolekcji. Tą kolekcją jest najczęściej zestaw komórek w zakresie, ale może też być kilka wykresów, arkuszy. Jeżeli kolekcja będzie pusta (np. kolekcja komentarzy w arkuszu) to pętla nie wykona się nawet raz.

Przed wykonaniem pętli musimy zadeklarować zmienną, która będzie przechowywać poszczególne elementy kolekcji. Obejrzyjmy kilka najczęstszych zastosowań tej pętli.

Na początek przeglądanie komórek w zakresie:

Teraz przeglądanie wierszy w zakresie:

    Dim lr_RoboczaKomorka As Range
    
    For Each lr_RoboczaKomorka In Selection
        lr_RoboczaKomorka = "test"
    Next lr_RoboczaKomorka

 

Różne metody odwołania do komórek znajdziemy w poprzedniej części kursu.

Inną kolekcję używamy do przeglądania arkuszy:

    Dim lr_RoboczyArkusz As Worksheet
    
    For Each lr_RoboczyArkusz In Worksheets
        lr_RoboczyArkusz.Tab.Color = vbGreen
    Next lr_RoboczyArkusz

Przy pomocy poniższej kolekcji usuniemy wszystkie komentarze.

    Dim lr_RoboczyKomentarz As Comment
    
    For Each lr_RoboczyKomentarz In ActiveSheet.Comments
        lr_RoboczyKomentarz.Delete
    Next lr_RoboczyKomentarz

 

Do prawidłowego wykorzystania tej pętli potrzebna jest znajomość modelu obiektowego Excela.

Exit Do / Exit  For

Przy pomocy powyższych instrukcji możliwe jest przerwanie działania pętli w dowolnym momencie bez sprawdzania odpowiednich warunków.

    Do
        Selection.Cells(1, 1) = "test"
        Selection.Offset(1, 0).Select
       
        If MsgBox("Kontynuować?", vbYesNo) = vbNo Then
            Exit Do
        End If
    Loop

Powyższa pętla może się zakończyć mimo, że sama nie ma warunków while ani until.

    For i = 1 To 10
        If i = 5 Then
            Exit For
        End If
    Next i

W powyższym przypadku licznik nie osiągnie wartości końcowej 10, ponieważ pętla zostanie wcześniej przerwana.

Nieskończone pętle

Naturalnie w komputerach nie ma nic nieskończonego w sensie matematycznym. Każdy program kiedyś przerwie działanie. Skończy się prąd, pamięć lub komputer się po prostu zepsuje. Nieskończoną pętlą nazywamy taką, która nigdy nie pozwoli wykonać się kodowi po niej umieszczonemu. Czyli nie ma warunku przerwania tej pętli lub nigdy się nie spełni. np.:

    Do
        MsgBox "To się nigdy nie skończy, brak warunku."
    Loop
    
    Do While True
        MsgBox "To się nigdy nie skończy, warunek zawsze prawdziwy."
    Loop
    
    Do
        MsgBox "To się nigdy nie skończy, warunek zawsze fałszywy."
    Loop Until False

    For licznik = 1 To 10 Step -1
        MsgBox "To się nigdy nie skończy, licznik nigdy nie będzie" & _              " większy niż 10."
    Next licznik

Powyższe przykłady są dość oczywiste i pewnie nikt specjalnie takiego kodu nie napiszę. Tworzenie nieskończonych pętli jest jednak częstym błędem. Należy zawsze sprawdzać (przed uruchomieniem programu, czyli „na sucho”), czy nasz warunek przerwania pętli ma szansę się spełnić. np.:

    Do While CzyPrzerwac
        MsgBox "Czy to się skończy?"
    Loop

CzyPrzerwać jest funkcją, należy sprawdzić, czy ma ona szansę zwrócić False.

    For i = 1 To 10
        i = NowaWartosc_i
    Next i

Licznik jest modyfikowany nie tylko przez instrukcję Next, ale i wewnątrz pętli przez funkcję. Może nigdy nie będzie większy niż 10, co jest warunkiem przerwania działania.

Pętla w pętli

Jeżeli czytałeś poprzednie części kursu, na pewno nie zaskoczy Cię, że jedną pętlę można umieścić w drugiej. Poniżej prosty przykład tworzący tabliczkę mnożenia.

    Dim i As Integer
    Dim j As Integer
    
    For i = 1 To 10
        For j = 1 To 10
            Cells(i, j) = i * j
        Next j
    Next i

Oczywiście można w ten sposób stworzyć kilka poziomów i mieszać różne rodzaje pętli.

<< cz. 13 – Instrukcje warunkowe | Spis treści | cz. 15 – Funkcje wbudowane >>Subskrybuj RSS, lub polub blog na Facebooku aby otrzymywać najnowsze informacje o rozwoju kursu.

Zapraszam do zadawania pytań w komentarzach.

Comments 8

  1. Dzień dobry, mam problem. Staram się napisać makro, które będzie działało w następujący sposób:

    Makro ma liczyć średnią liczb podanych przez użytkownika (najchętniej poprzez InputBox,dowolna ilość liczb). Podczas dodania ilości liczb poniżej 5 ma normalnie dodawać i pokazywać komunikat z wynikiem. Gdy dodawana ma być 5 liczba to powinien pokazać się komunikat w stylu „czy na pewno chcesz dalej dodawać?” Tak- pobieranie kolejnej/kolejnych liczb/y od użytkownika i dodawane do sumy poprzednich, Nie- pokazuje komunikat ze średnią podanych liczb przed komunikatem.

    Głównie używałam pętli For (raz jednej,raz dwóch) i instrukcji warunkowych, trochę prób z pętlą Do…Loop i zawsze jednak działa w którymś momencie źle. Raz bliżej, raz dalej zamierzonego celu, ale nigdy dobrze :).

    Z góry dziękuję za pomoc!

    1. Post
      Author
      1. W sumie wersji było dużo, wiele niezapisanych. Ostatnia zapisana to ta:
        (działa mniej więcej tak jak chce, ale źle liczy wynik, a nie mam pomysłu przez co podzielic sumę w ostatnim MsgBox, bo przez licznik nie mogę (chyba))

        Sub srednia()
        Dim licznik As Byte
        Dim x As Single
        Dim suma As Single
        Dim n As Byte
        Dim n1 As Byte
        suma = 0
        n = InputBox(„Ile wprowadzisz liczb całkotwitych?”, „Średnia liczb całkowitych”, „n to…”)
        For licznik = 1 To n
        If licznik 5 Then
        x = InputBox(„Podaj x” & licznik, „Średnia liczb naturalnych”, „liczba…”)
        suma = x + suma
        Else
        If MsgBox(„Czy chcesz podawać dalej liczby?”, vbYesNo, „Średnia”) = vbNo Then
        MsgBox „Średnia arytmetyczna = ” & suma / n
        Else
        n1 = InputBox(„Ile podasz nastepnych?”)
        x = InputBox(„Podaj x” & licznik, „Średnia liczb naturalnych”, „liczba…”)
        suma = x + suma
        End If
        End If
        Next
        MsgBox „Średnia arytmetyczna = ” & suma / (n1+n)
        End Sub

        1. Post
          Author

          Ciesze się, że spróbowała Pani sama rozwiązać zadania. Rzeczywiście może być zawiłe pod względem algorytmu. Ja bym je rozwiązał tak:

          Option Explicit
          
          Sub srednia()
          Dim suma As Integer
          Dim licznik As Integer
          Dim srednia As Double
          Dim pokazWynik As Integer
          Dim i, j As Integer
          
          Do
              i = InputBox("Ile liczb?")
              If i < 5 Then
                  licznik = licznik + i
                  For j = 1 To i
                      suma = suma + InputBox("Podaj liczbę numer " & j)
                  Next j
              ElseIf i = 5 Then
                  pokazWynik = MsgBox("Czy pokazac wynik?", vbYesNo)
              End If
          Loop Until pokazWynik = vbYes
          
          If licznik <> 0 Then
              srednia = suma / licznik
              
              MsgBox "Srednia : " & srednia
          End If
          
          End Sub
  2. Witam!

    W części o nieskończonych pętlach zabrakło – moim zdaniem – bardzo cennej rady. Jeśli już uruchomi się taką pętlę, działanie makra można przerwać kombinacją klawiszy Ctrl+Break.

    Pozdrawiam 🙂

  3. Witam,

    Generuję dokładnie te same raporty codziennie. W tabeli przestawnej mam tak skonstruowany filtr jak poniżej. Problem polega na tym, że te nazwy nie występują w każdym tygodniu i w tym momencie Makro pokazuję błąd. Potrzebował bym zmienić ten kod aby w pierwszej kolejności, kod był sprawdzany czy istnieje czy nie – w przypadku gdy nie to ignoruje, a w przypadku gdy istnieje to go odznacza w filtrze. Wydaje mi się, że mogę to zrobić przy pomocy If.. Else, ale do końca nie wiem jak. Proszę o pomoc.

    ActiveSheet.PivotTables(„Tabela_przestawna_QR4_1”).PivotFields( _
    „dept_short_desc”).CurrentPage = „(All)”
    With ActiveSheet.PivotTables(„Tabela_przestawna_QR4_1”).PivotFields( _
    „dept_short_desc”)
    .PivotItems(„Dyehouse”).Visible = False
    .PivotItems(„Knitting-Alfreton”).Visible = False
    .PivotItems(„Stenters”).Visible = False
    .PivotItems(„Weaving”).Visible = False
    End With
    ActiveSheet.PivotTables(„Tabela_przestawna_QR4_1”).PivotFields( _
    „dept_short_desc”).EnableMultiplePageItems = True

    1. Post
      Author

Dodaj komentarz

Twój adres e-mail nie zostanie opublikowany.