Lekcja została podzielona na następujące części:
Podstawowe informacje o pętlach
W przykładach zamieszczonych w początkowych lekcjach kursu pojawiło się makro printujące w arkuszu kolejne potęgi
poszczególnych liczb. Makro to przedstawiało się następująco:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
Sub wyswietlajPotegi(liczba As Byte, numerKolumny As Byte)
Dim potega As Long
potega = 1
Worksheets("Arkusz1").Cells(1, numerKolumny) = potega
potega = potega * liczba
Worksheets("Arkusz1").Cells(2, numerKolumny) = potega
potega = potega * liczba
Worksheets("Arkusz1").Cells(3, numerKolumny) = potega
potega = potega * liczba
Worksheets("Arkusz1").Cells(4, numerKolumny) = potega
potega = potega * liczba
Worksheets("Arkusz1").Cells(5, numerKolumny) = potega
End Sub
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
Sub wyswietlajPotegi(liczba As Byte, numerKolumny As Byte)
Dim potega As Long
potega = 1
Worksheets("Arkusz1").Cells(1, numerKolumny) = potega
potega = potega * liczba
Worksheets("Arkusz1").Cells(2, numerKolumny) = potega
potega = potega * liczba
Worksheets("Arkusz1").Cells(3, numerKolumny) = potega
potega = potega * liczba
Worksheets("Arkusz1").Cells(4, numerKolumny) = potega
potega = potega * liczba
Worksheets("Arkusz1").Cells(5, numerKolumny) = potega
End Sub
Za wyświetlanie każdej kolejnej potęgi odpowiedzialna była oddzielna linijka kodu. Zgodnie z założeniem, makro miało wypisać tylko pięć pierwszych potęg danej liczby, dlatego w miarę łatwo było stworzyć dla każdej z nich oddzielny wiersz.
Zwróć jednak uwagę, że wszystkie linijki drukujące poszczególne potęgi w arkuszu są do siebie bliźniaczo podobne (różnią się tylko numerem wiersza). Zawsze gdy napotkasz w kodzie niemal identyczne fragmenty, powinno to zwrócić Twoją uwagę - prawdopodobnie można z tych fragmentów stworzyć jeden wspólny, korzystając z jakichś wbudowanych elementów języka VBA (np. pętli, tak jak w tym przypadku) lub pisząc własną funkcję. Dzięki stworzeniu uniwersalnego fragmentu kodu, zastępującego kilka podobnych fragmentów, cały kod będzie łatwiejszy do zrozumienia i zajmie mniej miejsca, a w razie potrzeby wprowadzenia później jakichkolwiek zmian, będzie trzeba zmodyfikować tylko ten jeden wspólny fragment, zamiast kilku podobnych kawałków porozrzucanych po całej aplikacji.
Przytoczona powyżej argumentacja powinna wystarczyć, abyś przekonał się, że dotychczasowa postać kodu printującego potęgi nie jest optymalna. Jest jednak jeszcze jeden argument, można by rzec, że koronny. Wyobraź sobie bowiem sytuację, w której musiałbyś zmodyfikować powyższe makro tak, aby zamiast pięciu pierwszych potęg, wyświetlało ich 50 (albo napisać inne makro, które ma wyświetlić jakąś określoną wartość dla 10 tysięcy wierszy). Oczywiście ręczne wpisywanie jednego wiersza po drugim, tak jak to było dotychczas praktykowane w makrze printującym potęgi, byłoby kompletnie niedorzecznym pomysłem. Po ukończeniu tej czynności taki plik prawdopodobnie nie byłby już nikomu do niczego potrzebny, bo dane w nim zawarte byłyby nieaktualne o co najmniej kilka miesięcy. Zdecydowanie lepszym wyjściem z tej sytuacji byłoby wpisanie i przeciągnięcie odpowiednich formuł bezpośrednio w arkuszu Excela.
Na szczęście z pomocą idą pętlę, za pomocą których wystarczy jeden raz opisać wielokrotnie wykonywaną operację (w tym przypadku wstawianie potęg w komórkach arkusza), a potem określić tylko liczbę powtórzeń tej operacji.
Poniżej znajdziesz kod przedstawiający makro do printowania potęg zmodyfikowane w taki sposób, aby korzystało z
pętli, oraz analizę tego kodu linijka po linijce.
1
2
3
4
5
6
7
8
9
10
11
Sub wyswietlajPotegi(podstawa As Byte, ilePoteg As Integer, kolumna As Byte)
Dim i As Integer
Dim potega As Long
potega = 1
For i = 1 To ilePoteg
Worksheets("Arkusz1").Cells(i, kolumna) = potega
potega = potega * podstawa
Next i
End Sub
1
2
3
4
5
6
7
8
9
10
11
Sub wyswietlajPotegi(podstawa As Byte, ilePoteg As Integer, kolumna As Byte)
Dim i As Integer
Dim potega As Long
potega = 1
For i = 1 To ilePoteg
Worksheets("Arkusz1").Cells(i, kolumna) = potega
potega = potega * podstawa
Next i
End Sub
Aby przetestować działanie makra, wykonaj w oknie Immediate polecenie:
Call wyswietlajPotegi(2, 20, 1)
które powinno wyświetlić 20 pierwszych potęg dwójki w pierwszej kolumnie arkusza Arkusz1.
Call wyswietlajPotegi(2, 20, 1)
Procedura wyswietlajPotegi
posiada teraz trzy argumenty wejsciowe:
podstawa
: liczba typu Byte
(a więc z zakresu 0-255); jest to liczba, która będzie podnoszona do potęgi,
ilePoteg
: liczba typu Integer
; argument ten określa ile pierwszych potęg danej liczby (podanej jako argument podstawa
) ma zostać wyświetlonych w arkuszu,
kolumna
: liczba typu Byte
; określa, w której kolumnie arkusza mają być wyświetlone te potęgi.
podstawa
: liczba typu Byte
(a więc z zakresu 0-255); jest to liczba, która będzie podnoszona do potęgi,ilePoteg
: liczba typu Integer
; argument ten określa ile pierwszych potęg danej liczby (podanej jako argument podstawa
) ma zostać wyświetlonych w arkuszu,kolumna
: liczba typu Byte
; określa, w której kolumnie arkusza mają być wyświetlone te potęgi.
Dodatkowo w sekcji deklaracji zmiennych (wiersze 2-3) pojawiają się dwie kolejne zmienne:
i
: zmienna liczbowa służąca jako licznik w pętli; jej działanie zostanie szczegółowo opisane w dalszej części lekcji przy okazji omawiania schematu działania pętli,
potega
: zmienna typu Long
; przechowuje wartości poszczególnych potęg; przy każdym kolejnym powtórzeniu pętli zmienna ta będzie mnożona przez zmienną podstawa
, tworząc tym samym kolejną potęgę.
i
: zmienna liczbowa służąca jako licznik w pętli; jej działanie zostanie szczegółowo opisane w dalszej części lekcji przy okazji omawiania schematu działania pętli,potega
: zmienna typu Long
; przechowuje wartości poszczególnych potęg; przy każdym kolejnym powtórzeniu pętli zmienna ta będzie mnożona przez zmienną podstawa
, tworząc tym samym kolejną potęgę.
W wierszu 5 do zmiennej potega
przypisana zostaje liczba 1. Do tego momentu zmienna
potega
miała domyślną wartość 0 i gdyby nie przypisano do niej wartości 1, mnożenie jej przez zmienną
podstawa
, wykonywane przy każdym kolejnym powtórzeniu pętli, nie odnosiłoby żadnego skutku i nadal wynosiłaby
ona 0.
Wreszcie w wierszach 7-10 znajduje się pętla printująca poszczególne potęgi w arkuszu.
Postać ogólna pętli wygląda następująco:
For i = liczbaPoczatkowa To liczbaKoncowa
'operacje do wykonania
Next i
For i = liczbaPoczatkowa To liczbaKoncowa
'operacje do wykonania
Next i
Każda pętla rozpoczyna się od słowa kluczowego For
. Po tym słowie musi nastąpić nazwa tzw.
iteratora, czyli zmiennej przechowującej liczbę powtórzeń pętli. Przyjęło się, aby zmienną iteracyjną
nazywać i
(tak też uczyniono w omawianym przykładzie), ale nie jest to konieczne, więc możesz nadać
iteratorowi swoją własną nazwę. Zmienna iteracyjna nie różni się niczym od pozostałych zmiennych, dlatego, podobnie
jak one, przed swoim pierwszym wystąpieniem musiała zostać zadeklarowana.
Podstawowym zadaniem zmiennej iteracyjnej jest nadzorowanie, czy pętla nie powinna już zakończyć swojego działania.
Wywołując pętle należy podać wartość startową (liczbaPoczatkowa
) oraz końcową (liczbaKoncowa
)
iteratora, na podstawie których sprawdza on stan wykonania pętli.
Przy uruchomieniu pętli do iteratora przypisywana jest wartość początkowa określona w wierszu wywołania przez argument
liczbaPoczatkowa
. Po każdym wykonaniu pętli wartość iteratora jest zwiększana o 1, a przed przystąpieniem
do kolejnego powtórzenia sprawdzane jest czy aktualna wartość iteratora nie przekracza maksymalnej wartości (określonej
w wierszu wywołania pętli za pomocą argumentu liczbaKoncowa
). Jeżeli kompilator stwierdzi, że wartość
zmiennej iteracyjnej przekroczyła już górną granicę, wykonywanie kodu zostaje przeniesione do poleceń znajdujących
się bezpośrednio za pętlą.
Pamiętaj, że aby pętla wykonała jakiekolwiek operacje, wartość argumentu liczbaKoncowa
nie może być
mniejsza od wartości argumentu liczbaPoczatkowa
. Jeżeli będzie inaczej, kompilator już przy pierwszym
wywołaniu stwierdzi, że wartość iteratora (która przy starcie pętli przyjmuje wartość zmiennej
liczbaPoczatkowa
), jest większa od górnej granicy pętli (czyli wartości zmiennej liczbaKoncowa
),
a więc nadszedł czas na zakończenie działania pętli i przejście do kolejnych instrukcji.
Poniżej znajduje się przykład pętli, której instrukcje nigdy nie zostaną wykonane:
For i = 5 To 3
'operacje do wykonania
Next i
For i = 5 To 3
'operacje do wykonania
Next i
Zauważ, że już na starcie zmienna iteracyjna i
otrzymuje wartość 5. Równocześnie zostaje określone,
że pętla ma zakończyć swoje działanie w momencie, gdy zmienna ta osiągnie wartość większą niż 3 - a więc już w
momencie nadania zmiennej i
wartości początkowej przekracza ona górny limit, co doprowadza do
automatycznego wyjścia z pętli.
Najważniejszą częścią pętli jest jej wnętrze, w którym opisane są instrukcje, jakie mają być wykonane przy każdym powtórzeniu pętli.
W opisywanym przykładzie we wnętrzu pętli znajdują się dwie operacje: wpisanie wartości zmiennej potega
w odpowiedniej komórce arkusza oraz pomnożenie tej zmiennej przez podstawę potęgi przechowywaną w argumencie
podstawa
i stworzenie tym samym kolejnej potęgi.
Zauważ, że w operacji printowania wartości do arkusza wykorzystana została zmienna iteracyjna i
. Dzięki
temu, że przy każdym powtórzeniu pętli jest ona zwiększana o 1, każda kolejna potęga jest wypisywana jeden wiersz
niżej od swojej poprzedniczki.
Wykorzystanie zmiennej iteracyjnej w operacjach znajdujących się wewnątrz pętli jest powszechną praktyką i w zasadzie pętle, które tego nie czynią, należą do rzadkości.
Ostatnim elementem konstrukcji pętli jest wiersz zamknięcia:
Next i
składający się ze słowa kluczowego Next
oraz nazwy iteratora.
Next i
Po dotarciu do wiersza zamknięcia pętli, kompilator zwiększa wartość iteratora o 1 i powraca do wiersza otwarcia pętli. Kolejnym krokiem jest sprawdzenie czy po wykonanym przed momentem zwiększeniu wartości iteratora nie przekroczyła ona górnego limitu i ewentualne opuszczenie pętli (w przypadku przekroczenia przez iterator górnej granicy) lub kontynuowanie jej wykonywania (w przypadku gdy wartość iteratora nadal jest mniejsza niż górna granica pętli).
Jak zatem zachowuje się pętla i poszczególne zmienne w omówionym przykładzie?
7
8
9
10
For i = 1 To ilePoteg
Worksheets("Arkusz1").Cells(i, kolumna) = potega
potega = potega * podstawa
Next i
Zmienną iteracyjną jest w tej pętli zmienna i
, dla której początkową wartością jest liczba 1,
a końcową wartość zmiennej ilePoteg
(czyli liczba podana jako argument o takiej nazwie przy wywołaniu
procedury; załóżmy, że wywołałeś tę procedurę z argumentem ilePoteg = 20
).
Za każdym razem, kiedy wykonywanie kodu znajduje się w wierszu otwarcia pętli, zmienna iteracyjna sprawdzana jest pod
kątem przekraczania górnej granicy pętli (nawet jeżeli jest to dopiero pierwsze wywołanie tej pętli). W tym przypadku
zmienna i
wynosi 1, a górna granica pętli - 20, więc pętla nie musi być zakończona i wykonywanie kodu jest
przekazywane do wnętrza pętli.
Pierwszą instrukcją we wnętrzu pętli jest polecenie:
8
Worksheets("Arkusz1").Cells(i, kolumna) = potega
czyli wyprintowanie w arkuszu Arkusz1 wartości zmiennej potega
(która wynosi 1, gdyż taka wartość
została jej nadana przed wejściem do pętli, w wierszu 5). Wartość iteratora wynosi aktualnie 1,
więc zmienna zostanie wyświetlona w pierwszym wierszu arkusza oraz w kolumnie takiej, jaką określono za pomocą argumentu
kolumna
w momencie wywoływania procedury.
8
Worksheets("Arkusz1").Cells(i, kolumna) = potega
Drugą czynnością wykonywaną przez tę pętlę jest pomnożenie zmiennej potega
przez podstawę potęgi
przechowywaną w zmiennej podstawa
. Po wykonaniu tej operacji zmienna potega
będzie
miała wartość 2, czyli kolejną potęgę dwójki.
Następnie kompilator przechodzi do wiersza
10
Next i
w którym wartość zmiennej iteracyjnej i
jest zwiększana o 1 (a więc będzie od teraz wynosiła 2).
10
Next i
Po opuszczeniu wiersza zamknięcia pętli wykonanie kodu zawsze wraca do wiersza otwarcia pętli, gdzie kompilator ponownie sprawdza czy wartość zmiennej iteracyjnej nie przekroczyła górnego limitu. Jak przed momentem wspomniano, iterator przyjął dopiero wartość 2 (przy górnej granicy równej 20), a więc ponownie wykonywane są instrukcje zawarte we wnętrzu pętli.
Wartość zmiennej potega
wynosi w tym momencie 2 i taki wynik zostanie teraz wyprintowany w arkuszu
Arkusz1 w jego drugim wierszu (ponieważ, jak widzisz w kodzie, o wierszu, w którym będzie wypisana zmienna
potega
decyduje wartość iteratora i
, która aktualnie wynosi 2). W kolejnym wierszu zmienna
potega
ponownie jest mnożona przez podstawę, tworząc tym samym kolejną potęgę dwójki, po czym kod jest
przekazywany do wiersza zamknięcia pętli, gdzie zmienna iteracyjna jest zwiększana o 1 i przyjmuje teraz wartość 3.
Opisana sekwencja operacji jest powtarzana tak długo, aż wartość iteratora osiągnie 20. Wówczas kompilator wykona
jeszcze instrukcje zawarte we wnętrzu pętli, ponieważ iterator nie przekracza górnej granicy pętli, a jedynie się
z nią zrównuje - makro wyświetli więc w dwudziestym wierszu arkusza wartość dwudziestej potęgi dwójki oraz zwiększy
wartość zmiennej potega
do dwudziestej pierwszej potęgi dwójki. Ale po nadaniu zmiennej iteracyjnej w
wierszu Next i
kolejnej wartości - 21, iterator nie przejdzie już weryfikacji w wierszu otwarcia pętli,
ponieważ jego wartość jest od tej pory wyższa niż górny limit pętli (określony na 20). W tym momencie makro opuszcza
więc pętlę i przechodzi do wykonywania kolejnych instrukcji, znajdujących się w kodzie pod tą pętlą (w omawianym
przykładzie jest to już tylko zamknięcie procedury słowem kluczowym End Sub
).
Zmiana kroku pętli
Podczas omawiania przykładu wypisującego w arkuszu potęgi, wielokrotnie wspomniano, że po dotarciu do wiersza zamknięcia
pętli (Next i
), wartość iteratora jest zwiększana o 1. Nie dla każdej pętli jest to jednak prawda, ponieważ
w rzeczywistości sam możesz zadecydować o jaką wartość będzie powiększany iterator przy każdym wykonaniu pętli - masz w
tej kwestii całkowitą dowolność i jako wartość powiększającą iterator możesz równie dobrze użyć liczby naturalnej, jak i
ułamka czy liczby ujemnej.
Poniżej znajduje się omawiany wcześniej kod, zmodyfikowany tak, aby poszczególne potęgi były wyświetlane w co drugim
wierszu (przed jego uruchomieniem i przetestowaniem wyczyść arkusz, tak aby poprzednie wpisy nie wymieszały się z nowymi).
1
2
3
4
5
6
7
8
9
10
11
Sub wyswietlajPotegi(podstawa As Byte, ilePoteg As Integer, kolumna As Byte)
Dim i As Integer
Dim potega As Long
potega = 1
For i = 1 To ilePoteg Step 2
Worksheets("Arkusz1").Cells(i, kolumna) = potega
potega = potega * podstawa
Next i
End Sub
1
2
3
4
5
6
7
8
9
10
11
Sub wyswietlajPotegi(podstawa As Byte, ilePoteg As Integer, kolumna As Byte)
Dim i As Integer
Dim potega As Long
potega = 1
For i = 1 To ilePoteg Step 2
Worksheets("Arkusz1").Cells(i, kolumna) = potega
potega = potega * podstawa
Next i
End Sub
Jak widzisz, jedynym nowym elementem, jaki pojawił się w kodzie, jest polecenie Step 2
dopisane w wierszu
otwarcia pętli. W poprzednim przykładzie słowo kluczowe Step
zostało pominięte, co było równoznaczne z
nadaniem pętli wartości domyślnej 1. W obecnej postaci pętli, za każdym razem, kiedy wykonanie kodu dojdzie do wiersza
zamknięcia pętli, wartość iteratora będzie zwiększana o 2, a nie ja dotychczas o 1.
Zwróć też uwagę, że w tej sytuacji zostanie wyświetlonych tylko dziesięć pierwszych potęg dwójki, ponieważ od wartości startowej, wynoszącej 1, do górnej granicy pętli równej 20, przy każdorazowym zwiększaniu iteratora o 2, nastąpi tylko 10 wywołań pętli.
Pętla może być również skonstruowana w taki sposób, aby przy każdym jej wywołaniu wartość iteratora była zmniejszana.
Zasada działania jest w tym przypadku identyczna, należy po prostu w wierszu otwarcia pętli wstawić po słowie kluczowym
Step
liczbę ujemną, o jaką ma być pomniejszany iterator przy każdym powtórzeniu pętli.
1
2
3
4
5
6
7
8
9
10
11
Sub wyswietlajPotegi(podstawa As Byte, ilePoteg As Integer, kolumna As Byte)
Dim i As Integer
Dim potega As Long
potega = 1
For i = ilePoteg To 1 Step -1
Worksheets("Arkusz1").Cells(i, kolumna) = potega
potega = potega * podstawa
Next i
End Sub
Powyższe makro jest kolejną modyfikacją makra wyświetlającego w arkuszu dwadzieścia pierwszych potęg dwójki, z tym że teraz wyświetlane są one w odwrotnej kolejności - od dwudziestego do pierwszego wiersza.
Zauważ, że w wierszu otwarcia początkowa wartość iteratora jest większa od jej limitu, co jak wcześniej kilkukrotnie
wspomniano powinno oznaczać automatyczne zakończenie działania pętli. Otóż w przypadku zadeklarowania w pętli ujemnej
wartości Step
, sytuacja ulega zmianie o 180 stopni - teraz górny limit staje się dolnym limitem, a pętla
kończy działanie, kiedy wartość iteratora jest od tego limitu mniejsza.
Wykorzystując słowo kluczowe Step
można bardzo łatwo stworzyć
nieskończoną pętlę. Wystarczy wpisać w wierszu otwarcia pętli polecenie Step 0
.
Zmiana wartości iteratora wewnątrz pętli
We wszystkich omawianych dotychczas pętlach wartość iteratora była zmieniana tylko poprzez wiersz zamknięcia pętli -
Next i
. Nie oznacza to jednak, że zmienna iteracyjna nie może być modyfikowana
wewnątrz pętli.
Poniżej znajduje się przykład makra, które wypisuje w arkuszu wszystkie daty przypadające w dni powszednie, modyfikując
wartość iteratora tak, aby przeskakiwał dni przypadające w weekend.
1
2
3
4
5
6
7
8
9
10
11
12
13
Sub wyswietlajDniPowszednie()
Dim data As Date
Dim n As Long
n = 1
For data = #2010-01-01# To #2010-12-31#
Cells(n, 1) = Format(data, "Long Date")
n = n + 1
If Weekday(data, vbMonday) = 5 Then data = data + 2
Next data
End Sub
1
2
3
4
5
6
7
8
9
10
11
12
13
Sub wyswietlajDniPowszednie()
Dim data As Date
Dim n As Long
n = 1
For data = #2010-01-01# To #2010-12-31#
Cells(n, 1) = Format(data, "Long Date")
n = n + 1
If Weekday(data, vbMonday) = 5 Then data = data + 2
Next data
End Sub
W makrze zadeklarowane zostały dwie zmienne:
data
- zmienna typu Date
. Przechowuje kolejne daty, a równocześnie służy w
pętli jako zmienna iteracyjna,
n
- zmienna typu Long
. Przechowuje numer wiersza, w którym ma być wyświetlona
kolejna data. Wartość tej zmiennej jest zwiększana o 1 przy każdym kolejnym wykonaniu pętli, tak aby każda kolejna data
była wypisana jeden wiersz niżej od poprzedniej.
W poprzednich przykładach jako numer wiersza wykorzystywana była zmienna iteracyjna. W tym przypadku nie można
zastosować iteratora jako numeru wiersza z dwóch powodów: początkową wartością iteratora jest data
1 stycznia 2010, której odpowiada liczba 40 179, a więc printowanie rozpoczęłoby się dopiero od wiersza
o takim numerze; poza tym, aby pominąć soboty i niedziele, wartość iteratora czasami przeskakuje o 2, więc gdyby został
on wykorzystany jako numer wiersza, w arkuszu również występowałyby dwuwierszowe przerwy.
data
- zmienna typu Date
. Przechowuje kolejne daty, a równocześnie służy w
pętli jako zmienna iteracyjna,n
- zmienna typu Long
. Przechowuje numer wiersza, w którym ma być wyświetlona
kolejna data. Wartość tej zmiennej jest zwiększana o 1 przy każdym kolejnym wykonaniu pętli, tak aby każda kolejna data
była wypisana jeden wiersz niżej od poprzedniej.W poprzednich przykładach jako numer wiersza wykorzystywana była zmienna iteracyjna. W tym przypadku nie można zastosować iteratora jako numeru wiersza z dwóch powodów: początkową wartością iteratora jest data 1 stycznia 2010, której odpowiada liczba 40 179, a więc printowanie rozpoczęłoby się dopiero od wiersza o takim numerze; poza tym, aby pominąć soboty i niedziele, wartość iteratora czasami przeskakuje o 2, więc gdyby został on wykorzystany jako numer wiersza, w arkuszu również występowałyby dwuwierszowe przerwy.
W wierszu 5 kodu do zmiennej n
zostaje przypisana początkowa wartość 1, gdyż
wypisywanie dat ma się rozpocząć od pierwszego wiersza arkusza.
W wierszu 6 rozpoczyna się pętla, w której iteratorem jest zmienna data
, wartością
początkową 1 stycznia 2010, a wartością końcową 31 grudnia 2010.
Wnętrze pętli składa się z trzech poleceń.
Najpierw aktualna wartość zmiennej data
jest wyświetlana w arkuszu, w jego pierwszej kolumnie i wierszu
określonym przez zmienną n
(przy pierwszym wykonaniu pętli jest to pierwszy wiersz arkusza). Zmienna
Data
przed wyświetleniem w arkuszu jest jeszcze obrabiana przez funkcję Format
z parametrem
Long Date, która formatuje datę do postaci zawierającego pełną nazwę miesiąca (np. 1 styczeń 2010).
W kolejnym wierszu wartość zmiennej n
jest zwiększana o 1, tak aby każa kolejna data była wyświetlana
w jednym wierszu poniżej swojej poprzedniczki.
W ostatnim wierszu wnętrza pętli znajduje się instrukcja warunkowa, sprawdzająca czy aktualna data, przechowywana w
zmiennej data
, przypada w piątek. Jeżeli tak się dzieje, do wartości iteratora dodawana jest liczba 2,
tak aby ominąć dwa dni: sobotę oraz niedzielę. Zwróć uwagę, że po dodaniu do piątkowej daty dwóch dni, zmienna
data
przyjmie wartość będącą datą niedzielną. Należy jednak pamiętać, że w wierszu zamknięcia pętli
(Next data
), wartość zmiennej iteracyjnej jest dodatkowo zwiększana o 1,
więc ostatecznie przed kolejnym wkroczeniem kodu do wnętrza pętli zmienna data
będzie przechowywała
poniedziałkową datę.
Manipulując wartością iteratora we wnętrzu pętli również można łatwo stworzyć nieskończoną pętlę.
Wystarczy przed wierszem zamknięcia pętli odejmować od aktualnej wartości iteratora 1 (lub inną liczbę przypisaną w
wierszu otwarcia do argumentu Step
). Wówczas, tuż przed wejściem kodu do
linii zamykającej pętlę, iterator będzie zmniejszany o 1, a w samym wierszu zamknięcie będzie zwiększany o 1,
w związku z czym cały czas będzie miał tę samą wartość i nigdy nie przekroczy górnego limitu pętli.
Opuszczenie pętli przed jej zakończeniem
Czasem zdarzają się sytuacje, że pomimo określenia górnego limitu pętli, powinna ona zakończyć swoje działanie przed jego osiągnięciem, ponieważ po spełnieniu określonego warunku, jej dalsze działanie staje się bezcelowe.
Przykład takiej pętli znajdziesz w przedstawionej poniżej funkcji znajdzOdPrawej
. Funkcja ta znajduje pierwsze
od prawej strony wystąpienie znaku określonego jako argument char
w tekście podanym jako argument
tekst
.
Pętla powinna sprawdzić po kolei wszystkie znaki podanego tekstu, począwszy od ostatniego, a na pierwszym skończywszy. Jednak w momencie, gdy szukany znak zostanie odnaleziony, pętla powinna zakończyć działanie, ponieważ wynik funkcji jest już w tym momencie znany i nie ma sensu marnowanie czasu na dalsze wykonywanie pętli, skoro i tak w żaden sposób nie wpłynie to już na końcowy wynik funkcji.
Poniżej znajduje się kod oraz jego analiza:
1
2
3
4
5
6
7
8
9
10
Function znajdzOdPrawej(char As String, tekst As String) As Integer
Dim i As Integer
For i = Len(tekst) To 1 Step -1
If Mid(tekst, i, 1) = char Then
znajdzOdPrawej = i
Exit For
End If
Next i
End Function
1
2
3
4
5
6
7
8
9
10
Function znajdzOdPrawej(char As String, tekst As String) As Integer
Dim i As Integer
For i = Len(tekst) To 1 Step -1
If Mid(tekst, i, 1) = char Then
znajdzOdPrawej = i
Exit For
End If
Next i
End Function
Funkcja wymaga zadeklarowania dwóch argumentów: char
- czyli znaku, który będzie szukany w tekście bazowym
oraz sam tekst bazowy, określony w powyższym przykładzie jako tekst
.
Oprócz tego w funkcji zadeklarowana jest zmienna typu Integer
, która posłuży za zmienną iteracyjną w pętli.
Cała funkcja składa się jedynie z opisywanej pętli. W wierszu otwarcia pętli jako wartość początkowa iteratora została
podana długość tekstu bazowego, która jest wyliczana za pomocą funkcji Len(tekst)
(o funkcji Len
możesz poczytać
tutaj).
Jako końcowy limit pętli podana została wartość 1. Określono również argument Step
i nadano mu wartość -1, co oznacza, że po każdym powtórzeniu pętli wartość iteratora będzie zmniejszana o 1.
Wnętrze pętli składa się tylko z jednej instrukcji warunkowej, która sprawdza czy dany znak tekstu bazowego jest
poszukiwanym znakiem, określonym jako argument char
. Do sprawdzania poszczególnych znaków wyrazu bazowego
wykorzystana została funkcja Mid(tekst, i, 1)
, która zwraca pojedynczy znak z tekstu tekst
,
znajdujący się w tym tekście na pozycji takiej, jak argument i
(czyli na pozycji takiej, jaka jest aktualnie
wartość zmiennej iteracyjnej w pętli). Przy pierwszym wykonaniu pętli sprawdzany więc będzie ostatni znak tekstu bazowego,
przy drugim wykonaniu - przedostatni, itd. aż zmienna iteracyjna osiągnie wartość 1, czyli sprawdzony będzie pierwszy znak
tekstu.
W instrukcji warunkowej znajdującej się w pętli pominięty został blok Else
, ponieważ w sytuacji, gdy
sprawdzany znak nie jest szukanym znakiem, nie mają być wykonywane żadne czynności poza przejściem do następnego
wykonania pętli.
Natomiast w sytuacji, gdy okaże się, że sprawdzany znak jest szukanym znakiem, jako wartość funkcji zostaje przypisana
aktualna wartość zmiennej iteracyjnej i
, ponieważ na takiej właśnie pozycji został znaleziony poszukiwany
znak (wiersz 6).
Oprócz tego, w bloku czynności przewidzianych dla spełnionego warunku znajduje się instrukcja
Exit For
(wiersz 7), która oznacza natychmiastowe
opuszczenie pętli bez względu na to, jaka jest aktualna wartość zmiennej iteracyjnej. Tak jak wcześniej wspomniano,
polecenie to jest w tej sytuacji użyte, ponieważ po odnalezieniu w tekście bazowym szukanego znaku znana jest już końcowa
wartość funkcji i nie ma potrzeby dalszego wykonywania tej pętli. W zasadzie dalsze wykonywanie pętli mogłoby nawet
zniekształcić wynik funkcji, ponieważ jeśli szukany znak zostałby ponownie odnaleziony w bazowym tekście (bliżej
początku tego tekstu), to wynik funkcji zostałby nadpisany i funkcja zwracałaby pierwsze wystąpienie szukanego
znaku od lewej strony, zamiast od prawej.
Na koniec możesz sprawdzić działanie napisanej przed momentem funkcji znajdzOdPrawej
, wpisując kilka zapytań
w oknie Immediate:
Zagnieżdżanie pętli
Pętle, podobnie jak instrukcje warunkowe czy funkcje, mogą być w sobie zagnieżdżane.
Przykład takiej zagnieżdżonej pętli znajduje się w poniższym makrze, które wyświetla w arkuszu tabliczkę mnożenia
o dowolnym rozmiarze (podawanym jako argument przy wywoływaniu makra).
1
2
3
4
5
6
7
8
9
10
Sub tabliczkaMnozenia(rozmiar As Integer)
Dim i As Integer
Dim j As Integer
For i = 1 To rozmiar
For j = 1 To rozmiar
Cells(i, j) = i * j
Next j
Next i
End Sub
1
2
3
4
5
6
7
8
9
10
Sub tabliczkaMnozenia(rozmiar As Integer)
Dim i As Integer
Dim j As Integer
For i = 1 To rozmiar
For j = 1 To rozmiar
Cells(i, j) = i * j
Next j
Next i
End Sub
W procedurze tabliczkaMnozenia
zadeklarowany został jeden argument wejściowy - rozmiar
, który
decyduje o rozmiarze wyświetlanej w arkuszu tabliczki mnożenia.
Oprócz tego procedura zawiera dwie zmienne typu Integer
- i
oraz
j
, które pełnią rolę iteratorów w pętlach.
Wiersz 5 jest wierszem otwarcia pierwszej pętli (z iteratorem i
), która zostanie
wykonana tyle razy, ile wynosi wartość argumentu rozmiar
. Każde pojedyncze wykonanie tej pętli spowoduje
wyświetlenie w arkuszu kolejnego wiersza tabliczki mnożenia.
We wnętrzu tej pętli zagnieżdżona jest druga pętla (wiersze 6-8), w której iteratorem jest
zmienna j
. Liczba powtórzeń tej pętli także jest równa argumentowi rozmiar
. Jeżeli więc
wywołując procedurę tabliczkaMnozenia
jako wartość argumentu rozmiar
podałeś przykładowo
40, to pierwsza pętla zostanie wykonana 40 razy, a w każdym jej pojedynczym wykonaniu dodatkowo 40 razy wykonana
zostanie pętla, która jest w niej zagnieżdżona (ta druga pętla zostanie więc wykonana łącznie 1600 razy). Przy
każdym powtórzeniu wewnętrznej pętli wykonywane jest jedno polecenie:
7
Cells(i, j) = i * j
które mnoży przez siebie wartość obu iteratorów, a następnie wyświetla wynik tego mnożenia w aktywnym arkuszu,
w wierszu takim, jaka jest aktualna wartość iteratora i
i kolumnie o numerze równym aktualnej
wartości iteratora j
.
7
Cells(i, j) = i * j
Schemat działania powyższej procedury jest więc następujący (załóżmy, że przy wywołaniu funkcji jako argument
rozmiar
podano 40): kiedy wykonanie kodu dociera do wiersza 5 zostaje otwarta
pierwsza pętla, a iterator i
przyjmuje wartość 1. W kolejnym wierszu (6), otwarta
zostaje druga pętla, której iterator (czyli zmienna j
) również otrzymuje początkową wartość 1. Górną
granicą tej pętli jest wartość argumentu rozmiar
, a więc zostanie ona wykonana 40 razy. Zwróć uwagę,
że podczas powtarzania wewnętrznej pętli wartość iteratora i
cały czas jest równa 1, ponieważ kod nie
dotarł jeszcze do wiersza zamknięcia pierwszej pętli. Natomiast wartość iteratora tej wewnętrznej pętli (j
)
przy każdym jej powtórzeniu zwiększa się o 1. W związku z tym, przy pierwszym wykonaniu zagnieżdżonej pętli wykonana
zostanie operacja
7
Cells(1, 1) = 1 * 1
potem
7
Cells(1, 2) = 1 * 2
7
Cells(1, 3) = 1 * 3
'...
itd. Dopiero po wykonaniu 40 takich operacji opuszczona zostanie wewnętrzna pętla i kompilator natrafi na wiersz
zamknięcia pierwszej pętli. Wówczas wartość iteratora i
zostanie zwiększona o 1 (będzie teraz wynosić
2) i wykonywanie kodu ponownie trafi do wewnętrznej pętli. Cały ten proces powtarzany będzie tak długo, aż wartość
iteratora i
przekroczy górny limit wyznaczony dla zewnętrznej pętli (czyli w tym przypadku 40).
7
Cells(1, 1) = 1 * 1
7
Cells(1, 2) = 1 * 2
7
Cells(1, 3) = 1 * 3
'...