06 - Funkcje


Z drugiej lekcji dowiedzieliśmy się, że kod może być przechowywany w procedurach lub funkcjach.

Wszystkie dotychczas przedstawione przykłady opierały się jednak tylko na procedurach. Czas więc nadrobić zaległości i poświęcić trochę czasu funkcjom.

W tej lekcji ponownie będzie też mowa o przekazywaniu argumentów do procedur - temat ten zostanie rozwinięty o argumenty opcjonalne.

Nauczymy się też korzystać z okna Immediate, dzięki któremu można jeszcze szybciej i wygodniej uruchamiać makra oraz podglądać wartości zmiennych w momencie zatrzymania działania makra (a nawet bez jego zatrzymywania!).

Podstawy funkcji

Podstawową, a tak naprawdę jedyną, różnicą pomiędzy procedurą a funkcją jest fakt, że funkcja potrafi i powinna zwrócić jakąś wartość, zmienną lub obiekt.

Dotychczas wywoływaliśmy tylko procedury, które miały w sobie zadeklarowane jakieś zmienne, wykonywały jakieś polecenia, ale nie zwracały żadnej wartości na zewnątrz siebie.

Owszem, w przykładzie z porzedniej lekcji, widziałeś, że procedura może wpłynąć na wartość zmiennej z innej procedury, jeżeli zmienna ta została do niej przekazana jako argument. Jednak ciągle nie jest to zwracanie wartości, a tylko modyfikowanie otrzymanej zmiennej.

Aby trochę rozjaśnić cały temat, wróćmy do przykładu z poprzedniej lekcji, a mianowicie obliczania podatków. Kod tamtego przykładu wyglądał tak jak poniżej:
1
2
3
4
5
6
7
Sub zarobki()
    Dim pensja As Long

    pensja = 2500
    Call odliczeniePodatku(pensja)

End Sub
1
2
3
Sub odliczeniePodatku(podstawa As Long)
    podstawa = podstawa - (podstawa * 0.18)
End Sub

Dla przypomnienia, makro to działało w następujący sposób: istniała jedna zmienna pensja, do której na początku przypisywany był zarobek brutto, następnie zmienną pensja przekazywano do procedury odliczaniePodatku, w której od tej zmiennej odejmowany był podatek, i w efekcie zmienna wracała do bazowej procedury już w postaci netto.

Załóżmy jednak, że chcemy mieć w naszym programie dwie zmienne - pensjaNetto oraz pensjaBrutto i do każdej z nich przypisać odpowiednią wartość.

Na początku musimy oczywiście przypisać do zmiennej pensjaBrutto wysokość pensji (niech ponownie będzie to 2500).

Teraz jednak zaczynają się schody. Gdybyśmy postąpili tak, jak w poprzednim przykładzie, i po prostu wywołali procedurę odliczaniePodatku podając jako argument zmienną pensjaBrutto, to procedura ta zmieniłaby wartość zmiennej pensjaBrutto na 2050, natomiast zmienna pensjaNetto ciągle miałaby wartość 0 - a więc zupełnie niezgodnie z założeniami.

Poniżej znajduje się kod z takim błędnym rozwiązaniem, tak byś mógł go samodzielnie uruchomić i prześledzić (najlepiej ustaw punkty zatrzymania w każdej jego linijce i sprawdzaj po kolei co się dzieje ze zmiennymi).
1
2
3
4
5
6
7
8
Sub zarobki()
    Dim pensjaBrutto As Long
    Dim pensjaNetto As Long

    pensjaBrutto = 2500
    Call odliczeniePodatku(pensjaBrutto)

End Sub
1
2
3
Sub odliczeniePodatku(podstawa As Long)
    podstawa = podstawa - (podstawa * 0.18)
End Sub

Jednym z rozwiązań tego problemu byłoby poniższe makro (Również polecam wstawić punkty zatrzymania w każdej linijce i sprawdzać, co się dzieje ze zmiennymi. Ogólnie zalecam stosowanie tej metody tak długo, aż osiągniesz wprawę pozwalającą jednym rzutem oka na kod zauważyć, co się dzieje ze zmiennymi w każdym momencie wykonywania programu).
1
2
3
4
5
6
7
8
Sub zarobki()
    Dim pensjaBrutto As Long
    Dim pensjaNetto As Long

    pensjaBrutto = 2500
    Call odliczeniePodatku(pensjaBrutto, pensjaNetto)

End Sub
1
2
3
Sub odliczeniePodatku(podstawa As Long, pensjaNetto As Long)
    pensjaNetto = podstawa - (podstawa * 0.18)
End Sub

