4 Wyrażenia warunkowe

Języki programowania opierają się o dwa podstawowe narzędzia pozwalające na sterowanie przepływem operacji. Są to wyrażenia warunkowe oraz pętle. Wyrażenia warunkowe są głównym tematem tego rozdziału, natomiast pętle oraz ich alternatywy są omówione w rozdziale 8. Celem wyrażeń warunkowych jest wykonywanie różnego zadania w zależności od danych wejściowych.

4.1 Warunki

Wyrażenie if opiera się o spełnienie (lub niespełnienie) danego warunku. Jeżeli dany warunek jest spełniony, kod wewnątrz wyrażenia if() jest wykonywany. W przeciwnym razie cały blok kodu jest pomijany.

if (warunek){
  jeżeli warunek spełniony to wykonaj operację
}

Wyrażenie if oczekuje, że warunek jest wektorem logicznym o długości jeden, tj. takim który przyjmuje wartość TRUE lub FALSE. Istnieje szereg sposób uzyskania wektora logicznego w R, jednym z nich jest zastosowanie porównania wartości.

W poniższym przykładzie wyrażenie if() sprawdza czy wartość obiektu temperatura jest wyższa niż 0. W przypadku, gdy ten warunek jest spełniony (czyli przyjmuje TRUE), wyświetlany jest tekst "Dodatnia".

temperatura = 5.4
if (temperatura > 0) {
  "Dodatnia"
}
#> [1] "Dodatnia"

W przeciwnym razie, gdy warunek nie jest spełniony (czyli ma wartość FALSE), kod wewnątrz warunku nie jest wykonywany.

temperatura = -11
if (temperatura > 0) {
  "Dodatnia"
}

Warunek if można też tworzyć w uproszczonej formie:

if (warunek) spelniony else niespelniony

4.2 Warunki zagnieżdżone

Działanie wyrażenia if może być połączone z dodatkowymi wyrażeniami else if oraz else. Te dwa wyrażenia wymagają najpierw wywołania wyrażenia if(). Jeżeli warunek w wyrażeniu if() jest równy TRUE to wykonywany jest kod w nim zawarty, a następnie obliczenie jest kończone. W przypadku, gdy wyrażenie if() otrzyma wartość FALSE, to kod w nim zawarty nie jest wykonywany, a następuje przejście do kolejnego wyrażenia, np. else if() w poniższym przypadku.

temperatura = 8.8
if (temperatura > 0) {
  "Dodatnia"
} else if (temperatura < 0) {
  "Ujemna"
} else {
  "Zero"
}
#> [1] "Dodatnia"

Wyrażenie else if() różni się od else tym, że wymaga ono określenia jaki warunek ma być spełniony. W przypadku else wyliczane są wszystkie przypadki, które nie spełniają wcześniejszych warunków.

4.3 Operatory porównania

W tabeli 4.1 można znaleźć listę podstawowych operatorów porównania. Ich celem jest sprawdzanie pewnego warunku i zwrócenie wartości TRUE lub FALSE.

Tabela 4.1: Operatory porównania.
Operator Wyjaśnienie
== Równy
!= Nie równy
%in% Zawiera się w
>, < Większy/Mniejszy niż
>=, <= Większy/Mniejszy niż lub równy

Wyrażenie if() oczekuje wektora logicznego o długości jeden. Często jednak efektem porównania może być wektor o większej długości. Przykładowo, porównanie operatorem == daje w wyniku wektor o długości trzy, a porównanie z użyciem %in% skutkuje wektorem o długości jeden.

x = 1
y = c(1, 2, 3)
x == y
#> [1]  TRUE FALSE FALSE
x %in% y
#> [1] TRUE

Sterowanie tym, żeby uzyskany wynik miał oczekiwaną długość jeden może się odbywać też z pomocą operatorów logicznych i funkcji pomocniczych (tabela 4.2).

Tabela 4.2: Operatory logiczne i funkcje pomocniczne.
Operator Wyjaśnienie
! Negacja (nie)
&& Koniunkcja (i)
|| Alternatywa (lub)
all Wszystkie
any Którykolwiek

Pozwalają one na sprawdzenie czy wszystkie (all()) lub którykolwiek (any()) z elementów obiektu przyjmuje wartość TRUE.

x = 1
y = c(1, 2, 3)
all(x == y)
#> [1] FALSE
any(x == y)
#> [1] TRUE

Możliwe jest też łączenie bardziej złożonych zapytań używając operatora “i” (&&) oraz operatora “lub” (||).

x = 1
y = c(1, 2, 3)
z = 4
(x %in% y) || !(z %in% y)
#> [1] TRUE

Powyżej nastąpiło sprawdzenie czy element z obiektu x znajduje się w obiekcie y, a następnie czy element z obiektu z nie znajduje się w obiekcie y. Po wykonaniu ich sprawdzeń nastąpiło ich połączenie używając operatora ||, który daje wartość TRUE, gdy chociaż jedno z zapytań jest prawdziwe.

W R istnieją dwa dodatkowe operatory logiczne & i |, które są zwektoryzowaną wersją operatorów && i ||. Pierwsze dwa porównują wszystkie elementy zadanych wektorów i ich wynikiem może być wektor o długości większej niż 1. Operatory && i || porównują tylko pierwszy element każdego wektora, a w efekcie zawsze zwracają tylko jedną wartość. Dodatkowo, to one są zazwyczaj używane w wyrażeniach warunkowych.

