04 - Typy zmiennych


W poprzedniej lekcji zostało wprowadzone pojęcie zmiennej, nauczyliśmy się je deklarować i zadeklarowaliśmy pierwszą zmienną typu Long. W tej lekcji poznamy inne typy zmiennych i dowiemy się czym różnią się one od siebie.

Poniżej zostały wymienione podstawowe typy zmiennych występujące w Visual Basic. Klikając w każdą z nazw przeniesiesz się do jej szczegółowego opisu.

Oprócz wymienionych wyżej typów, zmienna może być także zadeklarowana jako obiekt (w tym przypadku liczba możliwych typów jest nieskończona, ponieważ sam możesz tworzyć swoje własne typy obiektów), ale ten temat zostanie poruszony w dalszej części kursu.

Zmienne tekstowe (String)

Tekstowy typ zmiennych nosi w języku Visual Basic nazwę String.

Deklaracja zmiennej jako tekstowej wygląda więc tak:
 
Dim zmienna As String

Wartością domyślną dla zmiennych tekstowych jest pusty ciąg znaków - "".

Na wstępie rozważań o zmiennych tekstowych należy wspomnieć kilka słów o tekstach w programowaniu VBA. Temat ten był już wstępnie poruszany w poprzedniej lekcji, ale poniżej znajdziesz jego dokładniejsze omówienie.

Kompilator VBA rozpoznaje czy jakiś kawałek kodu jest tekstem, na podstawie tego, czy jest on umieszczony w cudzysłowie.

Załóżmy, że chcesz wyświetlić w komórce A1 arkusza Arkusz1 tekst Polska. Nie można zrobić tego w taki sposób, jak poniżej:
 
Worksheets("Arkusz1").Cells(1,1) = Polska

gdyż w takiej sytuacji słowo Polska nie jest ujęte w cudzysłów i kompilator traktuje je tak, jakby było nazwą zmiennej.

W tej sytuacji możliwe są dwa zakończenia, niestety żadne z nich nie jest happy endem. Jeżeli w Twoim programie w ogóle nie ma zadeklarowanej zmiennej o nazwie Polska to program nawet się nie uruchomi. Kompilator zgłosi błąd przed rozpoczęciem działania, gdyż znajdzie wewnątrz kodu zmienną, która nie została zadeklarowana (zagadnienie to zostało omówione w lekcji poświęconej deklarowaniu zmiennych).

Jeżeli natomiast jakimś cudem miałbyś w programie zadeklarowaną zmienną o nazwie Polska (to bardzo piękna, ale jednak nietypowa nazwa zmiennej), wówczas w arkuszu i tak nie wyświetliłby się napis Polska lecz wartość, jaką zawiera w sobie zmienna Polska (chyba, że byłaby to zmienna typu tekstowego i miała przypisany właśnie tekst Polska).

Dokładnie w ten sposób postępowaliśmy w poprzedniej lekcji, wyświetlając w arkuszu wartość przechowywaną przez zmienną potega - wpisywaliśmy wówczas w kodzie nazwę potega bez cudzysłowu.

Mniej wątpliwości budzi sytuacja, gdy tekst, który chcesz wypisać w arkuszu (lub wykonać z nim jakąś inną operację) zawiera więcej niż jedno słowo, np:
 
Worksheets("Arkusz1").Cells(1,1) = Tekst do wyświetlenia

Taka konstrukcja nawet przed próbą uruchomienia programu podświetli się na czerwono i od razu zorientujesz się, że coś jest nie tak. Powyższy zapis jest dla kompilatora równoznaczny z wypisaniem jednym ciągiem 3 nazw zmiennych (tekst, do, wyświetlenia), co byłoby złamaniem co najmniej dwóch zakazów obowiązujących przy programowaniu, ponieważ nie powinno się używać polskich znaków w nazwach zmiennych, a ponadto nigdy żadne dwie nazwy zmiennych nie mogą znaleźć się koło siebie oddzielone tylko spacją.

W omawianych wyżej sytuacjach prawidłowymi zapisami byłyby:
 
Worksheets("Arkusz1").Cells(1,1) = "Polska"
 
Worksheets("Arkusz1").Cells(1,1) = "Tekst do wyświetlenia"