Zaprezentowany powyżej kod wprawdzie działa, a nawet zwraca prawidłowe wyniki, są jednak pewne ale ...

Najpoważniejszym minusem takiego rozwiązania jest fakt, że procedura odliczeniePodatku wymaga teraz podania dwóch argumentów, w tym jednego, który absolutnie nie jest potrzebny.

Wyobraź sobie, że musiałbyś odpowiedzieć na takie oto pytanie: Jeżeli znana jest stała stawka do obliczania podatków (18%), to jakie informacje są potrzebne, żeby móc obliczyć Twoją pensję netto?

Oczywiście odpowiedziałbyś, że jedyną potrzebną informacją jest pensja brutto. Zdajesz więc sobie sprawę, że wymagana jest tylko jedna wartość - pensja brutto. Natomiast procedura odliczaniePodatku w powyższym przykładzie wymagałaby podawania dwóch zmiennych. Jedna z nich - pensjaNetto - przekazywana jest jako pusta zmienna, czego absolutnie nie powinno się robić!

Dlaczego jest to takie ważne? Otóż każde przekazanie argumentu do procedury kosztuje trochę pamięci i czasu obliczeń. Oczywiście w skali jednego wywołania procedury są to mikrosekundy i nikt nie zauważy tego gołym okiem. Wyobraź sobie jednak, że Twoje makro będzie musiało obliczyć taki podatek dla 30 tysięcy osób z okresu 10 lat. Wtedy ta procedura będzie musiała być wykonana setki tysięcy razy, a czas zmarnowany na przekazywanie pustej zmiennej będzie już liczony w minutach, a nie mikrosekundach.

Drugi minus takiego rozwiązania jest taki, że jest to po prostu mało przejrzyste rozwiązanie.

Może trudno to sobie w tym momencie wyobrazić, ale jeżeli nadal będziesz się zajmował programowaniem w VBA, to po niedługim czasie jeden rzut oka na kod będzie pozwalał Tobie zauważyć wiele rzeczy, na przykład w którym miejscu do zmiennych zostały przypisane wartości. Niestety przy zastosowaniu takiego rozwiązania bardzo ciężko wyłapać miejsce, w którym zmienna pensjaNetto przestaje mieć wartość 0 i przyjmuje swoją właściwą wartość.

Powyżej pokazano dwa nieprawidłowe rozwiązania, których nie powinieneś stosować w takich sytuacjach. Jedno z nich wprawdzie zwracało prawidłowe wyniki, ale argumenty przedstawione przeciwko takiemu rozwiązaniu powinny przekonać Cię do jego niestosowania.

W poniższej ramce przedstawione zostało wreszcie prawidłowe rozwiązanie tego problemu, które, jak się pewnie domyślasz, wykorzystuje funkcję. Pod ramką z kodem znajdziesz natomiast jego szczegółowe omówienie.

1
2
3
4
5
6
7
8
Sub zarobki()
    Dim pensjaBrutto As Long
    Dim pensjaNetto As Long

    pensjaBrutto = 2500
    pensjaNetto = pensjaPoOpodatkowaniu(pensjaBrutto)

End Sub
1
2
3
Function pensjaPoOpodatkowaniu(podstawa As Long) As Long
    pensjaPoOpodatkowaniu = podstawa - (podstawa * 0.18)
End Function

Z kodu całkowicie została usunięta procedura odliczaniePodatku, w miejsce której pojawiła się funkcja pensjaPoOpodatkowaniu.

Całe makro składa się teraz z procedury zarobki oraz funkcji pensjaPoOpodatkowaniu.

W procedurze zarobki zadeklarowane są dwie zmienne typu Long - pensjaBrutto i pensjaNetto.

W wierszu 5 do zmiennej pensjaBrutto przypisana zostaje wartość 2500. W tym momencie jedynym zadaniem, jakie pozostało jeszcze do wykonania, jest obliczenie pensji netto i przypisanie jej do zmiennej pensjaNetto. Zadanie to zostanie wykonane przez wspomnianą funkcję.

Funkcję możesz sobie wyobrazić jako skrzynkę, do której wrzucane są argumenty, następnie skrzynka ta wykonuje jakieś obliczenia i inne operacje, a w efekcie zwraca tylko jedną wartość, którą można potem przypisać do jakiejś zmiennej albo wykorzystać w jakiś inny sposób (na przykład wyprintować w arkuszu).

W szóstej linijce opisywanego przykładu określono, że do zmiennej pensjaNetto przypisane zostanie to, co będzie wynikiem działania funkcji pensjaPoOpodatkowaniu przy określonej wartości argumentów (w tym przypadku argument pensjaBrutto wynosi 2500).

