Instrukcja warunkowa if
W tym rozdziale zapoznamy się z tworem zwanym instrukcją warunkową. Dzięki niemu możemy kazać programowi coś zrobić tylko pod pewnym warunkiem. Instrukcję warunkową tworzy się za pomocą słowa "if", czyli po prostu "jeśli".
Wygląda to następująco:
(Załóżmy, że gdzieś wcześniej w programie zdefiniowaliśmy już zmienne prawdopodobienstwo_wypadku_samochodem i prawdopodobienstwo_wypadku_samolotem. Pamiętaj, że żeby przetestować, czy ten if zadziała, te zmienne muszą istnieć.)
Całą powyższą instrukcję możemy przeczytać następująco: jeśli zmienna prawdopodobienstwo_wypadku_samochodem jest mniejsza od zmiennej prawdopodobienstwo_wypadku_samolotem, napisz "Jedzmy samochodem, tak bedzie bezpieczniej". R widząc instrukcję if oblicza wyrażenie w nawiasach - w tym wypadku, porównuje wartości dwóch zmiennych - i jeżeli wyszło mu TRUE, wykonuje wszystkie polecenia wewnątrz nawiasów klamrowych; jeśli wyszło mu FALSE, idzie dalej ignorując wszystkie polecenia, aż nie dojdzie do nawiasu klamrowego zamykającego tego if-a.
Zauważ, że kiedy napisaliśmy if (...) { i nacisnęliśmy enter, kolejna linia zaczęła się od plusa - R wie, że to jeszcze nie koniec, że napisana przez nas linia sama nie sensu, że jest wstępem do całego bloku instrukcji, i będzie uparcie zaczynał kolejne linie od plusa tak długo, jak długo nie skończymy bloku instrukcji poprzez zamknięcie nawiasu:instrukcją warunkową
> if (TRUE) { + + + + + + + # alez uparty z tymi plusami + + + } NULL
W tym przypadku warunek podany if-owi był na pewno spełniony - w końcu TRUE zawsze jest warte TRUE, ale wewnątrz ifa nie było żadnego polecenia, tylko jeden komentarz, więc wynikiem działania tego ifa okazał się NULL - nic.
W pierwszym przykładzie polecenie było tylko jedno, i tak naprawdę moglibyśmy sobie darować te nawiasy klamrowe, tak też by zadziałało:
ale kiedy poleceń jest więcej, R sam się przecież nie domyśli, które należą jeszcze do ifa, a które są po ifie i ma wykonać niezależnie od podanego ifowi warunku; musimy mu o tym powiedzieć otaczając nawiasami klamrowymi polecenia, które mają należeć do ifa. Spójrzmy na poniższy przykład, gdzie ifowi podajemy kolejny idiotyczny warunek, tym razem zawsze fałszywy:
Może rzuciło Ci się w oczy, że stawiam spacje przed poleceniami należącymi do ifa. Nie jest to konieczne, ale ułatwia to zdecydowanie czytanie programu; zwłaszcza, kiedy więcej ifów i innych konstrukcji zagnieździ się w sobie. Dzięki robieniu wcięć wiadomo, że wszystkie polecenia, które znajdują się w tym samym pionie należą do tego samego bloku. Wiele edytorów tekstowych (np. RStudio) automatycznie robią wcięcia po linijkach z ifem czy innymi instrukcjami, po których wcięcia by się przydały.
Oprócz "if" pożytecznym słowem kluczem jest "else", czyli "w przeciwnym wypadku":
R sprawdzi wartość wyrażenia w nawiasie, tj. sprawdzi, czy ryzyko_powodzi jest większe niż ryzyko_wybuchu_wulkanu; jeśli wyjdzie mu TRUE, wykona to, co jest w ifie, czyli wydrukuje "zamieszkajmy w islandii", a w przeciwnym wypadku (jeśli wyjdzie mu FALSE): wykona to, co jest w bloku komend po "else", czyli wydrukuje "zamieszkajmy nad morzem".
Ważna uwaga. Dopóki R wie, że nie skończyliśmy pisać instrukcji, możemy wciskać sobie enter ile razy nam się żywnie podoba; ale jeżeli zamkniemy nawias klamrowy po ifie i naciśniemy enter, R pomyśli, że skończyliśmy już, sprawdzi warunek w ifie i - zależnie od tego czy wyszedł mu prawdziwy czy fałszywy - wykona polecenia w nawiasach klamrowych lub nie. R nie ma pojęcia, czy chemy potem dopisać jakiegoś else'a. Dlatego jeśli piszemy else'a, musimy to zrobić w tej samej linii, w której znajduje się zamykający nawias klamrowy. Za tym jednym, dosyć zresztą logicznym, wyjątkiem entery w ifie możemy pacać gdzie nam się podoba:
No i tu pora przyznać, że właściwie pisząc "entery możemy pacać gdzie nam się podoba" mam w szczególności na myśli, że nie musimy ich pacać wcale. I to nie tylko w ifie, w ogóle wszędzie. Oczywiście R musi wiedzieć, że skończyliśmy jedno polecenie i zaczynamy następne, ale zamiast oddzielać enterem możemy je oddzielić średnikiem. Zdecydowanie tego nie polecam, bo wygląda zupełnie nieczytelnie, zresztą podobnie jak wciskanie enterów rozdzielających polecenia, jak wyżej. Zresztą, oceńcie sami:
No nie najlepiej to wygląda...
Ciekawym poleceniem jest ifelse(), który działa następująco: jako warunek dostaje wektor wartości TRUE / FALSE, a jako wynik zwraca wektor tej samej długości, z elementami wyliczonymi za pomocą jednego z dwóch podanych poleceń, pierwszego - gdy na danym miejscu było TRUE, lub drugiego w przeciwnym wypadku. Na przykład:
Trochę bardziej wyrafinowany przykład:
Na dwóch pierwszych miejscach mamy elementy wektora nazwanego wektor powiększone o dwa, na ostatnim - ostatni element zmniejszony o jeden. I jeszcze bardziej wyrafinowany przykład, bo tym razem warunku nie dostajemy na tacy, a obliczamy:
> ( wektor = 1:9 ) [1] 1 2 3 4 5 6 7 8 9 > ifelse( wektor %% 2 == 1, 0, wektor / 2 ) [1] 0 1 0 2 0 3 0 4 0
Warunkiem tu było wyrażenie "wektor %% 2 == 1", które zwróciło wektor wartości FALSE lub TRUE (zależnie od tego, czy liczba była parzysta, czy nie; przypominam, że %% oznacza resztę z dzielenia, a więc element wektora daje resztę z dzielenia przez 2 równą 1 gdy jest nieparzysty). Następnie zwraca wektor tej samej długości, który dla pozycji, gdzie były liczby nieparzyste ma zero, a dla pozostałych: tą liczbę podzieloną przez dwa.
Wyposażony w tę wiedzę możesz już spróbować zmierzyć się z zadaniem Operacje na plikach lub Instrukcje warunkowe.