Ogólna zasada jest więc taka, aby wszystko, co jest przypisywane do zmiennej String, znajdowało się w cudzysłowie. Jest jednak od tej reguły kilka odstępstw, które zostały opisane poniżej.

Pierwszą z nich jest bardzo popularna sytuacja, kiedy do zmiennej tekstowej chcesz przypisać inną zmienną tekstową.

Przyjrzyj się poniższemu przykładowi:
1
2
3
4
5
6
7
Sub przypisanieZmiennejTekstowej()
    Dim a As String
    Dim b As String

    a = "Polska"
    b = a
End Sub

W drugiej i trzeciej linijce powyższego kodu deklarowana jest obecność dwóch zmiennych typu tekstowego: a i b.

W piątej linijce do zmiennej a przypisany zostaje tekst Polska.

W linijce 6 do zmiennej b zostaje przypisana taka sama wartość, jaką zawiera w sobie zmienna a.

W tej sytuacji a ma oznaczać nazwę zmiennej, a nie przypisywany tekst, dlatego też nie dodajemy do tej nazwy cudzysłowu.

W ten sposób osiągnięty został zamierzony cel - najpierw do zmiennej a przypisany został tekst Polska, a następnie do zmiennej b przypisano wartość zmiennej a, więc po tej operacji zmienna b również będzie miała wartość Polska.

Gdyby przypisując zmienną a do zmiennej b, jej nazwa została ujęta w cudzysłów, kompilator zinterpretowałby to tak, jakbyś chciał do niej przypisać jednoliterowy tekst a. Po tej operacji zmienna a miałaby więc wartość Polska, natomiast zmienna b przechowywałaby tekst a, czyli zupełnie niezgodnie z początkowymi intencjami.

Drugą sytuacją, w której wartość przypisywana do zmiennej tekstowej nie jest ujęta w cudzysłów, jest przypisywanie do zmiennej tekstowej wartości nie będącej w rzeczywistości tekstem.

Typ String jest najbardziej elastycznym ze wszystkich typów zmiennych, ponieważ można do niego przypisać w zasadzie każdą dowolną wartość typu prymitywnego, nie powodując przy tym żadnego błędu w trakcie wykonywania programu. Niezależnie od tego czy jest to ciąg znaków, liczba czy data, wszystko, co jest przypisywane do zmiennej tekstowej jest traktowane przez kompilator jak tekst. Możesz więc przypisać do zmiennej typu String liczbę 1, ale wartość przechowywana przez tę zmienną nie będzie liczbą 1 lecz tekstem, składającym się tylko z pojedynczego znaku - cyfry 1.

Możesz mieć wątpliwości, czy jest jakaś różnica pomiędzy liczbą 1, a tekstem 1, dlatego poniżej został przytoczony przykład, który powinien je rozwiać (przykład będzie wykorzystywał wszystkie omówione dotychczas elementy, dlatego przy okazji będziesz mógł je sobie utrwalić):
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
Sub zmienneTekstowe()
    Dim s As String
    Dim t As String
    Dim a As Long
    Dim b As Long

    'przypisanie liczb 1 i 2 do zmiennych typu tekstowego
    s = 1
    t = 2

    'przypisanie liczb 1 i 2 do zmiennych typu liczbowego
    a = 1
    b = 2

    Worksheets("Arkusz1").Cells(1,1) = s + t
    Worksheets("Arkusz1").Cells(2,1) = a + b
End Sub

W wierszach 2-5 tego przykładu zadeklarowane zostały cztery zmienne: dwie zmienne typu tekstowego (s, t) oraz dwie zmienne typu liczbowego (a, b).

W wierszach 8-9 do zmiennych tekstowych zostały przypisane liczby 1 i 2, natomiast w wierszach 12-13 te same liczby przypisano do zmiennych liczbowych. Pozornie więc zmienne tekstowe s i t oraz zmienne liczbowe a i b przechowują te same wartości - 1 i 2. Jak się jednak za chwilę okaże nie do końca jest to prawdą.

W dwóch ostatnich wierszach przed zamknięciem procedury (15-16) wyświetlone zostają sumy poszczególnych zmiennych - najpierw sumę zmiennych s i t, a potem zmiennych a i b.

Jeżeli uruchomisz powyższy kod, a potem zajrzysz do arkusza, zobaczysz, że w komórce A1 wyświetlona została liczba 12, natomiast w komórce A2 liczba 3.