W momencie, w którym makro napotyka w swoim kodzie na funkcję, przenosi wykonywanie kodu do tej funkcji i powróci do procedury zarobki po jej zakończeniu.

Szczegółowego omówienia wymaga budowa i działanie samej funkcji.

Jak już wspominałem podczas drugiej lekcji, słowem kluczowym otwierającym funkcję jest Function. Po tym słowie następuje nazwa funkcji (zasady nazywania funkcji są takie same jak omówione już wcześniej zasady nazywania procedur).

Następnie należy w nawiasie wymienić argumenty niezbędne do obliczenia tej funkcji wraz z podaniem ich typu. Oczywiście czasem zdarzają się funkcje, które nie posiadają żadnych argumentów wejściowych - wówczas nawias pozostaje pusty.

Ostatnim elementem wiersza otwarcia funkcji jest podanie słowa kluczowego As oraz typu wartości jaki jest zwracany przez tę funkcję.

Ogólna postać wiersza otwarcia funkcji wygląda więc następująco:
 
Function nazwaFunkcji([arg1 As typ, ..., argN As typ]) [As typFunkcji]

Jeżeli w wierszu otwarcia funkcji nie podasz typu zwracanej wartości, edytor VBA będzie ją domyślnie traktował jako typ Variant, co, jak już wspomniano w lekcji poświęconej typom zmiennych, może znacznie obciążyć pamięć komputera i spowolnić działanie całego makra.

W powyższym przykładzie funkcja została nazwana pensjaPoOpodatkowaniu, tak aby od razu było widać, co takiego zwraca w wyniku. Funkcja otrzymała typ Long, dzięki czemu otrzymany wynik będzie zaokrąglany do części całkowitych. Jako argument podawana jest natomiast jedyna informacja potrzebna do jej wyliczenia, czyli zmienna pensjaBrutto, która również jest zmienną typu Long.

Uważaj na określanie typów argumentów oraz typ zwracanej wartości!

Jeżeli w wierszu otwarcia funkcji podasz np. że wymaga ona argumentu liczbowego, a wywołując tę funkcję w kodzie podasz jako argument tekst nie będący tekstową reprezentacją liczby, makro nie uruchomi się, a kompilator wyświetli błąd Run-time error 13: Type mismatch.

Omawiając wywoływanie procedur z argumentami, zaznaczyłem, że wymagane argumenty są tak jakby dodatkowymi zmiennymi w tej procedurze. Pod tym względem funkcje nie różnią się od procedur. Jeżeli więc funkcja pensjaPoOpodatkowaniu wymaga podania argumentu podstawa, oznacza to, że zawiera już co najmniej jedną zmienną - podstawa.

W przypadku funkcji dodatkową zmienną jest też nazwa samej funkcji. Jest to bardzo ważna zasada. Jeżeli nazwałeś funkcję pensjaPoOpodatkowaniu jest to równoznaczne z tym, jakbyś zadeklarował już w tej funkcji zmienną o takiej właśnie nazwie. Jeżeli spróbowałbyś dodać normalnym sposobem zmienną o takiej samej nazwie (a więc wpisując Dim pensjaPoOpodatkowaniu As Long) spowodowałoby to wyświetlenie podczas próby uruchomienia makra błędu Compile error: Duplicate declaration in current scope.

Musisz wiedzieć jeszcze jedną istotną rzecz na temat zmiennej, która jest równocześnie nazwą funkcji. Otóż nie jest to zwykła zmienna, taka, jak wszystkie pozostałe. Jest to najważniejsza zmienna w całej funkcji, która decyduje o tym, jaki będzie końcowy wynik działania funkcji.

Tak jak napisałem kilka akapitów wcześniej, funkcja jest jak skrzynka, która po wykonaniu przewidzianych w niej operacji zwraca jeden wynik. Wynik ten jest zawsze równy wartości właśnie tej najważniejszej zmiennej w momencie, kiedy funkcja kończy swoje działanie (a więc w momencie kiedy kod doszedł do linijki End Function).

W powyższym przykładzie funkcja zwróci więc taką wartość, jaka będzie przypisana do zmiennej o takiej samej nazwie jak sama funkcja, czyli pensjaPoOpodatkowaniu. Dlatego właśnie w drugim wierszu kodu funkcji przypisano do tej zmiennej działanie obliczające wartość pensji po opodatkowaniu (działanie to zostało już omówione w poprzedniej lekcji - od argumentu pensja zostaje odjęte 18% jego wartości).

Kolejny wiersz jest już poleceniem zakończenia funkcji, a więc w tym momencie wartość zmiennej pensjaPoOpodatkowaniu zostaje oficjalnie końcowym wynikiem działania funkcji.