4.4 Wyrażenia warunkowe w funkcjach

Wyrażenia warunkowe są często używanym elementem przy tworzeniu funkcji. Pozwalają one na nie tylko na określanie tego w jaki sposób dana funkcja zadziała, ale też pełnią rolę w sprawdzaniu czy do funkcji zostały wprowadzone poprawne argumenty.

Celem poniższej funkcji pogoda() jest wyświetlenie pewnego tekstu w zależności od podanej wartości argumentu temperatura. Pierwszym warunkiem, który można sprawdzić jest określenie czy użytkownik wprowadził do funkcji w postaci argumentu oczekiwany typ danych (więcej o typach danych można dowiedzieć się w rozdziale 5). W tym przypadku typ numeryczny jest oczekiwany, co można sprawdzić używając funkcji is.numeric(), która zwraca TRUE dla danych numerycznych i FALSE dla każdych innych.

pogoda = function(temperatura){
  if (is.numeric(temperatura)){
    cat(paste("Dzisiaj jest", temperatura, "stopni Celsjusza."))
  }
}
pogoda(10)
#> Dzisiaj jest 10 stopni Celsjusza.
pogoda(-20)
#> Dzisiaj jest -20 stopni Celsjusza.
pogoda("nie wiem")

Efekt działania powyższej funkcji jest teraz zależy od wejściowego typu danych - jeżeli podana jest wartość numeryczna zwracany jest tekst, a jeżeli ten warunek nie jest spełniony to nic się nie dzieje.

Warto, aby tworzona funkcja obsługiwała najczęściej potencjalnie używane rodzaje danych wejściowych. W tym przypadku, warto dodać wyrażenie else, którego efektem jest kolejny tekst sugerujący, że funkcja została wykonana, ale w inny sposób.

pogoda = function(temperatura){
  if (is.numeric(temperatura)){
    cat(paste("Dzisiaj jest", temperatura, "stopni Celsjusza."))
  } else {
    cat("Dzisiaj nie mamy pomiarów temperatury.")
  }
}
pogoda(10)
#> Dzisiaj jest 10 stopni Celsjusza.
pogoda(-20)
#> Dzisiaj jest -20 stopni Celsjusza.
pogoda("nie wiem")
#> Dzisiaj nie mamy pomiarów temperatury.

Wyrażenia warunkowe można też wielokrotnie zagnieżdżać wewnątrz zdefiniowanej funkcji.

pogoda = function(temperatura){
  if (is.numeric(temperatura)){
    cat(paste("Dzisiaj jest", temperatura, "stopni Celsjusza.\n"))
    if (temperatura < 5){
      cat("Ubierz się ciepło!")
    }
  } else {
    cat("Dzisiaj nie mamy pomiarów temperatury.")
  }
}
pogoda(10)
#> Dzisiaj jest 10 stopni Celsjusza.
pogoda(-20)
#> Dzisiaj jest -20 stopni Celsjusza.
#> Ubierz się ciepło!
pogoda("nie wiem")
#> Dzisiaj nie mamy pomiarów temperatury.

Przykładowo, powyżej komunikat "Ubierz się ciepło!" jest wyświetlany w momencie, gdy spełnione zostaną dwa warunki - najpierw wejściowy obiekt temperatura musi być typu numerycznego, a następnie wartość tego obiektu musi być niższa niż 5.

4.5 Zadania

  1. Spójrz na poniższe przykłady, ale ich nie wykonuj. Co będzie wynikiem działania każdego z tych przykładów?
liczby = c(1, 2)
liczby == 1          #1
liczby != 1          #2
liczby %in% 1        #3
all(liczby %in% 1)   #4
any(liczby %in% 1)   #5
  1. Spójrz na cztery poniższe przykłady, ale ich nie wykonuj. Co będzie wynikiem działania każdego z tych przykładów?
(c(1, 2) > 0) &  (c(-1, 2) > 0) #1
(c(1, 2) > 0) && (c(-1, 2) > 0) #2
(c(1, 2) > 0) |  (c(-1, 2) > 0) #3
(c(1, 2) > 0) || (c(-1, 2) > 0) #4
  1. Napisz funkcję, która przyjmuje trzy zmienne logiczne x, y i z. Jeżeli tylko jedna lub trzy ze zmiennych ma wartość TRUE wyświetl tekst "Nieparzysta liczba.", natomiast jeżeli dwie zmienne mają wartość TRUE wyświetl tekst "Parzysta liczba."
  2. Napisz funkcję, która przyjmuje dwie zmienne numeryczne x i y. Jeżeli wszystkie wartości zmiennej x są większe od y wyświetl tekst "Zwycięstwo.", a w przeciwnym razie wyświetl tekst "Porażka.".
  3. Napisz funkcję, która przyjmuje dwie zmienne numeryczne populacja i powierzchnia. Jeżeli wartości gęstości zaludnienia (liczba osób na jednostkę powierzchni) jest wyższa niż 123 wyświetl tekst "Wartość powyżej średniej dla Polski.".