Wynika to oczywiście z różnych typów zmiennych, w których były przechowywane te wartości.

Kiedy dodajesz do siebie teksty (tak jak teksty 1 i 2 przechowywane w zmiennych s i t), kompilator po prostu wypisuje je jeden za drugim (w tym przypadku wypisał więc obok siebie liczby 1 i 2, tworząc liczbę 12).

Kiedy natomiast zsumowałeś liczby 1 i 2 przechowywane w zmiennych liczbowych a i b, zostały one zwyczajnie do siebie dodane, dając w wyniku 3.

Jak więc widać na powyższym przykładzie, mimo że zmienne typu String są tak elastyczne i mogą przyjąć wszystkie inne typy zmiennych, nie powinno się ich używać do celów innych niż przechowywanie tekstów, ponieważ może to doprowadzić do niezamierzonych rezultatów.

Zmienne liczbowe (Byte, Integer, Long, Single, Double, Currency)

Liczbowe typy zmiennych omawiane są łącznie, ponieważ ich działanie jest niemal identyczne, a różnią się jedynie dopuszczalnym zakresem wartości oraz ilością zajmowanej pamięci komputera.

W Visual Basic możesz korzystać z następujących typów zmiennych liczbowych: Byte, Integer, Long, Single, Double oraz Currency.

Deklaracje poszczególnych typów zmiennych wyglądają następująco:
 
Dim liczba As Byte
 
Dim liczba As Integer
 
Dim liczba As Long
 
Dim liczba As Single
 
Dim liczba As Double
 
Dim liczba As Currency

Wartością domyślną dla wszystkich typów liczbowych jest 0.

Zmienne liczbowe nie są już tak elastyczne jak zmienne tekstowe String. Jak się już dowiedziałeś, do zmiennej tekstowej bez problemu można przypisać zmienną liczbową, jednak operacja w odwrotną stronę nie zawsze zadziała.

Niemożliwe jest na przykład przypisanie do zmiennej liczbowej wartości tekst. Kompilator w żaden sposób nie będzie potrafił przełożyć słowa tekst na liczbę i wyświetli błąd Run-time error '13': Type mismatch.

Zresztą możesz przekonać się o tym sam, wklejając i uruchamiając poniższy kod:
1
2
3
4
Sub blednePrzypisanie
    Dim a As Long
    a = "tekst"
End Sub

Są jednak sytuacje, w których przypisanie tekstu do zmiennej liczbowej nie spowoduje żadnego błędu. Dzieje się tak w sytuacji, gdy tekst, który ma być przypisany, jest po prostu tekstową reprezentacją jakiejś liczby.

Spójrz na poniższy fragment kodu:
1
2
3
4
Sub przypisanieTekstuDoZmiennejLiczbowej
    Dim a As Long
    a = "1"
End Sub

W trzeciej linii tego kodu do zmiennej typu Long przypisana zostaje wartość tekstowa "1". Jak dowiedziałeś się w poprzednim podrozdziale, poświęconym zmiennym tekstowym, wszystko, co zawarte jest w cudzysłowie, jest tekstem. Nie inaczej jest w tym przypadku - wartość "1" nie jest liczbą, lecz tekstem, ponieważ znajduje się w cudzysłowie. Jednak w tym przypadku kompilator potrafi przełożyć sobie tekst 1 na liczbę, dlatego przypisuje do zmiennej a liczbę 1, nie zgłaszając przy tym żadnego błędu.

Przejdźmy teraz do szczegółowego omówienia każdego z liczbowych typów zmiennych. Jak już wcześniej wspomniałem we wstępie podrozdziału poświęconego zmiennym liczbowym, różnią się one między sobą jedynie dopuszczalnym zakres wartości, szczegółowością przedstawiania części ułamkowych oraz wielkością pamięci, którą zajmują.

Byte

Zmienna typu Byte obsługuje tylko liczby naturalne od 0 do 255.

Powinieneś jej używać tylko w sytuacji, kiedy masz pewność, że nie przyjmie ona wartości spoza tego zakresu.

Jeżeli w trakcie wykonywania Twojego programu dojdzie do próby przypisania do zmiennej typu Byte liczby większej niż 255 lub liczby ujemnej, kompilator przerwie działanie programu i wyświetli błąd Run-time error '6' Overflow.