Jako, że funkcja zakończyła już swoje działanie, wykonywanie całego kodu wraca z powrotem do procedury zarobki, do wiersza 6, w którym tę procedurę opuściło, i przypisuje do zmiennej pensjaNetto wartość 2050, czyli wynik działania omówionej przed chwilą funkcji.

Funkcje mogą być też wywoływane za pomocą słowa kluczowego Call tak, jakby były zwykłymi procedurami.

W takiej sytuacji funkcja oczywiście nie zwróci żadnej wartości, bo nie było by nawet do czego jej przypisać, skoro wywoływana jest ona samoistnie.

Wywoływanie funkcji jak procedur ma sens tylko dla funkcji, które oprócz zwracania wartości wykonują jakieś inne czynności, np. wyświetlają komunikaty czy zapisują plik. Przykład takiej funkcji poznasz już w jednej z kolejnych lekcji, kiedy omawiane będzie wyświetlanie komunikatów dla użytkownika.

Wyjaśnienia wymaga jeszcze jedna kwestia, która może budzić wątpliwości.

Po co w ogóle w powyższym przykładzie użyta jest funkcja? Czy nie łatwiej byłoby to makro napisać po prostu tak:
1
2
3
4
5
6
7
8
Sub zarobki()
    Dim pensjaBrutto As Long
    Dim pensjaNetto As Long

    pensjaBrutto = 2500
    pensjaNetto = pensjaBrutto - (pensjaBrutto * 0.18)

End Sub

W powyższym fragmencie kodu wszystkie obliczenia jakie wykonywała funkcja pensjaPoOpodatkowaniu zostały wstawione bezpośrednio do głównej procedury (zarobki), która z tej funkcji korzystała.

Wydawać by się mogło, że takie rozwiązanie jest w tej sytuacji korzystniejsze, ponieważ skróciło długość całego kodu, a i szybkość wykonywania całego makra powinna się zwiększyć, gdyż kompilator nie marnuje już czasu na wywoływanie funkcji, przekazywanie do niej argumentów czy zwracanie wartości.

Faktycznie, w tak krótkim programie rozwiązanie takie jest do zaakceptowania. Jednak prawdziwe makra, które mają jakieś poważne znaczenie użytkowe, są o wiele bardziej rozbudowane i składają się z setek procedur i funkcji.

Wyobraź sobie teraz, że Twoje rozbudowane makro składa się z 20 różnych procedur, z których każda musi w pewnym momencie obliczyć pensję netto. Jeżeli nie zastosowałbyś funkcji i chciał wstawiać obliczenia bezpośrednio w kodzie procedury, tak jak w przytoczonym przed momentem przykładzie, musiałbyś 20 razy powtórzyć ten sam fragment kodu.

W powyższej sytuacji obliczenia wykonywane przez funkcję mieszczą się w jednej linijce, więc perspektywa ich 20-krotnego powtórzenia nie jest może zbyt przerażająca. Jest to jednak tylko uproszczony model obliczania podatku (z uwagi na to, że nie znasz jeszcze kilku ważnych operacji, na potrzeby tej lekcji założono, że podatek w każdym przypadku wynosi 18%). W rzeczywistości chcąc wyliczyć podatek musiałbyś najpierw sprawdzić do którego progu podatkowego należy pensja brutto, na tej podstawie wyznaczyć odpowiednią stawkę i wyliczyć podatek. Takie obliczenia z pewnością zajmowałaby co najmniej kilka linijek.

W tym momencie wyraźnie zarysowuje się już więc przewaga funkcji.

Wykorzystując funkcję musiałbyś opisać te kilka linijek wyliczających podatek tylko w jednym miejscu kodu, a każde późniejsze odniesienie do tej funkcji zajmowałoby już tylko jedną linijkę.

Gdybyś chciał się obejść bez funkcji i wstawiać obliczenia wyznaczające wysokość podatku bezpośrednio w procedurach, musiałbyś za każdym razem od nowa wpisywać owe kilka linijek, co przy 20-krotnym powtórzeniu obliczenia byłoby ogromnym marnotrawstwem czasu i miejsca.

Ale to jeszcze nie wszystkie argumenty przemawiające za korzystaniem z funkcji zamiast wstawiania obliczeń bezpośrednio w procedurach.

Wyobraź sobie teraz dodatkowo, że rząd wprowadził zmiany w zasadach obliczaniu podatków, które musisz uwzględnić w swojej aplikacji.

Jeżeli w swoim makrze korzystasz z funkcji obliczającej podatek, będziesz musiał dokonać tylko jednej, jedynej modyfikacji - właśnie wewnątrz tej funkcji.

