Głównym tematem tej lekcji będzie drugi z trzech dostępnych w VBA rodzajów pętli - pętla Do ... Loop.
Pętla ta umożliwia wykonywanie jakichś operacji bez uprzedniego określania ilości powtórzeń, jak to miało miejsce w omówionej w
lekcji dziesiątej pętli For ... Next.
Oprócz tego dowiemy się jak wymusić wcześniejsze wyjście z procedury i funkcji lub wcześniejsze zakończenie całego makra.
Na zakończenie tej lekcji poznamy też instrukcję Go To
pozwalającą na przenoszenie wykonywania makra w dowolne
miejsce kodu.
Podstawowe informacje o pętlach Do ... Loop
W lekcji dziesiątej
poznałeś pętlę For ... Next, która wykonywała przypisany do niej blok operacji określoną
liczbę razy, uzależnioną od początkowej wartości iteratora oraz górnej granicy pętli.
Często zdarza się jednak, że pętla powinna wykonywać jakieś operacje tak długo, aż zmienne osiągną określoną wartość, i
w momencie wywołania tej pętli zupełnie nie wiadomo ilu powtórzeń tej pętli będzie to wymagało.
W lekcji poświęconej pętli For ... Next omówiony został
sposób opuszczenia pętli w momencie osiągnięcia określonego
stanu przez którąś ze zmiennych (Exit For). Zauważ jednak, że przedstawiona w tamtym przykładzie
pętla analizowała po kolei każdy pojedynczy znak jakiegoś tekstu, nie było więc problemu z określeniem górnej granicy
pętli (iterator mógł osiągnąć maksymalnie taką wartość, ile znaków liczył rozpatrywany tekst). Zastosowanie w tej pętli
polecenia Exit For miało służyć jedynie temu, aby cały kod działał szybciej i nie wykonywał kolejnych
powtórzeń pętli w momencie, gdy znany był już wynik działania całej funkcji. W związku z tym, nawet po usunięciu z
tego przykładu polecenia Exit For funkcja działałaby prawidłowo, tyle że niezbyt efektywnie.
Zdarzają się jednak sytuacje, że w momencie rozpoczęcia działania pętli, określenie górnej granicy pętli, choćby w
przybliżeniu, jest zupełnie niemożliwe. Wyobraź sobie przykładowo makro, która ma za zadanie wypisywać w komórkach
arkusza kolejne potęgi jakiejś liczby (np. 1.01), tak długo, aż wartość potęgi osiągnie określony limit
(np. 1 000 000). W takim przypadku nie sposób nawet w przybliżeniu określić ile potwórzeń tej pętli będzie
potrzebnych, aby osiągnąć zamierzony cel.
Jedynym sposobem na uporanie się z tym problemem za pomocą zwykłej pętli For ... Next jest
wykorzystanie nieskończonej pętli. Sposób ten został przedstawiony w poniższej ramce
z kodem:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
Sub wypisujPotegi(podstawa As Double, limit As Double)
Dim potega As Double
Dim n As Long
Dim i As Long
potega = 1
For i = 1 To 1 Step 0
potega = potega * podstawa
If potega > limit Then
Exit For
Else
n = n + 1
Cells(n, 1) = potega
End If
Next i
End Sub
Powyższa procedura posiada dwa argumenty wejściowe:
-
podstawa
- typu Double; określa liczbę, która będzie
podnoszona do potęgi,
-
limit
- również typu Double; określa przy jakiej wartości
potęgi makro powinno zakończyć wypisywanie potęg w arkuszu.
Ponadto w procedurze zadeklarowane zostały trzy dodatkowe zmienne:
-
potega
- typu Double; przechowuje wartości
kolejnych potęg; przy każdym powtórzeniu pętli wartość tej zmiennej jest mnożona przez wartość argumentu
podstawa
, tworząc tym samym jej kolejną potęgę,
-
n
- typu Long; ta zmienna określa z kolei numer wiersza,
w którym ma zostać wyświetlona aktualna wartość zmiennej potega
; przy każdym powtórzeniu pętli
wartość tej zmiennej jest zwiększana o 1, dzięki czemu każda kolejna potęga znajdzie się w jednym wierszu
poniżej poprzedniej,
-
i
- typu Byte; zmienna ta będzie pełniła rolę iteratora w
pętli; ponieważ znajdująca się w powyższym przykładzie pętla jest pętlą nieskończoną, wartość zmiennej
iteracyjnej cały czas będzie wynosiła 1, dlatego bez obaw można ją zadeklarować jako najmniejszy typ -
Byte.
W wierszu 6 zmiennej potega
zostaje nadana wartość 1. Do tego momentu wartość
tej zmiennej wynosiła 0 i gdyby nie przypisano do niej wartości 1, mnożenie jej przez argument podstawa
,
które jest wykonywane przy każdym powtórzeniu pętli, nie dawałoby absolutnie żadnego rezultatu i wartość zmiennej
potega
cały czas wynosiłaby 0, zamiast przyjmować wartości poszczególnych potęg.
W ósmym wierszu opisywanej procedury rozpoczyna się wspomniana już wcześniej nieskończona pętla. Na końcu wiersza
otwarcia tej pętli znajduje się polecenie Step 0
. Dla przypomnienia, oznacza ono, że przy każdym
powtórzeniu pętli wartość iteratora będzie się zwiększać o 0, czyli pozostanie na tym samym poziomie, w związku
z czym nigdy nie przekroczy określonej w wierszu otwarcia górnej granicy pętli, co czyni tę pętlę nieskończoną.
Wewnątrz omawianej pętli znajduje się operacja pomnożenia zmiennej potega
przez liczbę podnoszoną do
potęgi (która jest przechowywana w zmiennej podstawa
), w efekcie czego zmienna potega
przyjmuje wartość kolejnej potęgi.
Drugim elementem wnętrza pętli jest instrukcja warunkowa If ... Then
,
która przy każdym powtórzeniu pętli sprawdza czy wartość zmiennej potega
nie przekroczyła określonego
wcześniej limitu.
Jeżeli warunek ten (potega > limit
) jest spełniony, a więc wartość aktualnej potęgi przekroczyła limit,
wywoływane jest polecenie Exit For (wiersz 11), wymuszające
natychmiastowe opuszczenie pętli.
Jeśli natomiast warunek zawarty w tej instrukcji warunkowej nie jest spełniony (czyli zmienna potega
nie
przekroczyła jeszcze limitu), najpierw zwiększana jest wartość zmiennej n
, tak aby reprezentowała ona
kolejny wiersz arkusza, a następnie w tym wierszu i pierwszej kolumnie drukowana jest aktualna wartość zmiennej
potega
.
Wynik działania opisanej powyżej procedury jest zgodny z wcześniejszymi założeniami - w arkuszu wyprintowane zostały
wartości poszczególnych potęg zadanej liczby aż do momentu przekroczenia przez nie określonego limitu. Wykorzystanie
pętli nieskończonej pozwoliło w sprytny sposób uniknąć konieczności określania górnej granicy pętli, co byłoby w tym
przypadku niewykonalne.
Mimo że przedstawione rozwiązanie działa prawidłowo, przy tworzeniu aplikacji powinno się unikać tego typu rozwiązań.
W składni języka VBA znajduje się konstrukcja przeznaczona specjalnie do tego typu zadań -
pętla Do ... Loop, która jest o wiele bardziej przejrzysta, zrozumiała i zajmuje
znacznie mniej miejsca.
Pętla Do ... Loop jest kombinacją pętli oraz instrukcji warunkowej. W wierszu otwarcia
lub zamknięcia pętli należy określić warunek, której spełnienie lub niespełnienie (w zależności od użytego słowa
kluczowego) spowoduje opuszczenie pętli.
Pętla Do ... Loop może występować w czterech odmianach:
Do ... Loop Until [warunek]
- warunek jest określony w wierszu zamknięcia pętli, a pętla kończy działanie w razie jego niespełnienia,
Do ... Loop While [warunek]
- warunek jest określony w wierszu zamknięcia pętli, a pętla kończy działanie w razie jego spełnienia,
Do Until [warunek] ... Loop
- warunek jest określony w wierszu otwarcia pętli, a pętla kończy działanie w razie jego niespełnienia,
Do While [warunek] ... Loop
- warunek jest określony w wierszu otwarcia pętli, a pętla kończy działanie w razie jego spełnienia.
Jak widzisz, pętle ze słowem kluczowym Until różnią się od pętli zawierających słowo While
tylko tym, że w tych pierwszych opuszczenie pętli następuje w przypadku niespełnienia warunku, natomiast w drugich w
razie jego spełnienia. Z uwagi na to bardzo łatwo jest zamienić jedną pętlę w drugą - wystarczy zmienić słowo kluczowe,
a do warunku dodać operator logiczny Not. Ogólna zasada jest taka, aby wybierać zawsze typ
pętli z takim słowem kluczowym (Until lub While),
który w warunku nie będzie wymagał operatora Not.
Poniżej przedstawiono omawiane wcześniej makro, wypisujące w arkuszu potęgę zadanej liczby aż do osiągnięcia określonego
limitu, w którym zamiast nieskończonej pętli For ... Next wykorzystano już pętlę
Do ... Loop.
1
2
3
4
5
6
7
8
9
10
11
12
Sub wypisujPotegi(podstawa As Double, limit
As Double)
Dim potega As Double
Dim n As Long
potega = 1
Do Until potega > limit
n = n + 1
potega = potega * podstawa
Cells(n, 1) = potega
Loop
End Sub
Już na pierwszy rzut oka widać, że w obecnej postaci makro jest o wiele krótsze i bardziej przejrzyste.
Pętle Do ... Loop, w przeciwieństwie do pętli For ... Next
nie korzystają ze zmiennych iteracyjnych, dlatego też z sekcji deklaracji zmiennych usunięta została zmienna
i
, która w poprzedniej wersji makra pełniła właśnie rolę iteratora w nieskończonej pętli. Pozostałe
zadeklarowane zmienne oraz argumenty wejściowe są identyczne jak w poprzedniej wersji makra.
W wierszach 7-11 powyższego kodu znajduje się pętla Do ... Loop.
Do sprawdzania jak długo powinna działać ta pętla użyty został warunek potega > limit
. Słowem kluczowym
poprzedzającym ten warunek jest Until, dlatego też pętla będzie wykonywana tak długo, jak
warunek ten będzie fałszywy (w momencie, kiedy warunek zostanie spełniony, czyli wartość zmiennej potega
przekroczy limit, pętla zostanie opuszczona i wykonywanie kodu zostanie przeniesione tuż za wiersz jej zamknięcia, czyli
do wiersza 12).
Zauważ, że warunek został umieszczony już w wierszu otwarcia pętli, dlatego też może zdarzyć się sytuacja, w której
pętla nie zostanie ani razu wykonana. Jeżeli przykładowo przy wywoływaniu tej procedury jako argument limit
zostanie podana liczba 0, to już przy otwarciu pętli warunek jej opuszczenia zostanie spełniony (ponieważ stwierdzenie
1 > 0
jest prawdziwe) i kompilator przeniesie wykonywanie kodu do wiersza 12,
czyli za wiersz zamknięcia tej pętli.
Na tym polega właśnie jedyna różnica pomiędzy pętlami zawierającymi warunek w wierszu otwarcia, a pętlami z warunkiem
w wierszu zamknięcia. W tych drugich, w momencie wejścia do pętli warunek nie jest jeszcze sprawdzany, więc wszystkie
operacje znajdujące się w jej wnętrzu są zawsze wykonywane przynajmniej raz. Dopiero po wykonaniu poleceń z wnętrza
pętli i dotarciu kodu do wiersza zamknięcia sprawdzany jest warunek i ewentualnie dochodzi do opuszczenia pętli.
Gdyby więc w powyższym przykładzie warunek opisujący moment wyjścia z pętli został umieszczony w wierszu zamknięcia:
1
2
3
4
5
6
7
8
9
10
11
12
Sub wypisujPotegi(podstawa As Double, limit As Double)
Dim potega As Double
Dim n As Long
potega = 1
Do Until
n = n + 1
potega = potega * podstawa
Cells(n, 1) = potega
Loop Until potega > limit
End Sub
to makro wyświetliłoby w arkuszu pierwszą potęgę zadanej liczby, mimo tego, że przekracza ona określony limit.
Schemat działania znajdującej się w powyższym przykładzie pętli Do ... Loop jest niemal
identyczny jak w przykładzie wykorzystującym nieskończoną pętlę For ... Next.
Przy każdym powtórzeniu pętli najpierw zwiększana jest wartość zmiennej n
, dzięki czemu każda kolejna wartość potęgi wyświetlana jest w następnym wierszu. Następnie wartość zmiennej potega
mnożona jest przez wartość argumentu podstawa
, który przechowuje liczbą podnoszoną do potęgi, dając w efekcie kolejną potęgę tej liczby. Wreszcie w ostatniej operacji umieszczonej we wnętrzu pętli aktualna wartość potęgi wypisywana jest w odpowiednim wierszu aktywnego arkusza.
W zdecydowanej większości przypadków tak naprawdę jest zupełnie bez znaczenia czy warunek opisany jest w wierszu
otwarcia czy zamknięcia pętli (aczkolwiek w poprzednim przykładzie sprawiało to subtelną różnicę). Zdarzają się
jednak sytuacje, że umiejscowienie warunku w istotny sposób pływa na wykonywanie pętli. Za przykład może posłużyć
przedstawione poniżej makro, którego schemat działania jest następujący: po jego uruchomieniu na ekranie pojawia
się okno InputBox
, w którym użytkownik wpisuje jakieś wartości (np. imiona i nazwiska). Po
każdorazowym wpisaniu wartości, jest ona dopisywana w kolejnym wierszu arkusza, a na ekranie pojawia się nowe,
puste okno InputBox
. Proces ten jest powtarzany tak długo, aż użytkownik zamknie okno, kliknie
przycisk Cancel lub zatwierdzi pusty wpis (czyli wciśnie przycisk OK nie wpisawszy wcześniej
żadnej wartości).
1
2
3
4
5
6
7
8
9
10
Sub podajWartosci()
Dim value As String 'wartość wpisywana w InputBoxie
Dim n As Long 'numer wiersza
Do
value = InputBox("Wpisz wartość")
n = n + 1
Cells(n, 1) = value
Loop While Len(value)
End Sub
W powyższym makrze zadeklarowane są dwie zmienne:
-
value
- do tej zmiennej przypisywane będą wyniki funkcji InputBox
, a więc
de facto wartości wprowadzane przez użytkownika w polu tekstowym okna InputBox
,
-
n
- ta zmienna reprezentuje aktualy wiersz w arkuszu; przy każdym powtórzeniu pętli zmienna
ta będzie zwiększana o 1, dzięki czemu każda kolejna wartość znajdzie się w jednym wierszu poniżej poprzedniej.
Główną część kodu stanowi pętla Do, znajdująca się w wierszach 5-9.
Warunkiem określonym dla tej pętli jest funkcja Len(value)
, poprzedzona słowem kluczowym
While. Oznacza to, że pętla jest wykonywana tak długo, jak wartość tej funkcji jest
równoznaczna wartości logicznej True. Dla przypomnienia - funkcja Len(value)
zwraca wartość liczbową, która odpowiada długości tekstu value
, a wartość liczbowa jest równoważna wartości
True, jeżeli jest różna od zera. W związku z tym całe wyrażenie jest prawdziwe, jeżeli
wartość funkcji Len(value)
jest różna od zera, a więc jeśli tekst przechowywany w argumencie
value
nie jest pustym ciągiem znaków.
W trakcie działania pętli, do zmiennej value
przypisywane są poszczególne wyniki funkcji InputBox
(wiersz 6). W dwóch kolejnych wierszach wartość zmiennej n
jest zwiększana o 1, tak
aby odpowiadała kolejnemu wierszowi, a następnie wartość zmiennej value
(czyli to, co użytkownik wpisał
chwilę wcześniej w oknie InputBox
) jest printowana w arkuszu.
Jak dowiedziałeś się w lekcji ósmej, jeżeli okno funkcji InputBox
zostanie zamknięte krzyżykiem w
prawym górnym rogu lub wciśnięty zostanie przycisk Cancel, to niezależnie od tego, co jest wpisane w polu
tekstowym tego okna, funkcja zwróci pusty ciąg znaków. Pusty ciąg zostanie zwrócony również w sytuacji, gdy użytkownik
pozostawi puste pole tekstowe i wciśnie przycisk OK.
W powyższych trzech sytuacjach (zamknięcie okna krzyżykiem, wciśnięcie Cancel i pozostawienie pustego pola
tekstowego) do zmiennej value
przypisany zostanie więc pusty ciąg znaków, w związku z czym warunek
Len(value)
będzie równoważny wartości logicznej False i pętla zakończy
swoje działanie.
Zwróć uwagę, że do zmiennej value
jakakolwiek wartość jest po raz pierwszy przypisywana podczas pierwszego
powtórzenia pętli, w wierszu 6. Do tego czasu zmienna ta przechowuje pusty ciąg znaków.
W związku z tym, gdyby warunek tej pętli umieszczony był w wierszu jej otwarcia, to już przy pierwszym uruchomieniu
pętli kompilator stwierdziłby, że pętla powinna zostać zakończona i przeszedłby od razu do instrukcji umieszczonych
za tą pętlą, ani razu nie wykonując jej wnętrza. W tym konkretnym przypadku położenie warunku opisującego pętlę
Do jest więc bardzo ważne i może zasadniczo wpłynąć na wynik działania całej procedury.
Korzystając z pętli Do ... Loop można stworzyć pętlę nieskończoną jeszcze łatwiej,
niż za pomocą pętli For ... Next.
Wystarczy stworzyć pętlę Do ... Loop, która ani w wierszu otwarcia, ani w wierszu
zamknięcia nie zawiera warunku opuszczenia pętli:
Wcześniejsze opuszczanie pętli Do ... Loop
Podobnie jak to miało miejsce w przypadku pętli For ... Next, język VBA umożliwia opuszczenie
pętli Do ... Loop wcześniej, niż wynikałoby to z przypisanych do tej pętli warunków.
Poniżej znajduje się przykładowa procedura korzystająca z polecenia Exit Do, które pozwala wymusić
natychmiastowe opuszczenie pętli Do ... Loop.
Użytkownik musi w niej wprowadzić hasło, zezwalające na uruchomienie dalszych czynności.
W razie trzykrotnego podania nieprawidłowego hasła, procedura wykonuje określone operacje zabezpieczające (np. tymczasowe
zablokowanie dostępu do makra z danego konta lub powiadomienie administratora o próbie uruchomienia makra przez niepowołaną
osobę). Rozwiązania takie często spotykane są przy logowaniu do internetowych kont bankowych - każdemu możę się zdarzyć
jedna lub nawet dwie pomyłki podczas wprowadzania hasła, ale jeśli taka sytuacja powtarza się trzy razy z rzędu, istnieje
duże prawdopodobieństwo, że ktoś próbuje złamać hasło i należy w takiej sytuacji podjąć jakieś kroki zaradcze.
Jeżeli natomiast użytkownik pomyślnie przejdzie weryfikację hasła, wykonywane są dalsze czynności przewidziane dla tego
makra (w przykładzie zostały one zastąpione komentarzem, ponieważ nie mają żadnego znaczenia dla omawianego zagadnienia).
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
Sub weryfikacjaHasla()
Dim haslo As String
Dim podaneHaslo As String
Dim czyPrawidlowe As Boolean
Dim i As Byte
haslo = "qwerty"
Do While i < 3
podaneHaslo = InputBox("Podaj hasło", "Hasło")
i = i + 1
If podaneHaslo = haslo Then
czyPrawidlowe = True
Exit Do
Else
Call MsgBox("Podane hasło jest nieprawidłowe." & vbCrLf & "Pozostało prób: " & 3 - i)
End If
Loop
If czyPrawidlowe Then
Else
End If
End Sub
W procedurze zadeklarowane są cztery zmienne:
-
haslo
- zmienna typu tekstowego, do której przypisane jest prawidłowe hasło,
-
podaneHaslo
- zmienna typu tekstowego; w dalszej części makra przypisywane jest do niej
hasło podawane przez użytkownika w oknie InputBox
,
-
czyPrawidlowe
- zmienna typu Boolean; przyjmuje wartość True,
jeżeli użytkownik podał prawidłowe hasło, oraz wartość False, jeżeli podane przez niego
hasło jest błędne,
-
i
- zmienna typu Byte; liczy ile razy próbowano już wpisać hasło; maksymalna liczba prób wynosi 3,
dlatego bez problemu można nadać tej zmiennej typ Byte.
W wierszu 7 do zmiennej haslo
zostaje przypisane prawidłowe hasło czyli tekst
qwerty.
W wierszach 9-20 znajduje się pętla Do ... Loop, której warunkiem
ograniczającym jest i < 3
ze słowem kluczowym While, co oznacza, że pętla
zakończy swoje działanie, jeżeli wartość zmiennej i
będzie wynosiła co najmniej 3 (a więc, jeśli użytkownik
wykonał już trzy próby wpisania hasła).
Przy każdym powtórzeniu pętli użytkownik jest proszony o wpisanie w oknie InputBox
hasła, które jest potem
przypisywane do zmiennej podaneHaslo
.
Po każdej takiej próbie wpisania hasła, zmienna i
jest zwiększana o 1, ponieważ jak wcześniej wspomniano,
zmienna ta służy jako licznik wykonanych prób.
Następnie kompilator sprawdza czy hasło podane przez użytkownika zgadza się z hasłem zapisanym jako prawidłowe
(wiersz 13).
Jeżeli oba hasła są identyczne, do zmiennej czyPrawidlowe
zostaje przypisana wartość
True i wywoływane jest polecenie Exit Do, oznaczające natychmiastowe opuszczenie
pętli. Gdyby w tym miejscu zabrakło tego polecenia, jedyną możliwością wyjścia z pętli byłoby osiągnięcie przez zmienną
i
wartości 3, a więc użytkownik za każdym razem musiałby trzykrotnie wpisywać hasło, nawet jeśli już za
pierwszym razem podałby prawidłowe hasło. Byłoby to oczywiście bardzo nieefektywne, a przede wszystkim irytujące
dla użytkowników.
Jeżeli natomiast użytkownik podał błędne hasło, na ekranie wyświetlany jest komunikat, że podane przez niego hasło jest
nieprawidłowe wraz z informacją o liczbie pozostałych prób (3-i
).
Pętla jest więc powtarzana tak długo, aż użytkownik poda prawidłowe hasło lub trzykrotnie poda błędne hasło.
Po wyjściu z pętli, kompilator sprawdza jaki był wynik weryfikacji hasła (przechowywany w zmiennej czyPrawidlowe
)
i, jeśli jest on pozytywny, wykonuje właściwe operacje. Jeżeli natomiast zmienna czyPrawidlowe
wynosi
False, uruchamiane są operacje zabezpieczające, jak np. powiadomienie administratora czy
zablokowanie na jakiś czas możliwośc korzystania z makra na danym komputerze (przykład ma tylko obrazować wykorzystanie
polecenia Exit Do, więc również nie ma sensu w tym miejscu szczegółowo opisywać tych operacji).
Powszechną sytuacją, w której użycie polecenia Exit Do może przynieść wiele pożytku, jest
operowanie na obiektach RecordSet
, czyli zestawach danych pobranych bezpośrednio z bazy danych. Jednak z
uwagi na to, że temat ten znacznie wykracza poza materiał przerobiony w dotychczasowych lekcjach, przykłady obrazujące
wykorzystanie polecenia Exit Do w tego typu procedurach pojawią się się dopiero podczas
szczegółowego omawiania baz danych.
Wcześniejsze opuszczanie procedur, funkcji i makr
Język VBA pozwala też wymusić wcześniejsze opuszczenie określonej funkcji lub procedury, a nawet całego makra.
Poniżej znajduje się prosty przykład obrazujący opuszczenie procedury, jeszcze zanim wykonywanie kodu dotrze do
wiersza jej zamknięcia.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
Sub sprawdzanieHasla()
Dim haslo As String
Dim podaneHaslo As String
haslo = "qwerty"
podaneHaslo = InputBox("Podaj hasło", "Hasło")
If haslo <> podaneHaslo Then Exit Sub
End Sub
Powyższa procedura zawiera dwie zmienne tekstowe: haslo
(przechowująca prawidłowe hasło) oraz wpisaneHaslo
(przechowująca hasło wpisane przez użytkownika).
W wierszach 5-6 do tych dwóch zmiennych przypisywane są odpowiednie wartości: do zmiennej haslo
ciąg znaków qwerty, natomiast do zmiennej podaneHaslo
wynik działania funkcji InputBox
, czyli hasło,
które użytkownik wpisuje w polu tekstowym okna InputBox
.
W wierszu 8 znajduje się instrukcja warunkowa, sprawdzająca czy hasło wpisane przez użytkownika
zgadza się z prawidłowym hasłem. Jeżeli oba hasła są od siebie różne, oznacza to, że użytkownik podał błędne hasło i nie
jest uprawniony do uruchomienia makra, w związku z czym wywoływane jest polecenie Exit Sub,
które oznacza natychmiastowe opuszczenie tej procedury.
Tak naprawdę dotychczasowa część makra służyła tylko weryfikacji użytkownika. Zasadnicza część makra znajduje się w
wierszach 10-13 i została zastąpiona komentarzem, gdyż nieistotne jest w tym momencie, co miałoby
wykonywać to makro. Jeżeli jednak użytkownik nie przejdzie wcześniejszej weryfikacji (a więc wpisze nieprawidłowe hasło),
wykonywanie kodu nigdy nie dojdzie do tego miejsca, ponieważ opuści tę procedurę już w wierszu 8
w wyniku działania polecenia Exit Sub.
Zwróć uwagę, że polecenie Exit Sub nie jest w tej sytuacji niezbędne. Makro działałoby
dokładnie tak samo, gdyby wszystkie operacje umieszczone w wierszach 10-13 znalazły się we wnętrzu
instrukcji If ... Then
, w bloku przeznaczonym dla spełnionego warunku. Kod wyglądałby
w tej sytuacji tak, jak w poniższej ramce (nowe fragmenty zaznaczono na czerwono):
1
2
3
4
5
6
7
8
9
10
11
12
13
14
Sub sprawdzanieHasla()
Dim haslo As String
Dim podaneHaslo As String
haslo = "qwerty"
podaneHaslo = InputBox("Podaj hasło", "Hasło")
If haslo = podaneHaslo Then
End If
End Sub
W tej postaci makra, po ustaleniu prawidłowego hasła oraz pobraniu hasła od użytkownika (wiersze
5-6) podobnie jak poprzednio, kod trafia na instrukcję warunkową
If ... Then
. Warunek przypisany do tej instrukcji jest jednak teraz dokładnie
odwrotny.
Cała zasadnicza część makra umieszczona została wewnątrz instrukcji warunkowej, w bloku dla spełnionego warunku,
dlatego też będzie wykonana tylko wtedy, jeżeli warunek przypisany dla tej instrukcji (haslo = podaneHaslo
)
będzie prawdziwy, a więc jeśli użytkownika poda prawidłowe hasło.
Jeżeli natomiast użytkownik poda błędne hasło, warunek przypisany do instrukcji warunkowej będzie fałszywy. Z uwagi na to,
że w instrukcji warunkowej nie przewidziano żadnych operacji dla niespełnionego warunku, wykonywanie kodu zostanie
przeniesione do wiersza zamknięcia instrukcji warunkowej (wiersz 13), po którym następuje już
tylko zakończenie całej procedury.
Możliwość wyeliminowania polecenia Exit Sub (lub Exit Function
,
bo poza tym, że jedno z nich używane jest w procedurach, a drugie w funkcjach, absolutnie się od siebie nie różnią)
przez takie niewielkie modyfikacje kodu nie jest niczym nadzwyczajnym. Tak naprawdę jest to reguła i w zasadzie
można byłoby zaryzykować stwierdzenie, że każde polecenie Exit Sub
(Exit Function) może być zastąpione innym elementem języka VBA bez utraty funkcjonalności makra.
Jeżeli wyeliminowanie polecenia Exit Sub (Exit Function)
jest tak proste, jak w powyższym przykładzie, zawsze lepiej jest stosować wersję niezawierającą tego polecenia, ponieważ
jego obecność w kodzie może w niektórych sytuacjach znacznie utrudnić późniejszą analizę kodu aplikacji.
Zdarzają się jednak sytuacje, że zastosowanie polecenia Exit Sub
(Exit Function) pozwala uprościć kod, a nawet przyśpieszyć nieco działanie danej
procedury lub funkcji, i wtedy jego użycie jest jak najbardziej wskazane.
Bardzo często spotykanym przykładem takiej sytuacji są funkcje sprawdzające kompletność danych wprowadzonych przez
użytkownika w formularzu.
Załóżmy, że makro wyświetla użytkownikowi następujący formularz, w którym pola oznaczone gwiazdką są obowiązkowe do
wypełnienia:
Po kliknięciu przycisku OK, dane z formularza powinny się zapisywać w bazie danych. Przed ich zapisaniem do bazy
należy jednak sprawdzić, czy wprowadzone dane są kompletne, czyli czy użytkownik wypełnił wszystkie pola oznaczone
jako wymagane.
Oczywiście nie będziemy się w tym miejscu zajmować tworzeniem tego typu formularzy, ani zapisywaniem ich do bazy danych
- na to wszystko przyjdzie pora w dalszej części kursu. W tym momencie istotna jest tylko funkcja sprawdzająca kompletność
danych.
Spójrz najpierw na kod i analizę tej funkcji przy założeniu, że w ogóle nie wykorzystuje ona polecenia
Exit Function:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
Function czyKompletneDane(Imie As String, Nazwisko As String, _
PESEL As String, Dowod As String, NIP As String, _
Ulica As String, Numer As String, KodPocztowy As String, _
Miasto As String, Telefon As String) As Boolean
If Len(Imie) Then
If Len(Nazwisko) Then
If Len(PESEL) Then
If Len(Dowod) Then
If Len(NIP) Then
If Len(Ulica) Then
If Len(Numer) Then
If Len(KodPocztowy) Then
If Len(Miasto) Then
If Len(Telefon) Then
czyKompletneDane = True
End If
End If
End If
End If
End If
End If
End If
End If
End If
End If
End Function
Funkcja czyKompletneDane
posiada 10 argumentów wejściowych, z których każdy odpowiada wartości
jednego z obowiązkowych pól na formularzu.
Na przykład przy tak wypełnieniu formularzu:
poszczególne argumenty posiadają następujące wartości:
Imie = "Krzysztof"
,
Nazwisko = "Jarzyna"
,
PESEL = "53050312114"
,
Dowod = "AAA000001"
,
NIP = "503-520-12-14"
,
Ulica = ""
,
Numer = ""
,
KodPocztowy = "70-003"
,
Miasto = "Szczecin"
,
Telefon = "601-540-321"
.
Funkcja czyKompletneDane
posiada typ Boolean, a więc może zwrócić tylko wartość
True (jeżeli dane są kompletne) lub wartość False (jeżeli
użytkownik nie uzupełnił któregoś z wymaganych pól).
Na początku funkcja przechowuje wartość False, ponieważ taka jest domyślna wartość dla
zmiennej typu logicznego, jeżeli nie przypisano do niej żadnej innej wartości.
W wierszach 6-26 znajduje się dziesięć zagnieżdżonych instrukcji warunkowych, z których każda
sprawdza jedną z wartości formularza. Ponieważ każda z tych zagnieżdżonych instrukcji rozpatruje inną zmienną, niemożliwe
jest zastąpienie ich instrukcją Select Case
(instrukcja
Select Case
rozpatruje różne warianty pojedynczej zmiennej, w związku z tym może
zastępować tylko zagnieżdżone instrukcje warunkowe, które również rozpatrują tylko pojedynczą zmienną).
Kompilator sprawdza po kolei każdą z instrukcji warunkowych i, jeżeli warunek w niej zawarty jest prawdziwy (czyli sprawdzana
w tej instrukcji wartość została przez użytkownika uzupełniona), przechodzi do sprawdzania kolejnej itd. Jeżeli sprawdzone
zostaną już wszystkie instrukcje i wszystkie będą prawdziwe, wykonanie kodu dociera do wiersza 16,
w którym do funkcji zostaje przypisana wartość True. Każda kolejna instrukcja warunkowa jest
zagnieżdżona w swojej poprzedniczce w bloku przewidzianym dla spełnionego warunku. Oznacza to, że jeżeli przy którejkolwiek
instrukcji warunkowej okaże się, że przypisany do niej warunek nie jest spełniony (czyli użytkownik nie wpisał tej wartości
w formularzu), wykonanie kodu nigdy nie dotrze do wiersza, w którym do funkcji zostaje przypisana wartość
True. Jest to oczywiście zgodne z założeniem, gdyż w przypadku braku choćby jednej wartości,
funkcja ma zwracać wartość False.
Powyższa postać funkcji działa wprawdzie prawidłowo, jednak tworzenie tak wielu zagnieżdżonych pętli nigdy nie jest
dobrym pomysłem, ponieważ znacznie wydłuża cały kod, a przy tym łatwo w takiej sytuacji o pomyłkę. Spójrz teraz na
umieszczony poniżej kod, który wykonuje dokładnie to samo zadanie, jednak zamiast zagnieżdżania instrukcji warunkowych
wykorzystano w nim instrukcje Exit Function:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
Function czyKompletneDane(Imie As String, Nazwisko As String, _
PESEL As String, Dowod As String, NIP As String, _
Ulica As String, Numer As String, KodPocztowy As String, _
Miasto As String, Telefon As String) As Boolean
If Len(Imie) = 0 Then Exit Function
If Len(Nazwisko) = 0 Then Exit Function
If Len(PESEL) = 0 Then Exit Function
If Len(Dowod) = 0 Then Exit Function
If Len(NIP) = 0 Then Exit Function
If Len(Ulica) = 0 Then Exit Function
If Len(Numer) = 0 Then Exit Function
If Len(KodPocztowy) = 0 Then Exit Function
If Len(Miasto) = 0 Then Exit Function
If Len(Telefon) = 0 Then Exit Function
czyKompletneDane = True
End Function
Ta postać funkcji również posiada dziesięć argumentów wejściowych, z których każdy odpowiada jednemu polu na formularzu.
Początkowa wartość funkcji wynosi False.
W wierszach 6-15 znajduje się 10 jednowierszowych instrukcji warunkowych
If ... Then
. Każda z tych instrukcji posiada warunek, który sprawdza czy
poszczególna wartość jest pustym ciągiem znaków. Jeżeli warunek ten jest spełniony (co jest równoznaczne z tym, że
użytkownik nie wpisał jej w formularzu), wywoływane jest polecenie Exit Function,
które wymusza natychmiastowe opuszczenie funkcji. Jeżeli kompilator został zmuszony do takiego nagłego wyjścia z
funkcji, wynikiem działania tej funkcji jest wartość, jaką posiadała ona w momencie jej opuszczania - w tym przypadku
jest to więc ciągle domyślna wartość False, gdyż dotychczas w żadnym miejscu kodu nie
przypisano do tej funkcji innej wartości. Jest to oczywiście zgodne z początkowym założeniem, ponieważ jeśli
jakakolwiek wartość nie została uzupełniona w formularzu, funkcja miała zwracać wartość False.
Jeżeli kompilator sprawdzi wszystkie 10 instrukcji warunkowych i przy żadnej z nich nie wywoła polecenia
Exit Function (co oznacza, że nie brakuje żadnej wartości), wykonywanie kodu dochodzi do
wiersza 16, w którym do funkcji zostaje przypisana wartość True.
Kolejnym wierszem jest już zakończenie całej funkcji, więc jej ostatecznym wynikiem będzie True,
co oczywiście również jest zgodne z założeniem (nie brakuje żadnej wartości, więc funkcja zwraca wartość
True).
W tym przypadku zastosowanie polecenia Exit Function niesie więc ze sobą wiele korzyści,
ponieważ funkcja, nie dość że jest zdecydowanie krótsza i bardziej przejrzysta, to jeszcze działa trochę szybciej niż
jej wersja wykorzystująca zagnieżdzone instrukcje warunkowe.
Język VBA posiada również polecenie End, umożliwiające natychmiastowe zatrzymanie i
zakończenie całej działającej aktualnie aplikacji.
Wróć do przykładu znajdującego się na początku tego podrozdziału, w którym w przypadku wpisania przez użytkownika
nieprawidłowego hasła wykonywanie kodu natrafiało na polecenie Exit Sub, co oznaczało
natychmiastowe opuszczenie tej procedury:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
Sub sprawdzanieHasla()
Dim haslo As String
Dim podaneHaslo As String
haslo = "qwerty"
podaneHaslo = InputBox("Podaj hasło", "Hasło")
If haslo <> podaneHaslo Then Exit Sub
End Sub
W przykładzie tym zasadnicza część makra (wiersze 10-13) polegała na wywoływaniu podprocedur
wykonujących poszczególne zadania. Zauważ, że część poświęcona weryfikacji użytkownika (wiersz 2-6)
jako jedyna nie została przeniesiona do oddzielnej procedury, lecz umieszczona w głównej procedurze.
Gdyby przenieść część weryfikacyjną do oddzielnej procedury, kod wyglądałby tak:
1
2
3
4
5
6
Sub aplikacja()
Call weryfikacja
End Sub
1
2
3
4
5
6
7
8
9
Sub weryfikacja()
Dim haslo As String
Dim podaneHaslo As String
haslo = "qwerty"
podaneHaslo = InputBox("Podaj hasło", "Hasło")
If haslo <> podaneHaslo Then Exit Sub
End Sub
Po uruchomieniu makra jako pierwsza uruchamiana jest podprocedura weryfikacja
, która ma sprawdzać czy
użytkownik jest w ogóle uprawniony do korzystania z makra. Wykonywanie kodu zostaje więc przeniesione z procedury
aplikacja
do procedury weryfikacja
. W procedurze weryfikacja
odbywa się proces,
który został już omówiony wcześniej (przy okazji omawiania polecenia Exit Sub), czyli
przypisanie prawidłowego hasła, pobranie hasła od użytkownika i sprawdzanie zgodności obu tych haseł.
Załóżmy teraz, że użytkownik wpisał nieprawidłowe hasło - w takiej sytuacji makro powinno zakończyć działanie i nie
wykonywać już żadnych innych czynności. Tak się jednak nie stanie. Gdy kompilator stwierdzi, że hasło systemowe
(qwerty) oraz hasło podane przez użytkownika (przechowywane w zmiennej podaneHaslo
) nie zgadzają
się ze sobą (wiersz 8 procedury weryfikacja
) to wywoła polecenie
Exit Sub, oznaczające natychmiastowe opuszczenie aktualnej procedury. Jeżeli więc opuszczona
zostanie aktualna procedura (czyli weryfikacja
), to wykonywanie kodu powróci do procedury dla niej
nadrzędnej, która ją wywołała (a więc aplikacja
) i będzie kontynuowała wykonywanie zawartych w niej
poleceń. W związku z tym, mimo że hasło podane przez użytkownika było nieprawidłowe, makro jak gdyby nigdy nic wykona
wszystkie przewidziane w nim operacje: podprocedura_1
, podprocedura_2
, itd.!
Jedynym sposobem na ominięcie tego problemu jest zastąpienie polecenia Exit Sub poleceniem
End:
1
2
3
4
5
6
7
8
9
Sub weryfikacja()
Dim haslo As String
Dim podaneHaslo As String
haslo = "qwerty"
podaneHaslo = InputBox("Podaj hasło", "Hasło")
If haslo <> podaneHaslo Then End
End Sub
W takiej postaci makra, jeśli kompilator stwierdzi, że hasło podane przez użytkownika nie zgadza się z hasłem systemowym,
wywołane zostanie polecenie End, które oznacza natychmiastowe zakończenie działania całej
aplikacji, niezależnie od tego, w której procedurze znajduje się aktualnie wykonywanie kodu.
Mimo że przedstawiony powyżej kod, korzystający z polecenia End, z formalnego punktu widzenia
jest całkowicie poprawny, to trzeba zaznaczyć, że używanie tego polecenia nie należy do dobrych praktyk programistycznych.
Głównym powodem jest to, że w przypadku natrafienia kodu na instrukcję End, oprócz zakończenia
działania makra, resetowane są wartości wszystkich zmiennych publicznych. Na razie nie poznałeś jeszcze nawet pojęcia
zmiennej publicznej, więc zapewne nie widzisz nic złego i niepokojącego w tym, że ich wartość mogłaby być wyzerowana.
Temat zmiennych publicznych zostanie dokładnie omówiony w jednej z kolejnych lekcji, jednak już przy tej okazji można
wspomnieć, że zmienne publiczne powinny zachowywać swoją wartość od momentu uruchomienia aplikacji, aż do czasu całkowitego
zamknięcia Excela. Ich zresetowanie poprzez użycie polecenia End sprawia więc, że tracą one
swoją główną zaletę, jaką jest długotrwałe przechowywanie wartości.
Zdarza się też, że jakiś niewielki błąd w kodzie aplikacji prowadzi do niepożądanego wywołania użytego w tym kodzie polecenia
End, przez co makro nieoczekiwanie kończy swoje działanie, irytując użytkowników końcowych.
Odnalezienie miejsca w kodzie, w którym znajduje się to polecenie End, jest bardzo trudne,
nawet z użyciem skrótu klawiaturowego Ctrl + F służącego do przeszukiwania
całego kodu, ponieważ oprócz poleceń End w kodzie znajduje się o wiele więcej słów End,
które stanowią część takich poleceń jak np. End Sub , End Function czy
End Enum.
Przenoszenie wykonywania makra do dowolnego miejsca kodu
W normalnych sytuacjach kompilator wykonuje poszczególne linijki kodu w takiej kolejności, w jakiej znajdują się one
w edytorze VBA. Najpierw wykonywana jest linijka 1, potem 2 itd. Zdążyłeś już jednak poznać kilka instrukcji (a w
zasadzie większość z nich), które pozwalają ingerować w kolejność wykonywania linii kodu, np. po natrafieniu w
kodzie na instrukcję warunkową If ... Then, w której warunek jest spełniony,
kompilator ominie te linijki, które opisują operacje dla niespełnionych warunków, a po natrafieniu na pętlę
For ... Next, a konkretnie na wiersz jej zamknięcia (Next
), kompilator
wróci o kilka linijek kodu do wiersza jej otwarcia.
W VBA istnieje ponadto polecenie GoTo, które również pozwala wymuszać zmianę kolejności
wykonywania wierszy i umożliwia cofnięcie się lub przeskoczenie o kilka linijek kodu do przodu.
Już na wstępie warto jednak zaznaczyć, że korzystanie z GoTo powinno być ograniczone do
minimum i w większości przypadków może, a nawet powinno być zastąpione którąś z wcześniej omówionych instrukcji
sterujących. Jedyną sytuacją, gdzie użycie polecenia GoTo nie budzi wątpliwości i nie ma
żadnej alternatywy, jest obsługa błędów, która będzie tematem jednej z kolejnych lekcji.
Mimo iż korzystanie z tego polecenia jest zalecane tylko w niewielu sytuacjach, należy je omówić, ponieważ będziesz go
czasem potrzebował przy wspomnianej obsłudze błędów, a poza tym może się w przyszłości zdarzyć, że będziesz musiał
pracować z aplikacją stworzoną przez inną osobę, chętnie korzystającą z GoTo.
Poniżej znajduje się przykład prostego makra wykorzystującego instrukcję GoTo
(chociaż optymalnym rozwiązaniem byłoby tu zastąpienie jej instrukcją If ... Then):
1
2
3
4
5
6
7
Sub wykorzystanieGoTo(i As Long)
If i <= 0 Then GoTo koniec
Call MsgBox("Podana liczba jest większa od 0")
koniec:
Call MsgBox("Koniec działania makra")
End Sub
Procedura z powyższego przykładu wymaga podania jednego argumentu wejściowego typu Long.
W trzeciej linijce kodu znajduje się instrukcja warunkowa sprawdzająca, czy liczba podana jako argument jest mniejsza
lub równa 0. Jeżeli warunek ten jest spełniony wywoływane jest polecenie GoTo koniec
.
Zwróć uwagę, że taka sama nazwa - koniec
- tyle, że z dwukropkiem, znajduje się w piątym wierszu omawianego
przykładu. Nazwa znajdująca się w piątym wierszu to tzw. etykieta, czyli tak jakby adres tego miejsca w kodzie,
do którego można potem przeskoczyć za pomocą instrukcji GoTo.
Etykietą mogą być liczby lub teksty (oczywiście z zachowaniem zasad nadawania nazw omówionych
w lekcji drugiej). Jeżeli jako etykieta wykorzystany jest tekst, trzeba po nim postawić
dwukropek, gdyż w przeciwnym przypadku traktowany jest on jako nazwa zmiennej lub procedury do wywołania. Liczby,
użyte jako etykiety, mogą ale nie muszą posiadać na końcu dwukropka.
Poniżej znajduje się kilka przykładowych etykiet wraz z komentarzem:
ETYKIETA | KOMENTARZ |
koniec: | prawidłowa nazwa etykiety - etykieta jest tekstem, więc zawiera na końcu dwukropek |
0: | prawidłowa nazwa etykiety - etykieta będąca liczbą może, ale nie musi zawierać na końcu dwukropek |
koniec | nieprawidłowa nazwa etykiety - etykieta jest tekstem, a nie ma po niej dwukropka |
0 | prawidłowa nazwa etykiety - etykieta będąca liczbą może, ale nie musi zawierać na końcu dwukropek |
Edytor VBA traktuje jako etykietę każdy wiersz, w którym znajduje się pojedyncza liczba (z dwukropkiem na końcu lub bez
niego) oraz wszystkie wiersze, w których znajdują się tylko pojedyncze nazwy bez cudzysłowów, ale z dwukropkiem na końcu.
W każdej procedurze lub funkcji może znaleźć się dowolna liczba etykiet, nie mogą się one jednak powtarzać w obrębie
pojedynczej procedury lub funkcji.
Za każdym razem, kiedy umieścisz w kodzie etykietę, edytor VBA automatycznie wyrówna ją do lewej strony i nie ma możliwości
przesunięcia jej w prawo.
Wróćmy z powrotem do analizy kodu. W momencie, w którym kompilator natrafił na polecenie GoTo
koniec
, wyszukuje on w tej procedurze etykietę o takiej właśnie nazwie i przenosi wykonywanie kodu do linijki
znajdującej się bezpośrednio za tą etykietą. W omawianym przypadku etykieta koniec:
znajduje się w
wierszu 5, więc wykonywanie kodu zostanie przeniesione do wiersza 6,
w którym znajduje się operacja wyświetlenia na ekranie okna MsgBox
z komunikatem Koniec działania makra.
Gdyby w drugim wierszu powyższego kodu warunek i <= 0
nie został spełniony, kompilator pominąłby polecenie
GoTo i wykonywanie kodu dalej biegłoby swoim zwykłym torem. Nie oznacza to bynajmniej, że
etykieta koniec:
i to, co znajduje się po niej zostałoby w takiej sytuacji pominięte. Jeżeli kompilator
natrafi w toku działania na wiersz etykiety, traktuje go po prostu jak komentarz i wykonuje operacje znajdujące się w
dalszej części kodu. W związku z tym, jeżeli w omawianym przykładzie zmienna i
miała wartość większą od
zera, najpierw na ekranie pojawiłby się komunikat Podana liczba jest większa od 0, a następnie komunikat
Koniec działania makra.
Jeżeli po słowie kluczowym GoTo
wpiszesz nazwę etykiety, która nie występuje w danej
procedurze lub funkcji, to w momencie, kiedy wykonywanie kodu dojdzie do tej funkcji lub procedury, wyświetlony
zostanie błąd: Compile error: label not defined.
Jak wspomniano jednak na wstępie, polecenie GoTo jest zazwyczaj marnym substytutem innych
instrukcji sterujących i powyższa procedura powinna raczej wykorzystywać instrukcję warunkową
If ... Then, zamiast GoTo:
1
2
3
4
5
6
7
Sub IfThen_Zamiast_GoTo(i As Long)
If i > 0 Then
Call MsgBox("Podana liczba jest większa od 0")
End If
Call MsgBox("Koniec działania makra")
End Sub
Poniżej znajduje się jeszcze jeden przykład korzystający z polecenia GoTo.
Oczywiście, również tutaj istnieje możliwość zastąpienia go inną instrukcją sterującą (a konkretnie pętlą
Do ... Loop), jednak jest to jeden z nielicznych przypadków, w którym użycie
GoTo jest całkiem rozsądnym pomysłem.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
Sub pobieranieNazwy()
Dim nazwa As String
retry:
nazwa = InputBox("Podaj nazwę użytkownika." & vbCrLf & _
"Nazwa musi liczyć co najmniej 4 znaki" , "Nazwa")
If Len(nazwa) < 4 Then
Call MsgBox("Nazwa musi liczyć co najmniej 4 znaki", vbExclamation, "Za krótka nazwa")
GoTo retry
End If
End Sub
Działanie procedury polega na pobraniu od użytkownika nazwy, która musi liczyć co najmniej 4 znaki, a następnie
wykonanie z tą nazwą jakiejś operacji (np. zapisanie jej do bazy danych; tak naprawdę w kontekście tego przykładu
i omawiania instrukcji GoTo nie ma to najmniejszego znaczenia). Jeżeli użytkownik poda
za krótką nazwę makro powinno wyświetlić odpowiedni komunikat i ponowić prośbę o wpisanie nazwy.
Procedura posiada jedną zmienną tekstową - nazwa
, do której w trakcie działania makra przypisywana jest
wartość wpisana przez użytkownika.
W czwartym wierszu kompilator napotyka na etykietę retry:
. Jednak, jak wcześniej wspomniano, napotkanie
przez kompilator etykiety nie wiążę się z wykonywaniem żadnych operacji, ponieważ etykieta nie jest żadnym poleceniem,
lecz czymś w rodzaju adresu w kodzie.
W kolejnym wierszu wywoływana jest funkcja InputBox
, w której użytkownik musi wprowadzić nazwę użytkownika,
licząca co najmniej 4 znaki. Nazwa ta jest następnie przypisywana do zmiennej nazwa
.
W wierszu ósmym znajduje się instrukcja warunkowa, która sprawdza czy podana przez użytkownika nazwa jest krótsza niż 4
znaki. Jeżeli kompilator stwierdzi, że wprowadzona nazwa rzeczywiście jest zbyt krótka, wyświetla najpierw okno z
odpowiednim komunikatem (wiersz 9), a następnie wywołuje polecenie
GoTo retry
, które przenosi wykonywanie kodu z powrotem do wiersza
5. Użytkownik ponownie jest więc proszony o wpisanie nazwy. Schemat ten jest powtarzany tak długo,
aż użytkownik poda w końcu nazwę liczącą co najmniej 4 znaki.
Jeżeli nazwa podana przez użytkownika będzie liczyła co najmniej 4 znaki, warunek przypisany do instrukcji warunkowej,
z wiersza 8 będzie fałszywy i kompilator przejdzie do wykonywania operacji znajdujących się za
tą instrukcją (w powyższym przykładzie operacje te zastąpiono jedynie komentarzem).
Gdyby w powyższym przykładzie instrukcja GoTo miała być zastąpiona pętlą
Do ... Loop, kod wyglądałby tak, jak w poniższej ramce:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
Sub pobieranieNazwy()
Dim nazwa As String
Do
nazwa = InputBox("Podaj nazwę użytkownika." & vbCrLf & _
"Nazwa musi liczyć co najmniej 4 znaki" , "Nazwa")
If Len(nazwa) < 4 Then
Call MsgBox("Nazwa musi liczyć co najmniej 4 znaki", vbExclamation, "Za krótka nazwa")
End If
Loop While Len(nazwa) < 4
End Sub
Znajdująca się w tej procedurze pętla Do ... Loop zawiera warunek Len(nazwa) < 4
poprzedzony słowem kluczowym While, co oznacza, że jest ona wykonywana tak długo, jak zmienna
nazwa
przechowuje tekst krótszy niż 4 znaki.
Zauważ jednak, że jeśli makro ma wyświetlać komunikat informujący użytkownika o zbyt krótkiej nazwie, wewnątrz tej pętli
trzeba umieścić instrukcję warunkową, sprawdzającą dokładnie ten sam warunek, który jest przypisany do pętli:
Len(nazwa) < 4
. Oznacza to, że przy każdym wywołaniu pętli warunek ten będzie sprawdzany dwukrotnie,
co jest złamaniem podstawowej zasady, mówiącej o unikaniu powtarzania tych samych czynności.
Można byłoby wstawić wspomniany komunikat MsgBox
w wierszu 5, przed przypisaniem do
zmiennej nazwa
wyniku funkcji InputBox
. Jednak w takiej sytuacji komunikat o zbyt krótkiej
nazwie pokazałby się na ekranie jeszcze zanim użytkownik zdążył podać jakąkolwiek nazwę, co jest również nie do przyjęcia.
Jeżeli komuś bardzo zależałoby na użyciu w powyższym przykładzie pętli Do ... Loop zamiast
polecenia GoTo, mógłby spróbować rozwiązać to za pomocą nieskończonej pętli i wykorzystania
polecenia Exit Do, jednak wydaje się, że użycie w tej sytuacji instrukcji
GoTo jest całkowicie akceptowalnym rozwiązaniem.