Jeżeli przypiszesz do zmiennej typu Byte liczbę, która nie jest liczbą naturalną, zostanie ona zaokrąglona, np. przy próbie przypisania liczby 1.2 zmienna przybierze wartość 1, natomiast przypisując liczbę 1.5 nadasz zmiennej wartość 2.

Pojedyncza zmienna typu Byte zajmuje w pamięci tylko jeden bajt i jest to najmniej pamięciożerny typ zmiennej. Dlatego jeśli jesteś pewien, że zmienna liczbowa nie przekroczy zakresu 0-255, warto zadeklarować ją jako typ Byte aby zaoszczędzić trochę pamięci i przyspieszyć tym samym działanie programu. Oczywiście w omawianych dotychczas niewielkich programach, korzystających tylko z jednej zmiennej, różnica w prędkości działania programu nie byłaby zauważalna, ale w bardziej rozbudowanych aplikacjach, gdzie znajdują się setki czy nawet tysiące takich zmiennych, korzyści ze stosowania odpowiednich rodzajów zmiennych mogą być całkiem znaczne.

Integer

Zmienna typu Integer obsługuje liczby całkowite z zakresu od -32 768 do 32 767.

Jeżeli w trakcie wykonywania Twojego programu dojdzie do próby przypisania do zmiennej typu Integer liczby spoza podanego zakresu, kompilator przerwie działanie programu i wyświetli błąd Run-time error '6' Overflow.

Zaokrąglanie ułamków odbywa się identycznie jak w przypadku opisanej powyżej zmiennej typu Byte.

Pojedyncza zmienna typu Integer zajmuje dwa bajty pamięci.

Long

Zmienna typu Long obsługuje liczby całkowite z zakresu od -2 147 483 648 do 2 147 483 647.

Jeżeli w trakcie wykonywania Twojego programu dojdzie do próby przypisania do zmiennej typu Long liczby spoza podanego zakresu, kompilator przerwie działanie programu i wyświetli błąd Run-time error '6' Overflow.

Zaokrąglanie ułamków odbywa się identycznie jak w przypadku zmiennych typu Byte i Integer.

Pojedyncza zmienna typu Long zajmuje cztery bajty pamięci.

Single

Zmienna typu Single obsługuje liczby rzeczywiste i obejmuje wartości z zakresu od -3.4⋅1038 do -1.4⋅10-45 dla wartości ujemnych oraz od 1.4⋅10-45 do 3.4⋅1038 dla wartości dodatnich.

Jeżeli w trakcie wykonywania Twojego programu dojdzie do próby przypisania do zmiennej typu Single liczby spoza podanego zakresu, kompilator przerwie działanie programu i wyświetli błąd Run-time error '6' Overflow.

Pojedyncza zmienna typu Single zajmuje cztery bajty pamięci.

Double

Zmienna typu Double obsługuje liczby rzeczywiste i obejmuje wartości z zakresu od -1.80⋅10308 do -4.94⋅10-324 dla wartości ujemnych oraz od 4.94⋅10-324 do 1.80⋅10308 dla wartości dodatnich..

Pojedyncza zmienna typu Double zajmuje osiem bajtów pamięci.

Currency

Zmienna typu Currency obsługuje liczby z zakresu od -922 337 203 685 477.5808 do 922 337 203 685 477.5807.

Pojedyncza zmienna typu Currency zajmuje osiem bajtów pamięci.

Głównym zastosowaniem typu Currency jest wykonywanie obliczeń, w których niezbędne jest uzyskanie dokładnych wyników, takich jak np. wyliczenia finansowe.

Zmienne typu Single i Double mają tę wadę, że nie zawsze zwracają nieprecyzyjne wyniki, przykładowo dla działania 2 + 2 zamiast wyniku 4, możesz czasem otrzymać 3.99999995. Stosując zmienne typu Currency zawsze uzyskasz dokładny wynik, jednak mogą one znacznie spowolnić działanie aplikacji w przypadku wykonywania wielu obliczeń.

Zmienne daty i czasu (Date)

Zmienne daty i czasu, jak sama nazwa wskazuje, służą do przechowywania informacji o dacie i czasie.

Deklaracja zmiennej jako zmiennej daty i czasu wygląda następująco:
 
Dim zmienna As Date