Gdybyś natomiast wstawiał obliczenia bezpośrednio w procedurach, musiałbyś się w takiej sytuacji podjąć czasochłonnego zadania, jakim jest odszukanie każdego miejsca, w którym takie obliczenie występuje i dokonanie na nim oddzielnej modyfikacji.

Jeżeli podejrzewasz, że jakieś obliczenia lub działania będziesz musiał wykorzystać więcej niż raz, warto stworzyć na potrzeby tych obliczeń oddzielną funkcję.

Stwórz sobie oddzielny moduł, który będzie Twoją biblioteką użytecznych funkcji i od początku swojej kariery programistycznej gromadź w nim wszystkie ważne funkcje, co do których masz podejrzenie, że w przyszłości będą mogły się Tobie przydać w innym programie.

Rozpoczynając pracę nad nowym makrem rozpocznij od skopiowania do niego tego modułu-biblioteki, tak byś nie musiał pisać od nowa uniwersalnych funkcji, kiedy zajdzie potrzeba skorzystania z nich.

Wykorzystanie własnych funkcji w arkuszu Excela

Wszystkie tworzone przez Ciebie funkcje mogą być wykorzystywane w tradycyjnym arkuszu Excelowym.

Skorzystanie z własnoręcznie napisanej funkcji jest bardzo proste i niczym nie różni się od korzystania z fabrycznie wbudowanych funkcji dostępnych w Excelu (ponieważ one także są po prostu zwykłymi funkcjami, tyle że napisanymi przez programistów Microsoftu).

Wystarczy więc, że w arkuszu wpiszesz znak równości, nazwę swojej funkcji i podasz niezbędne argumenty, aby w danej komórce pojawił się wynik zwrócony przez tę funkcję.

Poniżej znajduje się przykład wykorzystania w arkuszu omówionej wcześniej funkcji pensjaPoOpodatkowaniu.

Wykorzystanie funkcji VBA w arkuszu

REGUŁY KORZYSTANIA W ARKUSZU Z WŁASNYCH FUNKCJI

W arkuszu możesz korzystać ze wszystkich funkcji, które znajdują się w aktualnie otwartych plikach. Funkcja pensjaPoOpodatkowaniu może więc być użyta nie tylko w pliku, w którym została napisana, ale także w każdym innym, o ile plik zawierający tę funkcję pozostaje otwarty.

Jeśli chcesz skorzystać w arkuszu z funkcji znajdującej się w innym pliku, musisz podać najpierw nazwę tego pliku, wykrzyknik, a dopiero potem wpisać nazwę funkcji.

Wywołanie funkcji pensjaPoOpodatkowaniu w pliku, który zawiera tę funkcję (test.xls):
 
=pensjaPoOpodatkowaniu(5000)

Wywołanie funkcji pensjaPoOpodatkowaniu w każdym innym pliku:
 
=test.xls!pensjaPoOpodatkowaniu(5000)

Wyjątkiem są w tym przypadku funkcje zawarte w zainstalowanych dodatkach, które, mimo że znajdują się w innym pliku, do wywołania potrzebują tylko podania swojej nazwy.

Tak naprawdę pliki dodatków (format *.xla, *.xlax) to jedyny słuszny sposób przechowywania własnych funkcji przeznaczonych do wykorzystania w arkuszu.

Dodatek taki jest otwierany automatycznie wraz z otwarciem Excela (nie musisz się więc martwić o to, że zapomnisz go otworzyć i w arkuszu zamiast obliczeń pojawią się błędy).

Ponadto, tak jak wspomniałem przed momentem, wywoływanie funkcji znajdujących się w dodatku nie wymaga poprzedzania ich nazwy nazwą pliku, w którym są przechowywane (Więcej na temat tworzenia dodatków dowiesz się w dalszej części kursu).

Jeżeli zamkniesz plik zawierający funkcję, z której korzystają inne pliki, to po odświeżeniu danych w Excelu pojawią się błędy, takie jak na poniższym rysunku:

Błędy wynikające z zamknięcia pliku z funkcją VBA

Zwróć też uwagę, że nazwa pliku, którą wpisano przed nazwą funkcji (test.xls!), automatycznie zamieniła się w pełną ścieżkę dostępu do tego pliku.

Aby podejrzeć listę wszystkich dostępnych aktualnie funkcji użytkownika, musisz otworzyć okno Wstawianie funkcji.

Możesz tego dokonać na dwa sposoby:
  • wybrać z menu Excela polecenie
    • Wstaw
    • Funkcja...

    Uruchamianie okna Wstawianie funkcji z paska menu

  • kliknąć przycisk rozwijania przy ikonie funkcji (na poniższym rysunku zaznaczona na czerwono) i wybrać opcję Więcej funkcji... (zaznaczona na zielono).

    Uruchamianie okna Wstawianie funkcji za pomocą ikony

Następnie w oknie Wstawianie funkcji wybierz kategorię Użytkownika (zaznaczona na rysunku na czerwono), a na liście poniżej ujrzysz wszystkie funkcje znajdujące się w otwartych plikach.

Wykaz funkcji użytkownika

W tym miejscu warto zwrócić uwagę na jedną kwestię. Otóż wspomniana lista funkcji zawiera zarówno funkcje, którą są przeznaczone do użycia w arkuszu, jak i funkcje spełniające tylko jakieś role w makrach, które absolutnie nie nadają się do wykorzystania w arkuszu, a ich wywołanie spowoduje jedynie wyświetlenie komunikatu o błędzie.

Dla większej przejrzystości warto opracować sobie jakiś specjalny styl nazywania funkcji przeznaczonych do wykorzystania w arkuszu, tak aby momentalnie można je było odróżnić od innych. Przykładowo na powyższym rysunku nazwy wszystkich funkcji przeznaczonych do arkuszy zapisane są wielkimi literami.

To wszystko jeżeli chodzi o podstawy działania funkcji. Oczywiście nie oznacza to, że temat funkcji nie będzie już więcej poruszany. Wręcz przeciwnie, tak naprawdę to dopiero początek przygód z funkcjami, od teraz podczas każdej niemal lekcji będziesz poznawał przykłady jakichś funkcji i wzbogacał swoją wiedzę na ich temat, dzięki czemu nabierzesz coraz większej wprawy w ich stosowaniu.

Argumenty opcjonalne

W kilku poprzednich przykładach zapoznałeś się z procedurami i funkcjami, do których przekazywane były argumenty. Jak wspomniano w lekcji piątej, jeżeli procedura lub funkcja wymaga podania argumentów, to każda próba wywołania ich bez tych argumentów uniemożliwi uruchomienie makra i spowoduje wyświetlenie komunikatu o błędzie.

Zdarzają się jednak sytuacje, i to wcale nierzadko, kiedy funkcja lub procedura wymaga podania argumentu tylko w niektórych sytuacjach, podczas gdy w innych argument ten nie jest konieczny.

Pozostańmy przy przykładzie funkcji obliczającej pensje netto i brutto.

Załóżmy teraz, że w niektórych przypadkach do wyliczenia pensji netto konieczne będzie jeszcze podanie dodatkowego czynnika - na przykład wysokości kwoty wolnej od podatku. Równocześnie musi jednak pozostać dotychczasowa możliwość wyliczenia pensji netto - bez informacji na temat kwoty wolnej od podatku, tylko na podstawie wysokości pensji brutto.

Zmodyfikuj funkcję pensjaPoOpodatkowaniu tak, aby wyglądała jak poniżej (nowe elementy zostały wyróżnione na czerwono):
1
2
3
4
5
6
7
Function pensjaPoOpodatkowaniu(podstawa As Long, _
  Optional kwotaWolnaOdPodatku As Single) As Long
  Dim kwotaDoOpodatkowania As Single

  kwotaDoOpodatkowania = podstawa - kwotaWolnaOdPodatku
  pensjaPoOpodatkowaniu = podstawa - (kwotaDoOpodatkowania * 0.18)
End Function

W powyższej wersji funkcji pensjaPoOpodatkowaniu, w wierszu otwarcia funkcji pojawił się nowy argument - kwotaWolnaOdPodatku.

Tym, co odróżnia ten argument od wszystkich dotychczasowych, jest słowo Optional znajdujące się przed jego nazwą. Oznacza ono właśnie, że argument ten może być podany w wywołaniu funkcji, ale nie jest to konieczne i funkcja równie dobrze zadziała w sytuacji, kiedy w wywołaniu zabraknie tego argumentu.

Poprawne są więc oba poniższe wywołania tej funkcji:
 
pensjaNetto = pensjaPoOpodatkowaniu(5000)
 
pensjaNetto = pensjaPoOpodatkowaniu(5000, 2542.24)

Do argumentu opcjonalnego można przypisać jakąś wartość domyślną, którą przyjmie ten argument w razie pominięcia go przy wywoływaniu funkcji.

Wróćmy teraz do poprzedniej postaci funkcji pensjaPoOpodatkowaniu (tej bez argumentu opcjonalnego) i zmodyfikujmy ją tak, aby oprócz podstawy opodatkowania, umożliwiała podanie stopy podatkowej. Przyjmijmy jednak, że stopa podatkowa zasadniczo wynosi 18%, a tylko w wyjątkowych sytuacjach będzie posiadała inną wartość i tylko wtedy trzeba będzie ją podać jako argument przy wywoływaniu funkcji.