Zmienna typu Date obsługuje daty z zakresu 1 stycznia 100 do 31 grudnia 9999. Próba przypisania daty spoza tego zakresu zakończy się wyświetleniem błędu Run-time error 5: Invalid procedure Call or argument

Jeżeli do zmiennej typu Date chcesz ręcznie przypisać określoną datę, musisz otoczyć ją z obu stron znakiem #, a poszczególne składniki daty oddzielić od siebie znakiem myślnika (-) lub slasha (/).

Poniżej znajdują się przykłady przypisania do zmiennej daty 21 kwietnia 2010:
 
data = #21-04-2010#
 
data = #2010-04-21#
 
data = #2010/4/21#
 
data = #4/21/2010#

Podobnie należy postępować z przypisywaniem do zmiennej typu Date czasu - przypisywana wartość musi być z obu stron otoczona znakiem #, a poszczególne składniki czasu powinny być od siebie oddzielone dwukropkiem (:).

Obowiązkowym składnikiem czasu przypisywanego do zmiennej są godziny i minuty. Wartość sekundowa może zostać pominięta, w takiej sytuacji sekundy domyślnie otrzymają wartość 0.

Poniżej znajdują się przykłady przypisania czasu do zmiennej typu datowego:
 
czas = #12:00#
 
czas = #23:15:20#

Do zmiennej typu datowego możesz też przypisać wartość będącą połączeniem daty i czasu. Tak jak w omówionych wyżej przypadkach przypisywana wartość musi być otoczona znakiem #, obowiązują też opisane wyżej reguły zapisywania dat i czasu.

Poniżej znajdują się przykłady równoczesnego przypisania do zmiennej typu datowego daty i czasu:
 
czas = #21-04-2010 12:00#
 
czas = #2010/4/21 13:14:15#

Bądź ostrożny przy ręcznym wpisywaniu dat!

Edytor postępuje bardzo nielogicznie przy interpretowaniu ich formatu dat.

Jeżeli rozpoczniesz wpisywanie daty od roku, kolejna podana liczba jest interpretowana jako numer miesiąca, a ostatnia liczba jako numer dnia - tu akurat wszystko przebiega logiczne i bez żadnych wątpliwości.

Gorzej wygląda sprawa z zapisywaniem dat w formacie, w którym rok podany jest na końcu. Po wpisaniu daty w takim właśnie formacie, edytor sprawdza najpierw pierwszą podaną liczbę i jeżeli mieści się ona w zakresie 1-12, a więc może odpowiadać numerowi miesiąca, to tak jest właśnie interpretowana. Druga podana liczba jest w takiej sytuacji numerem dnia. Jeżeli natomiast pierwsza podana liczba jest większa niż 12, wówczas edytor VBA interpretuje ją jako numer dnia, a dopiero druga liczba jest traktowana jako numer miesiąca.

Niestety, widać tu pewną niekonsekwencję, więc trzeba zachować czujność przy ręcznym wpisywaniu dat, ponieważ moment nieuwagi może doprowadzić do błędów w działaniu programu.

Dziwne sytuacje dzieją się też, przy próbie określenia roku poprzez wpisanie tylko jego dwóch ostatnich cyfr.
Omówienie wszystkich możliwych sytuacji zajęłoby zbyt wiele miejsca, dlatego nie będziemy się nad tym rozwodzić. Najbezpieczniej jest po prostu wpisywać zawsze pełny numer roku, co pozwoli uniknąć niepotrzebnych kłopotów.

Pamiętaj, że zawsze po ręcznym wpisaniu daty w edytorze VBA, jej format zostanie automatycznie zmieniony do postaci #mm/dd/yyyy#.
Jest to moment, w którym możesz sprawdzić, czy sposób, w jaki edytor zinterpretował wpisaną przez Ciebie datę, odpowiada Twoim oczekiwaniom.

Podobnie, po ręcznym wpisaniu czasu, edytor VBA automatycznie zmieni jej format do postaci #hh:mm:ss (AM/PM)#, gdzie data zapisana jest w systemie 12-godzinnym, a dopisek po dacie informuje czy godzina dotyczy pierwszej czy drugiej części doby (AM - godziny między 00:00 a 12:00; PM - godziny między 12:00 a 24:00).