Funkcja powinna wówczas przyjąć następującą postać (nowe elementy zostały zaznaczone na czerwono):
1
2
3
4
5
Function pensjaPoOpodatkowaniu(podstawa As Long, _
  Optional stopaPodatku As Single = 0.18) As Long

  pensjaPoOpodatkowaniu = podstawa - (podstawa * stopaPodatku)
End Function

W powyższej wersji funkcji pensjaPoOpodatkowaniu argumentem opcjonalnym jest argument stopaPodatku.

Po określeniu tego argumentu jako typ Single dodatkowo podano jego wartość domyślną, wynoszącą 0.18 (czyli 18%). Oznacza to, że we wszystkich sytuacjach, kiedy przy wywołaniu funkcji nie określono argumentu stopaPodatku, będzie on równy wartości domyślnej (czyli 18%).

Procedura lub funkcja może maksymalnie posiadać 60 argumentów wejściowych, przy czym stosunek argumentów obowiązkowych do opcjonalnych może być zupełnie dowolny.

Jeżeli wymieniasz argumenty wejściowe danej funkcji, musisz zwrócić uwagę na to, aby argumenty opcjonalne znajdowały się na samym końcu listy argumentów!

Niedopuszczalny jest na przykład taki zapis:
 
 
Function pensjaPoOpodatkowaniu(Optional stopaPodatku As _
        Single
= 0.18, podstawa As Long)
ponieważ argument obowiązkowy występuje w nim po argumencie opcjonalnym.

Pamiętaj aby zawsze najpierw wymienić wszystkie argumenty obowiązkowe, a dopiero wtedy przejść do argumentów opcjonalnych. Właściwie to nawet nie musisz o tym pamiętać, gdyż będzie o tym za Ciebie pamiętał edytor VBA. Każdy wiersz otwarcia funkcji lub procedury, w którym argumenty opcjonalne pojawiają się przed obowiązkowymi jest automatycznie podświetlany na czerwono i do czasu poprawienia go do prawidłowej postaci niemożliwe jest uruchomienie makra.

Okno Immediate

Okno Immediate służy do wykonywania następujących czynności:
  • sprawdzanie wartości i stanu zmiennych,
  • uruchamianie procedur,
  • szybkie sprawdzanie wyników funkcji.

Okno Immediate można uruchomić poprzez kliknięcie kombinacji klawiszy Ctrl + G, a wygląda ono tak jak na poniższym rysunku (nie jest więc zbyt skomplikowane):

Wygląd okna Immediate

Sprawdzanie wartości zmiennych

Za pomocą okna Immediate można sprawdzać wartości zmiennych zarówno w momencie zatrzymania makra, jak też bez przerywania jego działania.

Oba te sposoby zostaną zaprezentowane na przykładzie omówionego w tej lekcji makra do wyliczania pensji netto i brutto.

Ustaw punkt zatrzymania w wierszu zamknięcia (7) procedury zarobki, czyli w wierszu End Sub , i uruchom makro.

Kiedy makro dojdzie do wiersza, w którym ustawiłeś punkt zatrzymania i wstrzyma swoje działanie, przejdź do okna Immediate (możesz tego dokonać klikając w to okno myszką lub klikając kombinację klawiszy Ctrl+G).

Gdy znajdziesz się już w oknie Immediate, wpisz znak zapytania oraz nazwę zmiennej, której wartość chcesz podejrzeć, a potem wciśnij Enter, np:

Przykładowe zapytanie w oknie Immediate

Po wciśnięciu Entera edytor VBA wyświetli w wierszu poniżej Twojego zapytania jego wynik, a kursor ustawi w następnym wierszu, tak byś mógł od razu wpisywać kolejne zapytania.

Być może ten sposób podpatrywania wartości zmiennych jest mniej wygodny i bardziej czasochłonny niż najeżdżanie kursorem na nazwę zmiennej, omówione w poprzedniej lekcji, pozwala jednak sprawdzić o wiele większy zakres danych.

Za pomocą zapytania w oknie Immediate możesz sprawdzić mnóstwo rzeczy, także systemowych, z których kilka zostało wymienionych w poniższej tabelce:

SPRAWDZANA WARTOŚĆZAPYTANIE
Nazwa aktywnego arkusza?ActiveSheet.Name
Nazwa aktywnego pliku?ActiveWorkbook.Name
Ścieżka do pliku?ThisWorkbook.Path
Aktualny czas?Time
Aktualna data?Date
Aktualna data i czas?Now
Nazwa zalogowanego użytkownika?Application.UserName

Okno Immediate umożliwia również sprawdzanie wartości zmiennych w poszczególnych momentach działania makra bez przerywania jego działania.

Przed zapoznaniem się z tym sposobem podpatrywania wartości zmiennych, wykasuj wszystko w oknie Immediate, aby stare wpisy nie mieszały się z nowymi, które zaraz się pojawią.

Załóżmy teraz, że chcesz sprawić, aby procedura zarobki podczas swojego działania dwukrotnie wyświetlała informację o wielkości zmiennej pensjaNetto - najpierw przed przypisaniem do niej wyniku funkcji pensjaPoOpodatkowaniu, a potem po przypisaniu tej funkcji (za pierwszym razem powinna się więc wyświetlić wartość 0, a za drugim 2050).

Zmodyfikuj procedurę zarobki tak, aby wyglądała tak jak poniżej (nowe wiersze tradycyjnie zostały wypisane czerwoną czcionką), a potem zdejmij z jej ostatniego wiersza punkt zatrzymania, który ustawiłeś testując poprzedni przykład.

1
2
3
4
5
6
7
8
9
10
Sub zarobki()
    Dim pensjaBrutto As Long
    Dim pensjaNetto As Long

    pensjaBrutto = 2500
    Debug.Print pensjaNetto
    pensjaNetto = pensjaPoOpodatkowaniu(pensjaBrutto)
    Debug.Print pensjaNetto

End Sub
1
2
3
Function pensjaPoOpodatkowaniu(podstawa As Long) As Long
    pensjaPoOpodatkowaniu = podstawa - (podstawa * 0.18)
End Function

Po uruchomieniu makra w oknie Immediate pojawią się następujące wpisy:

Wyprintowane wartości zmiennej pensjaNetto

a więc dokładnie takie wyniki, jakie miały się w tym oknie pojawić.

Podsumowując - aby wyświetlić w oknie Immediate wartość jakiejś zmiennej podczas działania makra bez konieczności jego zatrzymywania, musisz dodać następujący wpis w miejscu, w którym chcesz podejrzeć wartość zmiennej:
 
Debug.Print nazwaZmiennej

Jeżeli będziesz w czasie jednego uruchomienia makra wstawiał kilka takich Debug.Printów, możesz dodać do nich jakiś opis, abyś wiedział do której zmiennej odnosi się każdy z wpisów.

W powyższym przykładzie mógłbyś np. napisać w wierszu 6:
 
Debug.Print "Zmienna pensjaNetto przed przypisaniem funkcji: " & pensjaNetto
natomiast w wierszu 8:
 
Debug.Print "Zmienna pensjaNetto po przypisaniu funkcji: " & pensjaNetto

Wówczas po uruchomieniu makra, wyprintowane w oknie Immediate wyniki wyglądałyby następująco:

Wyprintowane wartości zmiennej pensjaNetto z opisem

Uruchamianie makr

Za pomocą okna Immediate można też bardzo łatwo uruchamiać całe makra.

Wystarczy wpisać w nowym wierszu okna Immediate słowo kluczowe Call oraz nazwę procedury i wcisnąć Enter.

Jeżeli wpiszesz np. taki wiersz:
 
Call zarobki
i wciśniesz Enter, edytor VBA uruchomi makro Zarobki.

Sprawdzanie wyników funkcji

Ostatnią ważną rolą okna Immediate jest szybkie sprawdzanie wyników funkcji.

Jeżeli chcesz na przykład szybko sprawdzić wynik funkcji pensjaPoOpodatkowaniu z poprzedniego przykładu dla pensji brutto wynoszącej 5000, wystarczy, że wpiszesz w oknie Immediate następujące polecenie:
 
?pensjaPoOpodatkowaniu(5000)
i wciśniesz Enter, a pod spodem pojawi się wynik 4100.

W ten sposób możesz szybko sprawdzać wyniki funkcji oraz testować nowe funkcje, zadając kilka takich zapytań i sprawdzając czy zwracają oczekiwane przez Ciebie rezultaty.

Przy wpisywaniu poleceń w oknie Immediate możesz korzystać z autopodpowiedzi.

Po wpisaniu dwóch pierwszych liter nazwy funkcji pensjaPoOpodatkowaniu wciśnij Ctrl+Spacja, a nazwa automatycznie wpisze się sama (ponieważ nie ma żadnej innej funkcji ani procedury rozpoczynające się literami pe).

Pamiętaj o autouzupełnianiu i korzystaj z niego cały czas, zarówno w normalnym edytorze VBA jak i w oknie Immediate. Znacznie zwiększy to efektywność Twojej pracy.