Jeżeli wpiszesz w edytorze datę, która nie ma prawa istnieć (np. podasz 13 jako numer miesiąca lub 32 jako numer dnia), zostanie ona podświetlona na czerwono, a makro nie zadziała, ponieważ przy próbie jego uruchomienia kompilator zgłosi błąd Compile error: Syntax error.

Bardzo ważną cechą zmiennych typu Date jest to, że każda z nich posiada swoją reprezentację liczbową. W liczbie reprezentującej daną datę część całkowita określa ile dni upłynęło od 30 grudnia 1899 do tej daty, natomiast część ułamkowa jest reprezentacją czasu i określa jaka część doby upłynęła od północy.

Dlatego też, do zmiennych typu datowego bez problemu można przypisać liczby i wykonywać na nich operacje arytmetyczne. Należy jednak wziąć pod uwagę to, co zostało powiedziane na wstępie omawiania typu datowego - typ ten obsługuje tylko daty z zakresu 01.01.100 - 31.12.9999. Dlatego też liczby, które mogą zostać przypisane do zmiennej tego typu, również mają ograniczony zakres i muszą mieścić się w przedziale od -657 434 do 2 958 465. Próba przypisania do zmiennej typu Date liczby spoza tego zakresu spowoduje wyświetlenie błędu Run time error '6': Overflow.

W poniższej tabelce znajduje się kilka przykładów dat i reprezentujących ich liczb:
DATALICZBA
30 grudnia 18990
6 lipca 200940 000
13 sierpnia 1872-10 000
6 lipca 2009, 6:0040 000.25
13 sierpnia 1872, 6:00-10 000.25

Zmienne logiczne (Boolean)

Zmienna logiczna określana jest w VBA słowem kluczowym Boolean.

Deklaracja tej zmiennej wygląda następująco:
 
Dim zmienna As Boolean

Zmienne typu Boolean mogą przyjmować dwie wartości: True oraz False.

Domyślną wartością dla zmiennych typu Boolean jest False.

Oprócz wartości True i False, do zmiennej typu Boolean można też przypisywać liczby. W takiej sytuacji kompilator traktuję liczbę 0 jako wartość False, natomiast każdą inną liczbę (także ujemne) jako równowartość True.

Do zmiennej typu Boolean można też przypisać teksty True oraz False. Próba przypisania jakiegokolwiek innego tekstu zakończy się wyświetleniem błędu Run-time error '13': Type mismatch.

Zmienne ogólne (Variant)

Typ Variant jest jak dżoker w kartach - zastępuje wszystkie inne typy zmiennych. Do zmiennej typu Variant możesz przypisać dowolną wartość.

Deklaracja zmiennej typu ogólnego wygląda następująco:
 
Dim zmienna As Variant

Możesz sobie w tym miejscu pomyśleć - skoro typ Variant jest taki uniwersalny, zastępuje wszystkie inne, wszystko można do niego przypisać - dlaczego nie wykorzystywać go dla każdej zmiennej, zamiast bawić się w jakieś rozdzielanie zmiennych na tekstowe, liczbowe czy logiczne. Owszem, przy zastosowaniu takiej zasady pisanie programów mogłoby być bardzo wygodne, ale ich późniejsze utrzymywanie i korzystanie z nich już niekoniecznie.

Zmienne typu Variant zajmują o wiele więcej pamięci (np. dla pojedynczej liczby 16 bajtów, podczas gdy zmienna typu Integer tylko 2 bajty), co znacznie obciążą programy i zwalnia prędkość ich działania.

Drugą istotną wadą deklarowania zmiennych jako Variant jest utrata kontroli typów. Jeżeli deklarujemy jakąś zmienną z określonym typem, pracując nad kodem na pierwszy rzut oka widać jakie wartości mogą być przypisane do tej zmiennej. Jeżeli przypadkowo przypiszemy do tej zmiennej wartość innego typu, zostaniemy o tym poinformowaniu już na etapie kompilacji. W przypadku zmiennych typu Variant kompilator nie zgłosi żadnych zastrzeżeń, bo zmienne tego typu akceptują wszystkie wartości, więc istnieje duże ryzyko, że o błędzie dowiemy się bardzo późno.

Oczywiście zdarzają się też sytuacje, w których zmienne typu Variant są najlepszym lub nawet jedynym rozwiązaniem. Przykłady takich sytuacji będziemy omawiać w dalszych lekcjach kursu.